{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["sub-heading","admonition","br"]},"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 checkout."]}]},{"$$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 payment flow, 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":"p","attributes":{},"children":["However, the 3DS payment flow is longer than the non-3DS one due to the additional authentication steps. It may also require active customer participation if a challenge is presented."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"payment-flow","__idx":2},"children":["Payment flow"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The 3D Secure flow is made up of nine key steps."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-1-submission","__idx":3},"children":["Step 1: Submission"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The customer taps the submit button or the payment is triggered programmatically. The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["submitAsync()"]}," method in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CardSubmitComponent"]}," is invoked and validation occurs inside it. If validation passes, the method continues with the payment processing. If it fails, the method exits early and doesn't proceed with the transaction."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-2-card-tokenisation","__idx":4},"children":["Step 2: Card tokenisation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If it's a new card, the SDK sends the card details to the tokenisation service. If it's a saved card, the SDK retrieves the existing token."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-3-evaluation","__idx":5},"children":["Step 3: Evaluation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK evaluates whether 3DS authentication is required. In this flow, 3DS is required based on factors like transaction amount, risk assessment, or regulatory requirements."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-4-pre-initiation","__idx":6},"children":["Step 4: Pre-initiation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This is the initial step in the 3DS authentication flow. It establishes the authentication session by sending transaction and card details to the payment processor."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This step has two associated callbacks:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreInitiateAuthentication"]},": ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["(() async -> PreInitiateIntegratedAuthenticationData?)?"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Purpose:"]}," Returns configuration for the authentication setup."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Return:"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PreInitiateIntegratedAuthenticationData"]}," containing provider ID, timeout, and authentication indicators."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Return nil:"]}," To skip 3DS authentication (e.g., for low-risk transactions)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Note:"]}," This callback is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["async"]}," as it returns data for the SDK to process."]}]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPostInitiateAuthentication"]},": ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["(AuthenticationResult) -> Void"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Purpose:"]}," Receives the result of the pre-initiation call."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Parameter:"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AuthenticationResult"]}," (either success or failure)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Contains:"]}," Authentication ID, state, SCA mandate status, applicable exemptions."]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-5-fingerprinting","__idx":7},"children":["Step 5: Fingerprinting"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["During this step, device information and browser characteristics are collected by the SDK. It enables risk assessment based on the user's device profile."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-6-authentication","__idx":8},"children":["Step 6: Authentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The 3DS server evaluates the transaction risk and determines the authentication path:"]},{"$$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, authentication completes automatically without customer interaction."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Challenge flow:"]}," If additional verification is needed, the customer completes the 3DS authentication challenge (PIN entry, SMS code, biometric verification, etc.)"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The authentication step has two associated callbacks:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreAuthentication"]},": ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["(() async -> InitiateIntegratedAuthenticationData?)?"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Purpose:"]}," Configures the main authentication parameters."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Return:"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["InitiateIntegratedAuthenticationData"]}," with challenge window size, timeout, merchant details."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Return nil:"]}," To abort the authentication process."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Note:"]}," This callback is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["async"]}," as it returns data for the SDK to process."]}]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPostAuthentication"]},": ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["(AuthenticationResult) -> Void"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Purpose:"]}," Receives authentication results."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Parameter:"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AuthenticationResult"]}," indicating success/failure status."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Contains:"]}," Complete authentication results for transaction processing."]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-7-authentication-result","__idx":9},"children":["Step 7: Authentication result"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK receives the 3DS authentication result indicating whether authentication was successful, failed, or requires additional action."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-8-authorisation","__idx":10},"children":["Step 8: Authorisation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This is the final step of the 3DS authentication flow. You receive the transaction data along with the 3DS authentication results and decide whether to proceed. At this point, you can still add additional data or cancel the transaction entirely. The SDK then sends the authorisation request to the payment gateway, including the 3DS authentication data."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The authorisation step has two associated callbacks:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreAuthorisation"]},": ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["((PreAuthorizationData?) async -> TransactionInitiationData?)?"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Purpose:"]}," Provides final transaction data, including 3DS authentication results. This is your last chance to modify the transaction before authorisation."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Parameter:"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PreAuthorizationData?"]}," (optional) containing token identifiers."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Return:"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TransactionInitiationData"]}," with additional transaction parameters."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Return nil:"]}," To cancel the transaction."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Note:"]}," This callback is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["async"]}," as it returns data for the SDK to process."]}]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPostAuthorisation"]},": ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["((BaseSubmitResult) -> Void)?"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Purpose:"]}," Receives the final transaction result from the payment gateway."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Parameter:"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["BaseSubmitResult"]}," (one of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AuthorisedSubmitResult"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CapturedSubmitResult"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RefusedSubmitResult"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FailedSubmitResult"]},")."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Contains:"]}," Final transaction status, provider response, authentication confirmation, transaction reference."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Usage:"]}," Handle payment success/failure, navigate to appropriate screens, update UI state."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Note:"]}," This callback is NOT ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["async"]}," as it only receives results."]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-9-authorisation-result","__idx":11},"children":["Step 9: 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 and final transaction details are available, along with 3DS authentication confirmation."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"implementation","__idx":12},"children":["Implementation"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"before-you-start","__idx":13},"children":["Before you start"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To use 3D Secure in your application, you first need to enable it in the Unity Portal:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["In the ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://portal.pxp.io","target":"_blank"},"children":["Unity Portal"]},", 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"]}," in the top right."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Click the toggle next to ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["ThreeD secure service"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You'll also need to get the following from your payment processor:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$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":14},"children":["Step 1: Configure your SDK"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To start, set up your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CheckoutConfig"]}," to include the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onGetShopper"]}," callback."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import PXPCheckoutSDK\n\nlet sessionData = SessionData(\n    sessionId: \"your-session-id\",\n    hmacKey: \"your-hmac-key\",\n    encryptionKey: \"your-encryption-key\"\n)\n\nlet transactionData = TransactionData(\n    amount: Decimal(string: \"99.99\")!,\n    currency: \"USD\",\n    entryType: .ecom,\n    intent: TransactionIntentData(card: .authorisation),\n    merchantTransactionId: \"order-\\(UUID().uuidString)\",\n    merchantTransactionDate: { Date() }\n)\n\nlet checkoutConfig = CheckoutConfig(\n    environment: .test,\n    session: sessionData,\n    transactionData: transactionData,\n    merchantShopperId: \"shopper-123\",\n    ownerType: \"MerchantGroup\",\n    ownerId: \"your-owner-id\",\n    onGetShopper: { async in\n        TransactionShopper(\n            id: \"shopper-123\",\n            email: \"customer@example.com\",\n            firstName: \"John\",\n            lastName: \"Doe\"\n        )\n    }\n)\n\nlet pxpCheckout = try PxpCheckout.initialize(config: checkoutConfig)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-2-implement-callbacks","__idx":15},"children":["Step 2: Implement callbacks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Next, implement your chosen callbacks. Note that some are required."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"var submitConfig = CardSubmitComponentConfig()\n\n// REQUIRED: Provide 3DS configuration\n// Note: This callback is async as it returns data\nsubmitConfig.onPreInitiateAuthentication = { async in\n    return PreInitiateIntegratedAuthenticationData(\n        providerId: \"your_3ds_provider_id\",\n        requestorAuthenticationIndicator: .paymentTransaction,\n        timeout: 120\n    )\n}\n\n// OPTIONAL: Handle the pre-initiation result\nsubmitConfig.onPostInitiateAuthentication = { result in\n    print(\"3DS pre-initiation completed: \\(result)\")\n    \n    // Your backend uses the authentication session to evaluate and update decisions\n    // for whether to proceed with authentication\n    if let failed = result as? FailedAuthenticationResult {\n        print(\"3DS setup failed: \\(failed.errorReason ?? \"Unknown error\")\")\n    } else {\n        print(\"3DS setup successful\")\n        // Backend evaluates pre-initiation result to update authentication decision\n    }\n}\n\n// REQUIRED: Configure main authentication\n// Note: This callback is async as it returns data\nsubmitConfig.onPreAuthentication = { async in\n    print(\"Configuring 3DS authentication\")\n    \n    // Get authentication decision (evaluated after onPostInitiateAuthentication)\n    let decision = await getAuthenticationDecision()\n    \n    if !decision {\n        // Not proceeding with authentication\n        return nil\n    }\n    \n    return InitiateIntegratedAuthenticationData(\n        merchantCountryNumericCode: \"840\",\n        merchantLegalName: \"Your Company Ltd\",\n        challengeWindowSize: .size5,\n        requestorChallengeIndicator: .noPreference,\n        shopper: ThreeDSShopper(email: \"customer@example.com\"),\n        timeout: 300\n    )\n}\n\n// OPTIONAL: Handle the authentication result\nsubmitConfig.onPostAuthentication = { authResult in\n    print(\"3DS authentication completed: \\(authResult)\")\n    \n    // Backend evaluates authentication result to update authorisation decision\n    if authResult is FailedAuthenticationResult {\n        print(\"Authentication failed\")\n        // Don't proceed to authorisation\n    } else {\n        print(\"Authentication successful\")\n        // Backend can now proceed with authorisation decision\n    }\n}\n\n// REQUIRED: Final transaction approval\n// Note: This callback is async as it returns data, and parameter is optional\nsubmitConfig.onPreAuthorisation = { preAuthData async in\n    print(\"Pre-authorisation data: \\(String(describing: preAuthData))\")\n    \n    // You can use gatewayTokenId to retrieve token details and update transaction decision\n    if let tokenId = preAuthData?.gatewayTokenId {\n        let transactionDecision = await getAuthorisationDecision(tokenId)\n        \n        if !transactionDecision {\n            // Not proceeding\n            return nil\n        }\n    }\n    \n    // Add risk screening data for fraud detection\n    return TransactionInitiationData(\n        riskScreeningData: RiskScreeningData(\n            performRiskScreening: true,\n            excludeDeviceData: false,\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                )\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                    )\n                )\n            ]\n        )\n    )\n}\n\n// OPTIONAL: Handle the final result\nsubmitConfig.onPostAuthorisation = { result in\n    switch result {\n    case let authorised as AuthorisedSubmitResult:\n        print(\"Payment successful with 3DS!\")\n        print(\"Transaction ID: \\(authorised.fundingData.transactionId)\")\n        // Navigate to success screen\n    case let captured as CapturedSubmitResult:\n        print(\"Payment captured with 3DS!\")\n        print(\"Transaction ID: \\(captured.fundingData.transactionId)\")\n    case let refused as RefusedSubmitResult:\n        print(\"Payment refused: \\(refused.stateData?.message ?? \"Unknown\")\")\n        // Show error message\n    case let failed as FailedSubmitResult:\n        print(\"Payment failed: \\(failed.errorReason ?? \"Unknown error\")\")\n        // Show error message\n    default:\n        print(\"Unknown result type\")\n    }\n}\n\nlet cardSubmit = try pxpCheckout.create(.cardSubmit, componentConfig: submitConfig) as! CardSubmitComponent\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-3-handle-common-scenarios","__idx":16},"children":["Step 3: Handle common scenarios"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"conditional-3ds","__idx":17},"children":["Conditional 3DS"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the following snippet to only trigger 3DS transactions above a certain amount."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"class Conditional3DSManager {\n    private let transactionAmount: Decimal\n    \n    init(transactionAmount: Decimal) {\n        self.transactionAmount = transactionAmount\n    }\n    \n    func createCardSubmitConfig() -> CardSubmitComponentConfig {\n        var config = CardSubmitComponentConfig()\n        \n        config.onPreInitiateAuthentication = { async in\n            if self.transactionAmount > 100.0 {\n                return PreInitiateIntegratedAuthenticationData(\n                    providerId: \"your_provider\",\n                    requestorAuthenticationIndicator: .paymentTransaction,\n                    timeout: 120\n                )\n            } else {\n                // Return nil to skip 3DS for small amounts\n                return nil\n            }\n        }\n        \n        config.onPreAuthorisation = { _ async in return TransactionInitiationData() }\n        config.onPostAuthorisation = { _ in }\n        \n        return config\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"different-transaction-types","__idx":18},"children":["Different transaction types"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the following snippet to handle different types of transactions."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"enum TransactionType {\n    case payment\n    case recurring\n    case addCard\n}\n\nclass TransactionType3DSManager {\n    \n    func get3DSConfig(transactionType: TransactionType) -> PreInitiateIntegratedAuthenticationData {\n        let indicator: RequestorAuthenticationIndicatorType\n        \n        switch transactionType {\n        case .payment:\n            indicator = .paymentTransaction\n        case .recurring:\n            indicator = .recurringTransaction\n        case .addCard:\n            indicator = .addCard\n        }\n        \n        return PreInitiateIntegratedAuthenticationData(\n            providerId: \"your_provider\",\n            requestorAuthenticationIndicator: indicator,\n            timeout: 120\n        )\n    }\n    \n    func createCardSubmitConfig(transactionType: TransactionType) -> CardSubmitComponentConfig {\n        var config = CardSubmitComponentConfig()\n        \n        config.onPreInitiateAuthentication = { async in\n            return self.get3DSConfig(transactionType: transactionType)\n        }\n        \n        config.onPreAuthorisation = { _ async in return TransactionInitiationData() }\n        config.onPostAuthorisation = { _ in }\n        \n        return config\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-4-handle-errors","__idx":19},"children":["Step 4: Handle errors"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Lastly, make sure to implement proper error handling."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"class ThreeDS3DSErrorHandler {\n    \n    func createCardSubmitConfigWithErrorHandling() -> CardSubmitComponentConfig {\n        var config = CardSubmitComponentConfig()\n        \n        config.onSubmitError = { error in\n            print(\"Payment error: \\(error)\")\n            self.handleSubmitError(error)\n        }\n        \n        config.onPostAuthentication = { authResult in\n            // Handle authentication failures\n            if let failed = authResult as? FailedAuthenticationResult {\n                print(\"Authentication failed: \\(failed.errorReason ?? \"Unknown error\")\")\n                self.showError(\"Card authentication failed\")\n                return\n            }\n            print(\"Authentication successful, proceeding to payment\")\n        }\n        \n        config.onPreAuthorisation = { _ async in return TransactionInitiationData() }\n        config.onPostAuthorisation = { _ in }\n        \n        return config\n    }\n    \n    private func handleSubmitError(_ error: BaseSdkException) {\n        // Handle specific 3DS errors\n        switch error.errorCode {\n        case \"AUTHENTICATION_FAILED\":\n            showError(\"Payment authentication failed. Please try again.\")\n        case \"CHALLENGE_TIMEOUT\":\n            showError(\"Authentication timed out. Please try again.\")\n        case \"AUTHENTICATION_REJECTED\":\n            showError(\"Payment was rejected by your bank.\")\n        case \"TOKEN_VAULT_EXCEPTION\":\n            showError(\"Token vault exception.\")\n        case \"VALIDATION_EXCEPTION\":\n            showError(\"Validation failed.\")\n        case \"TRANSACTION_AUTHENTICATION_REJECTED\":\n            showError(\"Payment was rejected by your bank.\")\n        case \"PRE_INITIATE_AUTHENTICATION_FAILED\":\n            showError(\"Pre-initiate authentication failed.\")\n        case \"TRANSACTION_AUTHENTICATION_REQUIRES_SCA_EXEMPTION\":\n            showError(\"Transaction authentication requires SCA exemption.\")\n        case \"TRANSACTION_AUTHENTICATION_INVALID\":\n            showError(\"Transaction authentication is invalid.\")\n        case \"NETWORK_SDK_EXCEPTION\":\n            showError(\"Network error occurred. Please try again.\")\n        case \"UNEXPECTED_SDK_EXCEPTION\":\n            showError(\"Payment failed. Please try again.\")\n        default:\n            showError(\"Payment failed. Please try again.\")\n        }\n    }\n    \n    private func showError(_ message: String) {\n        // Implementation depends on your UI framework\n        print(\"Error: \\(message)\")\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"complete-example","__idx":20},"children":["Complete example"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following example shows a simple 3DS implementation using the new card component in a complete SwiftUI view."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import SwiftUI\nimport PXPCheckoutSDK\n\nstruct ThreeDSPaymentView: View {\n    @StateObject private var viewModel = ThreeDSPaymentViewModel()\n    \n    var body: some View {\n        ScrollView {\n            VStack(alignment: .leading, spacing: 16) {\n                Text(\"3DS Secure Payment\")\n                    .font(.title)\n                    .padding(.bottom, 8)\n                \n                // Show authentication status\n                if let state = viewModel.authenticationState {\n                    HStack {\n                        Image(systemName: \"lock.shield\")\n                        Text(\"Authentication Status: \\(state)\")\n                    }\n                    .padding()\n                    .background(Color.blue.opacity(0.1))\n                    .cornerRadius(8)\n                }\n                \n                // Show error message if any\n                if let error = viewModel.errorMessage {\n                    HStack {\n                        Image(systemName: \"exclamationmark.triangle\")\n                        Text(error)\n                    }\n                    .padding()\n                    .background(Color.red.opacity(0.1))\n                    .foregroundColor(.red)\n                    .cornerRadius(8)\n                }\n                \n                // Render payment component\n                if let newCard = viewModel.newCardComponent {\n                    newCard.buildContent()\n                }\n                \n                // Loading indicator\n                if viewModel.isLoading {\n                    HStack {\n                        Spacer()\n                        VStack(spacing: 8) {\n                            ProgressView()\n                            Text(\"Processing 3DS Authentication...\")\n                                .font(.caption)\n                        }\n                        Spacer()\n                    }\n                    .padding()\n                }\n            }\n            .padding()\n        }\n        .onAppear {\n            viewModel.setupComponents()\n        }\n    }\n}\n\nclass ThreeDSPaymentViewModel: ObservableObject {\n    @Published var isLoading = false\n    @Published var errorMessage: String?\n    @Published var authenticationState: String?\n    var newCardComponent: NewCardComponent?\n    \n    private var pxpCheckout: PxpCheckout?\n    private let errorHandler = ThreeDS3DSErrorHandler()\n    \n    func setupComponents() {\n        do {\n            // Setup checkout\n            let sessionData = SessionData(\n                sessionId: \"session-\\(UUID().uuidString)\",\n                hmacKey: \"your-hmac-key\",\n                encryptionKey: \"your-encryption-key\"\n            )\n            \n            let transactionData = TransactionData(\n                amount: Decimal(string: \"99.99\")!,\n                currency: \"USD\",\n                entryType: .ecom,\n                intent: TransactionIntentData(card: .authorisation),\n                merchantTransactionId: \"order-\\(UUID().uuidString)\",\n                merchantTransactionDate: { Date() }\n            )\n            \n            let checkoutConfig = CheckoutConfig(\n                environment: .test,\n                session: sessionData,\n                transactionData: transactionData,\n                merchantShopperId: \"shopper-123\",\n                ownerType: \"MerchantGroup\",\n                ownerId: \"your-owner-id\",\n                onGetShopper: { async in\n                    TransactionShopper(\n                        id: \"shopper-123\",\n                        email: \"customer@example.com\",\n                        firstName: \"John\",\n                        lastName: \"Doe\"\n                    )\n                }\n            )\n            \n            pxpCheckout = try PxpCheckout.initialize(config: checkoutConfig)\n            \n            // Setup submit config with 3DS\n            var submitConfig = CardSubmitComponentConfig()\n            \n            // Step 1: Set up 3DS (async callback)\n            submitConfig.onPreInitiateAuthentication = { async in\n                return PreInitiateIntegratedAuthenticationData(\n                    providerId: \"your_3ds_provider_id\",\n                    requestorAuthenticationIndicator: .paymentTransaction,\n                    timeout: 120\n                )\n            }\n            \n            // Step 2: Configure authentication (async callback)\n            submitConfig.onPreAuthentication = { async in\n                return InitiateIntegratedAuthenticationData(\n                    merchantCountryNumericCode: \"840\",\n                    merchantLegalName: \"Your Company Ltd\",\n                    challengeWindowSize: .size5,\n                    requestorChallengeIndicator: .noPreference,\n                    timeout: 300\n                )\n            }\n            \n            // Step 3: Handle final authorisation (async callback, optional parameter)\n            submitConfig.onPreAuthorisation = { preAuthData async in\n                print(\"Pre-authorisation data: \\(String(describing: preAuthData))\")\n                \n                // Add risk screening data for fraud detection\n                return TransactionInitiationData(\n                    riskScreeningData: RiskScreeningData(\n                        performRiskScreening: true,\n                        excludeDeviceData: false,\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                            )\n                        ],\n                        fulfillments: [\n                            RiskScreeningFulfillment(\n                                type: .shipped,\n                                recipientPerson: RiskScreeningRecipientPerson(\n                                    phoneNumber: \"+1234567890\"\n                                )\n                            )\n                        ]\n                    )\n                )\n            }\n            \n            // Step 4: Handle success/failure\n            submitConfig.onPostAuthorisation = { [weak self] result in\n                DispatchQueue.main.async {\n                    switch result {\n                    case is AuthorisedSubmitResult, is CapturedSubmitResult:\n                        self?.navigateToSuccessScreen()\n                    case let refused as RefusedSubmitResult:\n                        self?.showError(\"Payment was declined: \\(refused.stateData?.message ?? \"Unknown\")\")\n                    default:\n                        self?.showError(\"Payment failed\")\n                    }\n                }\n            }\n            \n            // Step 5: Error handling\n            submitConfig.onSubmitError = { [weak self] error in\n                print(\"3DS Error: \\(error)\")\n                DispatchQueue.main.async {\n                    self?.showError(\"Payment authentication failed\")\n                }\n            }\n            \n            submitConfig.onStartSubmit = { [weak self] in\n                DispatchQueue.main.async {\n                    self?.isLoading = true\n                }\n            }\n            \n            // Create new card component\n            let newCardConfig = NewCardComponentConfig(submit: submitConfig)\n            newCardComponent = try pxpCheckout?.create(.newCard, componentConfig: newCardConfig) as? NewCardComponent\n            \n        } catch {\n            showError(\"Failed to initialise payment: \\(error.localizedDescription)\")\n        }\n    }\n    \n    private func navigateToSuccessScreen() {\n        // Navigate to success screen\n        print(\"Payment successful!\")\n    }\n    \n    private func showError(_ message: String) {\n        errorMessage = message\n        isLoading = false\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"callback-data","__idx":21},"children":["Callback data"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This section describes the data received by the different callbacks as part of the 3DS flow."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Note that the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreInitiateAuthentication"]}," callback doesn't receive anything so isn't included. Instead, it returns your 3DS configuration in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PreInitiateIntegratedAuthenticationData"]}," object."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"onpostinitiateauthentication","__idx":22},"children":["onPostInitiateAuthentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPostInitiateAuthentication"]}," callback receives only the authentication identifier. Use this ID to retrieve full authentication details from your backend."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"event-data","__idx":23},"children":["Event data"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Parameter"},"children":["Parameter"]},{"$$mdtype":"Tag","name":"th","attributes":{"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":["stateData"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["State information including code and message."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"example-implementation","__idx":24},"children":["Example implementation"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"submitConfig.onPostInitiateAuthentication = { result in\n    print(\"3DS pre-initiation completed: \\(result)\")\n    \n    // Check for failures\n    if let failed = result as? FailedAuthenticationResult {\n        print(\"3DS pre-initiation failed: \\(failed.errorReason ?? \"Unknown error\")\")\n        return\n    }\n    \n    // Success case - pre-initiation completed\n    print(\"3DS pre-initiation successful\")\n    \n    // Access state data if available\n    let stateData = result.stateData\n    print(\"State: \\(stateData.code) - \\(stateData.message)\")\n}\n        \n    // Update UI based on state\n    switch stateData.code {\n    case \"PendingClientData\":\n        print(\"Waiting for client data collection\")\n        updateAuthenticationState(\"Collecting device data...\")\n    case \"AuthenticationSuccessful\":\n        print(\"Authentication completed successfully\")\n        updateAuthenticationState(\"Authentication successful\")\n    default:\n        updateAuthenticationState(stateData.code)\n    }\n}\n\nprivate func updateAuthenticationState(_ state: String) {\n    // Update UI with authentication state\n    print(\"Authentication state updated: \\(state)\")\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"success","__idx":25},"children":["Success"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When successful, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPostInitiateAuthentication"]}," receives a success result with the following properties:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Parameter"},"children":["Parameter"]},{"$$mdtype":"Tag","name":"th","attributes":{"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":["state"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The state of the authentication.",{"$$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":["AuthenticationSuccessful"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AuthenticationFailed"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AuthenticationRejected"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PendingCustomerChallenge"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PendingClientData"]}]}]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["stateData"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Additional state information including code and message."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"failure","__idx":26},"children":["Failure"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When unsuccessful, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPostInitiateAuthentication"]}," receives a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FailedAuthenticationResult"]}," with error details:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Parameter"},"children":["Parameter"]},{"$$mdtype":"Tag","name":"th","attributes":{"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":["errorCode"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The error code."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["errorReason"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The reason for the error."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["correlationId"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The correlation ID for tracking."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"onpreauthentication","__idx":27},"children":["onPreAuthentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreAuthentication"]}," callback doesn't receive parameters. It should return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["InitiateIntegratedAuthenticationData"]}," with authentication configuration."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Here's an example:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"submitConfig.onPreAuthentication = { async in\n    print(\"Configuring 3DS authentication\")\n    \n    // Get authentication decision (evaluated after onPostInitiateAuthentication)\n    let decision = await getAuthenticationDecision()\n    \n    if !decision {\n        // Not proceeding with authentication\n        return nil\n    }\n    \n    return InitiateIntegratedAuthenticationData(\n        merchantCountryNumericCode: \"840\",\n        merchantLegalName: \"Your Company Ltd\",\n        challengeWindowSize: .size5,\n        requestorChallengeIndicator: .noPreference,\n        shopper: ThreeDSShopper(\n            email: \"customer@example.com\",\n            mobilePhone: \"+1234567890\"\n        ),\n        timeout: 300\n    )\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"onpostauthentication","__idx":28},"children":["onPostAuthentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPostAuthentication"]}," callback receives the authentication result with state information."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"event-data-1","__idx":29},"children":["Event data"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Parameter"},"children":["Parameter"]},{"$$mdtype":"Tag","name":"th","attributes":{"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":["state"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The state of the authentication.",{"$$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":["AuthenticationSuccessful"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AuthenticationFailed"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AuthenticationRejected"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PendingCustomerChallenge"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PendingClientData"]}]}]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["stateData"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Additional state information including code and message."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"example-implementation-1","__idx":30},"children":["Example implementation"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"submitConfig.onPostAuthentication = { authResult in\n    print(\"Authentication result: \\(authResult)\")\n    \n    // Access state data\n    let stateData = authResult.stateData\n    print(\"Authentication state: \\(stateData.code) - \\(stateData.message)\")\n    \n    // Check authentication result type\n    if authResult is SuccessAuthenticationResult {\n        print(\"3DS authentication successful\")\n    } else if let failed = authResult as? FailedAuthenticationResult {\n        print(\"3DS authentication failed: \\(failed.errorReason ?? \"Unknown error\")\")\n    }\n}\n            \n            print(\"Authentication result:\", fullAuthResult)\n            \n            // Check transaction status\n            switch fullAuthResult.transactionStatus {\n            case \"Y\":\n                print(\"Authentication successful - no challenge needed\")\n                showMessage(\"Card verified successfully\")\n                \n            case \"C\":\n                print(\"Challenge completed - checking result...\")\n                if fullAuthResult.state == \"AuthenticationSuccessful\" {\n                    print(\"Challenge completed successfully\")\n                    showMessage(\"Verification completed\")\n                } else {\n                    print(\"Challenge failed\")\n                    showError(\"Verification failed. Please try again.\")\n                    return\n                }\n                \n            case \"N\":\n                print(\"Authentication failed\")\n                showError(\"Card verification failed\")\n                return\n                \n            case \"R\":\n                print(\"Authentication rejected\")\n                showError(\"Payment was rejected by your bank\")\n                return\n                \n            default:\n                print(\"Unknown authentication status\")\n            }\n            \n            // Backend evaluates authentication result to update authorisation decision\n            let authorisationDecision = await evaluateAuthenticationAndUpdateAuthorization(fullAuthResult)\n            \n            print(\"Proceeding to final authorisation...\")\n        }\n    }\n    \n    // Backend evaluates authentication result to update authorisation decision\n    if authResult is FailedAuthenticationResult {\n        print(\"Authentication failed\")\n        showError(\"Card verification failed. Please try a different payment method.\")\n        // Don't proceed to authorisation - update session accordingly\n    } else {\n        print(\"Authentication successful\")\n        // Backend can now proceed with authorisation decision\n    }\n}\n\nprivate func showMessage(_ message: String) {\n    print(\"Message: \\(message)\")\n}\n\nprivate func showError(_ message: String) {\n    print(\"Error: \\(message)\")\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"onpreauthorisation","__idx":31},"children":["onPreAuthorisation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreAuthorisation"]}," callback receives only the gateway token ID. Use this ID to retrieve token details and make authorisation decisions on your backend."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"event-data-2","__idx":32},"children":["Event data"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Parameter"},"children":["Parameter"]},{"$$mdtype":"Tag","name":"th","attributes":{"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":["gatewayTokenId"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The token ID from the payment gateway. Use this ID to retrieve full token details from the Unity backend and update transaction decision."]}]}]}]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["gatewayTokenId"]}," with the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/apis/token-vault/other/get-masked-card-related-to-gateway-token"},"children":["Get masked card data related to gateway token"]}," API to retrieve full token details including card scheme, funding source (credit/debit), masked PAN, and expiry date."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"example-implementation-2","__idx":33},"children":["Example implementation"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"submitConfig.onPreAuthorisation = { preAuthData async in\n    print(\"Final authorisation data: \\(String(describing: preAuthData))\")\n    \n    // Access optional token data if available\n    if let tokenId = preAuthData?.gatewayTokenId {\n        print(\"Gateway token: \\(tokenId)\")\n        \n        // Use gatewayTokenId to retrieve token details and update transaction decision\n        let transactionDecision = await getAuthorisationDecision(tokenId)\n        \n        if !transactionDecision {\n            // Not proceeding\n            return nil\n        }\n    }\n    \n    // Add risk screening data for fraud detection\n    return TransactionInitiationData(\n        riskScreeningData: RiskScreeningData(\n            performRiskScreening: true,\n            excludeDeviceData: false,\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                )\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                    )\n                )\n            ]\n        )\n    )\n}\n\nprivate func generateDeviceSessionId() -> String {\n    return UUID().uuidString.replacingOccurrences(of: \"-\", with: \"\")\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["3DS external data (obtained from external authentication sources) shouldn't be provided via the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["threeDSecureData"]}," return parameter. Instead, provide this data to the backend via the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/apis/session/other/modify-session"},"children":["Modify session"]}," API."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["gatewayTokenId"]}," with the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/apis/token-vault/other/get-masked-card-related-to-gateway-token"},"children":["Get masked card data related to gateway token"]}," API to retrieve full token details including card scheme, funding source (credit/debit), masked PAN, and expiry date."]}]}]},"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: Submission","id":"step-1-submission","depth":3},{"value":"Step 2: Card tokenisation","id":"step-2-card-tokenisation","depth":3},{"value":"Step 3: Evaluation","id":"step-3-evaluation","depth":3},{"value":"Step 4: Pre-initiation","id":"step-4-pre-initiation","depth":3},{"value":"Step 5: Fingerprinting","id":"step-5-fingerprinting","depth":3},{"value":"Step 6: Authentication","id":"step-6-authentication","depth":3},{"value":"Step 7: Authentication result","id":"step-7-authentication-result","depth":3},{"value":"Step 8: Authorisation","id":"step-8-authorisation","depth":3},{"value":"Step 9: Authorisation result","id":"step-9-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 callbacks","id":"step-2-implement-callbacks","depth":3},{"value":"Step 3: Handle common scenarios","id":"step-3-handle-common-scenarios","depth":3},{"value":"Conditional 3DS","id":"conditional-3ds","depth":4},{"value":"Different transaction types","id":"different-transaction-types","depth":4},{"value":"Step 4: Handle errors","id":"step-4-handle-errors","depth":3},{"value":"Complete example","id":"complete-example","depth":2},{"value":"Callback data","id":"callback-data","depth":2},{"value":"onPostInitiateAuthentication","id":"onpostinitiateauthentication","depth":3},{"value":"Event data","id":"event-data","depth":4},{"value":"Example implementation","id":"example-implementation","depth":4},{"value":"Success","id":"success","depth":4},{"value":"Failure","id":"failure","depth":4},{"value":"onPreAuthentication","id":"onpreauthentication","depth":3},{"value":"onPostAuthentication","id":"onpostauthentication","depth":3},{"value":"Event data","id":"event-data-1","depth":4},{"value":"Example implementation","id":"example-implementation-1","depth":4},{"value":"onPreAuthorisation","id":"onpreauthorisation","depth":3},{"value":"Event data","id":"event-data-2","depth":4},{"value":"Example implementation","id":"example-implementation-2","depth":4}],"frontmatter":{"seo":{"title":"3DS transactions"}},"lastModified":"2026-06-12T11:56:36.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/checkout/components/ios/card/3ds","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}