{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["sub-heading","br","details","admonition","tabs","tab"]},"type":"markdown"},"seo":{"title":"Cards","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":"cards","__idx":0},"children":["Cards"]},{"$$mdtype":"Tag","name":"SubHeading","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Accept card payments with 3D Secure authentication, automatic validation, and saved cards for returning customers."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"overview","__idx":1},"children":["Overview"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Card payments are automatically included in the drop-in when enabled in your session. The drop-in handles all card field rendering, validation, 3D Secure authentication, and payment processing automatically."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"key-benefits","__idx":2},"children":["Key benefits"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Card fields appear automatically in the drop-in when cards are enabled in your session configuration."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The drop-in handles all card setup, so you don't need card-specific code."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Card payments use the same ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSuccess"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onError"]}," callbacks as other payment methods for a unified integration."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Automatic field validation for card number, expiry date, and CVC."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["3D Secure authentication is handled automatically with native iOS UI."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Returning customers can use saved cards when card-on-file is enabled."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["PCI DSS Level 1 compliant, with no card data stored on your server."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"how-it-works","__idx":3},"children":["How it works"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When a customer pays with a card:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The customer taps \"Card\" in the payment options list. The card form appears."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The customer enters their card number, expiry date, and CVC."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The drop-in validates the card details in real time."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The customer taps \"Pay\" and 3D Secure authentication begins (if required)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The customer completes authentication in the native iOS UI."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The payment is processed through Unity."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSuccess"]}," callback fires."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"configuration","__idx":4},"children":["Configuration"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Card-specific settings are configured through ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["methodConfig.global"]}," in the Drop-in configuration. The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DropInCardConfig"]}," class is currently an empty marker and doesn't have its own properties."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"configuration-properties","__idx":5},"children":["Configuration properties"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following properties in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DropInGlobalConfig"]}," apply to card payments:"]},{"$$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":{"width":"30%","data-label":"Property"},"children":["Property "]},{"$$mdtype":"Tag","name":"th","attributes":{"width":"70%","data-label":"Description"},"children":["Description "]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["acceptedCardNetworks"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["[DropInCardNetworks]?"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Which card brands to accept through card payments. Falls back to session configuration if not specified.",{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},"Possible values:",{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".visa"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".mastercard"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".amex"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".unionPay"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".diners"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".jcb"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".discover"]}]}]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["allowedCardFundingSource"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["[DropInCardFundingSource]?"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Which funding types to accept (Credit, Debit). Falls back to session restrictions if not specified.",{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},"Possible values:",{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".credit"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".debit"]}]}]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},"Note: To filter by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".prepaid"]},", use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CheckoutDropInConfig.restrictions"]}," instead: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["restrictions: Restrictions(card: Restrictions.Card(fundingSources: [.credit, .debit, .prepaid]))"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["allowedIssuerCountryCodes"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["[String]?"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Allowed card issuer countries as ISO country codes (e.g., ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[\"GB\", \"US\", \"FR\"]"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onGetConsent"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["(DropInPaymentMethod) -> Bool"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["For cards, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onGetConsent(.card)"]}," provides the consent value used during tokenisation. The SDK renders the card consent checkbox only when a shopper ID is available and the Unity site configuration requires asking for stored-card consent."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"complete-example","__idx":6},"children":["Complete example"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This example shows a full card configuration using global settings:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"methodConfig: DropInMethodConfig(\n    // Global settings that apply to card payments\n    global: DropInGlobalConfig(\n        // Restrict to specific card networks\n        acceptedCardNetworks: [\n            .visa,\n            .mastercard,\n            .amex\n        ],\n        \n        // Allow only credit and debit cards\n        allowedCardFundingSource: [\n            .credit,\n            .debit\n        ],\n        \n        // Restrict to cards issued in specific countries\n        allowedIssuerCountryCodes: [\"GB\", \"US\", \"FR\"],\n        \n        // Provide consent value for card tokenisation\n        onGetConsent: { paymentMethod in\n            // Return consent value for card vaulting\n            return paymentMethod == .card\n        }\n    ),\n    \n    // Card config is currently an empty marker\n    card: DropInCardConfig()\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"card-requirements","__idx":7},"children":["Card requirements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Card payments require the following to function correctly:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["iOS compatibility:"]}," iOS 14.0 or higher."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["HTTPS:"]}," Your backend endpoints must be served over HTTPS."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Unity Portal configuration:"]}," Cards must be enabled and configured in the Unity Portal."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Entry type:"]}," Cards support ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".ecom"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".moto"]}," entry types."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["3D Secure:"]}," Your merchant account must be configured for 3D Secure authentication."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Card-on-file payments require the shopper to have previously authorised your merchant account. Authorisations must be captured within the time window specified by your payment scheme (typically 7-30 days)."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"implementation","__idx":8},"children":["Implementation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Card payments work through the standard implementation, with no card-specific code needed:"]},{"$$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    \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            } 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        // Fetch session from backend\n        guard let sessionData = await fetchSessionFromBackend() else {\n            errorMessage = \"Failed to create session\"\n            return\n        }\n        \n        // Initialise Drop-in\n        let config = CheckoutDropInConfig(\n            environment: .test,\n            session: sessionData,\n            transactionData: DropInTransactionData(\n                amount: Decimal(string: \"99.99\") ?? 0,\n                currency: \"GBP\",\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            onGetShopper: {\n                // Provide shopper ID for vaulting\n                TransactionShopper(id: \"shopper-123\")\n            },\n            onSuccess: { result in\n                print(\"Card payment successful!\")\n                print(\"System transaction ID: \\(result.systemTransactionId)\")\n                print(\"Payment method: \\(result.paymentMethod.rawValue)\")\n                \n                // CRITICAL: Verify on backend\n                Task {\n                    await verifyPaymentOnBackend(result)\n                }\n            },\n            onError: { paymentMethod, error in\n                print(\"Card payment failed: \\(error.errorMessage)\")\n                Task { @MainActor in\n                    self.errorMessage = \"Payment failed: \\(error.errorMessage)\"\n                }\n            }\n        )\n        \n        do {\n            let instance = try CheckoutDropIn(config: config)\n            await instance.create()\n            dropIn = instance\n        } catch {\n            errorMessage = error.localizedDescription\n        }\n    }\n    \n    private func fetchSessionFromBackend() async -> SessionData? {\n        // Implementation to fetch session from your backend\n        // ...\n    }\n    \n    private func verifyPaymentOnBackend(_ result: DropInSubmitResult) async {\n        // Implementation to verify payment\n        // ...\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"session-configuration-backend","__idx":9},"children":["Session configuration (backend)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Enable cards in your session request. The backend response should include ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["allowedFundingTypes"]}," with card configuration that the SDK can decode directly into ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SessionData"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"// BACKEND: Create a session with cards enabled\nconst sessionRequest = {\n  merchant: \"MERCHANT-1\",\n  site: \"SITE-1\",\n  sessionTimeout: 120,\n  merchantTransactionId: crypto.randomUUID(),\n  transactionMethod: {\n    intent: {\n      card: \"Authorisation\"  // or \"Purchase\"\n    }\n  },\n  amounts: {\n    currencyCode: \"GBP\",\n    transactionValue: 99.99\n  },\n  allowedFundingTypes: {\n    // Card configuration from the Unity Portal will be used\n    // The SDK expects this response shape:\n    cards: [\"Visa\", \"Mastercard\", \"American Express\"],  // Array of card network strings\n    cardSchemes: [\"Visa\", \"Mastercard\", \"American Express\"]  // SDK resolves to DropInCardNetworks\n  },\n  allowTransaction: true,\n  serviceType: \"CheckoutDropIn\"\n};\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The backend response is decoded into ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SessionData"]},", which includes card network configuration that Drop-in uses to filter payment methods."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"payment-flows","__idx":10},"children":["Payment flows"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Drop-in supports two card payment flows, configured via the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["intent"]}," parameter:"]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"Confirm payment (authorisation)","disable":false},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Two-step payment: authorise now, capture later (within scheme-specific time windows)."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"transactionData: DropInTransactionData(\n    amount: Decimal(string: \"149.99\") ?? 0,\n    currency: \"GBP\",\n    entryType: .ecom,\n    intent: DropInTransactionIntentData(\n        card: .authorisation  // Confirm Payment flow\n    ),\n    merchantTransactionId: UUID().uuidString,\n    merchantTransactionDate: { Date() }\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use this flow for:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Physical products (capture on shipment)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Inventory validation needed"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Final amount may change (shipping, taxes)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Complex order workflows"]}]}]},{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"Pay now (purchase)","disable":false},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Immediate, single-step payment where funds are captured right away."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"transactionData: DropInTransactionData(\n    amount: Decimal(string: \"99.99\") ?? 0,\n    currency: \"GBP\",\n    entryType: .ecom,\n    intent: DropInTransactionIntentData(\n        card: .purchase  // Pay Now flow\n    ),\n    merchantTransactionId: UUID().uuidString,\n    merchantTransactionDate: { Date() }\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use this flow for:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Digital products"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Simple orders with immediate fulfillment"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Subscriptions"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Donations"]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"handling-responses","__idx":11},"children":["Handling responses"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"card-callback-data","__idx":12},"children":["Card callback data"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When a card payment succeeds, your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSuccess"]}," callback receives the same standard result as other payment methods:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"onSuccess: { result in\n    print(\"Payment details:\")\n    print(\"- System transaction ID: \\(result.systemTransactionId)\")\n    print(\"- Merchant transaction ID: \\(result.merchantTransactionId ?? \"N/A\")\")\n    print(\"- Payment method: \\(result.paymentMethod.rawValue)\") // \"Card\"\n    \n    // Note: Amount, currency, card details must be retrieved from backend\n    // 3D Secure authentication data is handled internally\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"error-handling","__idx":13},"children":["Error handling"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Handle card-specific errors:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"onError: { paymentMethod, error in\n    print(\"Error code: \\(error.errorCode)\")\n    print(\"Error message: \\(error.errorMessage)\")\n    \n    // Handle specific error codes\n    let userMessage: String\n    switch error.errorCode {\n    case \"SDK1114\":\n        userMessage = \"Authentication failed. Please try again.\"\n    case \"SDK1115\", \"SDK1116\":\n        userMessage = \"Card payment failed. Please try another card.\"\n    default:\n        // Fall back to message-based detection\n        if error.errorMessage.localizedCaseInsensitiveContains(\"declined\") {\n            userMessage = \"Card declined. Please try a different card.\"\n        } else if error.errorMessage.localizedCaseInsensitiveContains(\"insufficient\") {\n            userMessage = \"Insufficient funds. Please use a different card.\"\n        } else if error.errorMessage.localizedCaseInsensitiveContains(\"expired\") {\n            userMessage = \"Card expired. Please use a different card.\"\n        } else if error.errorMessage.localizedCaseInsensitiveContains(\"invalid\") {\n            userMessage = \"Invalid card details. Please check and try again.\"\n        } else if error.errorMessage.localizedCaseInsensitiveContains(\"3DS\") ||\n                  error.errorMessage.localizedCaseInsensitiveContains(\"authentication\") {\n            userMessage = \"Authentication failed. Please try again.\"\n        } else {\n            userMessage = \"Payment failed: \\(error.errorMessage)\"\n        }\n    }\n    \n    Task { @MainActor in\n        showError(userMessage)\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"common-error-scenarios","__idx":14},"children":["Common error scenarios"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following table describes common card error scenarios:"]},{"$$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":{"align":"left","data-label":"Scenario"},"children":["Scenario"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"How to detect"},"children":["How to detect"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Recommended action"},"children":["Recommended action"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Update allow transaction failed"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error.errorCode == \"SDK1113\""]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Retry the payment or contact support."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Authentication failed"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error.errorCode == \"SDK1114\""]}," or message contains \"authentication\""]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Suggest trying again or contacting the bank."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Authorisation failed"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error.errorCode == \"SDK1115\""]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Suggest trying a different card."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Card payment failed"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error.errorCode == \"SDK1116\""]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Suggest trying a different card."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Card declined"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error.errorMessage"]}," contains \"declined\""]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Suggest trying a different card."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Insufficient funds"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error.errorMessage"]}," contains \"insufficient\""]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Suggest using a different card or payment method."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Card expired"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error.errorMessage"]}," contains \"expired\""]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Request valid card details."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Invalid card"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error.errorMessage"]}," contains \"invalid\""]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Ask customer to check card details."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Network error"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error.errorMessage"]}," contains \"network\" or \"timeout\""]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Retry the payment after a brief delay."]}]}]}]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Card errors include both error codes (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1114"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1115"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1116"]},") and descriptive messages. Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error.errorCode"]}," for programmatic handling and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error.errorMessage"]}," for additional context. For production apps, implement robust error handling with retry logic and user-friendly messaging."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"backend-verification","__idx":15},"children":["Backend verification"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Always verify card payments on your backend to ensure payment success before fulfilling orders:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"onSuccess: { result in\n    // Send to backend for verification\n    Task {\n        do {\n            let response = try await apiClient.post(\"/api/verify-payment\", body: [\n                \"systemTransactionId\": result.systemTransactionId,\n                \"merchantTransactionId\": result.merchantTransactionId ?? \"\"\n            ])\n            \n            if response.success {\n                // Navigate to success screen\n                await MainActor.run {\n                    navigateToSuccess(orderId: response.orderId)\n                }\n            } else {\n                await MainActor.run {\n                    showError(\"Payment verification failed\")\n                }\n            }\n        } catch {\n            print(\"Verification error: \\(error.localizedDescription)\")\n            await MainActor.run {\n                showError(\"Failed to verify payment\")\n            }\n        }\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"backend-verification-code","__idx":16},"children":["Backend verification code"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the following backend code to verify card transactions via the PXP API:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"// BACKEND: Verify card payment\napp.post('/api/verify-payment', async (req, res) => {\n  const { systemTransactionId, merchantTransactionId } = req.body;\n  \n  try {\n    // Query the PXP API to get transaction details\n    const txnPath = `api/v1/transactions/${systemTransactionId}`;\n    const { authHeader, requestId } = createAuthHeader(\n      txnPath,\n      '',\n      process.env.PXP_TOKEN_ID,\n      process.env.PXP_TOKEN_VALUE\n    );\n    \n    const transaction = await fetch(\n      `https://api-services.pxp.io/${txnPath}`,\n      {\n        headers: {\n          'X-Client-Id': process.env.PXP_CLIENT_ID,\n          'X-Request-Id': requestId,\n          'Authorization': authHeader\n        }\n      }\n    ).then(r => r.json());\n    \n    // Verify transaction state\n    if (transaction.state !== 'Authorised' && transaction.state !== 'Captured') {\n      return res.json({ success: false, error: 'Transaction not successful' });\n    }\n    \n    // Verify merchant transaction ID matches\n    if (transaction.merchantTransactionId !== merchantTransactionId) {\n      return res.json({ success: false, error: 'Transaction ID mismatch' });\n    }\n    \n    // Verify amount matches expected amount from your order records\n    const order = await getOrderByMerchantTransactionId(merchantTransactionId);\n    const txnAmount = transaction.amounts?.transactionValue || transaction.amount || 0;\n    if (Math.abs(txnAmount - order.amount) > 0.01) {\n      return res.json({ success: false, error: 'Amount mismatch' });\n    }\n    \n    // Verify funding type is card\n    const fundingType = transaction.fundingData?.fundingType || \n                       transaction.fundingType || \n                       'Unknown';\n    if (fundingType !== 'Card') {\n      return res.json({ success: false, error: 'Invalid funding type' });\n    }\n    \n    // Fulfill order\n    const orderId = await fulfillOrder(transaction);\n    \n    return res.json({ success: true, orderId });\n    \n  } catch (error) {\n    console.error('Verification error:', error);\n    return res.json({ success: false, error: 'Verification failed' });\n  }\n});\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"advanced-card-flows","__idx":17},"children":["Advanced card flows"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"card-vaulting-card-on-file","__idx":18},"children":["Card vaulting (card-on-file)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Card vaulting allows returning customers to pay with a saved card. When enabled, customers who have previously saved a card can use it for faster checkout."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"how-it-works-1","__idx":19},"children":["How it works"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The customer pays with a card and agrees to save it."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Unity vaults the card and returns a vault ID."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["On return visit, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onGetShopper"]}," provides the shopper ID."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Drop-in displays the saved card for payment."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The customer completes the payment with the saved card (CVC or expiry date may be required depending on your site configuration)."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"enable-card-vaulting","__idx":20},"children":["Enable card vaulting"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Implement ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onGetShopper"]}," to enable vaulting:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import SwiftUI\nimport PXPCheckoutSDK\n\n@MainActor\nfinal class CheckoutViewModel: ObservableObject {\n    @Published var dropIn: CheckoutDropIn?\n    @Published var errorMessage: String?\n    \n    func loadDropIn() async {\n        // Fetch session from backend\n        guard let sessionData = await fetchSessionFromBackend() else {\n            errorMessage = \"Failed to create session\"\n            return\n        }\n        \n        // Initialise Drop-in\n        let config = CheckoutDropInConfig(\n            environment: .test,\n            session: sessionData,\n            transactionData: DropInTransactionData(\n                amount: Decimal(string: \"99.99\") ?? 0,\n                currency: \"GBP\",\n                entryType: .ecom,\n                intent: DropInTransactionIntentData(\n                    card: .purchase\n                ),\n                merchantTransactionId: UUID().uuidString,\n                merchantTransactionDate: { Date() }\n            ),\n            merchantShopperId: \"shopper-123\",\n            ownerId: \"MERCHANT-1\",\n            // REQUIRED: Provide shopper ID to enable card vaulting\n            onGetShopper: {\n                let user = getCurrentUser()\n                return TransactionShopper(id: user.shopperId) // e.g., TransactionShopper(id: \"shopper-123\")\n            },\n            onSuccess: { result in\n                Task {\n                    await verifyPaymentOnBackend(result)\n                    await MainActor.run {\n                        navigateToSuccess()\n                    }\n                }\n            },\n            onError: { paymentMethod, error in\n                print(\"Card payment failed: \\(error.errorMessage)\")\n                Task { @MainActor in\n                    self.errorMessage = \"Payment failed: \\(error.errorMessage)\"\n                }\n            }\n        )\n        \n        do {\n            let instance = try CheckoutDropIn(config: config)\n            await instance.create()\n            dropIn = instance\n        } catch {\n            errorMessage = error.localizedDescription\n        }\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onGetShopper"]}," returns a shopper ID, the SDK automatically:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Fetches saved cards from the PXP API."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Displays saved cards for faster checkout."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Handles vault setup during the first payment."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"card-vaulting-configuration","__idx":21},"children":["Card vaulting configuration"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Card vaulting is enabled by implementing ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onGetShopper"]}," and optionally controlling consent with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["methodConfig.global.onGetConsent"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"CheckoutDropInConfig(\n    // ... other config\n    merchantShopperId: \"shopper-123\",\n    ownerId: \"MERCHANT-1\",\n    onGetShopper: {\n        let user = getCurrentUser()\n        return TransactionShopper(id: user.shopperId)  // Required for vaulting\n    },\n    methodConfig: DropInMethodConfig(\n        global: DropInGlobalConfig(\n            onGetConsent: { paymentMethod in\n                // Provide consent value for card tokenisation\n                return paymentMethod == .card\n            }\n        ),\n        card: DropInCardConfig()  // Empty marker\n    )\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"3d-secure-authentication","__idx":22},"children":["3D Secure authentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["3D Secure authentication is handled automatically by the drop-in. When a card requires 3D Secure, the drop-in:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Detects that 3D Secure is required from the payment response."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Launches the native iOS 3D Secure UI."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Guides the customer through authentication (OTP, biometric, etc.)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Processes the authenticated payment."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Fires your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSuccess"]}," callback on completion."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["No additional code is required for 3D Secure authentication. The drop-in manages the entire flow automatically."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["3D Secure UI is rendered using native iOS components and adapts to the authentication method required by the card issuer (OTP, biometric, challenge questions, etc.). The UI is fully PCI DSS Level 1 compliant."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"recurring-card-payments","__idx":23},"children":["Recurring card payments"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For subscriptions and recurring charges, use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".authorisation"]}," flow with card vaulting enabled and the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recurring"]}," transaction field:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"transactionData: DropInTransactionData(\n    amount: Decimal(string: \"9.99\") ?? 0,\n    currency: \"GBP\",\n    entryType: .ecom,\n    intent: DropInTransactionIntentData(\n        card: .authorisation  // Use Authorisation for recurring\n    ),\n    merchantTransactionId: UUID().uuidString,\n    merchantTransactionDate: { Date() },\n    recurring: RecurringType(\n        frequencyInDays: 30,\n        frequencyExpiration: \"2026-12-31\"\n    )\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["After the first payment, use the vaulted card token to process subsequent recurring payments via the PXP API without customer interaction."]}]},"headings":[{"value":"Cards","id":"cards","depth":1},{"value":"Overview","id":"overview","depth":2},{"value":"Key benefits","id":"key-benefits","depth":3},{"value":"How it works","id":"how-it-works","depth":2},{"value":"Configuration","id":"configuration","depth":2},{"value":"Configuration properties","id":"configuration-properties","depth":3},{"value":"Complete example","id":"complete-example","depth":3},{"value":"Card requirements","id":"card-requirements","depth":3},{"value":"Implementation","id":"implementation","depth":2},{"value":"Session configuration (backend)","id":"session-configuration-backend","depth":3},{"value":"Payment flows","id":"payment-flows","depth":3},{"value":"Handling responses","id":"handling-responses","depth":2},{"value":"Card callback data","id":"card-callback-data","depth":3},{"value":"Error handling","id":"error-handling","depth":3},{"value":"Common error scenarios","id":"common-error-scenarios","depth":4},{"value":"Backend verification","id":"backend-verification","depth":2},{"value":"Backend verification code","id":"backend-verification-code","depth":3},{"value":"Advanced card flows","id":"advanced-card-flows","depth":2},{"value":"Card vaulting (card-on-file)","id":"card-vaulting-card-on-file","depth":3},{"value":"How it works","id":"how-it-works-1","depth":4},{"value":"Enable card vaulting","id":"enable-card-vaulting","depth":4},{"value":"Card vaulting configuration","id":"card-vaulting-configuration","depth":4},{"value":"3D Secure authentication","id":"3d-secure-authentication","depth":3},{"value":"Recurring card payments","id":"recurring-card-payments","depth":3}],"frontmatter":{"seo":{"title":"Cards"}},"lastModified":"2026-06-12T11:56:36.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/checkout/drop-in/ios/cards","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}