Skip to content

Data validation

Learn about built-in validation and implement additional scenarios for Apple Pay for iOS.

Overview

The Apple Pay component includes comprehensive validation to ensure data integrity and compliance with Apple's requirements. All built-in validation is performed before creating payment requests and during the Apple Pay flow. If it fails, the SDK will throw an ApplePayValidationException with detailed error information.

You can also easily build custom validation, depending on your business needs.

Built-in validation

By default, the Apple Pay component validates that:

  • Fields marked as required are provided.
  • Payment request data meets Apple Pay specifications.
  • Currency codes, country codes, and amounts are formatted properly.
  • Merchant capabilities and supported networks are valid.
  • Contact fields and shipping methods are properly configured.
  • Device and iOS compatibility requirements are met.

Error codes

The Apple Pay component returns structured error codes for different validation failures:

Error codeDescriptionCommon causes
REQUIRED_FIELDA required field is missing.Missing mandatory configuration.
INVALID_AMOUNT_FORMATThe amount format is invalid.Non-decimal amount value.
INVALID_CURRENCY_CODEThe currency code is invalid.Non-ISO 4217 currency format.
INVALID_COUNTRY_CODEThe country code is invalid.Non-ISO 3166-1 alpha-2 format.
INVALID_MERCHANT_CAPABILITIESMerchant capabilities invalid.Unsupported capability values.
INVALID_PAYMENT_NETWORKSPayment networks invalid.Unsupported network values.
MISSING_MERCHANT_IDThe merchant identifier is missing.Apple Pay merchant ID not configured.
INVALID_CONTACT_FIELDSContact fields invalid.Unsupported contact field values.
INVALID_SHIPPING_METHODSShipping methods invalid.Malformed shipping method data.
INVALID_LINE_ITEMSLine items invalid.Malformed payment summary items.
APPLE_PAY_NOT_AVAILABLEApple Pay not available.Unsupported device/iOS version.
MISSING_ENTITLEMENTSApple Pay entitlements missing.App not configured for Apple Pay.
INVALID_MERCHANT_CERTIFICATEMerchant certificate invalid.Certificate configuration issue.

Validation example

// Example of handling validation errors
let applePayConfig = ApplePayButtonComponentConfig()

applePayConfig.onError = { error in
    print("Apple Pay error: \(error)")
    
    if let validationError = error as? ApplePayValidationException {
        // Handle specific validation errors
        switch validationError.code {
        case "INVALID_MERCHANT_CAPABILITIES":
            print("Invalid merchant capabilities configured")
            
        case "INVALID_AMOUNT_FORMAT":
            print("Invalid total amount format")
            
        case "MISSING_MERCHANT_ID":
            print("Apple Pay merchant ID missing or invalid")
            
        case "APPLE_PAY_NOT_AVAILABLE":
            print("Apple Pay not available on this device")
            
        default:
            print("Validation error: \(validationError.localizedDescription)")
        }
        
        // Display user-friendly error messages
        DispatchQueue.main.async {
            self.showValidationError(validationError)
        }
    }
}

Custom validation

Pre-authorisation validation

Run validation before the Apple Pay authorisation to catch issues early and prevent failed payments.

let applePayConfig = ApplePayButtonComponentConfig()

applePayConfig.onPreAuthorisation = { [weak self] in
    do {
        // 1. Business logic validation
        let orderValidation = try await self?.validateOrder(
            cartItems: self?.getCartItems() ?? [],
            customerLocation: self?.getCustomerLocation(),
            paymentAmount: self?.getOrderTotal() ?? 0
        )
        
        guard let orderValidation = orderValidation, orderValidation.valid else {
            throw ValidationError.orderValidationFailed(orderValidation?.reason ?? "Unknown error")
        }
        
        // 2. Apple Pay specific validation
        let applePayValidation = try await self?.validateApplePayRequirements(
            merchantId: self?.getMerchantId() ?? "",
            totalAmount: self?.getOrderTotal() ?? 0
        )
        
        guard let applePayValidation = applePayValidation, applePayValidation.valid else {
            throw ValidationError.applePayRequirementsNotMet
        }
        
        // 3. Security validation
        let securityCheck = try await self?.performSecurityValidation(
            deviceID: UIDevice.current.identifierForVendor?.uuidString ?? "",
            paymentHistory: self?.getCustomerPaymentHistory() ?? []
        )
        
        if let securityCheck = securityCheck, securityCheck.riskLevel == .high {
            // Return additional security data for transaction
            return ApplePayTransactionInitData(
                threeDSecureData: ThreeDSecureData(
                    threeDSecureVersion: "2.1.0",
                    electronicCommerceIndicator: .eci5,
                    cardHolderAuthenticationVerificationValue: "AAABBBCCCDDDEEEFFFaaabbbcccdddeee",
                    directoryServerTransactionId: "8ac7ca4f-6068-4978-8b5e-c23c0c6d2260",
                    threeDSecureTransactionStatus: "Y"
                ),
                riskScreeningData: RiskScreeningData(
                    performRiskScreening: true,
                    deviceSessionId: self?.generateDeviceSessionId() ?? "",
                    riskScore: securityCheck.score
                )
            )
        }
        
        // 4. Inventory validation
        let inventoryCheck = try await self?.validateInventory(self?.getCartItems() ?? [])
        guard let inventoryCheck = inventoryCheck, inventoryCheck.allAvailable else {
            throw ValidationError.inventoryUnavailable
        }
        
        // 5. Regulatory compliance
        let complianceCheck = try await self?.validateCompliance(
            customerCountry: self?.getCustomerCountry() ?? "",
            orderAmount: self?.getOrderTotal() ?? 0,
            productTypes: self?.getProductTypes() ?? []
        )
        
        guard let complianceCheck = complianceCheck, complianceCheck.compliant else {
            throw ValidationError.regulatoryComplianceFailure
        }
        
        // Return transaction initialisation data
        return ApplePayTransactionInitData(
            identityVerification: IdentityVerification(nameVerification: true),
            addressVerification: AddressVerification(
                countryCode: "US",
                houseNumberOrName: "123 Main St",
                postalCode: "10001"
            ),
            riskScreeningData: RiskScreeningData(
                performRiskScreening: true,
                deviceSessionId: self?.generateDeviceSessionId() ?? ""
            )
        )
        
    } catch {
        print("Pre-authorisation validation failed: \(error)")
        // Returning nil will cancel the Apple Pay flow
        return nil
    }
}

enum ValidationError: Error {
    case orderValidationFailed(String)
    case applePayRequirementsNotMet
    case inventoryUnavailable
    case regulatoryComplianceFailure
}

Post-authorisation validation

Run validation after Apple Pay authorisation to ensure payment integrity before completing the transaction.

applePayConfig.onPostAuthorisation = { [weak self] result in
    DispatchQueue.main.async {
        do {
            if let authorizedResult = result as? AuthorisedSubmitResult {
                // 1. Validate transaction data
                let transactionValidation = try await self?.validateTransactionData(
                    transactionId: authorizedResult.provider.code,
                    amount: self?.getOrderTotal() ?? 0
                )
                
                guard let transactionValidation = transactionValidation, transactionValidation.valid else {
                    throw ValidationError.transactionDataInvalid
                }
                
                // 2. Final inventory check
                let finalInventoryCheck = try await self?.reserveInventory(orderId: self?.getOrderId() ?? "")
                guard let finalInventoryCheck = finalInventoryCheck, finalInventoryCheck.success else {
                    throw ValidationError.inventoryUnavailable
                }
                
                // 3. Validate device and security
                let deviceValidation = try await self?.validateDeviceSecurity(
                    deviceID: UIDevice.current.identifierForVendor?.uuidString ?? "",
                    transactionAmount: self?.getOrderTotal() ?? 0
                )
                
                guard let deviceValidation = deviceValidation, deviceValidation.valid else {
                    throw ValidationError.deviceSecurityFailure
                }
                
                // 4. Process successful payment
                try await self?.completeOrder(
                    transactionId: authorizedResult.provider.code,
                    deviceInfo: self?.getDeviceInfo()
                )
                
                // Navigate to success screen
                self?.navigateToSuccessScreen(transactionId: authorizedResult.provider.code)
                
            } else if let failedResult = result as? FailedSubmitResult {
                // Handle payment failure
                print("Payment failed: \(failedResult.errorReason)")
                self?.showError("Payment processing failed. Please try again.")
            }
            
        } catch {
            print("Post-authorisation validation failed: \(error)")
            self?.showError("Payment validation failed: \(error.localizedDescription)")
        }
    }
}

extension ValidationError {
    case transactionDataInvalid
    case deviceSecurityFailure
}

Shipping contact validation

Validate shipping addresses in real-time as customers select them in the Apple Pay sheet.

applePayConfig.onShippingContactSelected = { [weak self] contact in
    do {
        // 1. Validate address format
        let addressValidation = self?.validateAddressFormat(contact.postalAddress)
        guard let addressValidation = addressValidation, addressValidation.valid else {
            return PKPaymentRequestShippingContactUpdate(
                errors: [PKPaymentRequest.paymentShippingAddressInvalidError(
                    withKey: PKContactField.postalAddress,
                    localizedDescription: "Please enter a complete address"
                )],
                shippingMethods: [],
                paymentSummaryItems: []
            )
        }
        
        // 2. Check shipping restrictions
        let shippingValidation = try await self?.validateShippingAvailability(
            countryCode: contact.postalAddress?.isoCountryCode ?? "",
            postalCode: contact.postalAddress?.postalCode ?? "",
            cartItems: self?.getCartItems() ?? []
        )
        
        guard let shippingValidation = shippingValidation, shippingValidation.available else {
            return PKPaymentRequestShippingContactUpdate(
                errors: [PKPaymentRequest.paymentShippingAddressUnserviceableError(
                    withLocalizedDescription: "Shipping not available to this location"
                )],
                shippingMethods: [],
                paymentSummaryItems: []
            )
        }
        
        // 3. Calculate shipping costs
        let shippingCost = try await self?.calculateShippingCost(
            address: contact.postalAddress,
            items: self?.getCartItems() ?? [],
            expedited: false
        ) ?? 0
        
        // 4. Calculate taxes
        let tax = try await self?.calculateTax(
            address: contact.postalAddress,
            subtotal: self?.getSubtotal() ?? 0
        ) ?? 0
        
        // Return updated payment details
        let subtotal = self?.getSubtotal() ?? 0
        let newTotal = subtotal + shippingCost + tax
        
        let summaryItems = [
            PKPaymentSummaryItem(label: "Subtotal", amount: NSDecimalNumber(value: subtotal)),
            PKPaymentSummaryItem(label: "Shipping", amount: NSDecimalNumber(value: shippingCost)),
            PKPaymentSummaryItem(label: "Tax", amount: NSDecimalNumber(value: tax)),
            PKPaymentSummaryItem(label: "Total", amount: NSDecimalNumber(value: newTotal))
        ]
        
        let shippingMethods = try await self?.getAvailableShippingMethods(for: contact.postalAddress) ?? []
        
        return PKPaymentRequestShippingContactUpdate(
            errors: [],
            shippingMethods: shippingMethods,
            paymentSummaryItems: summaryItems
        )
        
    } catch {
        print("Shipping contact validation failed: \(error)")
        return PKPaymentRequestShippingContactUpdate(
            errors: [PKPaymentRequest.paymentShippingAddressUnserviceableError(
                withLocalizedDescription: "Unable to validate shipping address. Please try again."
            )],
            shippingMethods: [],
            paymentSummaryItems: []
        )
    }
}

Device compatibility validation

Ensure Apple Pay requirements are met before showing the component.

func validateApplePayCompatibility() -> ValidationResult {
    var validation = ValidationResult(valid: true, errors: [])
    
    // Check if Apple Pay is supported on device
    if !PKPaymentAuthorizationController.canMakePayments() {
        validation.valid = false
        validation.errors.append(ValidationError(
            code: "APPLE_PAY_NOT_AVAILABLE",
            message: "Apple Pay not supported on this device"
        ))
    }
    
    // Check iOS version compatibility
    if #available(iOS 10.3, *) {
        // Apple Pay is available
    } else {
        validation.valid = false
        validation.errors.append(ValidationError(
            code: "IOS_VERSION_INCOMPATIBLE",
            message: "iOS 10.3 or later required for Apple Pay"
        ))
    }
    
    // Check if user has cards set up
    let supportedNetworks: [PKPaymentNetwork] = [.visa, .masterCard, .amex, .discover]
    if !PKPaymentAuthorizationController.canMakePayments(usingNetworks: supportedNetworks) {
        validation.valid = false
        validation.errors.append(ValidationError(
            code: "NO_CARDS_AVAILABLE",
            message: "No compatible cards available in Apple Pay"
        ))
    }
    
    // Check app entitlements
    if !hasApplePayEntitlement() {
        validation.valid = false
        validation.errors.append(ValidationError(
            code: "MISSING_ENTITLEMENTS",
            message: "Apple Pay capability not enabled in app"
        ))
    }
    
    return validation
}

// Use validation before initialising
func initializeApplePayIfCompatible() {
    let compatibilityCheck = validateApplePayCompatibility()
    
    if compatibilityCheck.valid {
        // Initialise Apple Pay component
        initializeApplePay()
    } else {
        // Show alternative payment methods
        showAlternativePaymentMethods()
        print("Apple Pay not available: \(compatibilityCheck.errors)")
    }
}

struct ValidationResult {
    var valid: Bool
    var errors: [ValidationError]
}

struct ValidationError {
    let code: String
    let message: String
}

Validate geographic restrictions

Check in real-time if any cart items are restricted in the customer's country to prevent compliance violations.

func validateGeographicRestrictions(customerCountry: String, cartItems: [CartItem]) -> GeographicValidationResult {
    let restrictedItems = cartItems.filter { item in
        item.restrictions?.countries?.contains(customerCountry) == true ||
        item.restrictions?.applePayRestricted?.contains(customerCountry) == true
    }
    
    // Check Apple Pay specific restrictions
    let applePayRestrictions = getApplePayCountryRestrictions()
    let applePayBlocked = !applePayRestrictions.allowedCountries.contains(customerCountry)
    
    return GeographicValidationResult(
        valid: restrictedItems.isEmpty && !applePayBlocked,
        restrictedItems: restrictedItems,
        applePayBlocked: applePayBlocked,
        message: applePayBlocked 
            ? "Apple Pay not available in your country"
            : "Some items cannot be shipped to \(customerCountry)"
    )
}

struct GeographicValidationResult {
    let valid: Bool
    let restrictedItems: [CartItem]
    let applePayBlocked: Bool
    let message: String
}

struct CartItem {
    let id: String
    let name: String
    let price: Double
    let restrictions: ItemRestrictions?
}

struct ItemRestrictions {
    let countries: [String]?
    let applePayRestricted: [String]?
}

Calculate a risk score

Calculate a comprehensive fraud risk score based on multiple behavioural and historical factors, with Apple Pay specific considerations.

func calculateFraudScore(transactionData: TransactionData, deviceInfo: DeviceInfo) async throws -> RiskAssessment {
    let factors = RiskFactors(
        velocityScore: await checkPaymentVelocity(customerEmail: transactionData.customerEmail),
        locationScore: await validateLocation(deviceInfo: deviceInfo),
        deviceScore: await analyzeDeviceFingerprint(deviceInfo: deviceInfo),
        historyScore: await analyzePaymentHistory(customerId: transactionData.customerId),
        applePayScore: await analyzeApplePayData(deviceInfo: deviceInfo)
    )
    
    // Apple Pay specific risk factors
    let applePayFactors = ApplePayRiskFactors(
        deviceAccountScore: deviceInfo.hasDeviceAccount ? 0.1 : 0.3,
        biometricScore: deviceInfo.biometricAuthentication ? 0.1 : 0.4,
        walletScore: await analyzeWalletHistory(deviceId: deviceInfo.deviceId)
    )
    
    // Weight Apple Pay factors (generally lower risk)
    let baseScore = factors.averageScore
    let applePayAdjustment = applePayFactors.averageScore
    
    let totalScore = (baseScore * 0.7) + (applePayAdjustment * 0.3) // Apple Pay generally reduces risk
    
    return RiskAssessment(
        score: totalScore,
        riskLevel: RiskLevel.from(score: totalScore),
        factors: factors,
        applePayFactors: applePayFactors,
        applePayAdvantage: baseScore - totalScore // How much Apple Pay reduced the risk
    )
}

struct RiskFactors {
    let velocityScore: Double
    let locationScore: Double
    let deviceScore: Double
    let historyScore: Double
    let applePayScore: Double
    
    var averageScore: Double {
        return (velocityScore + locationScore + deviceScore + historyScore + applePayScore) / 5.0
    }
}

struct ApplePayRiskFactors {
    let deviceAccountScore: Double
    let biometricScore: Double
    let walletScore: Double
    
    var averageScore: Double {
        return (deviceAccountScore + biometricScore + walletScore) / 3.0
    }
}

struct RiskAssessment {
    let score: Double
    let riskLevel: RiskLevel
    let factors: RiskFactors
    let applePayFactors: ApplePayRiskFactors
    let applePayAdvantage: Double
}

enum RiskLevel {
    case low, medium, high
    
    static func from(score: Double) -> RiskLevel {
        if score > 0.8 {
            return .high
        } else if score > 0.5 {
            return .medium
        } else {
            return .low
        }
    }
}

struct TransactionData {
    let customerId: String
    let customerEmail: String
    let amount: Double
    let currency: String
}

struct DeviceInfo {
    let deviceId: String
    let hasDeviceAccount: Bool
    let biometricAuthentication: Bool
    let ipAddress: String
    let location: CLLocation?
}

Validate payment amounts

Ensure payment amounts meet Apple Pay requirements and business rules.

func validatePaymentAmounts(paymentRequest: PaymentRequestData) -> AmountValidationResult {
    var validation = AmountValidationResult(valid: true, errors: [])
    
    // Validate total amount format
    guard paymentRequest.totalAmount > 0 else {
        validation.valid = false
        validation.errors.append(AmountValidationError(
            field: "totalAmount",
            code: "INVALID_AMOUNT",
            message: "Total amount must be a positive number"
        ))
        return validation
    }
    
    // Validate line items sum up to total
    if !paymentRequest.lineItems.isEmpty {
        let lineItemsSum = paymentRequest.lineItems.reduce(0) { sum, item in
            return sum + item.amount
        }
        
        let difference = abs(lineItemsSum - paymentRequest.totalAmount)
        if difference > 0.01 { // Allow for small rounding differences
            validation.valid = false
            validation.errors.append(AmountValidationError(
                field: "lineItems",
                code: "AMOUNT_MISMATCH",
                message: "Line items do not sum to total amount"
            ))
        }
    }
    
    // Check minimum/maximum amounts
    let minAmount: Double = 0.50 // Apple Pay minimum
    let maxAmount: Double = 10000.00 // Business rule maximum
    
    if paymentRequest.totalAmount < minAmount {
        validation.valid = false
        validation.errors.append(AmountValidationError(
            field: "totalAmount",
            code: "AMOUNT_TOO_LOW",
            message: "Minimum amount is \(minAmount)"
        ))
    }
    
    if paymentRequest.totalAmount > maxAmount {
        validation.valid = false
        validation.errors.append(AmountValidationError(
            field: "totalAmount",
            code: "AMOUNT_TOO_HIGH",
            message: "Maximum amount is \(maxAmount)"
        ))
    }
    
    // Validate currency code
    if !isValidCurrencyCode(paymentRequest.currencyCode) {
        validation.valid = false
        validation.errors.append(AmountValidationError(
            field: "currencyCode",
            code: "INVALID_CURRENCY",
            message: "Invalid currency code: \(paymentRequest.currencyCode)"
        ))
    }
    
    return validation
}

struct PaymentRequestData {
    let totalAmount: Double
    let currencyCode: String
    let lineItems: [LineItem]
}

struct LineItem {
    let label: String
    let amount: Double
}

struct AmountValidationResult {
    var valid: Bool
    var errors: [AmountValidationError]
}

struct AmountValidationError {
    let field: String
    let code: String
    let message: String
}

func isValidCurrencyCode(_ code: String) -> Bool {
    let validCurrencies = ["USD", "EUR", "GBP", "JPY", "CAD", "AUD", "CHF", "SEK", "NOK", "DKK"]
    return validCurrencies.contains(code)
}

Complete validation implementation

Here's a comprehensive example showing all validation working together:

import UIKit
import PXPCheckoutSDK
import PassKit
import CoreLocation

class ValidationViewController: UIViewController {
    private var applePayComponent: ApplePayButtonComponent?
    private var checkout: PxpCheckout?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupApplePayWithValidation()
    }
    
    private func setupApplePayWithValidation() {
        // First validate compatibility
        let compatibilityCheck = validateApplePayCompatibility()
        guard compatibilityCheck.valid else {
            showCompatibilityError(compatibilityCheck.errors)
            return
        }
        
        let checkoutConfig = CheckoutConfig(
            environment: .test,
            session: SessionConfig(sessionId: "your-session-id"),
            transactionData: TransactionData(
                amount: 29.99,
                currency: "USD",
                entryType: .ecom,
                intent: .sale,
                merchantTransactionId: "order-\(Int(Date().timeIntervalSince1970))",
                merchantTransactionDate: Date()
            )
        )
        
        do {
            checkout = try PxpCheckout.initialize(config: checkoutConfig)
            let applePayConfig = createValidatedApplePayConfig()
            applePayComponent = try checkout?.create(.applePayButton, componentConfig: applePayConfig)
            
            if let componentView = applePayComponent?.render() {
                view.addSubview(componentView)
                setupConstraints(for: componentView)
            }
        } catch {
            print("Failed to initialize: \(error)")
            showError("Failed to setup Apple Pay: \(error.localizedDescription)")
        }
    }
    
    private func createValidatedApplePayConfig() -> ApplePayButtonComponentConfig {
        let config = ApplePayButtonComponentConfig()
        
        // Basic configuration with validation
        config.currencyCode = "USD"
        config.countryCode = "US"
        config.supportedNetworks = [.visa, .masterCard, .amex]
        config.merchantCapabilities = [.threeDSecure, .emv, .credit, .debit]
        config.requiredBillingContactFields = [.postalAddress, .name, .emailAddress]
        config.requiredShippingContactFields = [.postalAddress, .name]
        
        // Validate amounts before setting
        let paymentData = PaymentRequestData(
            totalAmount: 29.99,
            currencyCode: "USD",
            lineItems: [
                LineItem(label: "Product", amount: 25.99),
                LineItem(label: "Tax", amount: 4.00)
            ]
        )
        
        let amountValidation = validatePaymentAmounts(paymentRequest: paymentData)
        guard amountValidation.valid else {
            print("Amount validation failed: \(amountValidation.errors)")
            return config
        }
        
        // Payment items
        config.totalPaymentItem = ApplePayPaymentSummaryItem(
            amount: 29.99,
            label: "Total",
            type: .final
        )
        
        config.paymentItems = [
            ApplePayPaymentSummaryItem(amount: 25.99, label: "Product", type: .final),
            ApplePayPaymentSummaryItem(amount: 4.00, label: "Tax", type: .final)
        ]
        
        // Button appearance
        config.buttonType = .buy
        config.buttonStyle = .black
        config.buttonRadius = 8.0
        
        // Setup validation callbacks
        setupValidationCallbacks(for: config)
        
        return config
    }
    
    private func setupValidationCallbacks(for config: ApplePayButtonComponentConfig) {
        
        // Pre-authorisation validation
        config.onPreAuthorisation = { [weak self] in
            do {
                // Comprehensive pre-authorisation validation
                try await self?.performPreAuthorizationValidation()
                
                return ApplePayTransactionInitData(
                    identityVerification: IdentityVerification(nameVerification: true),
                    addressVerification: AddressVerification(
                        countryCode: "US",
                        houseNumberOrName: "123 Main St",
                        postalCode: "10001"
                    ),
                    riskScreeningData: RiskScreeningData(
                        performRiskScreening: true,
                        deviceSessionId: self?.generateDeviceSessionId() ?? ""
                    )
                )
                
            } catch {
                print("Pre-authorisation validation failed: \(error)")
                DispatchQueue.main.async {
                    self?.showError("Validation failed: \(error.localizedDescription)")
                }
                return nil
            }
        }
        
        // Post-authorisation validation
        config.onPostAuthorisation = { [weak self] result in
            DispatchQueue.main.async {
                self?.handlePostAuthorizationWithValidation(result: result)
            }
        }
        
        // Error handling with validation
        config.onError = { [weak self] error in
            print("Apple Pay error: \(error)")
            
            DispatchQueue.main.async {
                if let validationError = error as? ApplePayValidationException {
                    self?.handleValidationError(validationError)
                } else {
                    self?.handleGeneralError(error)
                }
            }
        }
        
        // Shipping contact validation
        config.onShippingContactSelected = { [weak self] contact in
            return await self?.validateAndUpdateShippingContact(contact) ?? PKPaymentRequestShippingContactUpdate(
                errors: [PKPaymentRequest.paymentShippingAddressUnserviceableError(
                    withLocalizedDescription: "Unable to validate shipping address"
                )],
                shippingMethods: [],
                paymentSummaryItems: []
            )
        }
    }
    
    // Validation methods
    private func performPreAuthorizationValidation() async throws {
        // 1. Order validation
        let orderValid = try await validateOrder()
        guard orderValid else {
            throw ValidationError.orderValidationFailed("Order validation failed")
        }
        
        // 2. Inventory validation
        let inventoryValid = try await validateInventory()
        guard inventoryValid else {
            throw ValidationError.inventoryUnavailable
        }
        
        // 3. Security validation
        let riskAssessment = try await calculateRiskScore()
        if riskAssessment.riskLevel == .high {
            throw ValidationError.highRiskTransaction
        }
        
        // 4. Geographic restrictions
        let geoValidation = validateGeographicRestrictions(
            customerCountry: getCustomerCountry(),
            cartItems: getCartItems()
        )
        guard geoValidation.valid else {
            throw ValidationError.geographicRestriction(geoValidation.message)
        }
    }
    
    private func validateAndUpdateShippingContact(_ contact: PKContact) async -> PKPaymentRequestShippingContactUpdate {
        do {
            // Validate address format
            guard let postalAddress = contact.postalAddress else {
                return PKPaymentRequestShippingContactUpdate(
                    errors: [PKPaymentRequest.paymentShippingAddressInvalidError(
                        withKey: PKContactField.postalAddress,
                        localizedDescription: "Address is required"
                    )],
                    shippingMethods: [],
                    paymentSummaryItems: []
                )
            }
            
            // Check shipping availability
            let shippingAvailable = try await checkShippingAvailability(to: postalAddress)
            guard shippingAvailable else {
                return PKPaymentRequestShippingContactUpdate(
                    errors: [PKPaymentRequest.paymentShippingAddressUnserviceableError(
                        withLocalizedDescription: "Shipping not available to this location"
                    )],
                    shippingMethods: [],
                    paymentSummaryItems: []
                )
            }
            
            // Calculate updated amounts
            let shippingCost = try await calculateShippingCost(to: postalAddress)
            let tax = try await calculateTax(for: postalAddress)
            let total = 25.99 + shippingCost + tax
            
            let summaryItems = [
                PKPaymentSummaryItem(label: "Product", amount: NSDecimalNumber(value: 25.99)),
                PKPaymentSummaryItem(label: "Shipping", amount: NSDecimalNumber(value: shippingCost)),
                PKPaymentSummaryItem(label: "Tax", amount: NSDecimalNumber(value: tax)),
                PKPaymentSummaryItem(label: "Total", amount: NSDecimalNumber(value: total))
            ]
            
            let shippingMethods = getShippingMethods()
            
            return PKPaymentRequestShippingContactUpdate(
                errors: [],
                shippingMethods: shippingMethods,
                paymentSummaryItems: summaryItems
            )
            
        } catch {
            return PKPaymentRequestShippingContactUpdate(
                errors: [PKPaymentRequest.paymentShippingAddressUnserviceableError(
                    withLocalizedDescription: "Unable to validate address. Please try again."
                )],
                shippingMethods: [],
                paymentSummaryItems: []
            )
        }
    }
    
    private func handlePostAuthorizationWithValidation(result: BaseSubmitResult) {
        do {
            if let authorizedResult = result as? AuthorisedSubmitResult {
                // Validate transaction result
                try validateTransactionResult(authorizedResult)
                
                // Complete order
                completeOrder(transactionId: authorizedResult.provider.code)
                
                // Navigate to success
                navigateToSuccess(transactionId: authorizedResult.provider.code)
                
            } else if let failedResult = result as? FailedSubmitResult {
                handlePaymentFailure(failedResult)
            }
        } catch {
            showError("Post-authorisation validation failed: \(error.localizedDescription)")
        }
    }
    
    private func handleValidationError(_ error: ApplePayValidationException) {
        var message = "Validation failed"
        
        switch error.code {
        case "APPLE_PAY_NOT_AVAILABLE":
            message = "Apple Pay is not available on this device"
            showAlternativePaymentMethods()
        case "MISSING_MERCHANT_ID":
            message = "Apple Pay configuration error. Please contact support."
        case "INVALID_AMOUNT_FORMAT":
            message = "Invalid payment amount. Please refresh and try again."
        default:
            message = error.localizedDescription
        }
        
        showError(message)
    }
    
    // Helper methods for validation
    private func validateOrder() async throws -> Bool {
        // Simulate order validation
        return true
    }
    
    private func validateInventory() async throws -> Bool {
        // Simulate inventory check
        return true
    }
    
    private func calculateRiskScore() async throws -> RiskAssessment {
        // Simulate risk calculation
        return RiskAssessment(
            score: 0.3,
            riskLevel: .low,
            factors: RiskFactors(velocityScore: 0.2, locationScore: 0.1, deviceScore: 0.2, historyScore: 0.1, applePayScore: 0.1),
            applePayFactors: ApplePayRiskFactors(deviceAccountScore: 0.1, biometricScore: 0.1, walletScore: 0.1),
            applePayAdvantage: 0.2
        )
    }
    
    private func checkShippingAvailability(to address: CNPostalAddress) async throws -> Bool {
        // Simulate shipping availability check
        return address.isoCountryCode == "US"
    }
    
    private func calculateShippingCost(to address: CNPostalAddress) async throws -> Double {
        // Simulate shipping cost calculation
        return 5.99
    }
    
    private func calculateTax(for address: CNPostalAddress) async throws -> Double {
        // Simulate tax calculation
        return 2.60
    }
    
    private func validateTransactionResult(_ result: AuthorisedSubmitResult) throws {
        // Validate the transaction was processed correctly
        guard !result.provider.code.isEmpty else {
            throw ValidationError.invalidTransactionResult
        }
    }
    
    // UI helper methods
    private func showCompatibilityError(_ errors: [ValidationError]) {
        let message = errors.map { $0.message }.joined(separator: "\n")
        showError("Apple Pay not available:\n\(message)")
        showAlternativePaymentMethods()
    }
    
    private func showError(_ message: String) {
        let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
    
    private func showAlternativePaymentMethods() {
        // Show alternative payment options
        print("Showing alternative payment methods")
    }
    
    private func navigateToSuccess(transactionId: String) {
        let alert = UIAlertController(title: "Success", message: "Payment completed! ID: \(transactionId)", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
    
    private func handlePaymentFailure(_ result: FailedSubmitResult) {
        showError("Payment failed: \(result.errorReason)")
    }
    
    private func handleGeneralError(_ error: Error) {
        showError("An error occurred: \(error.localizedDescription)")
    }
    
    // Data helper methods
    private func getCustomerCountry() -> String { return "US" }
    private func getCartItems() -> [CartItem] { return [] }
    private func generateDeviceSessionId() -> String { return UUID().uuidString }
    private func getShippingMethods() -> [PKShippingMethod] { return [] }
    private func completeOrder(transactionId: String) { print("Order completed: \(transactionId)") }
    
    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)
        ])
    }
}

// Additional validation error types
extension ValidationError {
    case highRiskTransaction
    case geographicRestriction(String)
    case invalidTransactionResult
}