Skip to content

Non-3DS

Process an Apple Pay transaction without 3DS for iOS applications.

Overview

By implementing a non-3DS Apple Pay payment flow on iOS, you benefit from:

  • Faster checkout: Streamlined Apple Pay process with minimal steps and no authentication redirects.
  • Better conversion: Apple Pay's inherent trust combined with reduced friction leads to higher completion rates.
  • Simplified integration: Fewer callbacks and less complex implementation while maintaining Apple Pay's security benefits.
  • Lower latency: Direct payment processing leveraging Apple Pay's built-in tokenisation without additional authentication steps.
  • Apple Pay advantages: Built-in device authentication (Touch ID/Face ID) provides security even without 3DS.

However, non-3DS payments typically don't qualify for full liability shift protection. They're best suited for low-risk transactions, trusted customers, transactions below regulatory thresholds, or when Apple Pay's built-in security is deemed sufficient.

Payment flow

The Apple Pay non-3DS payment flow on iOS is a streamlined process consisting of five key steps.

Step 1: Apple Pay authorisation

The customer authorises the payment using Touch ID, Face ID, or passcode in the Apple Pay sheet. The payment is triggered when the user interacts with the Apple Pay button. Apple's built-in validation occurs within the Apple Pay sheet. If validation passes, the payment token is generated. If it fails, the Apple Pay sheet shows an error and doesn't proceed.

Step 2: Payment token processing

Apple Pay generates an encrypted payment token containing the card details. This token is securely transmitted to your application without exposing actual card numbers.

Step 3: 3DS evaluation

The SDK evaluates whether 3DS authentication is required. In this flow, 3DS is not required based on factors such as:

  • Transaction amount falling below risk thresholds.
  • Apple Pay's built-in security being deemed sufficient.
  • Your configuration not requiring 3DS authentication.
  • Regulatory exemptions being applicable (low-value payments, transaction risk analysis).
  • Apple Pay providing adequate authentication through biometric verification.

Since 3DS is not required, the flow skips all authentication steps and proceeds directly to authorisation.

Step 4: Authorisation

This is the final processing step where the SDK sends the authorisation request directly to the payment gateway with the Apple Pay token but without any 3DS authentication data. The transaction data is processed using Apple Pay's secure tokenisation.

The authorisation step has two associated callbacks:

  • onPreAuthorisation: Provides Apple Pay transaction data for final review. This is your last chance to modify the transaction before authorisation. Note that in non-3DS flows, no authentication results are included.
  • onPostAuthorisation: Receives the final transaction result from the payment gateway along with Apple Pay specific data. The transaction is either approved or declined with standard payment processing details plus Apple Pay token information.

Step 5: Authorisation result

You receive the final authorisation response from the payment gateway. The transaction is either approved or declined and final transaction details are available, including Apple Pay payment data (billing contact, shipping contact if requested). Since this is a non-3DS flow, no authentication confirmation data is included in the response.

Implementation

Before you start

To use non-3DS Apple Pay payments in your iOS application:

  • Configure Apple Pay:
    • Set up your Apple Pay merchant identifier.
    • Add Apple Pay capability to your iOS app.
    • Configure your Apple Developer account with merchant IDs.
  • Configure merchant settings:
    • Ensure your merchant account supports Apple Pay.
    • Configure your risk settings appropriately in the Unity Portal.
    • Set up appropriate transaction limits for non-3DS processing.
  • Check device requirements:
    • iOS 10.3+ for basic Apple Pay support.
    • Touch ID, Face ID, or Passcode enabled on device.
    • Apple Pay enabled in device settings.

Step 1: Configure your SDK

Set up your CheckoutConfig with Apple Pay and basic transaction information.

let checkoutConfig = CheckoutConfig(
    environment: .test,
    session: SessionConfig(
        sessionId: "your-session-id",
        allowedFundingTypes: AllowedFundingTypes(
            wallets: WalletConfig(
                applePay: ApplePayConfig(
                    merchantId: "merchant.com.yourcompany"
                )
            )
        )
    ),
    transactionData: TransactionData(
        amount: 49.99,
        currency: "USD",
        entryType: .ecom,
        intent: .sale,
        merchantTransactionId: "order-123",
        merchantTransactionDate: Date(),
        // Optional shopper data for enhanced processing
        shopper: Shopper(
            email: "customer@example.com",
            firstName: "John",
            lastName: "Doe"
        )
    )
)

Step 2: Implement Apple Pay callbacks

Configure the Apple Pay component for non-3DS processing.

let applePayConfig = ApplePayButtonComponentConfig()

// Basic payment configuration
applePayConfig.currencyCode = "USD"
applePayConfig.countryCode = "US"

// Supported payment networks
applePayConfig.supportedNetworks = [.visa, .masterCard, .amex, .discover]

// Merchant capabilities - Note: NO .threeDSecure for non-3DS
applePayConfig.merchantCapabilities = [.emv, .credit, .debit]

// Contact field requirements
applePayConfig.requiredBillingContactFields = [.postalAddress, .name, .emailAddress]

// Payment items
applePayConfig.totalPaymentItem = ApplePayPaymentSummaryItem(
    amount: 49.99,
    label: "Total",
    type: .final
)

applePayConfig.paymentItems = [
    ApplePayPaymentSummaryItem(amount: 45.00, label: "Product", type: .final),
    ApplePayPaymentSummaryItem(amount: 4.99, label: "Tax", type: .final)
]

// Button appearance
applePayConfig.buttonType = .buy
applePayConfig.buttonStyle = .black
applePayConfig.buttonRadius = 8.0

// OPTIONAL: Provide additional transaction data (no 3DS configuration needed)
applePayConfig.onPreAuthorisation = {
    print("Processing non-3DS Apple Pay transaction")
    
    // Return minimal transaction data for non-3DS processing
    return ApplePayTransactionInitData(
        // NO threeDSecureData for non-3DS flow
        threeDSecureData: nil,
        
        // Identity verification
        identityVerification: IdentityVerification(
            nameVerification: true
        ),
        
        // Address verification can still be used
        addressVerification: AddressVerification(
            countryCode: "US",
            houseNumberOrName: "123 Main St",
            postalCode: "10001"
        ),
        
        // Risk screening data for fraud detection (without 3DS)
        riskScreeningData: RiskScreeningData(
            performRiskScreening: true,
            deviceSessionId: generateDeviceSessionId(),
            items: [
                Item(
                    name: "Product Name",
                    price: 45.00,
                    quantity: 1
                )
            ],
            transaction: TransactionRiskData(
                deviceChannel: "IOS_APP",
                paymentMethod: "APPLE_PAY",
                deviceAuthentication: "BIOMETRIC", // Touch ID/Face ID provides authentication
                walletProvider: "APPLE"
            )
        )
    )
}

// REQUIRED: Handle the final result
applePayConfig.onPostAuthorisation = { result in
    print("Apple Pay result: \(result)")
    
    if let authorizedResult = result as? AuthorisedSubmitResult {
        print("Apple Pay payment successful!")
        print("Transaction ID: \(authorizedResult.provider.code)")
        
        // Verify no 3DS data (should be nil)
        if authorizedResult.threeDSData != nil {
            print("Warning: Unexpected 3DS data in non-3DS Apple Pay flow")
        }
        
        // Log Apple Pay specific data
        print("Apple Pay token processed successfully")
        
        // Store transaction details
        storeTransactionRecord(
            transactionId: authorizedResult.provider.code,
            amount: 49.99,
            currency: "USD",
            paymentMethod: "APPLE_PAY",
            processingType: "non-3ds"
        )
        
        // Navigate to success screen
        DispatchQueue.main.async {
            self.showPaymentSuccess(transactionId: authorizedResult.provider.code)
        }
        
    } else if let failedResult = result as? FailedSubmitResult {
        print("Apple Pay payment failed: \(failedResult.errorReason)")
        
        DispatchQueue.main.async {
            self.showError("Payment failed. Please try again.")
        }
    }
}

// OPTIONAL: Handle Apple Pay errors
applePayConfig.onError = { error in
    print("Apple Pay error: \(error.localizedDescription)")
    
    DispatchQueue.main.async {
        // Handle specific non-3DS Apple Pay errors
        if let _ = error as? ApplePayValidationException {
            self.showError("Apple Pay configuration error. Please contact support.")
        } else if let _ = error as? ApplePayPaymentFailedException {
            self.showError("Payment processing failed. Please try again.")
        } else if error.localizedDescription.contains("Network") {
            self.showError("Connection failed. Please check your internet and try again.")
        } else {
            self.showError("Apple Pay failed. Please try again.")
        }
    }
}

// OPTIONAL: Handle cancellation
applePayConfig.onCancel = { error in
    print("Apple Pay cancelled by user")
    // User closed Apple Pay sheet - no error message needed
}

Step 3: Handle common scenarios

Amount-based processing

Use different processing approaches based on transaction amounts.

applePayConfig.onPreAuthorisation = {
    let amount = checkoutConfig.transactionData.amount
    
    // Add enhanced risk indicators for higher amounts
    if amount > 100 {
        return ApplePayTransactionInitData(
            addressVerification: AddressVerification(
                countryCode: "US",
                houseNumberOrName: "123 Main St",
                postalCode: "10001"
            ),
            riskScreeningData: RiskScreeningData(
                performRiskScreening: true,
                deviceSessionId: generateDeviceSessionId(),
                transaction: TransactionRiskData(
                    paymentMethod: "APPLE_PAY",
                    riskLevel: "MEDIUM", // Higher scrutiny for larger amounts
                    transactionContext: [
                        "amount": String(amount),
                        "isHighValue": "true"
                    ]
                )
            )
        )
    }
    
    // Minimal data for smaller amounts
    return ApplePayTransactionInitData(
        addressVerification: AddressVerification(
            countryCode: "US"
        ),
        riskScreeningData: RiskScreeningData(
            performRiskScreening: false, // Skip for low amounts
            transaction: TransactionRiskData(
                paymentMethod: "APPLE_PAY",
                riskLevel: "LOW"
            )
        )
    )
}

Customer type handling

Handle different customer types with varying risk profiles.

enum CustomerType {
    case new, returning, vip
}

func getCustomerRiskProfile(for customerType: CustomerType) -> [String: String] {
    switch customerType {
    case .new:
        return [
            "level": "MEDIUM", // Apple Pay reduces risk even for new customers
            "verification": "enhanced",
            "applePayTrust": "STANDARD"
        ]
    case .returning:
        return [
            "level": "LOW",
            "verification": "standard",
            "applePayTrust": "HIGH"
        ]
    case .vip:
        return [
            "level": "LOW",
            "verification": "minimal",
            "applePayTrust": "PREMIUM"
        ]
    }
}

applePayConfig.onPreAuthorisation = {
    let riskProfile = getCustomerRiskProfile(for: .returning)
    
    return ApplePayTransactionInitData(
        addressVerification: AddressVerification(
            countryCode: "US",
            houseNumberOrName: "123 Main St",
            postalCode: "10001"
        ),
        riskScreeningData: RiskScreeningData(
            performRiskScreening: true,
            deviceSessionId: generateDeviceSessionId(),
            transaction: TransactionRiskData(
                paymentMethod: "APPLE_PAY",
                customerRisk: riskProfile,
                applePayAdvantage: "BIOMETRIC_AUTH" // Leverages Touch ID/Face ID
            )
        )
    )
}

Geographic restrictions handling

Handle region-specific Apple Pay processing.

applePayConfig.onPreAuthorisation = {
    let customerCountry = getCustomerCountry()
    let isEUCustomer = EUCountries.contains(customerCountry)
    
    return ApplePayTransactionInitData(
        addressVerification: AddressVerification(
            countryCode: customerCountry,
            houseNumberOrName: "123 Main St",
            postalCode: "10001"
        ),
        riskScreeningData: RiskScreeningData(
            performRiskScreening: true,
            deviceSessionId: generateDeviceSessionId(),
            transaction: TransactionRiskData(
                paymentMethod: "APPLE_PAY",
                geography: [
                    "customerCountry": customerCountry,
                    "isEU": String(isEUCustomer),
                    // For EU, note SCA exemption reasons
                    "scaExemption": isEUCustomer ? "LOW_VALUE" : "N/A"
                ]
            )
        )
    )
}

Step 4: Handle errors

Implement comprehensive error handling for non-3DS Apple Pay payments.

applePayConfig.onError = { error in
    print("Apple Pay error: \(error.localizedDescription)")
    
    DispatchQueue.main.async {
        // Handle specific Apple Pay error types
        if let _ = error as? ApplePayValidationException {
            self.showError("Apple Pay setup error. Please contact support.")
        } else if let _ = error as? ApplePayPaymentFailedException {
            self.showError("Payment processing failed. Please try again.")
        } else if let _ = error as? ApplePaySessionCancelledException {
            print("User cancelled Apple Pay")
            // Don't show error for user cancellation
        } else if let _ = error as? ApplePayNotAvailableException {
            self.showError("Apple Pay is not available on this device.")
            self.showAlternativePaymentMethods()
        } else if error.localizedDescription.contains("Network") {
            self.showError("Connection failed. Please check your internet and try again.")
        } else {
            self.showError("Payment failed. Please try again.")
        }
    }
}

applePayConfig.onPostAuthorisation = { result in
    if let failedResult = result as? FailedSubmitResult {
        print("Apple Pay transaction failed: \(failedResult.errorReason)")
        
        DispatchQueue.main.async {
            // Handle specific failure scenarios
            switch failedResult.errorCode {
            case "INSUFFICIENT_FUNDS":
                self.showError("Insufficient funds. Please try a different payment method.")
            case "CARD_DECLINED":
                self.showError("Your card was declined. Please try a different card in Apple Pay.")
            case "EXPIRED_CARD":
                self.showError("Card expired. Please update your payment method in Apple Pay.")
            case "INVALID_MERCHANT":
                self.showError("Merchant configuration error. Please contact support.")
            case "NETWORK_ERROR":
                self.showError("Connection failed. Please check your internet and try again.")
            default:
                self.showError("Payment failed. Please try again or contact support.")
            }
        }
    }
}

Example

The following example shows a complete non-3DS Apple Pay implementation for iOS.

import UIKit
import PXPCheckoutSDK
import PassKit

class NonThreeDSCheckoutViewController: UIViewController {
    
    private var applePayComponent: ApplePayButtonComponent?
    private var checkout: PxpCheckout?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Check Apple Pay availability first
        if PKPaymentAuthorizationController.canMakePayments() {
            setupApplePayNon3DS()
        } else {
            print("Apple Pay not available")
            showAlternativePaymentMethods()
        }
    }
    
    private func setupApplePayNon3DS() {
        // Initialise the SDK
        let checkoutConfig = CheckoutConfig(
            environment: .test,
            session: SessionConfig(
                sessionId: "your-session-id",
                allowedFundingTypes: AllowedFundingTypes(
                    wallets: WalletConfig(
                        applePay: ApplePayConfig(
                            merchantId: "merchant.com.yourcompany"
                        )
                    )
                )
            ),
            transactionData: TransactionData(
                amount: 49.99,
                currency: "USD",
                entryType: .ecom,
                intent: .sale,
                merchantTransactionId: "order-\(Int(Date().timeIntervalSince1970))",
                merchantTransactionDate: Date(),
                shopper: Shopper(
                    email: "customer@example.com",
                    firstName: "John",
                    lastName: "Doe"
                )
            )
        )
        
        do {
            checkout = try PxpCheckout.initialize(config: checkoutConfig)
            
            // Create the Apple Pay component for non-3DS processing
            let applePayConfig = createNon3DSApplePayConfig()
            applePayComponent = try checkout?.create(.applePayButton, componentConfig: applePayConfig)
            
            // Add the component to your view
            if let componentView = applePayComponent?.render() {
                view.addSubview(componentView)
                setupConstraints(for: componentView)
            }
            
        } catch {
            print("Failed to initialize checkout: \(error)")
        }
    }
    
    private func createNon3DSApplePayConfig() -> ApplePayButtonComponentConfig {
        let config = ApplePayButtonComponentConfig()
        
        // Basic configuration
        config.currencyCode = "USD"
        config.countryCode = "US"
        config.supportedNetworks = [.visa, .masterCard, .amex, .discover]
        
        // Merchant capabilities - NO .threeDSecure for non-3DS
        config.merchantCapabilities = [.emv, .credit, .debit]
        
        // Contact fields
        config.requiredBillingContactFields = [.postalAddress, .name, .emailAddress]
        
        // Payment items
        config.totalPaymentItem = ApplePayPaymentSummaryItem(
            amount: 49.99,
            label: "Total",
            type: .final
        )
        
        config.paymentItems = [
            ApplePayPaymentSummaryItem(amount: 45.00, label: "Product", type: .final),
            ApplePayPaymentSummaryItem(amount: 4.99, label: "Tax", type: .final)
        ]
        
        // Button appearance
        config.buttonType = .buy
        config.buttonStyle = .black
        config.buttonRadius = 8.0
        
        // Step 1: Process Apple Pay transaction (no 3DS)
        config.onPreAuthorisation = { [weak self] in
            print("Processing Apple Pay without 3DS")
            
            return ApplePayTransactionInitData(
                // NO threeDSecureData for non-3DS flow
                threeDSecureData: nil,
                
                // Identity verification
                identityVerification: IdentityVerification(
                    nameVerification: true
                ),
                
                // Basic address verification
                addressVerification: AddressVerification(
                    countryCode: "US",
                    houseNumberOrName: "123 Main St",
                    postalCode: "10001"
                ),
                
                // Enhanced fraud detection leveraging Apple Pay security
                riskScreeningData: RiskScreeningData(
                    performRiskScreening: true,
                    deviceSessionId: self?.generateDeviceSessionId() ?? "",
                    items: [
                        Item(
                            name: "Product Name",
                            category: "General",
                            price: 45.00,
                            quantity: 1
                        )
                    ],
                    transaction: TransactionRiskData(
                        deviceChannel: "IOS_APP",
                        paymentMethod: "APPLE_PAY",
                        deviceAuthentication: "BIOMETRIC", // Touch ID/Face ID
                        walletProvider: "APPLE",
                        processingType: "non-3ds",
                        applePayAdvantages: [
                            "DEVICE_BOUND_TOKEN",
                            "BIOMETRIC_AUTH",
                            "HARDWARE_SECURITY"
                        ]
                    )
                )
            )
        }
        
        // Step 2: Handle success/failure
        config.onPostAuthorisation = { [weak self] result in
            print("Apple Pay non-3DS result: \(result)")
            
            DispatchQueue.main.async {
                if let authorizedResult = result as? AuthorisedSubmitResult {
                    print("Payment successful!")
                    print("Transaction ID: \(authorizedResult.provider.code)")
                    
                    // Verify this was indeed a non-3DS transaction
                    if authorizedResult.threeDSData != nil {
                        print("Warning: Unexpected 3DS data in non-3DS flow")
                    } else {
                        print("Confirmed non-3DS Apple Pay transaction")
                    }
                    
                    // Log Apple Pay advantages used
                    print("Apple Pay security features:", [
                        "deviceBoundToken": true,
                        "biometricAuth": true,
                        "hardwareSecurity": true,
                        "tokenization": true
                    ])
                    
                    // Store transaction record
                    self?.storeTransactionRecord(
                        transactionId: authorizedResult.provider.code,
                        amount: 49.99,
                        currency: "USD",
                        paymentMethod: "APPLE_PAY",
                        processingType: "non-3ds",
                        securityFeatures: [
                            "applePay": true,
                            "biometric": true,
                            "tokenized": true,
                            "threeDS": false
                        ]
                    )
                    
                    // Navigate to success screen
                    self?.showPaymentSuccess(transactionId: authorizedResult.provider.code)
                    
                } else if let failedResult = result as? FailedSubmitResult {
                    print("Payment failed: \(failedResult.errorReason)")
                    self?.showError("Payment failed: \(failedResult.errorReason ?? "Please try again")")
                }
            }
        }
        
        // Step 3: Error handling
        config.onError = { [weak self] error in
            print("Apple Pay error: \(error.localizedDescription)")
            
            DispatchQueue.main.async {
                if let _ = error as? ApplePayNotAvailableException {
                    self?.showAlternativePaymentMethods()
                } else {
                    self?.showError("Apple Pay failed. Please try again.")
                }
            }
        }
        
        // Step 4: Handle cancellation
        config.onCancel = { error in
            print("Apple Pay cancelled")
            // User cancelled - no error needed
        }
        
        return config
    }
    
    // Helper functions
    private func generateDeviceSessionId() -> String {
        return "device_\(UUID().uuidString.replacingOccurrences(of: "-", with: ""))_\(Int(Date().timeIntervalSince1970))"
    }
    
    private func showError(_ message: String) {
        let alert = UIAlertController(title: "Payment Error", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
    
    private func showPaymentSuccess(transactionId: String) {
        let alert = UIAlertController(title: "Payment Successful", 
                                    message: "Transaction ID: \(transactionId)", 
                                    preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
    
    private func showAlternativePaymentMethods() {
        // Hide Apple Pay and show alternative payment options
        print("Showing alternative payment methods")
    }
    
    private func storeTransactionRecord(transactionId: String, amount: Double, currency: String, paymentMethod: String, processingType: String, securityFeatures: [String: Bool]) {
        let transactionData: [String: Any] = [
            "transactionId": transactionId,
            "amount": amount,
            "currency": currency,
            "paymentMethod": paymentMethod,
            "processingType": processingType,
            "securityFeatures": securityFeatures,
            "timestamp": Date().timeIntervalSince1970
        ]
        
        // Store transaction details for audit/reconciliation
        UserDefaults.standard.set(transactionData, forKey: "last_transaction")
        
        // Send to analytics if needed
        print("Transaction stored: \(transactionData)")
    }
    
    private func setupConstraints(for componentView: UIView) {
        componentView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            componentView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            componentView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            componentView.widthAnchor.constraint(equalToConstant: 280),
            componentView.heightAnchor.constraint(equalToConstant: 50)
        ])
    }
}

Callback data

This section describes the data received by the different callbacks as part of the Apple Pay non-3DS flow on iOS.

onPreAuthorisation

The onPreAuthorisation callback receives no parameters for Apple Pay. It should return transaction initialisation data without 3DS configuration.

Return data structure

ApplePayTransactionInitData(
    // NO threeDSecureData for non-3DS flow
    threeDSecureData: nil,
    
    // Identity verification (optional)
    identityVerification: IdentityVerification(
        nameVerification: true
    ),
    
    // Address verification (optional but recommended)
    addressVerification: AddressVerification(
        countryCode: "US",
        houseNumberOrName: "123 Main St",
        postalCode: "10001"
    ),
    
    // Risk screening data (leveraging Apple Pay advantages)
    riskScreeningData: RiskScreeningData(
        performRiskScreening: true,
        deviceSessionId: "device_abc123_1704067200000",
        items: [
            Item(
                name: "Product Name",
                price: 49.99,
                quantity: 1
            )
        ],
        transaction: TransactionRiskData(
            paymentMethod: "APPLE_PAY",
            deviceAuthentication: "BIOMETRIC",
            walletProvider: "APPLE"
        )
    )
)

Here's an example of what to return:

config.onPreAuthorisation = {
    print("Preparing Apple Pay non-3DS transaction")
    
    // Enhanced risk assessment data leveraging Apple Pay's security
    return ApplePayTransactionInitData(
        // NO 3DS data for non-3DS flow
        threeDSecureData: nil,
        
        // Identity verification
        identityVerification: IdentityVerification(
            nameVerification: true
        ),
        
        // Address verification
        addressVerification: AddressVerification(
            countryCode: "US",
            houseNumberOrName: "123 Main St",
            postalCode: "10001"
        ),
        
        // Risk screening leveraging Apple Pay advantages
        riskScreeningData: RiskScreeningData(
            performRiskScreening: true,
            deviceSessionId: generateDeviceSessionId(),
            items: [
                Item(
                    name: "Product Name",
                    category: "General",
                    price: 49.99,
                    quantity: 1,
                    sku: "PROD-001"
                )
            ],
            fulfillments: [
                Fulfillment(
                    type: "SHIPPING",
                    address: Address(
                        countryCode: "US",
                        postalCode: "10001"
                    )
                )
            ],
            transaction: TransactionRiskData(
                deviceChannel: "IOS_APP",
                paymentMethod: "APPLE_PAY",
                deviceAuthentication: "BIOMETRIC", // Touch ID/Face ID
                walletProvider: "APPLE",
                processingType: "non-3ds",
                securityLevel: "HIGH", // Due to Apple Pay
                riskReduction: "APPLE_PAY_TOKENIZATION",
                applePayAdvantages: [
                    "deviceBoundToken",
                    "biometricAuthentication", 
                    "hardwareSecurityModule",
                    "tokenization"
                ]
            )
        )
    )
}

onPostAuthorisation

The onPostAuthorisation callback receives the transaction result (BaseSubmitResult) as a parameter.

Success result

If the transaction was successful, you'll receive an AuthorisedSubmitResult with transaction details.

// Transaction result structure
AuthorisedSubmitResult(
    state: "Authorised",
    provider: ProviderResult(
        code: "00",
        message: "Approved"
    ),
    fundingData: FundingData(
        cardVerificationCodeResult: "Not Applicable", // Apple Pay doesn't expose CVC
        addressVerificationServiceResult: "Y"
    ),
    threeDSData: nil // No 3DS data for non-3DS flow
)

Here's an example of handling the success result:

config.onPostAuthorisation = { result in
    print("Apple Pay non-3DS result: \(result)")
    
    DispatchQueue.main.async {
        if let authorizedResult = result as? AuthorisedSubmitResult {
            print("Apple Pay payment successful!")
            
            // Verify no 3DS data
            if authorizedResult.threeDSData != nil {
                print("Warning: Unexpected 3DS data in non-3DS Apple Pay flow")
            } else {
                print("Confirmed non-3DS processing")
            }
            
            // Apple Pay provides excellent security even without 3DS
            let securityFeatures: [String: Bool] = [
                "applePayTokenization": true,
                "biometricAuth": true, // Touch ID/Face ID
                "deviceBinding": true,
                "hardwareSecurity": true,
                "threeDS": false
            ]
            
            // Store transaction record
            self.storeTransactionRecord(
                transactionId: authorizedResult.provider.code,
                amount: 49.99,
                currency: "USD",
                paymentMethod: "APPLE_PAY",
                processingType: "non-3ds",
                securityFeatures: securityFeatures
            )
            
            // Navigate to success screen
            self.showPaymentSuccess(transactionId: authorizedResult.provider.code)
        }
    }
}

Failure result

If the bank or issuer declines the transaction, you'll receive a FailedSubmitResult.

// Failure result structure
FailedSubmitResult(
    errorCode: "CARD_DECLINED",
    errorReason: "Insufficient funds",
    correlationId: "corr_12345",
    httpStatusCode: 200,
    details: ["Transaction declined by issuing bank"]
)

Here's an example of handling failures:

config.onPostAuthorisation = { result in
    if let failedResult = result as? FailedSubmitResult {
        print("Apple Pay transaction failed: \(failedResult.errorReason)")
        
        DispatchQueue.main.async {
            // Handle Apple Pay specific decline scenarios
            switch failedResult.errorCode {
            case "INSUFFICIENT_FUNDS":
                self.showError("Insufficient funds. Please try a different card in Apple Pay.")
            case "CARD_DECLINED":
                self.showError("Your card was declined. Please try a different payment method.")
            case "EXPIRED_CARD":
                self.showError("Card expired. Please update your payment method in Apple Pay.")
            case "INVALID_MERCHANT":
                self.showError("Apple Pay merchant configuration error. Please contact support.")
            default:
                self.showError("Payment failed. Please try again or contact support.")
            }
            
            // Log failure for analysis
            self.logPaymentFailure(
                paymentMethod: "APPLE_PAY",
                processingType: "non-3ds",
                errorCode: failedResult.errorCode,
                errorReason: failedResult.errorReason,
                correlationId: failedResult.correlationId
            )
        }
    }
}