{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["sub-heading","admonition"]},"type":"markdown"},"seo":{"title":"Troubleshooting","description":"Transform your commerce with PXP's unified platform—seamless payments, real-time insights, and global growth in one powerful integration.","lang":"en-UK","siteUrl":"https://developer.pxp.io","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"troubleshooting","__idx":0},"children":["Troubleshooting"]},{"$$mdtype":"Tag","name":"SubHeading","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Learn how to diagnose and fix common issues with Checkout Drop-in."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"quick-diagnostics","__idx":1},"children":["Quick diagnostics"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you're experiencing issues with Drop-in, start with these quick diagnostic checks:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import PXPCheckoutSDK\nimport UIKit\nimport Network\n\n// Drop-in diagnostic helper\nfunc diagnoseDropIn(\n    sessionData: SessionData?,\n    config: CheckoutDropInConfig\n) {\n    print(\"=== Checkout Drop-in diagnostics ===\")\n    \n    // Check SDK import\n    print(\"SDK imported: \\(CheckoutDropIn.self)\")\n    \n    // Check session data\n    print(\"Session ID: \\(sessionData?.sessionId != nil ? \"Present\" : \"Missing\")\")\n    print(\"HMAC key: \\(sessionData?.hmacKey != nil ? \"Present\" : \"Missing\")\")\n    print(\"Encryption key: \\(sessionData?.encryptionKey != nil ? \"Present\" : \"Missing\")\")\n    if let sessionExpiry = sessionData?.sessionExpiry {\n        print(\"Session expiry: \\(sessionExpiry)\")\n    }\n    if let restrictions = sessionData?.restrictions {\n        print(\"Restrictions: \\(String(describing: restrictions))\")\n    }\n    print(\"Allowed funding types: \\(String(describing: sessionData?.allowedFundingTypes))\")\n    \n    // Check environment\n    print(\"Environment: \\(config.environment)\")\n    print(\"Owner ID: \\(config.ownerId)\")\n    print(\"Merchant Shopper ID: \\(config.merchantShopperId)\")\n    \n    // Check device info\n    print(\"iOS version: \\(UIDevice.current.systemVersion)\")\n    print(\"Device: \\(UIDevice.current.model)\")\n    print(\"Screen scale: \\(UIScreen.main.scale)\")\n    \n    // Check network connectivity\n    let monitor = NWPathMonitor()\n    let queue = DispatchQueue(label: \"NetworkMonitor\")\n    monitor.pathUpdateHandler = { path in\n        print(\"Network available: \\(path.status == .satisfied)\")\n    }\n    monitor.start(queue: queue)\n    \n    print(\"===================================\")\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"common-issues","__idx":2},"children":["Common issues"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"drop-in-not-rendering","__idx":3},"children":["Drop-in not rendering"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Symptoms:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The component doesn't appear in your SwiftUI view."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["No payment methods are visible."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["There are no errors in the console."]}]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Cause"},"children":["Cause"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Solution"},"children":["Solution"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["create()"]}," not completed."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Always ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["await dropIn.create()"]}," before rendering ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["buildContent()"]},". Until ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["create()"]}," finishes, the SDK returns ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["EmptyView()"]}," with no thrown error. If ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["getCheckoutDropInConfig"]}," fails, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["create()"]}," returns early and fires ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onError"]}," (for example ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1100"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0204"]},") — without a wired ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onError"]}," handler, failures look like a blank drop-in with no console error."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["SwiftUI view update issue."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ensure the session data is loaded before rendering ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CheckoutDropIn"]},". Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".task"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onAppear"]}," to fetch session data and conditional rendering."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Session data invalid."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verify that the session data includes ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sessionId"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["hmacKey"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["allowedFundingTypes"]},". Check the backend session creation logs."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No payment methods enabled."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Check ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["allowedFundingTypes"]}," in your session. At least one payment method must be enabled in the Unity Portal."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Incorrect configuration."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verify ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["environment"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ownerId"]}," are set correctly."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["SDK not properly linked."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verify that the Swift Package Manager dependency is properly resolved in Xcode."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Diagnostic steps:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import SwiftUI\nimport PXPCheckoutSDK\n\nstruct DiagnoseRenderingIssueView: View {\n    @StateObject private var viewModel = DiagnosticViewModel()\n    \n    var body: some View {\n        ScrollView {\n            VStack(alignment: .leading, spacing: 8) {\n                Text(viewModel.diagnostics)\n                    .font(.system(.caption, design: .monospaced))\n                \n                if let dropIn = viewModel.dropIn {\n                    dropIn.buildContent()\n                } else {\n                    ProgressView(\"Loading...\")\n                }\n            }\n            .padding()\n        }\n        .task {\n            await viewModel.diagnose()\n        }\n    }\n}\n\n@MainActor\nfinal class DiagnosticViewModel: ObservableObject {\n    @Published var dropIn: CheckoutDropIn?\n    @Published var diagnostics: String = \"\"\n    \n    func diagnose() async {\n        diagnostics += \"Step 1: Fetching session...\\n\"\n        \n        do {\n            let sessionData = try await fetchSessionFromBackend()\n            diagnostics += \"Step 2: Session fetched successfully\\n\"\n            diagnostics += \"Session ID: \\(sessionData.sessionId)\\n\"\n            \n            // Step 3: Validate session data\n            diagnostics += \"Step 3: Validating session data...\\n\"\n            \n            if sessionData.sessionId.isEmpty {\n                diagnostics += \"ERROR: Session ID missing\\n\"\n            } else {\n                diagnostics += \"Session ID present\\n\"\n            }\n            \n            if sessionData.allowedFundingTypes == nil {\n                diagnostics += \"ERROR: No allowed funding types\\n\"\n            } else {\n                var paymentMethodCount = 0\n                \n                // Cards: must not be nil\n                if sessionData.allowedFundingTypes?.cards != nil {\n                    paymentMethodCount += 1\n                }\n                \n                // PayPal: must have allowedFundingOptions present and non-empty\n                if let paypalConfig = sessionData.allowedFundingTypes?.wallets?.paypal,\n                   let allowedFundingOptions = paypalConfig.allowedFundingOptions,\n                   !allowedFundingOptions.isEmpty {\n                    paymentMethodCount += 1\n                }\n                \n                // Apple Pay: must have merchantId present and non-empty\n                if let applePayConfig = sessionData.allowedFundingTypes?.wallets?.applePay,\n                   let merchantId = applePayConfig.merchantId,\n                   !merchantId.isEmpty {\n                    paymentMethodCount += 1\n                }\n                \n                if paymentMethodCount == 0 {\n                    diagnostics += \"ERROR: No payment methods enabled (SDK0204)\\n\"\n                } else {\n                    diagnostics += \"\\(paymentMethodCount) payment method(s) available\\n\"\n                }\n            }\n            \n            // Initialise Drop-in\n            let config = CheckoutDropInConfig(\n                environment: .test,\n                session: sessionData,\n                transactionData: DropInTransactionData(\n                    amount: Decimal(string: \"1.00\") ?? 0,\n                    currency: \"USD\",\n                    entryType: .ecom,\n                    intent: DropInTransactionIntentData(\n                        card: .authorisation\n                    ),\n                    merchantTransactionId: \"test-\\(Date().timeIntervalSince1970)\",\n                    merchantTransactionDate: { Date() }\n                ),\n                merchantShopperId: \"shopper-123\",\n                ownerId: \"MERCHANT-1\",\n                onSuccess: { result in\n                    print(\"Success: \\(result.systemTransactionId)\")\n                },\n                onError: { paymentMethod, error in\n                    print(\"Error: \\(error.errorMessage)\")\n                }\n            )\n            \n            let instance = try CheckoutDropIn(config: config)\n            await instance.create()\n            dropIn = instance\n            \n        } catch {\n            diagnostics += \"ERROR: \\(error.localizedDescription)\\n\"\n            print(\"Diagnostic error: \\(error)\")\n        }\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"session-expired-errors","__idx":4},"children":["Session expired errors"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Symptoms:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A \"Session expired\" error message is displayed."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The drop-in loads but payment fails immediately."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Console shows session timeout errors."]}]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Cause"},"children":["Cause"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Solution"},"children":["Solution"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Session has expired."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Sessions expire based on your backend configuration. Check the optional ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sessionExpiry"]}," field returned by your backend when creating the session. Create a new session when needed."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Clock skew"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ensure that the server and device clocks are synchronised."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Session reused"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Treat sessions as checkout-attempt scoped unless your backend explicitly supports reuse. Always fetch a fresh session for new checkout attempts."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Invalid HMAC signature"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verify that the HMAC key matches between session creation and SDK initialisation."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Solution: Implement session refresh"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import SwiftUI\nimport PXPCheckoutSDK\n\nstruct CheckoutViewWithSessionRefresh: View {\n    @StateObject private var viewModel = CheckoutViewModel()\n    \n    var body: some View {\n        Group {\n            if let dropIn = viewModel.dropIn {\n                dropIn.buildContent()\n            } else if let errorMessage = viewModel.errorMessage {\n                VStack {\n                    Text(errorMessage)\n                        .foregroundColor(.red)\n                    \n                    Button(\"Refresh\") {\n                        Task {\n                            await viewModel.refreshSession()\n                        }\n                    }\n                }\n            } else {\n                ProgressView(\"Loading checkout...\")\n            }\n        }\n        .task {\n            await viewModel.loadDropIn()\n        }\n    }\n}\n\n@MainActor\nfinal class CheckoutViewModel: ObservableObject {\n    @Published var dropIn: CheckoutDropIn?\n    @Published var errorMessage: String?\n    \n    func loadDropIn() async {\n        do {\n            let sessionData = try await fetchSessionFromBackend()\n            try await initializeDropIn(with: sessionData)\n        } catch {\n            print(\"Failed to fetch session: \\(error)\")\n            errorMessage = \"Failed to load checkout. Please try again.\"\n        }\n    }\n    \n    func refreshSession() async {\n        errorMessage = nil\n        dropIn = nil\n        await loadDropIn()\n    }\n    \n    private func initializeDropIn(with sessionData: SessionData) async throws {\n        let config = CheckoutDropInConfig(\n            environment: .live,\n            session: sessionData,\n            transactionData: DropInTransactionData(\n                amount: Decimal(string: \"99.99\") ?? 0,\n                currency: \"USD\",\n                entryType: .ecom,\n                intent: DropInTransactionIntentData(\n                    card: .authorisation\n                ),\n                merchantTransactionId: UUID().uuidString,\n                merchantTransactionDate: { Date() }\n            ),\n            merchantShopperId: \"shopper-123\",\n            ownerId: \"MERCHANT-1\",\n            onSuccess: { result in\n                Task {\n                    await verifyPaymentOnBackend(result)\n                }\n            },\n            onError: { paymentMethod, error in\n                // Handle session expiry\n                if error.errorMessage.localizedCaseInsensitiveContains(\"expired\") ||\n                   error.errorMessage.localizedCaseInsensitiveContains(\"session\") {\n                    print(\"Session expired, refreshing...\")\n                    \n                    Task { @MainActor in\n                        self.errorMessage = \"Session expired. Please refresh.\"\n                    }\n                } else {\n                    print(\"Payment error: \\(error.errorMessage)\")\n                    Task { @MainActor in\n                        self.errorMessage = \"Payment failed: \\(error.errorMessage)\"\n                    }\n                }\n            }\n        )\n        \n        let instance = try CheckoutDropIn(config: config)\n        await instance.create()\n        dropIn = instance\n    }\n}\n\nfunc fetchSessionFromBackend() async throws -> SessionData {\n    // Implementation to fetch session from your backend\n    // ...\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"payment-method-not-appearing","__idx":5},"children":["Payment method not appearing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Symptoms:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["An expected payment method isn't shown."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Only the card payment method is visible."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Apple Pay doesn't appear even though it's enabled."]}]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Payment method"},"children":["Payment method"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Common causes"},"children":["Common causes"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Solutions"},"children":["Solutions"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Cards"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Cards aren't enabled in the Unity Portal or are missing from the session configuration."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Enable the Card service in the Unity Portal and verify that the session includes ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["allowedFundingTypes.cards"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["PayPal"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["PayPal onboarding wasn't completed or is missing from session configuration."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Complete PayPal onboarding in Unity Portal and verify that the session includes ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["allowedFundingTypes.wallets.paypal"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Apple Pay"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Apple Pay not configured in Xcode, no cards in Apple Wallet, or device doesn't support Apple Pay."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Enable Apple Pay capability in Xcode, add test cards to Wallet, and test on a physical device or simulator with Apple Pay support."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Diagnostic steps:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import PassKit\nimport PXPCheckoutSDK\n\nfunc diagnosePaymentMethodVisibility(\n    sessionData: SessionData?,\n    config: CheckoutDropInConfig\n) {\n    print(\"=== Payment method diagnostics ===\")\n    \n    let fundingTypes = sessionData?.allowedFundingTypes\n    print(\"Session funding types: \\(String(describing: fundingTypes))\")\n    \n    // Check cards - must not be nil\n    if fundingTypes?.cards != nil {\n        print(\"Cards enabled: \\(String(describing: fundingTypes?.cards))\")\n    } else {\n        print(\"WARNING: Cards not enabled in session (allowedFundingTypes.cards must not be nil)\")\n    }\n    \n    // Check PayPal - must have allowedFundingOptions present and non-empty\n    if let paypalConfig = fundingTypes?.wallets?.paypal,\n       let allowedFundingOptions = paypalConfig.allowedFundingOptions,\n       !allowedFundingOptions.isEmpty {\n        print(\"PayPal enabled with funding options: \\(allowedFundingOptions)\")\n    } else {\n        print(\"WARNING: PayPal not enabled (wallets.paypal.allowedFundingOptions must be present and non-empty)\")\n    }\n    \n    // Check Apple Pay - must have merchantId present and non-empty\n    if let applePayConfig = fundingTypes?.wallets?.applePay,\n       let merchantId = applePayConfig.merchantId,\n       !merchantId.isEmpty {\n        print(\"Apple Pay configuration present with merchant ID: \\(merchantId)\")\n        \n        // Check Apple Pay availability\n        if PKPaymentAuthorizationController.canMakePayments() {\n            print(\"Device supports Apple Pay\")\n            \n            // Check if user has cards configured for networks from Drop-in config\n            let networks = config.methodConfig?.global?.acceptedCardNetworks?.compactMap { network -> PKPaymentNetwork? in\n                switch network {\n                case .visa: return .visa\n                case .mastercard: return .masterCard\n                case .amex: return .amex\n                case .discover: return .discover\n                default: return nil\n                }\n            } ?? [.visa, .masterCard, .amex]\n            \n            if PKPaymentAuthorizationController.canMakePayments(usingNetworks: networks) {\n                print(\"Apple Pay has cards configured for accepted networks: \\(networks)\")\n            } else {\n                print(\"WARNING: No cards configured in Apple Pay for accepted networks\")\n            }\n        } else {\n            print(\"WARNING: Device does not support Apple Pay\")\n        }\n    } else {\n        print(\"WARNING: Apple Pay not configured (wallets.applePay.merchantId must be present and non-empty)\")\n    }\n    \n    // Check device info\n    print(\"iOS version: \\(UIDevice.current.systemVersion)\")\n    print(\"Device: \\(UIDevice.current.model)\")\n    \n    print(\"===================================\")\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"apple-pay-not-working","__idx":6},"children":["Apple Pay not working"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Symptoms:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Apple Pay button doesn't appear."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Apple Pay sheet doesn't open when tapped."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Payment fails with Apple Pay errors."]}]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Cause"},"children":["Cause"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Solution"},"children":["Solution"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Apple Pay capability not enabled."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Enable Apple Pay capability in Xcode project settings."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No cards in Apple Wallet."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Add test cards to Apple Wallet manually."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Minimum iOS version not met."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Apple Pay requires iOS 14.0 or higher."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Merchant ID missing or invalid."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Add a valid Apple Pay merchant ID in your Xcode project and Unity Portal."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Device not supported."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Use a physical device for final validation. Simulator is useful for UI checks only but may not fully represent production Apple Pay behaviour."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Solution:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import PassKit\n\nfunc checkApplePayAvailability() {\n    print(\"=== Apple Pay Diagnostics ===\")\n    \n    // Check if device supports Apple Pay\n    if PKPaymentAuthorizationController.canMakePayments() {\n        print(\"✓ Device supports Apple Pay\")\n    } else {\n        print(\"✗ Device does not support Apple Pay\")\n        return\n    }\n    \n    // Check if user has cards configured\n    let networks: [PKPaymentNetwork] = [.visa, .masterCard, .amex]\n    if PKPaymentAuthorizationController.canMakePayments(usingNetworks: networks) {\n        print(\"✓ Apple Pay has cards configured\")\n    } else {\n        print(\"✗ No cards configured in Apple Wallet\")\n        print(\"  Add cards to Wallet to use Apple Pay\")\n    }\n    \n    // Check iOS version\n    if #available(iOS 14.0, *) {\n        print(\"✓ iOS version compatible (14.0+)\")\n    } else {\n        print(\"✗ iOS version too old (requires 14.0+)\")\n    }\n    \n    print(\"=============================\")\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"backend-verification-failing","__idx":7},"children":["Backend verification failing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Symptoms:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Frontend ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSuccess"]}," fires but backend verification fails."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Orders aren't being fulfilled."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["\"Payment verification failed\" errors."]}]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Cause"},"children":["Cause"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Solution"},"children":["Solution"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Webhook not configured."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Set up a webhook URL in the Unity Portal and implement a webhook handler on your backend."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Webhook authentication failing."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verify your webhook signature/authentication. Check your HMAC implementation."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Race condition (GET before webhook)."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Implement a fallback to the Get transaction details API if the webhook hasn't arrived yet."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Amount mismatch."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ensure that the amount in the verification request exactly matches the transaction amount."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Transaction ID mismatch"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verify that the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["systemTransactionId"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["merchantTransactionId"]}," match database records."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Solution: Robust backend verification"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"// Backend webhook handler (Node.js/Express example)\napp.post('/webhooks/pxp', async (req, res) => {\n  try {\n    const events = req.body;\n    \n    // Verify webhook authenticity using HMAC\n    if (!verifyWebhookSignature(req)) {\n      console.error('Invalid webhook signature');\n      return res.status(401).json({ error: 'Unauthorised' });\n    }\n    \n    for (const event of events) {\n      if (event.eventCategory === 'Transaction') {\n        const txn = event.eventData;\n        \n        console.log('Processing transaction:', txn.systemTransactionId);\n        \n        // Idempotency check\n        const existing = await db.transactions.findOne({\n          systemTransactionId: txn.systemTransactionId\n        });\n        \n        if (existing) {\n          console.log('Transaction already processed, skipping');\n          continue;\n        }\n        \n        // Verify transaction state\n        if (txn.state === 'Authorised' || txn.state === 'Captured') {\n          // Find order by merchant transaction ID\n          const order = await db.orders.findOne({\n            merchantTransactionId: txn.merchantTransactionId\n          });\n          \n          if (!order) {\n            console.error('Order not found:', txn.merchantTransactionId);\n            continue;\n          }\n          \n          // Verify amount matches\n          const transactionAmount = txn.amounts?.transactionValue || txn.amount;\n          if (Math.abs(transactionAmount - order.amount) > 0.01) {\n            console.error('Amount mismatch:', {\n              expected: order.amount,\n              actual: transactionAmount\n            });\n            continue;\n          }\n          \n          // Mark transaction as processed\n          await db.transactions.create({\n            systemTransactionId: txn.systemTransactionId,\n            merchantTransactionId: txn.merchantTransactionId,\n            amount: transactionAmount,\n            state: txn.state,\n            processedAt: new Date()\n          });\n          \n          // Fulfill order\n          await fulfillOrder(order.id, txn.systemTransactionId);\n          \n          console.log('Order fulfilled:', order.id);\n        }\n      }\n    }\n    \n    // Always return success\n    res.json({ state: 'Success' });\n  } catch (error) {\n    console.error('Webhook processing error:', error);\n    res.status(500).json({ error: 'Internal server error' });\n  }\n});\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"ios-specific-issues","__idx":8},"children":["iOS-specific issues"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"memory-leaks","__idx":9},"children":["Memory leaks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Symptoms:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["App performance degrades over time."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Xcode Instruments shows growing memory usage."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Out of memory crashes after multiple checkout attempts."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Solution: Proper lifecycle management"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import SwiftUI\nimport PXPCheckoutSDK\n\nstruct CheckoutView: View {\n    @StateObject private var viewModel = CheckoutViewModel()\n    @Environment(\\.scenePhase) private var scenePhase\n    \n    var body: some View {\n        Group {\n            if let dropIn = viewModel.dropIn {\n                dropIn.buildContent()\n            } else if let errorMessage = viewModel.errorMessage {\n                Text(errorMessage)\n                    .foregroundColor(.red)\n            } else {\n                ProgressView(\"Loading...\")\n            }\n        }\n        .task {\n            await viewModel.loadDropIn()\n        }\n        .onChange(of: scenePhase) { oldPhase, newPhase in\n            switch newPhase {\n            case .background:\n                print(\"App backgrounded\")\n            case .inactive:\n                print(\"App inactive\")\n            case .active:\n                print(\"App active\")\n            @unknown default:\n                break\n            }\n        }\n        .onDisappear {\n            viewModel.cleanup()\n        }\n    }\n}\n\n@MainActor\nfinal class CheckoutViewModel: ObservableObject {\n    @Published var dropIn: CheckoutDropIn?\n    @Published var errorMessage: String?\n    \n    func loadDropIn() async {\n        // Implementation\n    }\n    \n    func cleanup() {\n        dropIn?.destroy()\n        dropIn = nil\n        errorMessage = nil\n        print(\"Checkout view cleaned up\")\n    }\n    \n    deinit {\n        print(\"CheckoutViewModel deallocated\")\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"keyboard-covering-input-fields","__idx":10},"children":["Keyboard covering input fields"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Symptoms:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Keyboard covers the submit button."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["User can't see what they're typing."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Layout doesn't adjust when keyboard appears."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Solution: Proper keyboard avoidance"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import SwiftUI\n\nstruct CheckoutView: View {\n    @StateObject private var viewModel = CheckoutViewModel()\n    @FocusState private var focusedField: Field?\n    \n    enum Field {\n        case cardNumber, expiry, cvv\n    }\n    \n    var body: some View {\n        ScrollView {\n            VStack(spacing: 16) {\n                if let dropIn = viewModel.dropIn {\n                    dropIn.buildContent()\n                }\n            }\n            .padding()\n        }\n        .scrollDismissesKeyboard(.interactively)\n        .ignoresSafeArea(.keyboard, edges: .bottom)\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Alternatively, use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["KeyboardAvoidingView"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"struct CheckoutView: View {\n    var body: some View {\n        GeometryReader { geometry in\n            ScrollView {\n                VStack {\n                    // Your checkout content\n                }\n                .frame(minHeight: geometry.size.height)\n            }\n        }\n        .ignoresSafeArea(.keyboard)\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"error-codes-reference","__idx":11},"children":["Error codes reference"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"error-codes-to-check-first","__idx":12},"children":["Error codes to check first"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When troubleshooting issues, start by checking for these common error codes:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0107"]},": Card is missing in allow funding types"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0108"]},": PayPal is missing in allow funding types"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0109"]},": Apple Pay is missing in allow funding types"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0204"]},": No payment methods available after ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["create()"]},". Fires ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onError(nil, …)"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paymentMethod: nil"]}," (initialisation error, not tied to a specific method). An inline ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["noPaymentMethodsError"]}," message is still shown in the UI."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1100"]},": Failed to retrieve checkout drop-in configuration"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1116"]},": Card payment failed"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1117"]},": PayPal payment failed"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1119"]},": Apple Pay payment failed"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1120"]},": Invalid PayPal entry type"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"get-additional-help","__idx":13},"children":["Get additional help"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you're still experiencing issues, try these troubleshooting steps."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"enable-debug-logging","__idx":14},"children":["Enable debug logging"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Add detailed logging using print statements or OSLog:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import OSLog\n\nlet logger = Logger(subsystem: \"com.yourapp.checkout\", category: \"DropIn\")\n\nCheckoutDropInConfig(\n    // ... other config\n    analyticsEvent: { event in\n        #if DEBUG\n        let encoder = JSONEncoder()\n        encoder.outputFormatting = [.prettyPrinted, .sortedKeys]\n        \n        var eventData: [String: Any] = [\n            \"eventName\": event.eventName,\n            \"sessionId\": event.sessionId,\n            \"timestamp\": event.timestamp\n        ]\n        \n        if let propertiesJson = try? encoder.encode(event),\n           let propertiesString = String(data: propertiesJson, encoding: .utf8) {\n            logger.debug(\"Event: \\(propertiesString)\")\n        } else {\n            logger.debug(\"Event: \\(event.eventName)\")\n        }\n        #endif\n    }\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"collect-diagnostic-information","__idx":15},"children":["Collect diagnostic information"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When contacting support, include:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import UIKit\n\nfunc collectDiagnosticInfo(sessionData: SessionData?, config: CheckoutDropInConfig) -> String {\n    var info: [String: Any] = [:]\n    \n    // Device information\n    info[\"device\"] = UIDevice.current.model\n    info[\"systemName\"] = UIDevice.current.systemName\n    info[\"systemVersion\"] = UIDevice.current.systemVersion\n    info[\"identifierForVendor\"] = UIDevice.current.identifierForVendor?.uuidString ?? \"N/A\"\n    \n    // Screen information\n    let screen = UIScreen.main\n    info[\"screenWidth\"] = screen.bounds.width\n    info[\"screenHeight\"] = screen.bounds.height\n    info[\"screenScale\"] = screen.scale\n    \n    // App information\n    if let bundleInfo = Bundle.main.infoDictionary {\n        info[\"appVersion\"] = bundleInfo[\"CFBundleShortVersionString\"] ?? \"N/A\"\n        info[\"buildNumber\"] = bundleInfo[\"CFBundleVersion\"] ?? \"N/A\"\n    }\n    \n    // SDK information - source from package metadata or SDK constant\n    // info[\"sdkVersion\"] = CheckoutDropIn.sdkVersion (if available from SDK)\n    info[\"environment\"] = String(describing: config.environment)\n    \n    // Session info (sanitised - DO NOT log hmacKey, encryptionKey, or API secrets)\n    info[\"sessionIdPresent\"] = sessionData?.sessionId != nil\n    info[\"hmacKeyPresent\"] = sessionData?.hmacKey != nil\n    info[\"encryptionKeyPresent\"] = sessionData?.encryptionKey != nil\n    info[\"allowedFundingTypes\"] = String(describing: sessionData?.allowedFundingTypes)\n    \n    // Timestamp\n    info[\"timestamp\"] = ISO8601DateFormatter().string(from: Date())\n    \n    // Convert to JSON string\n    if let jsonData = try? JSONSerialization.data(withJSONObject: info, options: .prettyPrinted),\n       let jsonString = String(data: jsonData, encoding: .utf8) {\n        print(jsonString)\n        \n        // Copy to pasteboard\n        UIPasteboard.general.string = jsonString\n        \n        return jsonString\n    }\n    \n    return \"Failed to generate diagnostics\"\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When contacting support, always include your merchant ID, environment (test or production), iOS version, device model, and any relevant error messages or console logs. For Paze issues, also note whether ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["wallets.paze"]}," is in the session, currency is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["USD"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["merchantCategoryCode"]}," is present, and the URL callback scheme is registered."]}]}]},"headings":[{"value":"Troubleshooting","id":"troubleshooting","depth":1},{"value":"Quick diagnostics","id":"quick-diagnostics","depth":2},{"value":"Common issues","id":"common-issues","depth":2},{"value":"Drop-in not rendering","id":"drop-in-not-rendering","depth":3},{"value":"Session expired errors","id":"session-expired-errors","depth":3},{"value":"Payment method not appearing","id":"payment-method-not-appearing","depth":3},{"value":"Apple Pay not working","id":"apple-pay-not-working","depth":3},{"value":"Backend verification failing","id":"backend-verification-failing","depth":3},{"value":"iOS-specific issues","id":"ios-specific-issues","depth":2},{"value":"Memory leaks","id":"memory-leaks","depth":3},{"value":"Keyboard covering input fields","id":"keyboard-covering-input-fields","depth":3},{"value":"Error codes reference","id":"error-codes-reference","depth":2},{"value":"Error codes to check first","id":"error-codes-to-check-first","depth":3},{"value":"Get additional help","id":"get-additional-help","depth":2},{"value":"Enable debug logging","id":"enable-debug-logging","depth":3},{"value":"Collect diagnostic information","id":"collect-diagnostic-information","depth":3}],"frontmatter":{"seo":{"title":"Troubleshooting"}},"lastModified":"2026-07-14T09:12:22.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/checkout/drop-in/ios/troubleshooting","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}