# 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 code | Description | Common causes |
|  --- | --- | --- |
| `REQUIRED_FIELD` | A required field is missing. | Missing mandatory configuration. |
| `INVALID_AMOUNT_FORMAT` | The amount format is invalid. | Non-decimal amount value. |
| `INVALID_CURRENCY_CODE` | The currency code is invalid. | Non-ISO 4217 currency format. |
| `INVALID_COUNTRY_CODE` | The country code is invalid. | Non-ISO 3166-1 alpha-2 format. |
| `INVALID_MERCHANT_CAPABILITIES` | Merchant capabilities invalid. | Unsupported capability values. |
| `INVALID_PAYMENT_NETWORKS` | Payment networks invalid. | Unsupported network values. |
| `MISSING_MERCHANT_ID` | The merchant identifier is missing. | Apple Pay merchant ID not configured. |
| `INVALID_CONTACT_FIELDS` | Contact fields invalid. | Unsupported contact field values. |
| `INVALID_SHIPPING_METHODS` | Shipping methods invalid. | Malformed shipping method data. |
| `INVALID_LINE_ITEMS` | Line items invalid. | Malformed payment summary items. |
| `APPLE_PAY_NOT_AVAILABLE` | Apple Pay not available. | Unsupported device/iOS version. |
| `MISSING_ENTITLEMENTS` | Apple Pay entitlements missing. | App not configured for Apple Pay. |
| `INVALID_MERCHANT_CERTIFICATE` | Merchant certificate invalid. | Certificate configuration issue. |


### Validation example

```swift
// 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.

```swift
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.

```swift
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.

```swift
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.

```swift
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.

```swift
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.

```swift
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.

```swift
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:

```swift
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
}
```