{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["sub-heading","admonition"]},"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 payouts."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"overview","__idx":1},"children":["Overview"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The PayPal payout components include comprehensive validation to ensure data integrity and compliance with PayPal's requirements and payout regulations. All built-in validation is performed before executing payouts. If validation fails, the SDK will trigger the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onError"]}," callback with detailed error information."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can also implement custom validation for business rules, compliance checks, and fraud prevention."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"applicable-components","__idx":2},"children":["Applicable components"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This validation guide applies to the payout submission component (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutSubmissionComponentConfig"]},") for withdrawal flows."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"built-in-validation","__idx":3},"children":["Built-in validation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["By default, the PayPal payout submission component validates that:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The payer ID is provided, properly formatted, and within length constraints (maximum 13 characters)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The payout amount is a valid, positive number (not NaN or infinite)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The transaction data contains all required fields with proper formats."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Session credentials are valid and not expired."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"error-codes","__idx":4},"children":["Error codes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The PayPal payout submission component returns structured error codes accessible via ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["error.errorCode"]},"."]},{"$$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":{"width":"35%","data-label":"Error code"},"children":["Error code "]},{"$$mdtype":"Tag","name":"th","attributes":{"width":"65%","data-label":"Description"},"children":["Description "]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0809"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutPayerIdRequiredException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The payer ID is empty or not provided. The payer ID must be provided in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayPalWallet"]}," configuration."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0810"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutAmountInvalidException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The payout amount is NaN (Not a Number) or infinite. Check the transaction amount configuration."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0811"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutAmountNotPositiveException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The payout amount must be greater than zero. Negative or zero amounts aren't allowed."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0812"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutAmountRequiredException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The payout amount is required but wasn't provided in the transaction data."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0813"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutCurrencyRequiredException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The currency code is required. Must be a non-empty string."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0814"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutCurrencyLengthInvalidException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The currency code must be exactly 3 characters (ISO 4217 format, e.g., \"USD\", \"EUR\")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0815"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutCurrencyCodeInvalidException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Invalid currency code. Must be a valid ISO 4217 currency code."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0816"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutEmailMaxLengthException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The email address exceeds maximum length of 127 characters."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0817"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutPayerIdMaxLengthException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The payer ID exceeds the maximum length of 13 characters."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0818"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutPayerIdInvalidException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The payer ID contains invalid characters. Only alphanumeric characters (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["A-Z"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["a-z"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0-9"]},") are allowed."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0819"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutFailedException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The payout transaction failed. Check the error message for details."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0803"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PaypalPayoutReceiverRequiredException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The receiver is required. Only applies to direct payout components (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutAmountComponent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayPalPayoutReceiverComponent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutSubmissionComponent"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0804"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutReceiverTypeInvalidException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Invalid receiver type. Only applies to direct payout components (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutAmountComponent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayPalPayoutReceiverComponent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutSubmissionComponent"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0805"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PaypalPayoutReceiverTypeEmailOnlyException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The receiver type must be ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Email"]},". Only applies to direct payout components (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutAmountComponent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayPalPayoutReceiverComponent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutSubmissionComponent"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0806"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutWalletInvalidException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Invalid wallet value. Only applies to direct payout components (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutAmountComponent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayPalPayoutReceiverComponent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutSubmissionComponent"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0807"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutWalletRequiresEmailException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The wallet requires an email receiver type. Only applies to direct payout components (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutAmountComponent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayPalPayoutReceiverComponent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutSubmissionComponent"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0808"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PaypalPayoutEmailInvalidException"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Invalid email format. Only applies to direct payout components (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutAmountComponent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayPalPayoutReceiverComponent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutSubmissionComponent"]},")."]}]}]}]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Always implement error handling in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onError"]}," callback to catch and handle these validation errors appropriately."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"validation-example","__idx":5},"children":["Validation example"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following example shows error handling for the payout submission component."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"let config = PayoutSubmissionComponentConfig(\n    submitText: \"Withdraw with PayPal\",\n    \n    onError: { error in\n        print(\"Validation error: \\(error.errorMessage)\")\n        print(\"Error code: \\(error.errorCode)\")\n        \n        // Handle specific validation errors\n        switch error.errorCode {\n        case \"SDK0803\":\n            showError(\"PayPal receiver information is missing.\")\n            logValidationError(\"missing_receiver\")\n            \n        case \"SDK0805\":\n            showError(\"Invalid receiver type. Only email is supported.\")\n            logValidationError(\"invalid_receiver_type\")\n            \n        case \"SDK0808\":\n            showError(\"Invalid PayPal email format.\")\n            logValidationError(\"invalid_email\")\n            \n        case \"SDK0809\":\n            showError(\"PayPal payer ID is missing. Please link your account.\")\n            logValidationError(\"missing_payer_id\")\n            \n        case \"SDK0810\":\n            showError(\"Invalid payout amount. Please contact support.\")\n            logValidationError(\"invalid_amount_format\")\n            \n        case \"SDK0811\":\n            showError(\"Payout amount must be greater than zero.\")\n            logValidationError(\"negative_or_zero_amount\")\n            \n        case \"SDK0812\":\n            showError(\"Payout amount is required.\")\n            logValidationError(\"missing_amount\")\n            \n        case \"SDK0813\", \"SDK0814\", \"SDK0815\":\n            showError(\"Invalid currency configuration. Please contact support.\")\n            logValidationError(\"currency_error\")\n            \n        case \"SDK0817\":\n            showError(\"PayPal account identifier is too long.\")\n            logValidationError(\"payer_id_too_long\")\n            \n        case \"SDK0818\":\n            showError(\"Invalid PayPal account format.\")\n            logValidationError(\"invalid_payer_id_format\")\n            \n        case \"SDK0819\":\n            showError(\"Payout transaction failed. Please try again.\")\n            logValidationError(\"payout_failed\")\n            \n        default:\n            showError(\"Payout failed. Please try again or contact support.\")\n            logValidationError(\"unknown_error\", details: error.errorCode)\n        }\n        \n        // Log for debugging\n        Crashlytics.crashlytics().log(\"Payout error [\\(error.errorCode)]: \\(error.errorMessage)\")\n    }\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"custom-validation","__idx":6},"children":["Custom validation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Custom validation allows you to implement business-specific rules, compliance checks, and fraud prevention before executing payouts."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"when-to-use-custom-validation","__idx":7},"children":["When to use custom validation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Before component creation:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["validatePayoutAmount()"]},": Check amount limits before showing the UI."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["validatePayoutFrequency()"]},": Check daily/weekly/monthly limits before allowing payout request"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["validatePayoutCompliance()"]},": Pre-check KYC status, AML requirements."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["In ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPrePayoutSubmit"]}," callback"]}," (when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["proceedPayoutWithSdk: true"]},"):"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["validatePayoutEligibility()"]},": Final check of user KYC status before approval."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["performComplianceChecks()"]},": Perform AML, sanctions checks at approval time."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["validateFraudRisk()"]},": Calculate risk score before executing payout."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"onprepayoutsubmit-return-types","__idx":8},"children":["onPrePayoutSubmit return types"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayoutSubmissionComponentConfig"]}," uses ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PrePayoutSubmitResult"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"onPrePayoutSubmit: (() async -> PrePayoutSubmitResult?)\n\n// Return object only includes isApproved\nreturn PrePayoutSubmitResult(\n    isApproved: true  // No payerId - comes from PayPalWallet config\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The payer ID is already provided in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PayPalWallet"]}," configuration during SDK initialisation."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"pre-payout-approval-validation","__idx":9},"children":["Pre-payout approval validation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Run comprehensive validation before approving payouts to catch issues early and prevent failed transactions."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"let config = PayoutSubmissionComponentConfig(\n    submitText: \"Withdraw with PayPal\",\n    \n    onPrePayoutSubmit: {\n        do {\n            // 1. Validate payout eligibility\n            let eligibilityCheck = try await validatePayoutEligibility(\n                userId: currentUser.id,\n                accountStatus: currentUser.accountStatus,\n                kycStatus: currentUser.kycStatus\n            )\n            \n            guard eligibilityCheck.isEligible else {\n                await MainActor.run {\n                    showError(\"You are not eligible for payouts: \\(eligibilityCheck.reason)\")\n                }\n                return nil\n            }\n            \n            // 2. Validate payout amount\n            let amountValidation = try await validatePayoutAmount(\n                amount: getCurrentPayoutAmount(),\n                currency: \"USD\",\n                userId: currentUser.id\n            )\n            \n            guard amountValidation.isValid else {\n                await MainActor.run {\n                    showError(amountValidation.errorMessage ?? \"Invalid payout amount\")\n                }\n                return nil\n            }\n            \n            // 3. Compliance checks\n            let complianceCheck = try await performComplianceChecks(\n                userId: currentUser.id,\n                amount: getCurrentPayoutAmount(),\n                country: currentUser.country,\n                accountAge: currentUser.accountAge\n            )\n            \n            guard complianceCheck.passed else {\n                await MainActor.run {\n                    showError(\"Compliance check failed: \\(complianceCheck.reason)\")\n                }\n                return nil\n            }\n            \n            // 4. Anti-fraud validation\n            let fraudCheck = try await validateFraudRisk(\n                userId: currentUser.id,\n                amount: getCurrentPayoutAmount(),\n                payoutFrequency: getUserPayoutHistory()\n            )\n            \n            if fraudCheck.riskLevel == .high {\n                // Require additional verification\n                let verified = try await requestAdditionalVerification()\n                guard verified else {\n                    await MainActor.run {\n                        showError(\"Additional verification required for this payout\")\n                    }\n                    return nil\n                }\n            }\n            \n            // 5. Balance verification\n            let balanceCheck = try await validateSufficientBalance(\n                merchantId: getMerchantId(),\n                payoutAmount: getCurrentPayoutAmount()\n            )\n            \n            guard balanceCheck.hasSufficientFunds else {\n                await MainActor.run {\n                    showError(\"Insufficient funds to process this payout\")\n                }\n                return nil\n            }\n            \n            // 6. Show approval dialog\n            let approved = await showApprovalDialog(\n                amount: getCurrentPayoutAmount(),\n                currency: \"USD\",\n                recipient: getUserPayPalEmail()\n            )\n            \n            guard approved else {\n                return nil\n            }\n            \n            // All validations passed - approve payout\n            return PrePayoutSubmitResult(isApproved: true)\n            \n        } catch {\n            print(\"Pre-payout validation failed: \\(error.localizedDescription)\")\n            await MainActor.run {\n                showError(\"Validation failed: \\(error.localizedDescription)\")\n            }\n            return nil\n        }\n    }\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"validate-payout-amount-limits","__idx":10},"children":["Validate payout amount limits"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Ensure the payout amount meets minimum and maximum requirements."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"struct PayoutAmountValidation {\n    let isValid: Bool\n    let errorMessage: String?\n}\n\nfunc validatePayoutAmount(\n    amount: Double,\n    currency: String,\n    userId: String\n) async throws -> PayoutAmountValidation {\n    // Define limits per currency\n    let currencyLimits: [String: (min: Double, max: Double)] = [\n        \"USD\": (1.0, 60000.0),\n        \"EUR\": (1.0, 50000.0),\n        \"GBP\": (1.0, 45000.0)\n    ]\n    \n    guard let (minAmount, maxAmount) = currencyLimits[currency] else {\n        return PayoutAmountValidation(\n            isValid: false,\n            errorMessage: \"Unsupported currency: \\(currency)\"\n        )\n    }\n    \n    // Currency symbols for display\n    let currencySymbols: [String: String] = [\n        \"USD\": \"$\",\n        \"EUR\": \"€\",\n        \"GBP\": \"£\"\n    ]\n    let symbol = currencySymbols[currency] ?? currency\n    \n    // Validate amount is positive and valid\n    guard amount.isFinite && !amount.isNaN else {\n        return PayoutAmountValidation(\n            isValid: false,\n            errorMessage: \"Invalid payout amount format\"\n        )\n    }\n    \n    if amount <= 0 {\n        return PayoutAmountValidation(\n            isValid: false,\n            errorMessage: \"Payout amount must be greater than zero\"\n        )\n    }\n    \n    if amount < minAmount {\n        return PayoutAmountValidation(\n            isValid: false,\n            errorMessage: \"Minimum payout amount is \\(symbol)\\(minAmount)\"\n        )\n    }\n    \n    if amount > maxAmount {\n        return PayoutAmountValidation(\n            isValid: false,\n            errorMessage: \"Maximum payout amount is \\(symbol)\\(maxAmount)\"\n        )\n    }\n    \n    // Check user-specific limits\n    let userLimit = try await getUserPayoutLimit(userId: userId)\n    if amount > userLimit {\n        return PayoutAmountValidation(\n            isValid: false,\n            errorMessage: \"Amount exceeds your payout limit of \\(symbol)\\(userLimit)\"\n        )\n    }\n    \n    return PayoutAmountValidation(isValid: true, errorMessage: nil)\n}\n\n// Usage before creating component\nlet validation = try await validatePayoutAmount(\n    amount: 100.0,\n    currency: \"USD\",\n    userId: currentUser.id\n)\n\nif !validation.isValid {\n    showError(validation.errorMessage ?? \"Invalid payout amount\")\n    return\n}\n\n// Proceed with payout component creation\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"validate-payer-id-format","__idx":11},"children":["Validate payer ID format"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Validate payer ID format before storing during account linking or before using in payouts."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"struct PayerIdValidation {\n    let isValid: Bool\n    let errorMessage: String?\n}\n\nfunc validatePayerId(_ payerId: String?) -> PayerIdValidation {\n    // Check if payer ID is provided\n    guard let payerId = payerId, !payerId.isEmpty else {\n        return PayerIdValidation(\n            isValid: false,\n            errorMessage: \"Payer ID is required\"\n        )\n    }\n    \n    // Check length (max 13 characters)\n    if payerId.count > 13 {\n        return PayerIdValidation(\n            isValid: false,\n            errorMessage: \"Payer ID must be 13 characters or less\"\n        )\n    }\n    \n    // Check format (alphanumeric)\n    let alphanumericSet = CharacterSet.alphanumerics\n    if payerId.rangeOfCharacter(from: alphanumericSet.inverted) != nil {\n        return PayerIdValidation(\n            isValid: false,\n            errorMessage: \"Payer ID must contain only letters and numbers\"\n        )\n    }\n    \n    // Optional: Additional custom format validation\n    // Note: This is an example of custom business logic - PayPal payer IDs\n    // don't have a guaranteed prefix pattern. Remove or modify based on your needs.\n    // if !payerId.hasPrefix(\"P\") && !payerId.hasPrefix(\"M\") {\n    //     return PayerIdValidation(\n    //         isValid: false,\n    //         errorMessage: \"Invalid payer ID format\"\n    //     )\n    // }\n    \n    return PayerIdValidation(isValid: true, errorMessage: nil)\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"calculate-fraud-risk-score","__idx":12},"children":["Calculate fraud risk score"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Calculate a comprehensive fraud risk score for payout transactions. Use this validation in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPrePayoutSubmit"]}," when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["proceedPayoutWithSdk: true"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"struct PayoutRiskFactors {\n    let velocityScore: Double\n    let accountAgeScore: Double\n    let activityScore: Double\n    let complianceScore: Double\n}\n\nstruct PayoutFraudRiskResult {\n    let score: Double\n    let riskLevel: RiskLevel\n    let factors: PayoutRiskFactors\n    let requiresVerification: Bool\n}\n\nenum RiskLevel {\n    case low, medium, high\n}\n\nfunc calculatePayoutFraudRisk(\n    userId: String,\n    amount: Double,\n    payoutHistory: [PayoutTransaction]\n) async throws -> PayoutFraudRiskResult {\n    // Calculate individual risk factors\n    let velocityScore = try await checkPayoutVelocity(\n        userId: userId,\n        recentPayouts: payoutHistory,\n        requestedAmount: amount\n    )\n    \n    let accountAgeScore = try await evaluateAccountAge(userId: userId)\n    \n    let activityScore = try await analyzeUserActivity(userId: userId)\n    \n    let complianceScore = try await checkComplianceHistory(userId: userId)\n    \n    let factors = PayoutRiskFactors(\n        velocityScore: velocityScore,\n        accountAgeScore: accountAgeScore,\n        activityScore: activityScore,\n        complianceScore: complianceScore\n    )\n    \n    // Calculate overall risk score (weighted average)\n    let totalScore = (velocityScore * 0.4 +\n                     accountAgeScore * 0.2 +\n                     activityScore * 0.2 +\n                     complianceScore * 0.2)\n    \n    // Determine risk level\n    let riskLevel: RiskLevel\n    let requiresVerification: Bool\n    \n    if totalScore > 0.75 {\n        riskLevel = .high\n        requiresVerification = true\n    } else if totalScore > 0.45 {\n        riskLevel = .medium\n        requiresVerification = amount > 1000.0 // Verify if amount > $1000\n    } else {\n        riskLevel = .low\n        requiresVerification = false\n    }\n    \n    return PayoutFraudRiskResult(\n        score: totalScore,\n        riskLevel: riskLevel,\n        factors: factors,\n        requiresVerification: requiresVerification\n    )\n}\n\n// Usage in payout component\nlet config = PayoutSubmissionComponentConfig(\n    submitText: \"Withdraw with PayPal\",\n    \n    onPrePayoutSubmit: {\n        let riskResult = try await calculatePayoutFraudRisk(\n            userId: currentUser.id,\n            amount: getCurrentPayoutAmount(),\n            payoutHistory: getUserPayoutHistory()\n        )\n        \n        // Log risk assessment\n        logRiskAssessment(\n            userId: currentUser.id,\n            riskScore: riskResult.score,\n            riskLevel: riskResult.riskLevel\n        )\n        \n        switch riskResult.riskLevel {\n        case .high:\n            print(\"High risk payout detected\")\n            \n            // Block payout or require manual review\n            await MainActor.run {\n                showError(\"This payout requires additional review. Please contact support.\")\n            }\n            \n            // Flag for manual review\n            await flagPayoutForManualReview(\n                userId: currentUser.id,\n                amount: getCurrentPayoutAmount(),\n                riskScore: riskResult.score\n            )\n            \n            return nil\n            \n        case .medium:\n            print(\"Medium risk payout\")\n            \n            if riskResult.requiresVerification {\n                // Require additional verification\n                let verified = try await requestAdditionalVerification()\n                guard verified else {\n                    await MainActor.run {\n                        showError(\"Additional verification required\")\n                    }\n                    return nil\n                }\n            }\n            \n            // Add extra monitoring\n            await flagPayoutForMonitoring(\n                userId: currentUser.id,\n                riskScore: riskResult.score\n            )\n            \n        case .low:\n            print(\"Low risk payout - proceeding normally\")\n        }\n        \n        // Show approval dialog\n        let approved = await showApprovalDialog()\n        guard approved else { return nil }\n        \n        return PrePayoutSubmitResult(isApproved: true)\n    }\n)\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"validate-payout-frequency","__idx":13},"children":["Validate payout frequency"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Ensure users don't exceed daily, weekly, or monthly payout limits. Use this validation before component creation to provide immediate feedback."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"struct PayoutFrequencyValidation {\n    let isValid: Bool\n    let errorMessage: String?\n    let nextAvailableDate: Date?\n}\n\nfunc validatePayoutFrequency(\n    userId: String,\n    requestedAmount: Double\n) async throws -> PayoutFrequencyValidation {\n    let history = try await getUserPayoutHistory(userId: userId)\n    let now = Date()\n    \n    // Daily limit: Max 3 payouts per day\n    let todayPayouts = history.filter { payout in\n        Calendar.current.isDateInToday(payout.createdAt)\n    }\n    \n    if todayPayouts.count >= 3 {\n        let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: now)!\n        let startOfTomorrow = Calendar.current.startOfDay(for: tomorrow)\n        \n        return PayoutFrequencyValidation(\n            isValid: false,\n            errorMessage: \"Daily payout limit reached (3 payouts per day)\",\n            nextAvailableDate: startOfTomorrow\n        )\n    }\n    \n    // Weekly limit: Max $10,000 per week\n    let weekAgo = Calendar.current.date(byAdding: .day, value: -7, to: now)!\n    let weeklyPayouts = history.filter { $0.createdAt > weekAgo }\n    let weeklyTotal = weeklyPayouts.reduce(0.0) { $0 + $1.amount }\n    \n    if weeklyTotal + requestedAmount > 10000.0 {\n        return PayoutFrequencyValidation(\n            isValid: false,\n            errorMessage: \"Weekly payout limit of $10,000 would be exceeded. Current total: $\\(String(format: \"%.2f\", weeklyTotal))\",\n            nextAvailableDate: Calendar.current.date(byAdding: .day, value: 7, to: weekAgo)\n        )\n    }\n    \n    // Monthly limit: Max $50,000 per month\n    let monthAgo = Calendar.current.date(byAdding: .month, value: -1, to: now)!\n    let monthlyPayouts = history.filter { $0.createdAt > monthAgo }\n    let monthlyTotal = monthlyPayouts.reduce(0.0) { $0 + $1.amount }\n    \n    if monthlyTotal + requestedAmount > 50000.0 {\n        return PayoutFrequencyValidation(\n            isValid: false,\n            errorMessage: \"Monthly payout limit of $50,000 would be exceeded. Current total: $\\(String(format: \"%.2f\", monthlyTotal))\",\n            nextAvailableDate: Calendar.current.date(byAdding: .month, value: 1, to: monthAgo)\n        )\n    }\n    \n    return PayoutFrequencyValidation(\n        isValid: true,\n        errorMessage: nil,\n        nextAvailableDate: nil\n    )\n}\n\n// Usage before creating components\nlet frequencyCheck = try await validatePayoutFrequency(\n    userId: currentUser.id,\n    requestedAmount: 100.0\n)\n\nif !frequencyCheck.isValid {\n    var errorMessage = frequencyCheck.errorMessage ?? \"Payout limit exceeded\"\n    \n    if let nextDate = frequencyCheck.nextAvailableDate {\n        let formatter = DateFormatter()\n        formatter.dateStyle = .medium\n        errorMessage += \"\\nNext available: \\(formatter.string(from: nextDate))\"\n    }\n    \n    showError(errorMessage)\n    return\n}\n\n// Proceed with component creation\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"validate-compliance-requirements","__idx":14},"children":["Validate compliance requirements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Check KYC, AML, and other regulatory compliance before processing payouts. Use this validation in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPrePayoutSubmit"]}," (when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["proceedPayoutWithSdk: true"]},") or before component creation."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"struct ComplianceValidation {\n    let passed: Bool\n    let reason: String?\n    let requiresAction: Bool\n    let actionUrl: String?\n}\n\nfunc validatePayoutCompliance(\n    userId: String,\n    amount: Double,\n    country: String\n) async throws -> ComplianceValidation {\n    // 1. Check KYC (Know Your Customer) status\n    let kycStatus = try await getKYCStatus(userId: userId)\n    \n    switch kycStatus {\n    case .notStarted:\n        return ComplianceValidation(\n            passed: false,\n            reason: \"Identity verification required before receiving payouts\",\n            requiresAction: true,\n            actionUrl: \"/kyc/start\"\n        )\n        \n    case .pending:\n        return ComplianceValidation(\n            passed: false,\n            reason: \"Identity verification is pending review\",\n            requiresAction: false,\n            actionUrl: nil\n        )\n        \n    case .rejected:\n        return ComplianceValidation(\n            passed: false,\n            reason: \"Identity verification was rejected. Please contact support.\",\n            requiresAction: true,\n            actionUrl: \"/support\"\n        )\n        \n    case .verified:\n        // KYC passed, continue checks\n        break\n    }\n    \n    // 2. Check AML (Anti-Money Laundering) thresholds\n    if amount > 3000.0 {\n        let amlCheck = try await performAMLCheck(\n            userId: userId,\n            amount: amount,\n            country: country\n        )\n        \n        guard amlCheck.passed else {\n            return ComplianceValidation(\n                passed: false,\n                reason: \"Additional documentation required for payouts over $3,000\",\n                requiresAction: true,\n                actionUrl: \"/compliance/aml\"\n            )\n        }\n    }\n    \n    // 3. Check country-specific regulations\n    let countryCompliance = try await checkCountryRegulations(\n        country: country,\n        amount: amount,\n        userId: userId\n    )\n    \n    guard countryCompliance.allowed else {\n        return ComplianceValidation(\n            passed: false,\n            reason: countryCompliance.restriction ?? \"Payouts not available in your region\",\n            requiresAction: false,\n            actionUrl: nil\n        )\n    }\n    \n    // 4. Check for sanctions or watchlists\n    let sanctionsCheck = try await checkSanctionsList(userId: userId)\n    guard sanctionsCheck.clear else {\n        return ComplianceValidation(\n            passed: false,\n            reason: \"Account flagged for compliance review\",\n            requiresAction: true,\n            actionUrl: \"/support\"\n        )\n    }\n    \n    return ComplianceValidation(\n        passed: true,\n        reason: nil,\n        requiresAction: false,\n        actionUrl: nil\n    )\n}\n\n// Usage in payout flow (before component creation or in onPrePayoutSubmit)\nlet complianceCheck = try await validatePayoutCompliance(\n    userId: currentUser.id,\n    amount: getCurrentPayoutAmount(),\n    country: currentUser.country\n)\n\nif !complianceCheck.passed {\n    var errorMessage = complianceCheck.reason ?? \"Compliance check failed\"\n    \n    if complianceCheck.requiresAction, let actionUrl = complianceCheck.actionUrl {\n        // Navigate to action URL\n        navigateToCompliance(url: actionUrl)\n    } else {\n        showError(errorMessage)\n    }\n    \n    return\n}\n\n// Proceed with payout\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"email-format-validation","__idx":15},"children":["Email format validation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Validate email addresses for payout notifications and records."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"func validateEmail(_ email: String) -> Bool {\n    let emailRegex = \"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\\\.[A-Za-z]{2,64}\"\n    let emailPredicate = NSPredicate(format: \"SELF MATCHES %@\", emailRegex)\n    return emailPredicate.evaluate(with: email)\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"best-practices","__idx":16},"children":["Best practices"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"fail-early","__idx":17},"children":["Fail early"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Validate data before displaying payout components to provide immediate feedback and prevent unnecessary processing."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"@objc func requestPayoutButtonTapped() {\n    // Validate amount before showing payout components\n    let amountValidation = validatePayoutAmount(\n        amount: getPayoutAmount(),\n        currency: \"USD\",\n        userId: currentUser.id\n    )\n    \n    guard amountValidation.isValid else {\n        showAlert(\n            title: \"Invalid Amount\",\n            message: amountValidation.errorMessage ?? \"Payout amount validation failed\"\n        )\n        return\n    }\n    \n    // Check frequency limits\n    let frequencyCheck = try await validatePayoutFrequency(\n        userId: currentUser.id,\n        requestedAmount: getPayoutAmount()\n    )\n    \n    guard frequencyCheck.isValid else {\n        showAlert(\n            title: \"Payout Limit Reached\",\n            message: frequencyCheck.errorMessage ?? \"Payout frequency limit exceeded\"\n        )\n        return\n    }\n    \n    // Check compliance\n    let complianceCheck = try await validatePayoutCompliance(\n        userId: currentUser.id,\n        amount: getPayoutAmount(),\n        country: currentUser.country\n    )\n    \n    guard complianceCheck.passed else {\n        handleComplianceFailure(complianceCheck)\n        return\n    }\n    \n    // All validations passed - show payout screen\n    showPayoutScreen()\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"provide-clear-error-messages","__idx":18},"children":["Provide clear error messages"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When validation fails, provide specific, actionable error messages."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"config.onError = { error in\n    let errorTitle: String\n    let errorMessage: String\n    let actionButton: String?\n    \n    switch error.errorCode {\n    case \"SDK0809\":\n        errorTitle = \"Account Information Missing\"\n        errorMessage = \"PayPal account information is missing. Please try linking your account again.\"\n        actionButton = \"Retry\"\n        \n    case \"SDK0810\":\n        errorTitle = \"Invalid Amount\"\n        errorMessage = \"The payout amount is invalid. Please contact support for assistance.\"\n        actionButton = \"Contact Support\"\n        \n    case \"SDK0811\":\n        errorTitle = \"Invalid Amount\"\n        errorMessage = \"Payout amount must be greater than zero. Please check your available balance.\"\n        actionButton = \"Check Balance\"\n        \n    case \"SDK0812\":\n        errorTitle = \"Missing Amount\"\n        errorMessage = \"Payout amount is required. Please check your configuration.\"\n        actionButton = \"Retry\"\n        \n    case \"SDK0813\", \"SDK0814\", \"SDK0815\":\n        errorTitle = \"Currency Error\"\n        errorMessage = \"Invalid currency configuration. Please contact support.\"\n        actionButton = \"Contact Support\"\n        \n    case \"SDK0817\":\n        errorTitle = \"Account Error\"\n        errorMessage = \"PayPal account identifier is too long. Please try again.\"\n        actionButton = \"Retry\"\n        \n    case \"SDK0818\":\n        errorTitle = \"Invalid PayPal Account\"\n        errorMessage = \"Your PayPal account information is invalid. Please link your account again.\"\n        actionButton = \"Relink Account\"\n        \n    case \"SDK0819\":\n        errorTitle = \"Payout Failed\"\n        errorMessage = \"The payout transaction failed. Please try again or contact support.\"\n        actionButton = \"Retry\"\n        \n    default:\n        errorTitle = \"Payout Failed\"\n        errorMessage = \"An error occurred while processing your payout. Please try again.\"\n        actionButton = \"Retry\"\n    }\n    \n    showAlert(\n        title: errorTitle,\n        message: errorMessage,\n        primaryButton: actionButton\n    )\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"log-validation-errors","__idx":19},"children":["Log validation errors"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Log all validation errors for debugging, analytics, and compliance monitoring."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"config.onError = { error in\n    // Log to analytics\n    Analytics.logEvent(\"payout_validation_error\", parameters: [\n        \"error_code\": error.errorCode,\n        \"error_message\": error.errorMessage,\n        \"user_id\": currentUser.id,\n        \"payout_amount\": getCurrentPayoutAmount(),\n        \"timestamp\": Date().timeIntervalSince1970\n    ])\n    \n    // Log to crash reporting\n    Crashlytics.crashlytics().record(error: error)\n    Crashlytics.crashlytics().setCustomValue(currentUser.id, forKey: \"user_id\")\n    Crashlytics.crashlytics().setCustomValue(getCurrentPayoutAmount(), forKey: \"payout_amount\")\n    \n    // Log to server for compliance\n    logPayoutErrorToServer(\n        userId: currentUser.id,\n        errorCode: error.errorCode,\n        errorMessage: error.errorMessage,\n        amount: getCurrentPayoutAmount(),\n        timestamp: Date()\n    )\n    \n    // Console log\n    print(\"Payout error [\\(error.errorCode)]: \\(error.errorMessage)\")\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"test-edge-cases","__idx":20},"children":["Test edge cases"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Test validation with edge cases and boundary values."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"func testPayoutValidation() async {\n    // Test minimum amount\n    var validation = try await validatePayoutAmount(amount: 1.0, currency: \"USD\", userId: \"test\")\n    assert(validation.isValid, \"Minimum amount should be valid\")\n    \n    validation = try await validatePayoutAmount(amount: 0.99, currency: \"USD\", userId: \"test\")\n    assert(!validation.isValid, \"Below minimum should be invalid\")\n    \n    // Test maximum amount\n    validation = try await validatePayoutAmount(amount: 60000.0, currency: \"USD\", userId: \"test\")\n    assert(validation.isValid, \"Maximum amount should be valid\")\n    \n    validation = try await validatePayoutAmount(amount: 60000.01, currency: \"USD\", userId: \"test\")\n    assert(!validation.isValid, \"Above maximum should be invalid\")\n    \n    // Test zero and negative\n    validation = try await validatePayoutAmount(amount: 0.0, currency: \"USD\", userId: \"test\")\n    assert(!validation.isValid, \"Zero amount should be invalid\")\n    \n    validation = try await validatePayoutAmount(amount: -10.0, currency: \"USD\", userId: \"test\")\n    assert(!validation.isValid, \"Negative amount should be invalid\")\n    \n    // Test invalid numbers\n    validation = try await validatePayoutAmount(amount: Double.nan, currency: \"USD\", userId: \"test\")\n    assert(!validation.isValid, \"NaN should be invalid\")\n    \n    validation = try await validatePayoutAmount(amount: Double.infinity, currency: \"USD\", userId: \"test\")\n    assert(!validation.isValid, \"Infinity should be invalid\")\n    \n    // Test payer ID validation\n    var payerIdCheck = validatePayerId(\"PAYERID123\")\n    assert(payerIdCheck.isValid, \"Valid payer ID should pass\")\n    \n    payerIdCheck = validatePayerId(\"\")\n    assert(!payerIdCheck.isValid, \"Empty payer ID should fail\")\n    \n    payerIdCheck = validatePayerId(\"TOOLONGPAYERID123\")\n    assert(!payerIdCheck.isValid, \"Payer ID over 13 chars should fail\")\n    \n    payerIdCheck = validatePayerId(\"INVALID@ID\")\n    assert(!payerIdCheck.isValid, \"Non-alphanumeric should fail\")\n    \n    // Test email validation\n    assert(validateEmail(\"user@example.com\"), \"Valid email should pass\")\n    assert(!validateEmail(\"invalid.email\"), \"Invalid email should fail\")\n    assert(!validateEmail(\"@example.com\"), \"Email without user should fail\")\n    assert(!validateEmail(\"user@\"), \"Email without domain should fail\")\n    \n    print(\"All payout validation tests passed ✅\")\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"monitor-validation-metrics","__idx":21},"children":["Monitor validation metrics"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Track validation failure rates to identify issues early."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"func trackValidationMetrics(error: BaseSdkException) {\n    // Track error distribution\n    Analytics.logEvent(\"payout_error_distribution\", parameters: [\n        \"error_type\": error.errorCode,\n        \"timestamp\": Date().timeIntervalSince1970\n    ])\n    \n    // Increment error counter\n    incrementErrorCounter(errorCode: error.errorCode)\n    \n    // Alert if error rate is high\n    let errorRate = getRecentErrorRate()\n    if errorRate > 0.1 { // More than 10% error rate\n        sendAlertToMonitoring(\n            message: \"High payout error rate detected: \\(errorRate * 100)%\",\n            severity: \"high\"\n        )\n    }\n}\n","lang":"swift"},"children":[]}]},"headings":[{"value":"Data validation","id":"data-validation","depth":1},{"value":"Overview","id":"overview","depth":2},{"value":"Applicable components","id":"applicable-components","depth":3},{"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":4},{"value":"Custom validation","id":"custom-validation","depth":2},{"value":"When to use custom validation","id":"when-to-use-custom-validation","depth":3},{"value":"onPrePayoutSubmit return types","id":"onprepayoutsubmit-return-types","depth":3},{"value":"Pre-payout approval validation","id":"pre-payout-approval-validation","depth":3},{"value":"Validate payout amount limits","id":"validate-payout-amount-limits","depth":3},{"value":"Validate payer ID format","id":"validate-payer-id-format","depth":3},{"value":"Calculate fraud risk score","id":"calculate-fraud-risk-score","depth":3},{"value":"Validate payout frequency","id":"validate-payout-frequency","depth":3},{"value":"Validate compliance requirements","id":"validate-compliance-requirements","depth":3},{"value":"Email format validation","id":"email-format-validation","depth":3},{"value":"Best practices","id":"best-practices","depth":2},{"value":"Fail early","id":"fail-early","depth":3},{"value":"Provide clear error messages","id":"provide-clear-error-messages","depth":3},{"value":"Log validation errors","id":"log-validation-errors","depth":3},{"value":"Test edge cases","id":"test-edge-cases","depth":3},{"value":"Monitor validation metrics","id":"monitor-validation-metrics","depth":3}],"frontmatter":{"seo":{"title":"Data validation"}},"lastModified":"2026-06-12T11:56:36.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/checkout/components/ios/paypal/payouts/data-validation","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}