{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["sub-heading"]},"type":"markdown"},"seo":{"title":"3DS transactions","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":"3ds-transactions","__idx":0},"children":["3DS transactions"]},{"$$mdtype":"Tag","name":"SubHeading","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Integrate 3D Secure (3DS) into your Apple Pay checkout for iOS applications."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"overview","__idx":1},"children":["Overview"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["By implementing 3DS authentication into your Apple Pay payment flow on iOS, you benefit from:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Additional security:"]}," 3DS adds multiple layers of authentication and risk assessment."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Liability shift:"]}," Successful 3DS authentication typically shifts fraud liability from merchant to card issuer."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Higher success rate:"]}," Banks are more likely to approve 3DS-authenticated transactions."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Apple Pay integration:"]}," 3DS works seamlessly with Apple Pay's built-in security features."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["However, the 3DS payment flow may be longer than the non-3DS one due to the additional authentication steps. With Apple Pay on iOS, 3DS often runs in the background during the Apple Pay authorisation process."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"payment-flow","__idx":2},"children":["Payment flow"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Apple Pay 3D Secure flow on iOS is made up of seven key steps."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-1-apple-pay-authorisation","__idx":3},"children":["Step 1: Apple Pay authorisation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The customer authorises payment using Touch ID, Face ID, or passcode in the Apple Pay sheet. The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreAuthorisation"]}," callback in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ApplePayButtonComponent"]}," is invoked with the Apple Pay payment token."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-2-3ds-evaluation","__idx":4},"children":["Step 2: 3DS evaluation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK evaluates whether 3DS authentication is required based on factors like transaction amount, risk assessment, merchant configuration, or regulatory requirements."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-3-payment-token-processing","__idx":5},"children":["Step 3: Payment token processing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Apple Pay payment token is processed and prepared for 3DS authentication. The token contains encrypted payment data that will be used in the 3DS flow."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-4-pre-authorisation-validation","__idx":6},"children":["Step 4: Pre-authorisation validation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This is where you can provide additional transaction data for 3DS processing. The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreAuthorisation"]}," callback allows you to specify risk screening data, address verification, and 3DS configuration."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-5-3ds-authentication","__idx":7},"children":["Step 5: 3DS authentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The 3DS server evaluates the transaction risk using both Apple Pay data and additional merchant data:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Frictionless flow:"]}," If the transaction is low-risk (enhanced by Apple Pay's built-in security), authentication completes automatically."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Challenge flow:"]}," If additional verification is needed, the customer may complete a 3DS challenge, though this is less common with Apple Pay due to its inherent security."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-6-transaction-authorisation","__idx":8},"children":["Step 6: Transaction authorisation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK sends the authorisation request to the payment gateway, including both the Apple Pay token and 3DS authentication data. This combines Apple Pay's security with 3DS protection."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-7-authorisation-result","__idx":9},"children":["Step 7: Authorisation result"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You receive the final authorisation response from the payment gateway. The transaction is either approved or declined, with both Apple Pay and 3DS authentication confirmation."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"implementation","__idx":10},"children":["Implementation"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"before-you-start","__idx":11},"children":["Before you start"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To use 3D Secure with Apple Pay in your iOS application, you first need to:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Enable 3DS in the Unity Portal:",{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Go to ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Merchant setup > Merchant groups"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Select a merchant group."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Click the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Services"]}," tab."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Click ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Edit"]}," in the ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Card service"]}," row."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Click ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Configure modules"]}," and enable ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["ThreeD secure service"]},"."]}]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Configure Apple Pay:",{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Set up your Apple Pay merchant identifier."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Add Apple Pay capability to your iOS app."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Configure your Apple Developer account with merchant IDs."]}]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Get 3DS credentials from your payment processor:",{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["acquirerProfileId"]},": Your acquirer profile identifier."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["providerId"]},": Your 3DS provider identifier."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Test credentials for the sandbox environment."]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-1-configure-your-sdk","__idx":12},"children":["Step 1: Configure your SDK"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Set up your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CheckoutConfig"]}," to include both Apple Pay and 3DS-friendly data."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"let checkoutConfig = CheckoutConfig(\n    environment: .test,\n    session: SessionConfig(\n        sessionId: \"your-session-id\",\n        allowedFundingTypes: AllowedFundingTypes(\n            wallets: WalletConfig(\n                applePay: ApplePayConfig(\n                    merchantId: \"merchant.com.yourcompany\"\n                )\n            )\n        )\n    ),\n    transactionData: TransactionData(\n        amount: 99.99,\n        currency: \"USD\",\n        entryType: .ecom,\n        intent: .sale,\n        merchantTransactionId: \"order-123\",\n        merchantTransactionDate: Date(),\n        // Include 3DS-friendly shopper data\n        shopper: Shopper(\n            email: \"customer@example.com\",\n            firstName: \"John\",\n            lastName: \"Doe\"\n        )\n    )\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-2-implement-apple-pay-callbacks","__idx":13},"children":["Step 2: Implement Apple Pay callbacks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Configure the Apple Pay component with 3DS capabilities."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"let applePayConfig = ApplePayButtonComponentConfig()\n\n// Basic payment configuration\napplePayConfig.currencyCode = \"USD\"\napplePayConfig.countryCode = \"US\"\n\n// Supported payment networks\napplePayConfig.supportedNetworks = [.visa, .masterCard, .amex, .discover]\n\n// Merchant capabilities - IMPORTANT: Include .threeDSecure\napplePayConfig.merchantCapabilities = [.threeDSecure, .emv, .credit, .debit]\n\n// Contact field requirements for 3DS\napplePayConfig.requiredBillingContactFields = [.postalAddress, .name, .phoneNumber, .emailAddress]\napplePayConfig.requiredShippingContactFields = [.postalAddress, .name, .phoneNumber]\n\n// Payment items\napplePayConfig.totalPaymentItem = ApplePayPaymentSummaryItem(\n    amount: 99.99,\n    label: \"Total\",\n    type: .final\n)\n\napplePayConfig.paymentItems = [\n    ApplePayPaymentSummaryItem(amount: 89.99, label: \"Product\", type: .final),\n    ApplePayPaymentSummaryItem(amount: 10.00, label: \"Tax\", type: .final)\n]\n\n// Button appearance\napplePayConfig.buttonType = .buy\napplePayConfig.buttonStyle = .black\napplePayConfig.buttonRadius = 8.0\n\n// REQUIRED: Provide 3DS configuration and additional transaction data\napplePayConfig.onPreAuthorisation = {\n    let logMessage = \"onPreAuthorisation: Called before authorisation to update transaction details\"\n    print(logMessage)\n    \n    return ApplePayTransactionInitData(\n        // 3DS configuration\n        threeDSecureData: ThreeDSecureData(\n            threeDSecureVersion: \"2.1.0\",\n            electronicCommerceIndicator: .eci5, // Authenticated\n            cardHolderAuthenticationVerificationValue: \"AAABBBCCCDDDEEEFFFaaabbbcccdddeee\",\n            directoryServerTransactionId: \"8ac7ca4f-6068-4978-8b5e-c23c0c6d2260\",\n            threeDSecureTransactionStatus: \"Y\"\n        ),\n        \n        // Identity verification\n        identityVerification: IdentityVerification(\n            nameVerification: true\n        ),\n        \n        // Address verification (helps with 3DS risk assessment)\n        addressVerification: AddressVerification(\n            countryCode: \"US\",\n            houseNumberOrName: \"123 Main St\",\n            postalCode: \"10001\"\n        ),\n        \n        // Risk screening data for fraud detection\n        riskScreeningData: RiskScreeningData(\n            performRiskScreening: true,\n            deviceSessionId: generateDeviceSessionId(),\n            userIp: \"192.168.1.100\",\n            account: RiskScreeningAccount(\n                id: \"user_12345678\",\n                creationDateTime: \"2024-01-15T10:30:00.000Z\"\n            ),\n            items: [\n                RiskScreeningItem(\n                    price: 89.99,\n                    quantity: 1,\n                    category: \"Electronics\",\n                    sku: \"APAY-PROD-001\"\n                )\n            ],\n            fulfillments: [\n                RiskScreeningFulfillment(\n                    type: .shipped,\n                    shipping: RiskScreeningShipping(\n                        shippingMethod: .express\n                    ),\n                    recipientPerson: RiskScreeningRecipientPerson(\n                        phoneNumber: \"+1234567890\",\n                        email: \"customer@example.com\",\n                        address: RiskScreeningAddress(\n                            line1: \"123 Main St\",\n                            city: \"New York\",\n                            region: \"NY\",\n                            countryCode: \"US\",\n                            postalCode: \"10001\"\n                        )\n                    )\n                )\n            ]\n        )\n    )\n}\n\n// REQUIRED: Handle the final transaction result\napplePayConfig.onPostAuthorisation = { result in\n    if let authorizedResult = result as? AuthorisedSubmitResult {\n        print(\"Payment successful with Apple Pay + 3DS!\")\n        print(\"Transaction ID: \\(authorizedResult.provider.code)\")\n        \n        // Check if 3DS was used\n        if let threeDSData = authorizedResult.threeDSData {\n            print(\"3DS authentication confirmed\")\n            print(\"ECI: \\(threeDSData.electronicCommerceIndicator?.rawValue ?? \"N/A\")\")\n            \n            // Check liability shift\n            if threeDSData.electronicCommerceIndicator == .eci5 || \n               threeDSData.electronicCommerceIndicator == .eci6 {\n                print(\"Liability shift achieved - protected from chargebacks\")\n            }\n        }\n        \n        // Navigate to success screen\n        DispatchQueue.main.async {\n            // Present success view controller or update UI\n            self.showPaymentSuccess(transactionId: authorizedResult.provider.code)\n        }\n        \n    } else if let failedResult = result as? FailedSubmitResult {\n        print(\"Payment failed: \\(failedResult.errorReason)\")\n        \n        DispatchQueue.main.async {\n            self.showError(\"Payment failed. Please try again.\")\n        }\n    }\n}\n\n// OPTIONAL: Handle Apple Pay errors\napplePayConfig.onError = { error in\n    print(\"Apple Pay error: \\(error.localizedDescription)\")\n    \n    DispatchQueue.main.async {\n        // Handle specific 3DS + Apple Pay errors\n        if error.localizedDescription.contains(\"3DS\") {\n            self.showError(\"Payment verification failed. Please try again.\")\n        } else if error.localizedDescription.contains(\"Apple Pay\") {\n            self.showError(\"Apple Pay is not available. Please try a different payment method.\")\n        } else {\n            self.showError(\"Payment failed. Please try again.\")\n        }\n    }\n}\n\n// OPTIONAL: Handle cancellation\napplePayConfig.onCancel = { error in\n    print(\"Apple Pay cancelled by user\")\n    // User closed Apple Pay sheet\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-3-handle-common-scenarios","__idx":14},"children":["Step 3: Handle common scenarios"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"conditional-3ds-based-on-amount","__idx":15},"children":["Conditional 3DS based on amount"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use 3DS only for transactions above a certain amount."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"applePayConfig.onPreAuthorisation = {\n    let transactionAmount = checkoutConfig.transactionData.amount\n    \n    // Only use 3DS for amounts over $100\n    if transactionAmount > 100 {\n        return ApplePayTransactionInitData(\n            threeDSecureData: ThreeDSecureData(\n                threeDSecureVersion: \"2.1.0\",\n                electronicCommerceIndicator: .eci5,\n                cardHolderAuthenticationVerificationValue: \"AAABBBCCCDDDEEEFFFaaabbbcccdddeee\",\n                directoryServerTransactionId: \"8ac7ca4f-6068-4978-8b5e-c23c0c6d2260\",\n                threeDSecureTransactionStatus: \"Y\"\n            ),\n            addressVerification: AddressVerification(\n                countryCode: \"US\",\n        houseNumberOrName: \"123 Main St\",\n        postalCode: \"10001\"\n    ),\n    riskScreeningData: RiskScreeningData(\n        performRiskScreening: true,\n        deviceSessionId: generateDeviceSessionId(),\n        userIp: \"192.168.1.100\",\n        account: RiskScreeningAccount(\n            id: \"user_12345678\",\n            creationDateTime: \"2024-01-15T10:30:00.000Z\"\n        ),\n        fulfillments: [\n            RiskScreeningFulfillment(\n                type: .shipped,\n                recipientPerson: RiskScreeningRecipientPerson(\n                    phoneNumber: \"+1234567890\"\n                )\n            )\n        ]\n    )\n)\n    }\n    \n    // For smaller amounts, just provide basic data\n    return ApplePayTransactionInitData(\n        addressVerification: AddressVerification(\n            countryCode: \"US\",\n            houseNumberOrName: \"123 Main St\",\n            postalCode: \"10001\"\n        )\n    )\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"different-transaction-types","__idx":16},"children":["Different transaction types"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Handle different types of Apple Pay transactions with appropriate 3DS settings."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"func get3DSConfig(for transactionType: TransactionType) -> ThreeDSecureData {\n    let baseVersion = \"2.1.0\"\n    let dsTransactionId = \"8ac7ca4f-6068-4978-8b5e-c23c0c6d2260\"\n    \n    switch transactionType {\n    case .payment:\n        return ThreeDSecureData(\n            threeDSecureVersion: baseVersion,\n            electronicCommerceIndicator: .eci5, // Authenticated\n            cardHolderAuthenticationVerificationValue: \"AAABBBCCCDDDEEEFFFaaabbbcccdddeee\",\n            directoryServerTransactionId: dsTransactionId,\n            threeDSecureTransactionStatus: \"Y\"\n        )\n    case .recurring:\n        return ThreeDSecureData(\n            threeDSecureVersion: baseVersion,\n            electronicCommerceIndicator: .eci6, // Recurring authenticated\n            cardHolderAuthenticationVerificationValue: \"AAABBBCCCDDDEEEFFFaaabbbcccdddeee\",\n            directoryServerTransactionId: dsTransactionId,\n            threeDSecureTransactionStatus: \"Y\"\n        )\n    case .addCard:\n        return ThreeDSecureData(\n            threeDSecureVersion: baseVersion,\n            electronicCommerceIndicator: .eci1, // Card verification\n            cardHolderAuthenticationVerificationValue: \"AAABBBCCCDDDEEEFFFaaabbbcccdddeee\",\n            directoryServerTransactionId: dsTransactionId,\n            threeDSecureTransactionStatus: \"Y\"\n        )\n    }\n}\n\nenum TransactionType {\n    case payment, recurring, addCard\n}\n\napplePayConfig.onPreAuthorisation = {\n    return ApplePayTransactionInitData(\n        threeDSecureData: get3DSConfig(for: .payment),\n        addressVerification: AddressVerification(\n            countryCode: \"US\",\n            houseNumberOrName: \"123 Main St\",\n            postalCode: \"10001\"\n        ),\n        riskScreeningData: RiskScreeningData(\n            performRiskScreening: true,\n            deviceSessionId: generateDeviceSessionId(),\n            userIp: \"192.168.1.100\",\n            account: RiskScreeningAccount(\n                id: \"user_12345678\",\n                creationDateTime: \"2024-01-15T10:30:00.000Z\"\n            ),\n            fulfillments: [\n                RiskScreeningFulfillment(\n                    type: .shipped,\n                    shipping: RiskScreeningShipping(\n                        shippingMethod: .express\n                    ),\n                    recipientPerson: RiskScreeningRecipientPerson(\n                        phoneNumber: \"+1234567890\"\n                    )\n                )\n            ]\n        )\n    )\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"apple-pay-specific-risk-assessment","__idx":17},"children":["Apple Pay specific risk assessment"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Leverage Apple Pay's built-in security for better risk assessment."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"applePayConfig.onPreAuthorisation = {\n    return ApplePayTransactionInitData(\n        threeDSecureData: ThreeDSecureData(\n            threeDSecureVersion: \"2.1.0\",\n            electronicCommerceIndicator: .eci5,\n            cardHolderAuthenticationVerificationValue: \"AAABBBCCCDDDEEEFFFaaabbbcccdddeee\",\n            directoryServerTransactionId: \"8ac7ca4f-6068-4978-8b5e-c23c0c6d2260\",\n            threeDSecureTransactionStatus: \"Y\" // Request no challenge (Apple Pay provides security)\n        ),\n        \n        // Enhanced risk data\n        riskScreeningData: RiskScreeningData(\n            performRiskScreening: true,\n            deviceSessionId: generateDeviceSessionId(),\n            userIp: \"192.168.1.100\",\n            account: RiskScreeningAccount(\n                id: \"user_12345678\",\n                creationDateTime: \"2024-01-15T10:30:00.000Z\"\n            ),\n            items: getCartItems(),\n            fulfillments: [\n                RiskScreeningFulfillment(\n                    type: .shipped,\n                    shipping: RiskScreeningShipping(\n                        shippingMethod: .standard\n                    ),\n                    recipientPerson: RiskScreeningRecipientPerson(\n                        phoneNumber: \"+1234567890\",\n                        address: getShippingAddress()\n                    )\n                )\n            ],\n            transaction: RiskScreeningTransaction(\n                subtotal: 99.99\n            )\n        )\n    )\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-4-handle-errors","__idx":18},"children":["Step 4: Handle errors"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Implement comprehensive error handling for Apple Pay + 3DS scenarios."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"applePayConfig.onError = { error in\n    print(\"Apple Pay + 3DS error: \\(error.localizedDescription)\")\n    \n    DispatchQueue.main.async {\n        // Handle specific error types\n        if let applePayError = error as? ApplePayValidationException {\n            self.showError(\"Apple Pay configuration error. Please contact support.\")\n        } else if let paymentError = error as? ApplePayPaymentFailedException {\n            self.showError(\"Payment processing failed. Please try again.\")\n        } else if let sessionError = error as? ApplePaySessionCancelledException {\n            print(\"User cancelled Apple Pay\")\n            // Don't show error for user cancellation\n        } else if error.localizedDescription.contains(\"3DS\") {\n            self.showError(\"Payment verification failed. Please try again.\")\n        } else if error.localizedDescription.contains(\"Network\") {\n            self.showError(\"Connection failed. Please check your internet and try again.\")\n        } else {\n            self.showError(\"Payment failed. Please try again.\")\n        }\n    }\n}\n\napplePayConfig.onPostAuthorisation = { result in\n    if let failedResult = result as? FailedSubmitResult {\n        print(\"Transaction failed: \\(failedResult.errorReason)\")\n        \n        DispatchQueue.main.async {\n            // Handle specific failure scenarios\n            switch failedResult.errorCode {\n            case \"AUTHENTICATION_FAILED\":\n                self.showError(\"Payment verification failed. Please try again.\")\n            case \"CHALLENGE_TIMEOUT\":\n                self.showError(\"Verification timed out. Please try again.\")\n            case \"AUTHENTICATION_REJECTED\":\n                self.showError(\"Payment was rejected by your bank.\")\n            case \"INSUFFICIENT_FUNDS\":\n                self.showError(\"Insufficient funds. Please try a different payment method.\")\n            default:\n                self.showError(\"Payment failed. Please try again or contact support.\")\n            }\n        }\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"example","__idx":19},"children":["Example"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Here's a complete example of Apple Pay with 3DS implementation for iOS."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import UIKit\nimport PXPCheckoutSDK\n\nclass CheckoutViewController: UIViewController {\n    \n    private var applePayComponent: ApplePayButtonComponent?\n    private var checkout: PxpCheckout?\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        setupApplePayWith3DS()\n    }\n    \n    private func setupApplePayWith3DS() {\n        // Initialise the SDK\n        let checkoutConfig = CheckoutConfig(\n            environment: .test,\n            session: SessionConfig(\n                sessionId: \"your-session-id\",\n                allowedFundingTypes: AllowedFundingTypes(\n                    wallets: WalletConfig(\n                        applePay: ApplePayConfig(\n                            merchantId: \"merchant.com.yourcompany\"\n                        )\n                    )\n                )\n            ),\n            transactionData: TransactionData(\n                amount: 150.00,\n                currency: \"USD\",\n                entryType: .ecom,\n                intent: .sale,\n                merchantTransactionId: \"order-\\(Int(Date().timeIntervalSince1970))\",\n                merchantTransactionDate: Date(),\n                shopper: Shopper(\n                    email: \"customer@example.com\",\n                    firstName: \"John\",\n                    lastName: \"Doe\"\n                )\n            )\n        )\n        \n        do {\n            checkout = try PxpCheckout.initialize(config: checkoutConfig)\n            \n            // Create Apple Pay component with 3DS\n            let applePayConfig = createApplePayConfig()\n            applePayComponent = try checkout?.create(.applePayButton, componentConfig: applePayConfig)\n            \n            // Add the component to your view\n            if let componentView = applePayComponent?.render() {\n                view.addSubview(componentView)\n                setupConstraints(for: componentView)\n            }\n            \n        } catch {\n            print(\"Failed to initialise checkout: \\(error)\")\n        }\n    }\n    \n    private func createApplePayConfig() -> ApplePayButtonComponentConfig {\n        let config = ApplePayButtonComponentConfig()\n        \n        // Basic configuration\n        config.currencyCode = \"USD\"\n        config.countryCode = \"US\"\n        config.supportedNetworks = [.visa, .masterCard, .amex, .discover]\n        config.merchantCapabilities = [.threeDSecure, .emv, .credit, .debit]\n        \n        // Contact fields for 3DS\n        config.requiredBillingContactFields = [.postalAddress, .name, .emailAddress]\n        config.requiredShippingContactFields = [.postalAddress, .name]\n        \n        // Payment items\n        config.totalPaymentItem = ApplePayPaymentSummaryItem(\n            amount: 150.00,\n            label: \"Total\",\n            type: .final\n        )\n        \n        config.paymentItems = [\n            ApplePayPaymentSummaryItem(amount: 140.00, label: \"Premium Product\", type: .final),\n            ApplePayPaymentSummaryItem(amount: 10.00, label: \"Tax\", type: .final)\n        ]\n        \n        // Button appearance\n        config.buttonType = .buy\n        config.buttonStyle = .black\n        config.buttonRadius = 8.0\n        \n        // Step 1: Provide 3DS configuration\n        config.onPreAuthorisation = { [weak self] in\n            print(\"Configuring 3DS for Apple Pay transaction\")\n            \n            return ApplePayTransactionInitData(\n                // 3DS Authentication configuration\n                threeDSecureData: ThreeDSecureData(\n                    threeDSecureVersion: \"2.1.0\",\n                    electronicCommerceIndicator: .eci5, // Authenticated\n                    cardHolderAuthenticationVerificationValue: \"AAABBBCCCDDDEEEFFFaaabbbcccdddeee\",\n                    directoryServerTransactionId: \"8ac7ca4f-6068-4978-8b5e-c23c0c6d2260\",\n                    threeDSecureTransactionStatus: \"Y\"\n                ),\n                \n                // Identity verification\n                identityVerification: IdentityVerification(\n                    nameVerification: true\n                ),\n                \n                // Address verification\n                addressVerification: AddressVerification(\n                    countryCode: \"US\",\n                    houseNumberOrName: \"123 Main St\",\n                    postalCode: \"10001\"\n                ),\n                \n                // Enhanced risk assessment\n                riskScreeningData: RiskScreeningData(\n                    performRiskScreening: true,\n                    deviceSessionId: self?.generateDeviceSessionId() ?? \"\",\n                    userIp: \"192.168.1.100\",\n                    account: RiskScreeningAccount(\n                        id: \"user_12345678\",\n                        creationDateTime: \"2024-01-15T10:30:00.000Z\"\n                    ),\n                    items: [\n                        RiskScreeningItem(\n                            price: 140.00,\n                            quantity: 1,\n                            category: \"Electronics\",\n                            sku: \"PREM-001\"\n                        )\n                    ],\n                    fulfillments: [\n                        RiskScreeningFulfillment(\n                            type: .shipped,\n                            shipping: RiskScreeningShipping(\n                                shippingMethod: .express\n                            ),\n                            recipientPerson: RiskScreeningRecipientPerson(\n                                phoneNumber: \"+1234567890\",\n                                address: RiskScreeningAddress(\n                                    line1: \"123 Main St\",\n                                    city: \"New York\",\n                                    region: \"NY\",\n                                    countryCode: \"US\",\n                                    postalCode: \"10001\"\n                                )\n                            )\n                        )\n                    ],\n                    transaction: RiskScreeningTransaction(\n                        subtotal: 140.00\n                    )\n                )\n            )\n        }\n        \n        // Step 2: Handle successful/failed authorisation\n        config.onPostAuthorisation = { [weak self] result in\n            print(\"Apple Pay + 3DS result: \\(result)\")\n            \n            DispatchQueue.main.async {\n                if let authorizedResult = result as? AuthorisedSubmitResult {\n                    print(\"Payment successful!\")\n                    print(\"Transaction ID: \\(authorizedResult.provider.code)\")\n                    \n                    // Log 3DS authentication details\n                    if let threeDSData = authorizedResult.threeDSData {\n                        print(\"3DS Authentication Details:\")\n                        print(\"Version: \\(threeDSData.threeDSecureVersion ?? \"N/A\")\")\n                        print(\"ECI: \\(threeDSData.electronicCommerceIndicator?.rawValue ?? \"N/A\")\")\n                        print(\"CAVV: \\(threeDSData.cardHolderAuthenticationVerificationValue ?? \"N/A\")\")\n                        print(\"DS Trans ID: \\(threeDSData.directoryServerTransactionId ?? \"N/A\")\")\n                        \n                        // Check liability shift\n                        if threeDSData.electronicCommerceIndicator == .eci5 || \n                           threeDSData.electronicCommerceIndicator == .eci6 {\n                            print(\"Liability shift achieved - protected from chargebacks\")\n                        }\n                    }\n                    \n                    // Store transaction details\n                    self?.storeTransactionRecord(\n                        transactionId: authorizedResult.provider.code,\n                        amount: 150.00,\n                        currency: \"USD\",\n                        paymentMethod: \"APPLE_PAY\",\n                        threeDSStatus: authorizedResult.threeDSData != nil ? \"AUTHENTICATED\" : \"NOT_REQUIRED\"\n                    )\n                    \n                    // Navigate to success screen\n                    self?.showPaymentSuccess(transactionId: authorizedResult.provider.code)\n                    \n                } else if let failedResult = result as? FailedSubmitResult {\n                    print(\"Payment failed: \\(failedResult.errorReason)\")\n                    \n                    // Handle specific failure scenarios\n                    switch failedResult.errorCode {\n                    case \"AUTHENTICATION_FAILED\":\n                        self?.showError(\"Payment verification failed. Please try again.\")\n                    case \"AUTHENTICATION_REJECTED\":\n                        self?.showError(\"Payment was rejected by your bank. Please try a different card.\")\n                    case \"INSUFFICIENT_FUNDS\":\n                        self?.showError(\"Insufficient funds. Please try a different payment method.\")\n                    case \"CARD_EXPIRED\":\n                        self?.showError(\"Card expired. Please update your payment method in Apple Pay.\")\n                    default:\n                        self?.showError(\"Payment failed. Please try again or contact support.\")\n                    }\n                }\n            }\n        }\n        \n        // Step 3: Error handling\n        config.onError = { [weak self] error in\n            print(\"Apple Pay + 3DS Error: \\(error.localizedDescription)\")\n            \n            DispatchQueue.main.async {\n                if let _ = error as? ApplePayValidationException {\n                    self?.showError(\"Apple Pay configuration error. Please contact support.\")\n                } else if let _ = error as? ApplePayPaymentFailedException {\n                    self?.showError(\"Payment processing failed. Please try again.\")\n                } else if error.localizedDescription.contains(\"3DS\") {\n                    self?.showError(\"Payment verification failed. Please try again.\")\n                } else if error.localizedDescription.contains(\"Network\") {\n                    self?.showError(\"Connection failed. Please check your internet and try again.\")\n                } else {\n                    self?.showError(\"An unexpected error occurred. Please try again.\")\n                }\n            }\n        }\n        \n        // Step 4: Handle cancellation\n        config.onCancel = { error in\n            print(\"Apple Pay cancelled by user\")\n            // User closed Apple Pay sheet - no error message needed\n        }\n        \n        return config\n    }\n    \n    // Helper functions\n    private func generateDeviceSessionId() -> String {\n        return \"device_\\(UUID().uuidString.replacingOccurrences(of: \"-\", with: \"\"))_\\(Int(Date().timeIntervalSince1970))\"\n    }\n    \n    private func showError(_ message: String) {\n        let alert = UIAlertController(title: \"Payment Error\", message: message, preferredStyle: .alert)\n        alert.addAction(UIAlertAction(title: \"OK\", style: .default))\n        present(alert, animated: true)\n    }\n    \n    private func showPaymentSuccess(transactionId: String) {\n        let alert = UIAlertController(title: \"Payment Successful\", \n                                    message: \"Transaction ID: \\(transactionId)\", \n                                    preferredStyle: .alert)\n        alert.addAction(UIAlertAction(title: \"OK\", style: .default))\n        present(alert, animated: true)\n    }\n    \n    private func storeTransactionRecord(transactionId: String, amount: Double, currency: String, paymentMethod: String, threeDSStatus: String) {\n        let transactionData: [String: Any] = [\n            \"transactionId\": transactionId,\n            \"amount\": amount,\n            \"currency\": currency,\n            \"paymentMethod\": paymentMethod,\n            \"threeDSStatus\": threeDSStatus,\n            \"timestamp\": Date().timeIntervalSince1970\n        ]\n        \n        // Store transaction details for audit/reconciliation\n        UserDefaults.standard.set(transactionData, forKey: \"last_transaction\")\n        \n        // Send to analytics if needed\n        print(\"Transaction stored: \\(transactionData)\")\n    }\n    \n    private func setupConstraints(for componentView: UIView) {\n        componentView.translatesAutoresizingMaskIntoConstraints = false\n        \n        NSLayoutConstraint.activate([\n            componentView.centerXAnchor.constraint(equalTo: view.centerXAnchor),\n            componentView.centerYAnchor.constraint(equalTo: view.centerYAnchor),\n            componentView.widthAnchor.constraint(equalToConstant: 280),\n            componentView.heightAnchor.constraint(equalToConstant: 50)\n        ])\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"callback-data","__idx":20},"children":["Callback data"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This section describes the data received by the different callbacks as part of the Apple Pay 3DS flow on iOS."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"onpreauthorisation","__idx":21},"children":["onPreAuthorisation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreAuthorisation"]}," callback receives no parameters for Apple Pay. It should return transaction initialisation data with 3DS configuration enabled."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"return-data-structure","__idx":22},"children":["Return data structure"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"ApplePayTransactionInitData(\n    // 3D Secure configuration (required for 3DS flow)\n    threeDSecureData: ThreeDSecureData(\n        attemptN3d: false,\n        challengeIndicator: \"04\",\n        cardHolderName: \"John Doe\",\n        email: \"customer@example.com\",\n        homePhone: \"+1234567890\",\n        mobilePhone: \"+1234567890\",\n        workPhone: \"+1234567890\",\n        billAddrCity: \"New York\",\n        billAddrCountry: \"840\",\n        billAddrLine1: \"123 Main St\",\n        billAddrLine2: \"Apt 4B\",\n        billAddrPostCode: \"10001\",\n        billAddrState: \"NY\",\n        shipAddrCity: \"New York\",\n        shipAddrCountry: \"840\",\n        shipAddrLine1: \"123 Main St\",\n        shipAddrLine2: \"Apt 4B\",\n        shipAddrPostCode: \"10001\",\n        shipAddrState: \"NY\"\n    ),\n    \n    // Identity verification (optional)\n    identityVerification: IdentityVerification(\n        nameVerification: true\n    ),\n    \n    // Address verification (optional but recommended with 3DS)\n    addressVerification: AddressVerification(\n        countryCode: \"US\",\n        houseNumberOrName: \"123 Main St\",\n        postalCode: \"10001\"\n    ),\n    \n    // Risk screening data (enhanced with 3DS)\n    riskScreeningData: RiskScreeningData(\n        performRiskScreening: true,\n        deviceSessionId: \"device_abc123_1704067200000\",\n        userIp: \"192.168.1.100\",\n        account: RiskScreeningAccount(\n            id: \"user_12345678\",\n            creationDateTime: \"2024-01-15T10:30:00.000Z\"\n        ),\n        items: [\n            RiskScreeningItem(\n                price: 99.99,\n                quantity: 1,\n                category: \"Electronics\"\n            )\n        ],\n        fulfillments: [\n            RiskScreeningFulfillment(\n                type: .shipped,\n                shipping: RiskScreeningShipping(\n                    shippingMethod: .standard\n                ),\n                recipientPerson: RiskScreeningRecipientPerson(\n                    phoneNumber: \"+1234567890\"\n                )\n            )\n        ],\n        transaction: RiskScreeningTransaction(\n            subtotal: 99.99\n        )\n    )\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Here's an example of what to return:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"config.onPreAuthorisation = {\n    print(\"Preparing Apple Pay 3DS transaction\")\n    \n    // Get customer information for 3DS authentication\n    let customerInfo = await getCustomerInformation()\n    let shippingInfo = await getShippingInformation()\n    \n    return ApplePayTransactionInitData(\n        // 3DS configuration - required for 3DS flow\n        threeDSecureData: ThreeDSecureData(\n            attemptN3d: false,\n            challengeIndicator: \"04\", // Request authentication\n            cardHolderName: customerInfo.fullName,\n            email: customerInfo.email,\n            homePhone: customerInfo.phone,\n            mobilePhone: customerInfo.mobile,\n            workPhone: customerInfo.workPhone,\n            \n            // Billing address information\n            billAddrCity: customerInfo.billingAddress.city,\n            billAddrCountry: \"840\", // US country code\n            billAddrLine1: customerInfo.billingAddress.line1,\n            billAddrLine2: customerInfo.billingAddress.line2,\n            billAddrPostCode: customerInfo.billingAddress.postalCode,\n            billAddrState: customerInfo.billingAddress.state,\n            \n            // Shipping address information\n            shipAddrCity: shippingInfo.city,\n            shipAddrCountry: \"840\",\n            shipAddrLine1: shippingInfo.line1,\n            shipAddrLine2: shippingInfo.line2,\n            shipAddrPostCode: shippingInfo.postalCode,\n            shipAddrState: shippingInfo.state\n        ),\n        \n        // Identity verification\n        identityVerification: IdentityVerification(\n            nameVerification: true\n        ),\n        \n        // Address verification\n        addressVerification: AddressVerification(\n            countryCode: \"US\",\n            houseNumberOrName: customerInfo.billingAddress.line1,\n            postalCode: customerInfo.billingAddress.postalCode\n        ),\n        \n        // Enhanced risk screening for 3DS\n        riskScreeningData: RiskScreeningData(\n            performRiskScreening: true,\n            deviceSessionId: generateDeviceSessionId(),\n            userIp: \"192.168.1.100\",\n            account: RiskScreeningAccount(\n                id: \"user_12345678\",\n                creationDateTime: \"2024-01-15T10:30:00.000Z\"\n            ),\n            items: [\n                RiskScreeningItem(\n                    price: 99.99,\n                    quantity: 1,\n                    category: \"Electronics\",\n                    sku: \"PROD-3DS-001\"\n                )\n            ],\n            fulfillments: [\n                RiskScreeningFulfillment(\n                    type: .shipped,\n                    shipping: RiskScreeningShipping(\n                        shippingMethod: .express\n                    ),\n                    recipientPerson: RiskScreeningRecipientPerson(\n                        phoneNumber: \"+1234567890\",\n                        address: RiskScreeningAddress(\n                            countryCode: \"US\",\n                            postalCode: shippingInfo.postalCode\n                        )\n                    )\n                )\n            ],\n            transaction: RiskScreeningTransaction(\n                subtotal: 99.99\n            )\n        )\n    )\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"onpostauthorisation","__idx":23},"children":["onPostAuthorisation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPostAuthorisation"]}," callback receives the transaction result (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["BaseSubmitResult"]},")."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"basesubmitresult-types","__idx":24},"children":["BaseSubmitResult types"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The result can be one of several types:"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["AuthorisedSubmitResult"]}," - Successful 3DS authentication and authorisation:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"AuthorisedSubmitResult(\n    provider: ProviderResult(\n        code: \"AUTH123456\",\n        message: \"Transaction approved with 3DS authentication\"\n    ),\n    transactionId: \"txn_abc123def456\",\n    threeDSecureResult: ThreeDSecureResult(\n        status: \"authenticated_and_authorized\",\n        eci: \"05\", // Electronic Commerce Indicator\n        cavv: \"jJ81HADVRtXfCBATEp01CJUAAAA=\", // Cardholder Authentication Verification Value\n        xid: \"Nmp3NmJhZGI2MjAzOTI5Mw==\",\n        version: \"2.1.0\",\n        directoryServerTransactionId: \"c5b808e7-1de1-4069-a17b-f70d3b3b1645\",\n        acsTransactionId: \"13c701a3-5a88-4c45-89e9-ef65e50b8bf9\",\n        challengeResult: \"completed_successfully\",\n        challengeCompletionIndicator: \"Y\"\n    ),\n    riskResult: RiskResult(\n        riskScore: 15,\n        riskLevel: \"LOW\",\n        riskFactors: [\"apple_pay_3ds_protection\"]\n    )\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["DeclinedSubmitResult"]}," - 3DS authentication failed or transaction declined:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"DeclinedSubmitResult(\n    errorReason: \"3DS authentication failed\",\n    errorCode: \"3DS_AUTH_FAILED\",\n    threeDSecureResult: ThreeDSecureResult(\n        status: \"authentication_failed\",\n        eci: \"07\",\n        version: \"2.1.0\",\n        challengeResult: \"abandoned_by_consumer\",\n        challengeCompletionIndicator: \"N\"\n    ),\n    declineReason: \"Issuer declined after failed 3DS challenge\"\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["FailedSubmitResult"]}," - Technical error during 3DS processing:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"FailedSubmitResult(\n    errorReason: \"3DS server timeout\",\n    errorCode: \"3DS_TIMEOUT\",\n    errorDetails: \"Authentication request timed out\",\n    errorType: \"NETWORK_ERROR\"\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"example-implementation","__idx":25},"children":["Example implementation"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"config.onPostAuthorisation = { result in\n    print(\"3DS Apple Pay transaction completed: \\(result)\")\n    \n    DispatchQueue.main.async {\n        if let authorizedResult = result as? AuthorisedSubmitResult {\n            // Successful 3DS authentication and authorisation\n            print(\"3DS authentication successful!\")\n            print(\"Transaction ID: \\(authorizedResult.transactionId)\")\n            print(\"Authorisation code: \\(authorizedResult.provider.code)\")\n            print(\"3DS status: \\(authorizedResult.threeDSecureResult.status)\")\n            print(\"ECI: \\(authorizedResult.threeDSecureResult.eci)\")\n            print(\"CAVV: \\(authorizedResult.threeDSecureResult.cavv)\")\n            \n            // Store 3DS authentication results for compliance\n            self.storeThreeDSResults(ThreeDSComplianceData(\n                transactionId: authorizedResult.transactionId,\n                eci: authorizedResult.threeDSecureResult.eci,\n                cavv: authorizedResult.threeDSecureResult.cavv,\n                xid: authorizedResult.threeDSecureResult.xid,\n                version: authorizedResult.threeDSecureResult.version,\n                challengeResult: authorizedResult.threeDSecureResult.challengeResult\n            ))\n            \n            // Process order with high confidence\n            self.processOrder(OrderData(\n                transactionId: authorizedResult.transactionId,\n                paymentMethod: \"APPLE_PAY_3DS\",\n                securityLevel: \"MAXIMUM\",\n                riskScore: authorizedResult.riskResult.riskScore,\n                threeDSAuthenticated: true\n            ))\n            \n            // Show success message\n            self.showSuccessMessage(\"Payment successful with enhanced security!\")\n            \n            // Navigate to order confirmation\n            self.navigateToOrderConfirmation(transactionId: authorizedResult.transactionId)\n            \n        } else if let declinedResult = result as? DeclinedSubmitResult {\n            // 3DS authentication failed or transaction declined\n            print(\"3DS authentication or transaction failed\")\n            print(\"Error: \\(declinedResult.errorReason)\")\n            print(\"3DS Status: \\(declinedResult.threeDSecureResult?.status ?? \"unknown\")\")\n            \n            // Handle specific 3DS failure scenarios\n            if let challengeResult = declinedResult.threeDSecureResult?.challengeResult {\n                switch challengeResult {\n                case \"abandoned_by_consumer\":\n                    self.showErrorMessage(\"Authentication was cancelled. Please try again.\")\n                case \"authentication_failed\":\n                    self.showErrorMessage(\"Authentication failed. Please verify your identity and try again.\")\n                default:\n                    self.showErrorMessage(\"Payment was declined. Please try a different payment method.\")\n                }\n            } else {\n                self.showErrorMessage(\"Payment was declined. Please try a different payment method.\")\n            }\n            \n            // Log for analysis\n            self.logPaymentFailure(PaymentFailureData(\n                errorCode: declinedResult.errorCode,\n                errorReason: declinedResult.errorReason,\n                threeDSStatus: declinedResult.threeDSecureResult?.status,\n                challengeResult: declinedResult.threeDSecureResult?.challengeResult\n            ))\n            \n        } else if let failedResult = result as? FailedSubmitResult {\n            // Technical error during 3DS processing\n            print(\"3DS processing error: \\(failedResult.errorReason)\")\n            \n            switch failedResult.errorCode {\n            case \"3DS_TIMEOUT\":\n                self.showErrorMessage(\"Authentication timed out. Please try again.\")\n            case \"3DS_SERVER_ERROR\":\n                self.showErrorMessage(\"Authentication service unavailable. Please try again later.\")\n            default:\n                self.showErrorMessage(\"Payment processing error. Please try again or contact support.\")\n            }\n            \n            // Log technical errors\n            self.logTechnicalError(TechnicalErrorData(\n                errorCode: failedResult.errorCode,\n                errorReason: failedResult.errorReason,\n                errorType: failedResult.errorType,\n                timestamp: Date()\n            ))\n        }\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"onerror","__idx":26},"children":["onError"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onError"]}," callback receives error information when the Apple Pay 3DS process encounters issues:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"config.onError = { error in\n    print(\"Apple Pay 3DS error: \\(error)\")\n    \n    DispatchQueue.main.async {\n        if let validationError = error as? ApplePayValidationException {\n            // Validation errors specific to 3DS setup\n            if validationError.localizedDescription.contains(\"3DS\") {\n                self.showErrorMessage(\"3D Secure configuration error. Please contact support.\")\n            } else {\n                self.showErrorMessage(\"Payment validation failed. Please check your information.\")\n            }\n        } else if let networkError = error as? URLError {\n            // Network errors during 3DS authentication\n            self.showErrorMessage(\"Connection error during authentication. Please check your internet and try again.\")\n        } else if let pkError = error as? PKPaymentError {\n            // Apple Pay specific errors with 3DS context\n            switch pkError.code {\n            case .paymentNotAllowed:\n                self.showErrorMessage(\"Payment not allowed. Please check your settings.\")\n            case .paymentNetworkNotSupported:\n                self.showErrorMessage(\"Card network not supported for 3D Secure. Please try a different card.\")\n            case .deviceCannotMakePayments:\n                self.showErrorMessage(\"Apple Pay is not available on this device.\")\n            default:\n                self.showErrorMessage(\"3D Secure authentication unavailable. Please try a different payment method.\")\n            }\n        } else {\n            // Generic errors\n            self.showErrorMessage(\"Payment error occurred. Please try again.\")\n        }\n        \n        // Log all errors for debugging\n        self.logError(ErrorData(\n            error: error.localizedDescription,\n            type: String(describing: type(of: error)),\n            context: \"3ds_apple_pay_ios\",\n            timestamp: Date()\n        ))\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"oncancel","__idx":27},"children":["onCancel"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onCancel"]}," callback is triggered when the user cancels the Apple Pay process:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"config.onCancel = { error in\n    print(\"Apple Pay 3DS cancelled by user\")\n    \n    DispatchQueue.main.async {\n        // Track cancellation for analytics\n        self.trackEvent(\"apple_pay_3ds_cancelled\", parameters: [\n            \"step\": error != nil ? \"authentication\" : \"payment_sheet\",\n            \"reason\": error?.localizedDescription ?? \"user_cancelled\",\n            \"platform\": \"ios\"\n        ])\n        \n        // Show cancellation message\n        self.showInfoMessage(\"Payment was cancelled. You can try again anytime.\")\n        \n        // Reset payment form state\n        self.resetPaymentForm()\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"pkpaymentauthorizationcontrollerdelegate-methods","__idx":28},"children":["PKPaymentAuthorizationControllerDelegate methods"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When using 3DS with Apple Pay on iOS, you may also need to implement additional delegate methods for enhanced functionality:"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"shipping-contact-updates-with-3ds-context","__idx":29},"children":["Shipping contact updates with 3DS context"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"func paymentAuthorizationController(\n    _ controller: PKPaymentAuthorizationController,\n    didSelectShippingContact contact: PKContact,\n    handler completion: @escaping (PKPaymentRequestShippingContactUpdate) -> Void\n) {\n    // Calculate shipping with 3DS risk assessment\n    let shippingCost = calculateShippingWithRiskAssessment(for: contact)\n    let tax = calculateTaxWithCompliance(for: contact)\n    let total = baseAmount + shippingCost + tax\n    \n    // Enhanced shipping update for 3DS\n    let update = PKPaymentRequestShippingContactUpdate(\n        errors: [],\n        shippingMethods: [\n            PKShippingMethod(\n                label: \"Standard Shipping (3DS Secure)\",\n                amount: NSDecimalNumber(value: shippingCost),\n                type: .final,\n                identifier: \"standard_3ds\",\n                detail: \"5-7 business days with enhanced security\"\n            )\n        ],\n        paymentSummaryItems: [\n            PKPaymentSummaryItem(label: \"Product\", amount: NSDecimalNumber(value: baseAmount)),\n            PKPaymentSummaryItem(label: \"Tax\", amount: NSDecimalNumber(value: tax)),\n            PKPaymentSummaryItem(label: \"Shipping\", amount: NSDecimalNumber(value: shippingCost)),\n            PKPaymentSummaryItem(label: \"Your Store (3DS Secure)\", amount: NSDecimalNumber(value: total))\n        ]\n    )\n    \n    completion(update)\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"payment-method-selection-with-3ds-validation","__idx":30},"children":["Payment method selection with 3DS validation"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"@available(iOS 15.0, *)\nfunc paymentAuthorizationController(\n    _ controller: PKPaymentAuthorizationController,\n    didSelectPaymentMethod paymentMethod: PKPaymentMethod,\n    handler completion: @escaping (PKPaymentRequestPaymentMethodUpdate) -> Void\n) {\n    // Validate payment method for 3DS compatibility\n    let isThreeDSCompatible = validate3DSCompatibility(for: paymentMethod)\n    \n    let update = PKPaymentRequestPaymentMethodUpdate(\n        errors: isThreeDSCompatible ? [] : [\n            PKPaymentError(.paymentMethodInvalidError, \n                          userInfo: [NSLocalizedDescriptionKey: \"This card does not support 3D Secure authentication\"])\n        ],\n        paymentSummaryItems: [\n            PKPaymentSummaryItem(label: \"Product\", amount: NSDecimalNumber(value: 99.99)),\n            PKPaymentSummaryItem(label: \"3DS Processing Fee\", amount: NSDecimalNumber(value: 0.00)),\n            PKPaymentSummaryItem(label: \"Total (3DS Secure)\", amount: NSDecimalNumber(value: 99.99))\n        ]\n    )\n    \n    completion(update)\n}\n\nprivate func validate3DSCompatibility(for paymentMethod: PKPaymentMethod) -> Bool {\n    // Check if the payment network supports 3DS\n    let threeDSNetworks: [PKPaymentNetwork] = [.visa, .masterCard, .amex]\n    return threeDSNetworks.contains(paymentMethod.network)\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"helper-methods-for-3ds-data-handling","__idx":31},"children":["Helper methods for 3DS data handling"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"// MARK: - 3DS Helper Methods\n\nprivate func generateDeviceSessionId() -> String {\n    let deviceId = UIDevice.current.identifierForVendor?.uuidString ?? \"unknown\"\n    let timestamp = Int(Date().timeIntervalSince1970)\n    return \"ios_3ds_\\(deviceId)_\\(timestamp)\"\n}\n\nprivate func getCustomerInformation() async -> CustomerInfo {\n    // Retrieve customer information for 3DS authentication\n    return CustomerInfo(\n        fullName: \"John Doe\",\n        email: \"customer@example.com\",\n        phone: \"+1234567890\",\n        mobile: \"+1234567890\",\n        workPhone: \"+1234567890\",\n        billingAddress: Address(\n            line1: \"123 Main St\",\n            line2: \"Apt 4B\",\n            city: \"New York\",\n            state: \"NY\",\n            postalCode: \"10001\",\n            country: \"US\"\n        )\n    )\n}\n\nprivate func getShippingInformation() async -> Address {\n    // Retrieve shipping information for 3DS\n    return Address(\n        line1: \"123 Main St\",\n        line2: \"Apt 4B\",\n        city: \"New York\",\n        state: \"NY\",\n        postalCode: \"10001\",\n        country: \"US\"\n    )\n}\n\nprivate func storeThreeDSResults(_ data: ThreeDSComplianceData) {\n    // Store 3DS results for compliance and dispute resolution\n    ComplianceManager.shared.store3DSResults(data)\n}\n\nprivate func processOrder(_ orderData: OrderData) {\n    // Process order with 3DS authentication confidence\n    OrderManager.shared.processSecureOrder(orderData)\n}\n\nprivate func showSuccessMessage(_ message: String) {\n    let alert = UIAlertController(title: \"Success\", message: message, preferredStyle: .alert)\n    alert.addAction(UIAlertAction(title: \"OK\", style: .default))\n    present(alert, animated: true)\n}\n\nprivate func showErrorMessage(_ message: String) {\n    let alert = UIAlertController(title: \"Error\", message: message, preferredStyle: .alert)\n    alert.addAction(UIAlertAction(title: \"OK\", style: .default))\n    present(alert, animated: true)\n}\n\nprivate func showInfoMessage(_ message: String) {\n    let alert = UIAlertController(title: \"Information\", message: message, preferredStyle: .alert)\n    alert.addAction(UIAlertAction(title: \"OK\", style: .default))\n    present(alert, animated: true)\n}\n","lang":"swift"},"children":[]}]},"headings":[{"value":"3DS transactions","id":"3ds-transactions","depth":1},{"value":"Overview","id":"overview","depth":2},{"value":"Payment flow","id":"payment-flow","depth":2},{"value":"Step 1: Apple Pay authorisation","id":"step-1-apple-pay-authorisation","depth":3},{"value":"Step 2: 3DS evaluation","id":"step-2-3ds-evaluation","depth":3},{"value":"Step 3: Payment token processing","id":"step-3-payment-token-processing","depth":3},{"value":"Step 4: Pre-authorisation validation","id":"step-4-pre-authorisation-validation","depth":3},{"value":"Step 5: 3DS authentication","id":"step-5-3ds-authentication","depth":3},{"value":"Step 6: Transaction authorisation","id":"step-6-transaction-authorisation","depth":3},{"value":"Step 7: Authorisation result","id":"step-7-authorisation-result","depth":3},{"value":"Implementation","id":"implementation","depth":2},{"value":"Before you start","id":"before-you-start","depth":3},{"value":"Step 1: Configure your SDK","id":"step-1-configure-your-sdk","depth":3},{"value":"Step 2: Implement Apple Pay callbacks","id":"step-2-implement-apple-pay-callbacks","depth":3},{"value":"Step 3: Handle common scenarios","id":"step-3-handle-common-scenarios","depth":3},{"value":"Conditional 3DS based on amount","id":"conditional-3ds-based-on-amount","depth":4},{"value":"Different transaction types","id":"different-transaction-types","depth":4},{"value":"Apple Pay specific risk assessment","id":"apple-pay-specific-risk-assessment","depth":4},{"value":"Step 4: Handle errors","id":"step-4-handle-errors","depth":3},{"value":"Example","id":"example","depth":3},{"value":"Callback data","id":"callback-data","depth":2},{"value":"onPreAuthorisation","id":"onpreauthorisation","depth":3},{"value":"Return data structure","id":"return-data-structure","depth":4},{"value":"onPostAuthorisation","id":"onpostauthorisation","depth":3},{"value":"BaseSubmitResult types","id":"basesubmitresult-types","depth":4},{"value":"Example implementation","id":"example-implementation","depth":4},{"value":"onError","id":"onerror","depth":3},{"value":"onCancel","id":"oncancel","depth":3},{"value":"PKPaymentAuthorizationControllerDelegate methods","id":"pkpaymentauthorizationcontrollerdelegate-methods","depth":3},{"value":"Shipping contact updates with 3DS context","id":"shipping-contact-updates-with-3ds-context","depth":4},{"value":"Payment method selection with 3DS validation","id":"payment-method-selection-with-3ds-validation","depth":4},{"value":"Helper methods for 3DS data handling","id":"helper-methods-for-3ds-data-handling","depth":4}],"frontmatter":{"seo":{"title":"3DS transactions"}},"lastModified":"2026-04-22T11:04:41.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/checkout/components/ios/apple-pay/3ds","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}