{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["sub-heading"]},"type":"markdown"},"seo":{"title":"Data validation","description":"Transform your commerce with PXP's unified platform—seamless payments, real-time insights, and global growth in one powerful integration.","lang":"en-UK","siteUrl":"https://developer.pxp.io","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"data-validation","__idx":0},"children":["Data validation"]},{"$$mdtype":"Tag","name":"SubHeading","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Learn about built-in validation and implement additional scenarios for Apple Pay for iOS."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"overview","__idx":1},"children":["Overview"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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 ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ApplePayValidationException"]}," with detailed error information."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can also easily build custom validation, depending on your business needs."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"built-in-validation","__idx":2},"children":["Built-in validation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["By default, the Apple Pay component validates that:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Fields marked as required are provided."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Payment request data meets Apple Pay specifications."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Currency codes, country codes, and amounts are formatted properly."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Merchant capabilities and supported networks are valid."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Contact fields and shipping methods are properly configured."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Device and iOS compatibility requirements are met."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"error-codes","__idx":3},"children":["Error codes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Apple Pay component returns structured error codes for different validation failures:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Error code"},"children":["Error code"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Common causes"},"children":["Common causes"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["REQUIRED_FIELD"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A required field is missing."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Missing mandatory configuration."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["INVALID_AMOUNT_FORMAT"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The amount format is invalid."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Non-decimal amount value."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["INVALID_CURRENCY_CODE"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The currency code is invalid."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Non-ISO 4217 currency format."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["INVALID_COUNTRY_CODE"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The country code is invalid."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Non-ISO 3166-1 alpha-2 format."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["INVALID_MERCHANT_CAPABILITIES"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Merchant capabilities invalid."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Unsupported capability values."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["INVALID_PAYMENT_NETWORKS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Payment networks invalid."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Unsupported network values."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MISSING_MERCHANT_ID"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The merchant identifier is missing."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Apple Pay merchant ID not configured."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["INVALID_CONTACT_FIELDS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Contact fields invalid."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Unsupported contact field values."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["INVALID_SHIPPING_METHODS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Shipping methods invalid."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Malformed shipping method data."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["INVALID_LINE_ITEMS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Line items invalid."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Malformed payment summary items."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["APPLE_PAY_NOT_AVAILABLE"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Apple Pay not available."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Unsupported device/iOS version."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MISSING_ENTITLEMENTS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Apple Pay entitlements missing."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["App not configured for Apple Pay."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["INVALID_MERCHANT_CERTIFICATE"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Merchant certificate invalid."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Certificate configuration issue."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"validation-example","__idx":4},"children":["Validation example"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"// Example of handling validation errors\nlet applePayConfig = ApplePayButtonComponentConfig()\n\napplePayConfig.onError = { error in\n    print(\"Apple Pay error: \\(error)\")\n    \n    if let validationError = error as? ApplePayValidationException {\n        // Handle specific validation errors\n        switch validationError.code {\n        case \"INVALID_MERCHANT_CAPABILITIES\":\n            print(\"Invalid merchant capabilities configured\")\n            \n        case \"INVALID_AMOUNT_FORMAT\":\n            print(\"Invalid total amount format\")\n            \n        case \"MISSING_MERCHANT_ID\":\n            print(\"Apple Pay merchant ID missing or invalid\")\n            \n        case \"APPLE_PAY_NOT_AVAILABLE\":\n            print(\"Apple Pay not available on this device\")\n            \n        default:\n            print(\"Validation error: \\(validationError.localizedDescription)\")\n        }\n        \n        // Display user-friendly error messages\n        DispatchQueue.main.async {\n            self.showValidationError(validationError)\n        }\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"custom-validation","__idx":5},"children":["Custom validation"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"pre-authorisation-validation","__idx":6},"children":["Pre-authorisation validation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Run validation before the Apple Pay authorisation to catch issues early and prevent failed payments."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"let applePayConfig = ApplePayButtonComponentConfig()\n\napplePayConfig.onPreAuthorisation = { [weak self] in\n    do {\n        // 1. Business logic validation\n        let orderValidation = try await self?.validateOrder(\n            cartItems: self?.getCartItems() ?? [],\n            customerLocation: self?.getCustomerLocation(),\n            paymentAmount: self?.getOrderTotal() ?? 0\n        )\n        \n        guard let orderValidation = orderValidation, orderValidation.valid else {\n            throw ValidationError.orderValidationFailed(orderValidation?.reason ?? \"Unknown error\")\n        }\n        \n        // 2. Apple Pay specific validation\n        let applePayValidation = try await self?.validateApplePayRequirements(\n            merchantId: self?.getMerchantId() ?? \"\",\n            totalAmount: self?.getOrderTotal() ?? 0\n        )\n        \n        guard let applePayValidation = applePayValidation, applePayValidation.valid else {\n            throw ValidationError.applePayRequirementsNotMet\n        }\n        \n        // 3. Security validation\n        let securityCheck = try await self?.performSecurityValidation(\n            deviceID: UIDevice.current.identifierForVendor?.uuidString ?? \"\",\n            paymentHistory: self?.getCustomerPaymentHistory() ?? []\n        )\n        \n        if let securityCheck = securityCheck, securityCheck.riskLevel == .high {\n            // Return additional security data for transaction\n            return ApplePayTransactionInitData(\n                threeDSecureData: ThreeDSecureData(\n                    threeDSecureVersion: \"2.1.0\",\n                    electronicCommerceIndicator: .eci5,\n                    cardHolderAuthenticationVerificationValue: \"AAABBBCCCDDDEEEFFFaaabbbcccdddeee\",\n                    directoryServerTransactionId: \"8ac7ca4f-6068-4978-8b5e-c23c0c6d2260\",\n                    threeDSecureTransactionStatus: \"Y\"\n                ),\n                riskScreeningData: RiskScreeningData(\n                    performRiskScreening: true,\n                    deviceSessionId: self?.generateDeviceSessionId() ?? \"\",\n                    riskScore: securityCheck.score\n                )\n            )\n        }\n        \n        // 4. Inventory validation\n        let inventoryCheck = try await self?.validateInventory(self?.getCartItems() ?? [])\n        guard let inventoryCheck = inventoryCheck, inventoryCheck.allAvailable else {\n            throw ValidationError.inventoryUnavailable\n        }\n        \n        // 5. Regulatory compliance\n        let complianceCheck = try await self?.validateCompliance(\n            customerCountry: self?.getCustomerCountry() ?? \"\",\n            orderAmount: self?.getOrderTotal() ?? 0,\n            productTypes: self?.getProductTypes() ?? []\n        )\n        \n        guard let complianceCheck = complianceCheck, complianceCheck.compliant else {\n            throw ValidationError.regulatoryComplianceFailure\n        }\n        \n        // Return transaction initialisation data\n        return ApplePayTransactionInitData(\n            identityVerification: IdentityVerification(nameVerification: true),\n            addressVerification: AddressVerification(\n                countryCode: \"US\",\n                houseNumberOrName: \"123 Main St\",\n                postalCode: \"10001\"\n            ),\n            riskScreeningData: RiskScreeningData(\n                performRiskScreening: true,\n                deviceSessionId: self?.generateDeviceSessionId() ?? \"\"\n            )\n        )\n        \n    } catch {\n        print(\"Pre-authorisation validation failed: \\(error)\")\n        // Returning nil will cancel the Apple Pay flow\n        return nil\n    }\n}\n\nenum ValidationError: Error {\n    case orderValidationFailed(String)\n    case applePayRequirementsNotMet\n    case inventoryUnavailable\n    case regulatoryComplianceFailure\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"post-authorisation-validation","__idx":7},"children":["Post-authorisation validation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Run validation after Apple Pay authorisation to ensure payment integrity before completing the transaction."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"applePayConfig.onPostAuthorisation = { [weak self] result in\n    DispatchQueue.main.async {\n        do {\n            if let authorizedResult = result as? AuthorisedSubmitResult {\n                // 1. Validate transaction data\n                let transactionValidation = try await self?.validateTransactionData(\n                    transactionId: authorizedResult.provider.code,\n                    amount: self?.getOrderTotal() ?? 0\n                )\n                \n                guard let transactionValidation = transactionValidation, transactionValidation.valid else {\n                    throw ValidationError.transactionDataInvalid\n                }\n                \n                // 2. Final inventory check\n                let finalInventoryCheck = try await self?.reserveInventory(orderId: self?.getOrderId() ?? \"\")\n                guard let finalInventoryCheck = finalInventoryCheck, finalInventoryCheck.success else {\n                    throw ValidationError.inventoryUnavailable\n                }\n                \n                // 3. Validate device and security\n                let deviceValidation = try await self?.validateDeviceSecurity(\n                    deviceID: UIDevice.current.identifierForVendor?.uuidString ?? \"\",\n                    transactionAmount: self?.getOrderTotal() ?? 0\n                )\n                \n                guard let deviceValidation = deviceValidation, deviceValidation.valid else {\n                    throw ValidationError.deviceSecurityFailure\n                }\n                \n                // 4. Process successful payment\n                try await self?.completeOrder(\n                    transactionId: authorizedResult.provider.code,\n                    deviceInfo: self?.getDeviceInfo()\n                )\n                \n                // Navigate to success screen\n                self?.navigateToSuccessScreen(transactionId: authorizedResult.provider.code)\n                \n            } else if let failedResult = result as? FailedSubmitResult {\n                // Handle payment failure\n                print(\"Payment failed: \\(failedResult.errorReason)\")\n                self?.showError(\"Payment processing failed. Please try again.\")\n            }\n            \n        } catch {\n            print(\"Post-authorisation validation failed: \\(error)\")\n            self?.showError(\"Payment validation failed: \\(error.localizedDescription)\")\n        }\n    }\n}\n\nextension ValidationError {\n    case transactionDataInvalid\n    case deviceSecurityFailure\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"shipping-contact-validation","__idx":8},"children":["Shipping contact validation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Validate shipping addresses in real-time as customers select them in the Apple Pay sheet."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"applePayConfig.onShippingContactSelected = { [weak self] contact in\n    do {\n        // 1. Validate address format\n        let addressValidation = self?.validateAddressFormat(contact.postalAddress)\n        guard let addressValidation = addressValidation, addressValidation.valid else {\n            return PKPaymentRequestShippingContactUpdate(\n                errors: [PKPaymentRequest.paymentShippingAddressInvalidError(\n                    withKey: PKContactField.postalAddress,\n                    localizedDescription: \"Please enter a complete address\"\n                )],\n                shippingMethods: [],\n                paymentSummaryItems: []\n            )\n        }\n        \n        // 2. Check shipping restrictions\n        let shippingValidation = try await self?.validateShippingAvailability(\n            countryCode: contact.postalAddress?.isoCountryCode ?? \"\",\n            postalCode: contact.postalAddress?.postalCode ?? \"\",\n            cartItems: self?.getCartItems() ?? []\n        )\n        \n        guard let shippingValidation = shippingValidation, shippingValidation.available else {\n            return PKPaymentRequestShippingContactUpdate(\n                errors: [PKPaymentRequest.paymentShippingAddressUnserviceableError(\n                    withLocalizedDescription: \"Shipping not available to this location\"\n                )],\n                shippingMethods: [],\n                paymentSummaryItems: []\n            )\n        }\n        \n        // 3. Calculate shipping costs\n        let shippingCost = try await self?.calculateShippingCost(\n            address: contact.postalAddress,\n            items: self?.getCartItems() ?? [],\n            expedited: false\n        ) ?? 0\n        \n        // 4. Calculate taxes\n        let tax = try await self?.calculateTax(\n            address: contact.postalAddress,\n            subtotal: self?.getSubtotal() ?? 0\n        ) ?? 0\n        \n        // Return updated payment details\n        let subtotal = self?.getSubtotal() ?? 0\n        let newTotal = subtotal + shippingCost + tax\n        \n        let summaryItems = [\n            PKPaymentSummaryItem(label: \"Subtotal\", amount: NSDecimalNumber(value: subtotal)),\n            PKPaymentSummaryItem(label: \"Shipping\", amount: NSDecimalNumber(value: shippingCost)),\n            PKPaymentSummaryItem(label: \"Tax\", amount: NSDecimalNumber(value: tax)),\n            PKPaymentSummaryItem(label: \"Total\", amount: NSDecimalNumber(value: newTotal))\n        ]\n        \n        let shippingMethods = try await self?.getAvailableShippingMethods(for: contact.postalAddress) ?? []\n        \n        return PKPaymentRequestShippingContactUpdate(\n            errors: [],\n            shippingMethods: shippingMethods,\n            paymentSummaryItems: summaryItems\n        )\n        \n    } catch {\n        print(\"Shipping contact validation failed: \\(error)\")\n        return PKPaymentRequestShippingContactUpdate(\n            errors: [PKPaymentRequest.paymentShippingAddressUnserviceableError(\n                withLocalizedDescription: \"Unable to validate shipping address. Please try again.\"\n            )],\n            shippingMethods: [],\n            paymentSummaryItems: []\n        )\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"device-compatibility-validation","__idx":9},"children":["Device compatibility validation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Ensure Apple Pay requirements are met before showing the component."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"func validateApplePayCompatibility() -> ValidationResult {\n    var validation = ValidationResult(valid: true, errors: [])\n    \n    // Check if Apple Pay is supported on device\n    if !PKPaymentAuthorizationController.canMakePayments() {\n        validation.valid = false\n        validation.errors.append(ValidationError(\n            code: \"APPLE_PAY_NOT_AVAILABLE\",\n            message: \"Apple Pay not supported on this device\"\n        ))\n    }\n    \n    // Check iOS version compatibility\n    if #available(iOS 10.3, *) {\n        // Apple Pay is available\n    } else {\n        validation.valid = false\n        validation.errors.append(ValidationError(\n            code: \"IOS_VERSION_INCOMPATIBLE\",\n            message: \"iOS 10.3 or later required for Apple Pay\"\n        ))\n    }\n    \n    // Check if user has cards set up\n    let supportedNetworks: [PKPaymentNetwork] = [.visa, .masterCard, .amex, .discover]\n    if !PKPaymentAuthorizationController.canMakePayments(usingNetworks: supportedNetworks) {\n        validation.valid = false\n        validation.errors.append(ValidationError(\n            code: \"NO_CARDS_AVAILABLE\",\n            message: \"No compatible cards available in Apple Pay\"\n        ))\n    }\n    \n    // Check app entitlements\n    if !hasApplePayEntitlement() {\n        validation.valid = false\n        validation.errors.append(ValidationError(\n            code: \"MISSING_ENTITLEMENTS\",\n            message: \"Apple Pay capability not enabled in app\"\n        ))\n    }\n    \n    return validation\n}\n\n// Use validation before initialising\nfunc initializeApplePayIfCompatible() {\n    let compatibilityCheck = validateApplePayCompatibility()\n    \n    if compatibilityCheck.valid {\n        // Initialise Apple Pay component\n        initializeApplePay()\n    } else {\n        // Show alternative payment methods\n        showAlternativePaymentMethods()\n        print(\"Apple Pay not available: \\(compatibilityCheck.errors)\")\n    }\n}\n\nstruct ValidationResult {\n    var valid: Bool\n    var errors: [ValidationError]\n}\n\nstruct ValidationError {\n    let code: String\n    let message: String\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"validate-geographic-restrictions","__idx":10},"children":["Validate geographic restrictions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Check in real-time if any cart items are restricted in the customer's country to prevent compliance violations."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"func validateGeographicRestrictions(customerCountry: String, cartItems: [CartItem]) -> GeographicValidationResult {\n    let restrictedItems = cartItems.filter { item in\n        item.restrictions?.countries?.contains(customerCountry) == true ||\n        item.restrictions?.applePayRestricted?.contains(customerCountry) == true\n    }\n    \n    // Check Apple Pay specific restrictions\n    let applePayRestrictions = getApplePayCountryRestrictions()\n    let applePayBlocked = !applePayRestrictions.allowedCountries.contains(customerCountry)\n    \n    return GeographicValidationResult(\n        valid: restrictedItems.isEmpty && !applePayBlocked,\n        restrictedItems: restrictedItems,\n        applePayBlocked: applePayBlocked,\n        message: applePayBlocked \n            ? \"Apple Pay not available in your country\"\n            : \"Some items cannot be shipped to \\(customerCountry)\"\n    )\n}\n\nstruct GeographicValidationResult {\n    let valid: Bool\n    let restrictedItems: [CartItem]\n    let applePayBlocked: Bool\n    let message: String\n}\n\nstruct CartItem {\n    let id: String\n    let name: String\n    let price: Double\n    let restrictions: ItemRestrictions?\n}\n\nstruct ItemRestrictions {\n    let countries: [String]?\n    let applePayRestricted: [String]?\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"calculate-a-risk-score","__idx":11},"children":["Calculate a risk score"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Calculate a comprehensive fraud risk score based on multiple behavioural and historical factors, with Apple Pay specific considerations."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"func calculateFraudScore(transactionData: TransactionData, deviceInfo: DeviceInfo) async throws -> RiskAssessment {\n    let factors = RiskFactors(\n        velocityScore: await checkPaymentVelocity(customerEmail: transactionData.customerEmail),\n        locationScore: await validateLocation(deviceInfo: deviceInfo),\n        deviceScore: await analyzeDeviceFingerprint(deviceInfo: deviceInfo),\n        historyScore: await analyzePaymentHistory(customerId: transactionData.customerId),\n        applePayScore: await analyzeApplePayData(deviceInfo: deviceInfo)\n    )\n    \n    // Apple Pay specific risk factors\n    let applePayFactors = ApplePayRiskFactors(\n        deviceAccountScore: deviceInfo.hasDeviceAccount ? 0.1 : 0.3,\n        biometricScore: deviceInfo.biometricAuthentication ? 0.1 : 0.4,\n        walletScore: await analyzeWalletHistory(deviceId: deviceInfo.deviceId)\n    )\n    \n    // Weight Apple Pay factors (generally lower risk)\n    let baseScore = factors.averageScore\n    let applePayAdjustment = applePayFactors.averageScore\n    \n    let totalScore = (baseScore * 0.7) + (applePayAdjustment * 0.3) // Apple Pay generally reduces risk\n    \n    return RiskAssessment(\n        score: totalScore,\n        riskLevel: RiskLevel.from(score: totalScore),\n        factors: factors,\n        applePayFactors: applePayFactors,\n        applePayAdvantage: baseScore - totalScore // How much Apple Pay reduced the risk\n    )\n}\n\nstruct RiskFactors {\n    let velocityScore: Double\n    let locationScore: Double\n    let deviceScore: Double\n    let historyScore: Double\n    let applePayScore: Double\n    \n    var averageScore: Double {\n        return (velocityScore + locationScore + deviceScore + historyScore + applePayScore) / 5.0\n    }\n}\n\nstruct ApplePayRiskFactors {\n    let deviceAccountScore: Double\n    let biometricScore: Double\n    let walletScore: Double\n    \n    var averageScore: Double {\n        return (deviceAccountScore + biometricScore + walletScore) / 3.0\n    }\n}\n\nstruct RiskAssessment {\n    let score: Double\n    let riskLevel: RiskLevel\n    let factors: RiskFactors\n    let applePayFactors: ApplePayRiskFactors\n    let applePayAdvantage: Double\n}\n\nenum RiskLevel {\n    case low, medium, high\n    \n    static func from(score: Double) -> RiskLevel {\n        if score > 0.8 {\n            return .high\n        } else if score > 0.5 {\n            return .medium\n        } else {\n            return .low\n        }\n    }\n}\n\nstruct TransactionData {\n    let customerId: String\n    let customerEmail: String\n    let amount: Double\n    let currency: String\n}\n\nstruct DeviceInfo {\n    let deviceId: String\n    let hasDeviceAccount: Bool\n    let biometricAuthentication: Bool\n    let ipAddress: String\n    let location: CLLocation?\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"validate-payment-amounts","__idx":12},"children":["Validate payment amounts"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Ensure payment amounts meet Apple Pay requirements and business rules."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"func validatePaymentAmounts(paymentRequest: PaymentRequestData) -> AmountValidationResult {\n    var validation = AmountValidationResult(valid: true, errors: [])\n    \n    // Validate total amount format\n    guard paymentRequest.totalAmount > 0 else {\n        validation.valid = false\n        validation.errors.append(AmountValidationError(\n            field: \"totalAmount\",\n            code: \"INVALID_AMOUNT\",\n            message: \"Total amount must be a positive number\"\n        ))\n        return validation\n    }\n    \n    // Validate line items sum up to total\n    if !paymentRequest.lineItems.isEmpty {\n        let lineItemsSum = paymentRequest.lineItems.reduce(0) { sum, item in\n            return sum + item.amount\n        }\n        \n        let difference = abs(lineItemsSum - paymentRequest.totalAmount)\n        if difference > 0.01 { // Allow for small rounding differences\n            validation.valid = false\n            validation.errors.append(AmountValidationError(\n                field: \"lineItems\",\n                code: \"AMOUNT_MISMATCH\",\n                message: \"Line items do not sum to total amount\"\n            ))\n        }\n    }\n    \n    // Check minimum/maximum amounts\n    let minAmount: Double = 0.50 // Apple Pay minimum\n    let maxAmount: Double = 10000.00 // Business rule maximum\n    \n    if paymentRequest.totalAmount < minAmount {\n        validation.valid = false\n        validation.errors.append(AmountValidationError(\n            field: \"totalAmount\",\n            code: \"AMOUNT_TOO_LOW\",\n            message: \"Minimum amount is \\(minAmount)\"\n        ))\n    }\n    \n    if paymentRequest.totalAmount > maxAmount {\n        validation.valid = false\n        validation.errors.append(AmountValidationError(\n            field: \"totalAmount\",\n            code: \"AMOUNT_TOO_HIGH\",\n            message: \"Maximum amount is \\(maxAmount)\"\n        ))\n    }\n    \n    // Validate currency code\n    if !isValidCurrencyCode(paymentRequest.currencyCode) {\n        validation.valid = false\n        validation.errors.append(AmountValidationError(\n            field: \"currencyCode\",\n            code: \"INVALID_CURRENCY\",\n            message: \"Invalid currency code: \\(paymentRequest.currencyCode)\"\n        ))\n    }\n    \n    return validation\n}\n\nstruct PaymentRequestData {\n    let totalAmount: Double\n    let currencyCode: String\n    let lineItems: [LineItem]\n}\n\nstruct LineItem {\n    let label: String\n    let amount: Double\n}\n\nstruct AmountValidationResult {\n    var valid: Bool\n    var errors: [AmountValidationError]\n}\n\nstruct AmountValidationError {\n    let field: String\n    let code: String\n    let message: String\n}\n\nfunc isValidCurrencyCode(_ code: String) -> Bool {\n    let validCurrencies = [\"USD\", \"EUR\", \"GBP\", \"JPY\", \"CAD\", \"AUD\", \"CHF\", \"SEK\", \"NOK\", \"DKK\"]\n    return validCurrencies.contains(code)\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"complete-validation-implementation","__idx":13},"children":["Complete validation implementation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Here's a comprehensive example showing all validation working together:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import UIKit\nimport PXPCheckoutSDK\nimport PassKit\nimport CoreLocation\n\nclass ValidationViewController: UIViewController {\n    private var applePayComponent: ApplePayButtonComponent?\n    private var checkout: PxpCheckout?\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        setupApplePayWithValidation()\n    }\n    \n    private func setupApplePayWithValidation() {\n        // First validate compatibility\n        let compatibilityCheck = validateApplePayCompatibility()\n        guard compatibilityCheck.valid else {\n            showCompatibilityError(compatibilityCheck.errors)\n            return\n        }\n        \n        let checkoutConfig = CheckoutConfig(\n            environment: .test,\n            session: SessionConfig(sessionId: \"your-session-id\"),\n            transactionData: TransactionData(\n                amount: 29.99,\n                currency: \"USD\",\n                entryType: .ecom,\n                intent: .sale,\n                merchantTransactionId: \"order-\\(Int(Date().timeIntervalSince1970))\",\n                merchantTransactionDate: Date()\n            )\n        )\n        \n        do {\n            checkout = try PxpCheckout.initialize(config: checkoutConfig)\n            let applePayConfig = createValidatedApplePayConfig()\n            applePayComponent = try checkout?.create(.applePayButton, componentConfig: applePayConfig)\n            \n            if let componentView = applePayComponent?.render() {\n                view.addSubview(componentView)\n                setupConstraints(for: componentView)\n            }\n        } catch {\n            print(\"Failed to initialize: \\(error)\")\n            showError(\"Failed to setup Apple Pay: \\(error.localizedDescription)\")\n        }\n    }\n    \n    private func createValidatedApplePayConfig() -> ApplePayButtonComponentConfig {\n        let config = ApplePayButtonComponentConfig()\n        \n        // Basic configuration with validation\n        config.currencyCode = \"USD\"\n        config.countryCode = \"US\"\n        config.supportedNetworks = [.visa, .masterCard, .amex]\n        config.merchantCapabilities = [.threeDSecure, .emv, .credit, .debit]\n        config.requiredBillingContactFields = [.postalAddress, .name, .emailAddress]\n        config.requiredShippingContactFields = [.postalAddress, .name]\n        \n        // Validate amounts before setting\n        let paymentData = PaymentRequestData(\n            totalAmount: 29.99,\n            currencyCode: \"USD\",\n            lineItems: [\n                LineItem(label: \"Product\", amount: 25.99),\n                LineItem(label: \"Tax\", amount: 4.00)\n            ]\n        )\n        \n        let amountValidation = validatePaymentAmounts(paymentRequest: paymentData)\n        guard amountValidation.valid else {\n            print(\"Amount validation failed: \\(amountValidation.errors)\")\n            return config\n        }\n        \n        // Payment items\n        config.totalPaymentItem = ApplePayPaymentSummaryItem(\n            amount: 29.99,\n            label: \"Total\",\n            type: .final\n        )\n        \n        config.paymentItems = [\n            ApplePayPaymentSummaryItem(amount: 25.99, label: \"Product\", type: .final),\n            ApplePayPaymentSummaryItem(amount: 4.00, label: \"Tax\", type: .final)\n        ]\n        \n        // Button appearance\n        config.buttonType = .buy\n        config.buttonStyle = .black\n        config.buttonRadius = 8.0\n        \n        // Setup validation callbacks\n        setupValidationCallbacks(for: config)\n        \n        return config\n    }\n    \n    private func setupValidationCallbacks(for config: ApplePayButtonComponentConfig) {\n        \n        // Pre-authorisation validation\n        config.onPreAuthorisation = { [weak self] in\n            do {\n                // Comprehensive pre-authorisation validation\n                try await self?.performPreAuthorizationValidation()\n                \n                return ApplePayTransactionInitData(\n                    identityVerification: IdentityVerification(nameVerification: true),\n                    addressVerification: AddressVerification(\n                        countryCode: \"US\",\n                        houseNumberOrName: \"123 Main St\",\n                        postalCode: \"10001\"\n                    ),\n                    riskScreeningData: RiskScreeningData(\n                        performRiskScreening: true,\n                        deviceSessionId: self?.generateDeviceSessionId() ?? \"\"\n                    )\n                )\n                \n            } catch {\n                print(\"Pre-authorisation validation failed: \\(error)\")\n                DispatchQueue.main.async {\n                    self?.showError(\"Validation failed: \\(error.localizedDescription)\")\n                }\n                return nil\n            }\n        }\n        \n        // Post-authorisation validation\n        config.onPostAuthorisation = { [weak self] result in\n            DispatchQueue.main.async {\n                self?.handlePostAuthorizationWithValidation(result: result)\n            }\n        }\n        \n        // Error handling with validation\n        config.onError = { [weak self] error in\n            print(\"Apple Pay error: \\(error)\")\n            \n            DispatchQueue.main.async {\n                if let validationError = error as? ApplePayValidationException {\n                    self?.handleValidationError(validationError)\n                } else {\n                    self?.handleGeneralError(error)\n                }\n            }\n        }\n        \n        // Shipping contact validation\n        config.onShippingContactSelected = { [weak self] contact in\n            return await self?.validateAndUpdateShippingContact(contact) ?? PKPaymentRequestShippingContactUpdate(\n                errors: [PKPaymentRequest.paymentShippingAddressUnserviceableError(\n                    withLocalizedDescription: \"Unable to validate shipping address\"\n                )],\n                shippingMethods: [],\n                paymentSummaryItems: []\n            )\n        }\n    }\n    \n    // Validation methods\n    private func performPreAuthorizationValidation() async throws {\n        // 1. Order validation\n        let orderValid = try await validateOrder()\n        guard orderValid else {\n            throw ValidationError.orderValidationFailed(\"Order validation failed\")\n        }\n        \n        // 2. Inventory validation\n        let inventoryValid = try await validateInventory()\n        guard inventoryValid else {\n            throw ValidationError.inventoryUnavailable\n        }\n        \n        // 3. Security validation\n        let riskAssessment = try await calculateRiskScore()\n        if riskAssessment.riskLevel == .high {\n            throw ValidationError.highRiskTransaction\n        }\n        \n        // 4. Geographic restrictions\n        let geoValidation = validateGeographicRestrictions(\n            customerCountry: getCustomerCountry(),\n            cartItems: getCartItems()\n        )\n        guard geoValidation.valid else {\n            throw ValidationError.geographicRestriction(geoValidation.message)\n        }\n    }\n    \n    private func validateAndUpdateShippingContact(_ contact: PKContact) async -> PKPaymentRequestShippingContactUpdate {\n        do {\n            // Validate address format\n            guard let postalAddress = contact.postalAddress else {\n                return PKPaymentRequestShippingContactUpdate(\n                    errors: [PKPaymentRequest.paymentShippingAddressInvalidError(\n                        withKey: PKContactField.postalAddress,\n                        localizedDescription: \"Address is required\"\n                    )],\n                    shippingMethods: [],\n                    paymentSummaryItems: []\n                )\n            }\n            \n            // Check shipping availability\n            let shippingAvailable = try await checkShippingAvailability(to: postalAddress)\n            guard shippingAvailable else {\n                return PKPaymentRequestShippingContactUpdate(\n                    errors: [PKPaymentRequest.paymentShippingAddressUnserviceableError(\n                        withLocalizedDescription: \"Shipping not available to this location\"\n                    )],\n                    shippingMethods: [],\n                    paymentSummaryItems: []\n                )\n            }\n            \n            // Calculate updated amounts\n            let shippingCost = try await calculateShippingCost(to: postalAddress)\n            let tax = try await calculateTax(for: postalAddress)\n            let total = 25.99 + shippingCost + tax\n            \n            let summaryItems = [\n                PKPaymentSummaryItem(label: \"Product\", amount: NSDecimalNumber(value: 25.99)),\n                PKPaymentSummaryItem(label: \"Shipping\", amount: NSDecimalNumber(value: shippingCost)),\n                PKPaymentSummaryItem(label: \"Tax\", amount: NSDecimalNumber(value: tax)),\n                PKPaymentSummaryItem(label: \"Total\", amount: NSDecimalNumber(value: total))\n            ]\n            \n            let shippingMethods = getShippingMethods()\n            \n            return PKPaymentRequestShippingContactUpdate(\n                errors: [],\n                shippingMethods: shippingMethods,\n                paymentSummaryItems: summaryItems\n            )\n            \n        } catch {\n            return PKPaymentRequestShippingContactUpdate(\n                errors: [PKPaymentRequest.paymentShippingAddressUnserviceableError(\n                    withLocalizedDescription: \"Unable to validate address. Please try again.\"\n                )],\n                shippingMethods: [],\n                paymentSummaryItems: []\n            )\n        }\n    }\n    \n    private func handlePostAuthorizationWithValidation(result: BaseSubmitResult) {\n        do {\n            if let authorizedResult = result as? AuthorisedSubmitResult {\n                // Validate transaction result\n                try validateTransactionResult(authorizedResult)\n                \n                // Complete order\n                completeOrder(transactionId: authorizedResult.provider.code)\n                \n                // Navigate to success\n                navigateToSuccess(transactionId: authorizedResult.provider.code)\n                \n            } else if let failedResult = result as? FailedSubmitResult {\n                handlePaymentFailure(failedResult)\n            }\n        } catch {\n            showError(\"Post-authorisation validation failed: \\(error.localizedDescription)\")\n        }\n    }\n    \n    private func handleValidationError(_ error: ApplePayValidationException) {\n        var message = \"Validation failed\"\n        \n        switch error.code {\n        case \"APPLE_PAY_NOT_AVAILABLE\":\n            message = \"Apple Pay is not available on this device\"\n            showAlternativePaymentMethods()\n        case \"MISSING_MERCHANT_ID\":\n            message = \"Apple Pay configuration error. Please contact support.\"\n        case \"INVALID_AMOUNT_FORMAT\":\n            message = \"Invalid payment amount. Please refresh and try again.\"\n        default:\n            message = error.localizedDescription\n        }\n        \n        showError(message)\n    }\n    \n    // Helper methods for validation\n    private func validateOrder() async throws -> Bool {\n        // Simulate order validation\n        return true\n    }\n    \n    private func validateInventory() async throws -> Bool {\n        // Simulate inventory check\n        return true\n    }\n    \n    private func calculateRiskScore() async throws -> RiskAssessment {\n        // Simulate risk calculation\n        return RiskAssessment(\n            score: 0.3,\n            riskLevel: .low,\n            factors: RiskFactors(velocityScore: 0.2, locationScore: 0.1, deviceScore: 0.2, historyScore: 0.1, applePayScore: 0.1),\n            applePayFactors: ApplePayRiskFactors(deviceAccountScore: 0.1, biometricScore: 0.1, walletScore: 0.1),\n            applePayAdvantage: 0.2\n        )\n    }\n    \n    private func checkShippingAvailability(to address: CNPostalAddress) async throws -> Bool {\n        // Simulate shipping availability check\n        return address.isoCountryCode == \"US\"\n    }\n    \n    private func calculateShippingCost(to address: CNPostalAddress) async throws -> Double {\n        // Simulate shipping cost calculation\n        return 5.99\n    }\n    \n    private func calculateTax(for address: CNPostalAddress) async throws -> Double {\n        // Simulate tax calculation\n        return 2.60\n    }\n    \n    private func validateTransactionResult(_ result: AuthorisedSubmitResult) throws {\n        // Validate the transaction was processed correctly\n        guard !result.provider.code.isEmpty else {\n            throw ValidationError.invalidTransactionResult\n        }\n    }\n    \n    // UI helper methods\n    private func showCompatibilityError(_ errors: [ValidationError]) {\n        let message = errors.map { $0.message }.joined(separator: \"\\n\")\n        showError(\"Apple Pay not available:\\n\\(message)\")\n        showAlternativePaymentMethods()\n    }\n    \n    private func showError(_ message: String) {\n        let alert = UIAlertController(title: \"Error\", message: message, preferredStyle: .alert)\n        alert.addAction(UIAlertAction(title: \"OK\", style: .default))\n        present(alert, animated: true)\n    }\n    \n    private func showAlternativePaymentMethods() {\n        // Show alternative payment options\n        print(\"Showing alternative payment methods\")\n    }\n    \n    private func navigateToSuccess(transactionId: String) {\n        let alert = UIAlertController(title: \"Success\", message: \"Payment completed! ID: \\(transactionId)\", preferredStyle: .alert)\n        alert.addAction(UIAlertAction(title: \"OK\", style: .default))\n        present(alert, animated: true)\n    }\n    \n    private func handlePaymentFailure(_ result: FailedSubmitResult) {\n        showError(\"Payment failed: \\(result.errorReason)\")\n    }\n    \n    private func handleGeneralError(_ error: Error) {\n        showError(\"An error occurred: \\(error.localizedDescription)\")\n    }\n    \n    // Data helper methods\n    private func getCustomerCountry() -> String { return \"US\" }\n    private func getCartItems() -> [CartItem] { return [] }\n    private func generateDeviceSessionId() -> String { return UUID().uuidString }\n    private func getShippingMethods() -> [PKShippingMethod] { return [] }\n    private func completeOrder(transactionId: String) { print(\"Order completed: \\(transactionId)\") }\n    \n    private func setupConstraints(for componentView: UIView) {\n        componentView.translatesAutoresizingMaskIntoConstraints = false\n        \n        NSLayoutConstraint.activate([\n            componentView.centerXAnchor.constraint(equalTo: view.centerXAnchor),\n            componentView.centerYAnchor.constraint(equalTo: view.centerYAnchor),\n            componentView.widthAnchor.constraint(equalToConstant: 280),\n            componentView.heightAnchor.constraint(equalToConstant: 50)\n        ])\n    }\n}\n\n// Additional validation error types\nextension ValidationError {\n    case highRiskTransaction\n    case geographicRestriction(String)\n    case invalidTransactionResult\n}\n","lang":"swift"},"children":[]}]},"headings":[{"value":"Data validation","id":"data-validation","depth":1},{"value":"Overview","id":"overview","depth":2},{"value":"Built-in validation","id":"built-in-validation","depth":2},{"value":"Error codes","id":"error-codes","depth":3},{"value":"Validation example","id":"validation-example","depth":3},{"value":"Custom validation","id":"custom-validation","depth":2},{"value":"Pre-authorisation validation","id":"pre-authorisation-validation","depth":3},{"value":"Post-authorisation validation","id":"post-authorisation-validation","depth":3},{"value":"Shipping contact validation","id":"shipping-contact-validation","depth":3},{"value":"Device compatibility validation","id":"device-compatibility-validation","depth":3},{"value":"Validate geographic restrictions","id":"validate-geographic-restrictions","depth":3},{"value":"Calculate a risk score","id":"calculate-a-risk-score","depth":3},{"value":"Validate payment amounts","id":"validate-payment-amounts","depth":3},{"value":"Complete validation implementation","id":"complete-validation-implementation","depth":3}],"frontmatter":{"seo":{"title":"Data validation"}},"lastModified":"2026-03-24T15:21:03.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/checkout/components/ios/apple-pay/data-validation","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}