Skip to content

Troubleshooting

Learn how to fix common issues with the PayPal component on iOS.

Common issues and solutions

IssueDescriptionNext steps
PayPal SDK not loadingThe Console shows PayPal SDK loading errors. This could be due to network issues, incorrect configuration, or SSL/TLS errors.|
  • Check your internet connection.
  • Verify that session configuration is correct.
  • Ensure proper SSL/TLS certificates.
  • Check the Console logs for detailed errors.
  • Verify SDK is properly initialised.
Button not renderingThe PayPal button doesn't appear in the SwiftUI view.
  • Verify that the component is properly created and rendered.
  • Check that the SwiftUI view provides adequate space (.frame(height:)).
  • Ensure that the component is in the view hierarchy.
  • Check the Console for initialisation errors.
  • Verify that the SDK is initialised before creating components.
Validation errorsYou receive errors via the onSubmitError callback.
  • Check that all required fields are provided.
  • Verify the email format for payeeEmailAddress.
  • Ensure that the currency codes match the transaction currency.
  • Validate all configuration parameters.
  • Check the Console for detailed validation messages.
Funding source issuesCertain funding sources not available.
  • Verify funding sources are supported: .paypal, .paylater.
  • Check geographic restrictions: Pay Later availability varies by region.
  • Verify PayPal account supports requested funding sources.
  • Ensure proper fundingSource configuration.
Network connectivityThe SDK fails to load and there are timeout errors.
  • Check network reachability using NWPathMonitor.
  • Handle network errors in onError callback.
  • Show user-friendly message: "No internet connection".
  • Implement retry mechanism.
  • Check the Console for network-related errors.
SSL/TLS certificate errorsPayment flow fails to load with SSL errors. This could be due to expired certificates or incorrect device time.
  • **Never ignore SSL errors in production**
  • Ensure device date/time is correct.
  • Check certificate validity.
  • Test on physical device (simulator may behave differently).
  • For debug only: Log SSL errors in Console.
Memory issuesThe app crashes or becomes unresponsive during payment.
  • Monitor memory using Xcode Instruments.
  • Check for memory leaks with Instruments' Leaks tool.
  • Ensure components are properly released.
  • Profile memory usage during payment flow.
  • Check for retain cycles in closures.
SwiftUI state issuesComponent state is lost or doesn't update.
  • Use @State for component references.
  • Store SDK instance in appropriate scope.
  • Use @StateObject or @ObservedObject if needed.
  • Ensure state updates on @MainActor.
  • Check for proper state management in parent views.
Event callbacks not firingonApprove, onError, or other callbacks aren't called.
  • Verify callbacks are defined in configuration.
  • Check Console for error messages.
  • Ensure callbacks are not nil.
  • Add logging to all callbacks to verify execution.
  • Check SDK initialisation completed successfully.
Payment authorisation failsThe payment starts but fails to complete. For example, due to insufficient PayPal funds, declined payment method, or fraud detection.
  • User should check PayPal account status.
  • Verify payment method is valid and active.
  • Check transaction amount is within limits.
  • Review PayPal dashboard for declined reasons.
  • Implement proper error handling in onError.
Button styling not appliedThe button doesn't match the expected style.
  • Verify all PayPalButtonStyleConfig parameters are valid.
  • Check color values: .gold, .blue, .silver, .white, .black, .darkBlue.
  • Check size values: .mini, .collapsed, .expanded, .full.
  • Check edges values: .hardEdges, .softEdges, .rounded.
  • Ensure contentInsets are properly configured.
Locale/language issuesThe button is displayed in the wrong language.
  • Set locale explicitly: config.locale = "en_US"
  • Verify locale format is correct (language_COUNTRY).
  • Check device locale settings.
  • Ensure locale is supported by PayPal.
Consent component not workingThe consent checkbox doesn't affect payment.
  • Verify paypalConsentComponent is passed to PayPal config.
  • Implement onGetConsent callback correctly.
  • Check component is created before PayPal component.
  • Ensure getValue() returns Bool.
  • Test consent state changes in Console.
Custom content not displayingCustom SwiftUI content for button not showing.
  • Verify customContent closure returns valid AnyView.
  • Check SwiftUI view has proper modifiers.
  • Ensure view is not nil or empty.
  • Test custom content in isolation first.
  • Check Console for SwiftUI warnings.
Simulator vs device differencesPayment works in simulator but fails on device (or vice versa).
  • Always test on physical devices for production validation.
  • Check for simulator-specific configurations.
  • Verify code signing and entitlements.
  • Check network conditions on device.
  • Review device Console logs.
App Store rejectionApp rejected due to PayPal integration issues.
  • Ensure all required privacy descriptions in Info.plist.
  • Verify network usage descriptions if needed.
  • Test in sandbox environment thoroughly.
  • Provide test accounts for App Review.
  • Document PayPal integration in review notes.

Logging and debugging

To diagnose issues, enable comprehensive logging.

Console logging

Use Console.app or Xcode's console to view logs:

let config = PayPalButtonComponentConfig()

// Add verbose logging to callbacks
config.onClick = {
    print("PayPal button clicked")
}

config.onApprove = { approvalData in
    print("✓ Payment successful")
    print("Order ID: \(approvalData.orderID)")
    print("Payer ID: \(approvalData.payerID)")
}

config.onError = { error in
    print("✗ Payment error: \(error.errorMessage)")
    print("Error code: \(error.errorCode)")
}

config.onCancel = { error in
    print("⚠ Payment cancelled")
    print("Cancellation reason: \(error.errorMessage)")
}

config.onOrderCreated = { submitResult in
    print("📦 Order created")
    print("Merchant TX ID: \(submitResult.merchantTransactionId)")
    print("System TX ID: \(submitResult.systemTransactionId)")
}

Console filters

Use Console.app filters to view specific logs:

# PXP Checkout logs
subsystem:com.pxp.checkout

# PayPal-specific logs
process:YourAppName AND paypal

# All SDK logs
category:PXPCheckoutSDK

Xcode debugging

Enable detailed logging in Xcode:

  1. Open your scheme by going to Product > Scheme > Edit Scheme.
  2. Select Run > Arguments.
  3. Add these environment variables:
    • OS_ACTIVITY_MODE = disable (for cleaner logs)
    • IDEPreferLogStreaming = YES

Useful code snippets

Check network reachability

Use this network monitoring implementation before initiating payment flows or when diagnosing network-related errors. The PayPal SDK requires an active internet connection, and this snippet uses Apple's Network framework to provide real-time network status updates. Call this before creating PayPal components or implement continuous monitoring throughout your app's lifecycle.

import Network

class NetworkMonitor {
    static let shared = NetworkMonitor()
    private let monitor = NWPathMonitor()
    private let queue = DispatchQueue(label: "NetworkMonitor")
    
    var isConnected: Bool = false
    
    func startMonitoring() {
        monitor.pathUpdateHandler = { path in
            self.isConnected = path.status == .satisfied
            
            if !self.isConnected {
                print("⚠ Network disconnected")
            } else {
                print("✓ Network connected")
            }
        }
        
        monitor.start(queue: queue)
    }
    
    func stopMonitoring() {
        monitor.cancel()
    }
}

// Usage example:
NetworkMonitor.shared.startMonitoring()

if !NetworkMonitor.shared.isConnected {
    showAlert(title: "No Connection", 
             message: "Please check your internet connection and try again.")
    return
}

// Proceed with PayPal SDK initialisation

Monitor memory usage

Monitor memory usage during performance testing or when diagnosing crashes to identify leaks or excessive usage. This function reports current memory usage and helps detect potential memory issues. For comprehensive memory profiling, use Xcode Instruments alongside this logging approach.

func logMemoryUsage() {
    var taskInfo = mach_task_basic_info()
    var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size) / 4
    
    let result = withUnsafeMutablePointer(to: &taskInfo) {
        $0.withMemoryRebound(to: integer_t.self, capacity: 1) {
            task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
        }
    }
    
    if result == KERN_SUCCESS {
        let usedMemory = Double(taskInfo.resident_size) / 1024 / 1024
        print("Memory usage: \(String(format: "%.2f", usedMemory)) MB")
        
        if usedMemory > 200 {
            print("⚠ High memory usage detected")
        }
    }
}

// Usage example:
logMemoryUsage()
// Create PayPal component
logMemoryUsage() // Check memory after creation

Validate configuration

Validate your PayPal configuration before creating components to catch errors early. This validation function checks required fields and ensures configuration consistency. Call it after building your configuration object but before attempting to create the component.

struct ValidationResult {
    let isValid: Bool
    let errors: [String]
}

func validatePayPalConfig(_ config: PayPalButtonComponentConfig) -> ValidationResult {
    var errors: [String] = []
    
    if config.payeeEmailAddress != nil && !isValidEmail(config.payeeEmailAddress!) {
        errors.append("payeeEmailAddress format is invalid")
    }
    
    if config.fundingSource == nil {
        errors.append("fundingSource is required")
    }
    
    return ValidationResult(
        isValid: errors.isEmpty,
        errors: errors
    )
}

func isValidEmail(_ email: String) -> Bool {
    let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
    let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)
    return emailPredicate.evaluate(with: email)
}

// Usage example:
let config = PayPalButtonComponentConfig()
config.fundingSource = .paypal
config.payeeEmailAddress = "merchant@example.com"

let validation = validatePayPalConfig(config)
if !validation.isValid {
    print("❌ Configuration errors: \(validation.errors.joined(separator: ", "))")
    return
}

// Proceed with component creation

Check device capabilities

Verify device capabilities before initialising PayPal to ensure the device meets minimum requirements. This function checks iOS version, network connectivity, and available memory to confirm the device can support PayPal payments. Call it during app launch or before initiating payment flows.

func checkDeviceCapabilities() -> (canProceed: Bool, issues: [String]) {
    var issues: [String] = []
    
    // Check iOS version
    if #unavailable(iOS 13.0) {
        issues.append("iOS 13.0 or later required")
    }
    
    // Check network capability
    if !NetworkMonitor.shared.isConnected {
        issues.append("Network connection required")
    }
    
    // Check memory (simplified check)
    let physicalMemory = ProcessInfo.processInfo.physicalMemory
    let memoryGB = Double(physicalMemory) / 1024 / 1024 / 1024
    if memoryGB < 1.0 {
        issues.append("Low device memory may cause issues")
    }
    
    return (issues.isEmpty, issues)
}

// Usage example:
let (canProceed, issues) = checkDeviceCapabilities()
if !canProceed {
    print("⚠ Device capability issues: \(issues.joined(separator: ", "))")
    showAlert(title: "Device requirements", 
             message: "This device may not support PayPal payments: \(issues.joined(separator: ", "))")
}

Debug SwiftUI view hierarchy

Use these debugging extensions when components don't appear or layout issues occur. The extensions help identify view hierarchy and layout problems by adding visual borders and console logging. Add them to parent views during debugging to understand rendering and layout behaviour.

extension View {
    func debugPrint(_ message: String) -> some View {
        print("🔍 \(message)")
        return self
    }
    
    func debugBorder(_ color: Color = .red) -> some View {
        self.border(color, width: 2)
    }
}

// Usage example:
struct PaymentView: View {
    var body: some View {
        VStack {
            if let component = paypalComponent {
                component.buildContent()
                    .frame(height: 50)
                    .debugBorder(.red) // Visualise button area
                    .debugPrint("PayPal button rendered")
            } else {
                Text("Loading...")
                    .debugPrint("Still loading component")
            }
        }
        .debugBorder(.blue) // Visualise container
    }
}

Handle async initialisation errors

Implement proper error handling when SDK or component initialisation might fail. This pattern captures detailed error information and provides appropriate user feedback while logging to analytics. Use this approach in async component creation flows to ensure graceful failure handling.

@MainActor
func createPayPalComponent() async {
    do {
        let pxpCheckout = try PxpCheckout.initialize(config: checkoutConfig)
        
        let config = PayPalButtonComponentConfig()
        config.fundingSource = .paypal
        
        let component = try pxpCheckout.create(
            .paypalButton,
            componentConfig: config
        )
        
        self.paypalComponent = component
        self.isLoading = false
        
    } catch let error as NSError {
        print("❌ Initialisation error: \(error.localizedDescription)")
        print("Error domain: \(error.domain)")
        print("Error code: \(error.code)")
        print("Error userInfo: \(error.userInfo)")
        
        self.errorMessage = "Failed to initialise PayPal: \(error.localizedDescription)"
        self.isLoading = false
        
        // Track error for analytics
        logError(error)
    }
}

func logError(_ error: Error) {
    // Log to your analytics/crash reporting service
    print("📊 Logging error to analytics")
}

Testing best practices

Test in sandbox environment

Always test thoroughly in sandbox before production:

let config = PayPalButtonComponentConfig()
config.fundingSource = .paypal

// Configure test callbacks
config.onApprove = { approvalData in
    print("✓ SANDBOX: Payment approved: \(approvalData.orderID)")
    // Test your success flow
}

config.onError = { error in
    print("❌ SANDBOX: Error: \(error.errorMessage)")
    // Test your error handling
}

Test on multiple devices

Test on various iOS devices and versions:

  • iPhone (various models).
  • iPad (if supported).
  • Different iOS versions.
  • Different network conditions (WiFi, cellular, airplane mode).
  • Different regions/locales.

Test error scenarios

Deliberately test error conditions:

  • Network interruptions. Use the Network Link Conditioner.
  • Insufficient funds. Use sandbox test accounts.
  • Cancelled payments.
  • Timeout scenarios.
  • Invalid session configurations.