# Data validation

Learn about built-in validation and error handling for payout components.

## Overview

The payout components include comprehensive validation to ensure data integrity and compliance with PayPal and Venmo requirements. All built-in validation is performed before executing payouts. If validation fails, the SDK will throw a specific exception with detailed error information.

You can handle these errors through the `onError` callback and display appropriate messages to your customers.

## Built-in validation

By default, the payout components validate that:

* Required fields are provided (amount, currency, recipient wallet details).
* Amount is a valid positive number greater than zero.
* Currency codes are valid 3-letter ISO 4217 codes.
* Email addresses are properly formatted.
* Phone numbers contain only numeric characters (Venmo).
* Recipient types match the provided receiver format.
* Field lengths don't exceed maximum limits.
* Venmo payouts use USD currency (the only supported currency for Venmo).


## Error codes reference

### SDK configuration errors

| Error code  | Exception  | Description  | Resolution  |
|  --- | --- | --- | --- |
| SDK1000 | `PaypalConfigRequiredException` | PayPal configuration is required. | Ensure `paypalConfig` is provided in SDK initialisation. |


### Amount validation errors

| Error code  | Exception  | Description  | Resolution  |
|  --- | --- | --- | --- |
| SDK0810 | `PaypalPayoutAmountInvalidException` | Amount must be a valid number. | Provide a valid numeric amount. |
| SDK0811 | `PaypalPayoutAmountNotPositiveException` | Amount must be a positive number greater than 0. | Ensure amount is greater than zero. |
| SDK0812 | `PaypalPayoutAmountRequiredException` | Amount is required. | Provide an amount in `transactionData.amount`. |


### Currency validation errors

| Error code  | Exception  | Description  | Resolution  |
|  --- | --- | --- | --- |
| SDK0813 | `PaypalPayoutCurrencyRequiredException` | Currency code must be a non-empty string. | Provide a currency code in `transactionData.currency`. |
| SDK0814 | `PaypalPayoutCurrencyLengthInvalidException` | Currency code must be a 3-letter ISO 4217 code. | Use a valid 3-letter code like `USD`, `EUR`, `GBP`. |
| SDK0815 | `PaypalPayoutCurrencyCodeInvalidException` | Invalid currency code. | Use a valid ISO 4217 currency code. |


### PayPal receiver validation errors

| Error code  | Exception  | Description  | Resolution  |
|  --- | --- | --- | --- |
| SDK0803 | `PaypalPayoutReceiverRequiredException` | Receiver value is required. | Provide a PayPal email or Payer ID. |
| SDK0804 | `PaypalPayoutReceiverTypeInvalidException` | Invalid receiver type. | Use `Email`, `Phone`, `PaypalId`, or `UserHandle`. |
| SDK0805 | `PaypalPayoutReceiverTypeEmailOnlyException` | Receiver type must be `Email`. | PayPal wallet requires `Email` as receiver type. |
| SDK0806 | `PaypalPayoutWalletInvalidException` | Invalid wallet value. | Use `Paypal` for PayPal payouts. |
| SDK0807 | `PaypalPayoutWalletRequiresEmailException` | Receiver type must be `Email` when wallet is `Paypal`. | Ensure receiver type is `Email`. |
| SDK0808 | `PaypalPayoutEmailInvalidException` | Invalid email format. | Provide a valid email address. |
| SDK0809 | `PaypalPayoutPayerIdRequiredException` | PayPal Payer ID is required. | Provide a PayPal Payer ID or email. |
| SDK0816 | `PaypalPayoutReceiverTypeRequiredException` | Receiver type is required. | Specify the receiver type (e.g., `Email`, `PaypalId`). |
| SDK0818 | `PaypalPayoutFieldLengthExceededException` | Field exceeds maximum length. | Reduce the field length to within limits. |


### Venmo receiver validation errors

| Error code  | Exception  | Description  | Resolution  |
|  --- | --- | --- | --- |
| SDK0900 | `VenmoPayoutWalletInvalidException` | Venmo wallet is required. | Provide `venmoWallet` configuration. |
| SDK0901 | `VenmoPayoutReceiverRequiredException` | Receiver value is required. | Provide a Venmo receiver (email, phone, or handle). |
| SDK0902 | `VenmoPayoutRecipientTypeRequiredException` | Venmo recipient type is required. | Specify `recipientType` as `Email`, `Phone`, or `UserHandle`. |
| SDK0903 | `VenmoPayoutRecipientTypeInvalidException` | Invalid recipient type. | Use `Email`, `Phone`, or `UserHandle`. |
| SDK0904 | `VenmoPayoutPhoneInvalidException` | Invalid phone format. | Phone number must contain only numeric characters. |
| SDK0905 | `VenmoPayoutCurrencyInvalidException` | Venmo only supports USD currency. | Use `USD` for Venmo payouts. |


### Payout submission errors

| Error code  | Exception  | Description  | Resolution  |
|  --- | --- | --- | --- |
| SDK1001 | `PayoutRecipientWalletRequiredException` | Recipient wallet is required. | Specify `recipientWallet` as `Paypal` or `Venmo`. |
| SDK1002 | `PayoutReceiverAndRecipientTypeRequiredException` | Receiver and recipient type are required. | Provide both receiver value and recipient type. |
| SDK1003 | `UnsupportedPayoutRecipientWalletException` | Unsupported recipient wallet. | Use `Paypal` or `Venmo`. |
| SDK1004 | `PayoutNoteTooLongException` | Payout note exceeds maximum length. | Notes longer than 4000 characters will be automatically truncated with a console warning. |


## Error handling

### Basic error handling

Handle validation errors through the `onError` callback:


```typescript
const submitConfig = {
  recipientWallet: "Paypal",
  
  onError: (error) => {
    console.error("Payout error:", error.ErrorCode, error.message);
    
    // Display user-friendly message
    showErrorMessage(error.message);
  }
};
```

### Comprehensive error handling

Implement detailed error handling based on error codes:


```typescript
const submitConfig = {
  recipientWallet: "Paypal",
  
  onError: (error) => {
    console.error("Payout error:", error);
    
    let userMessage: string;
    let action: string = "";
    
    switch (error.ErrorCode) {
      // Amount errors
      case "SDK0810":
        userMessage = "Invalid withdrawal amount.";
        action = "Please enter a valid number.";
        break;
      case "SDK0811":
        userMessage = "Withdrawal amount must be greater than zero.";
        action = "Please enter a positive amount.";
        break;
      case "SDK0812":
        userMessage = "Withdrawal amount is missing.";
        action = "Please contact support.";
        break;
        
      // Currency errors
      case "SDK0813":
      case "SDK0814":
      case "SDK0815":
        userMessage = "Invalid currency configuration.";
        action = "Please contact support.";
        break;
        
      // PayPal receiver errors
      case "SDK0803":
      case "SDK0809":
        userMessage = "PayPal account information is missing.";
        action = "Please link your PayPal account.";
        break;
      case "SDK0808":
        userMessage = "Invalid PayPal email address.";
        action = "Please update your PayPal email.";
        break;
      case "SDK0818":
        userMessage = "Account information exceeds maximum length.";
        action = "Please contact support.";
        break;
        
      // Venmo receiver errors
      case "SDK0901":
        userMessage = "Venmo account information is missing.";
        action = "Please link your Venmo account.";
        break;
      case "SDK0903":
        userMessage = "Invalid Venmo recipient type.";
        action = "Please contact support.";
        break;
      case "SDK0904":
        userMessage = "Invalid phone number format.";
        action = "Please update your Venmo phone number.";
        break;
      case "SDK0905":
        userMessage = "Venmo only supports USD withdrawals.";
        action = "Please select a different payout method.";
        break;
        
      // Payout submission errors
      case "SDK1001":
      case "SDK1002":
      case "SDK1003":
        userMessage = "Invalid payout configuration.";
        action = "Please contact support.";
        break;
      case "SDK1004":
        userMessage = "Payout note is too long.";
        action = "Notes longer than 4000 characters will be truncated.";
        break;
        
      // Configuration errors
      case "SDK1000":
        userMessage = "Payout is not configured correctly.";
        action = "Please contact support.";
        break;
        
      default:
        userMessage = "An error occurred during withdrawal.";
        action = "Please try again or contact support.";
    }
    
    // Display error to user
    showErrorDialog({
      title: "Withdrawal Error",
      message: userMessage,
      action: action,
      errorCode: error.ErrorCode
    });
    
    // Log for debugging
    logError("payout_validation_error", {
      errorCode: error.ErrorCode,
      errorMessage: error.message,
      timestamp: new Date().toISOString()
    });
  }
};
```

## Custom validation

### Pre-payout validation

Run validation before the payout is executed to catch issues early:


```typescript
const submitConfig = {
  recipientWallet: "Paypal",
  
  onPrePayoutSubmit: async () => {
    try {
      // 1. Validate withdrawal limits
      const limitsCheck = await validateWithdrawalLimits({
        userId: getCurrentUserId(),
        amount: getPayoutAmount(),
        currency: getPayoutCurrency()
      });
      
      if (!limitsCheck.valid) {
        showError(`Withdrawal limit exceeded. Maximum: ${limitsCheck.maxAmount}`);
        return { isApproved: false };
      }
      
      // 2. Verify sufficient balance
      const balanceCheck = await verifyBalance({
        userId: getCurrentUserId(),
        amount: getPayoutAmount()
      });
      
      if (!balanceCheck.sufficient) {
        showError("Insufficient balance for this withdrawal.");
        return { isApproved: false };
      }
      
      // 3. Compliance check
      const complianceCheck = await performComplianceCheck({
        userId: getCurrentUserId(),
        amount: getPayoutAmount(),
        recipientType: getRecipientType()
      });
      
      if (!complianceCheck.passed) {
        showError("This withdrawal requires additional verification.");
        return { isApproved: false };
      }
      
      // 4. Fraud prevention
      const fraudCheck = await checkFraudIndicators({
        userId: getCurrentUserId(),
        amount: getPayoutAmount(),
        ipAddress: getClientIP(),
        deviceFingerprint: getDeviceFingerprint()
      });
      
      if (fraudCheck.riskLevel === "HIGH") {
        showError("This withdrawal has been flagged for review.");
        return { isApproved: false };
      }
      
      // All validations passed
      return { isApproved: true };
      
    } catch (error) {
      console.error("Pre-payout validation failed:", error);
      showError("Unable to validate withdrawal. Please try again.");
      return { isApproved: false };
    }
  }
};
```

### Validate withdrawal limits

Check if the payout amount is within allowed limits:


```typescript
async function validateWithdrawalLimits(userId: string, amount: number, currency: string) {
  // Get user's withdrawal limits
  const limits = await getUserWithdrawalLimits(userId);
  
  // Check daily limit
  const todayWithdrawals = await getTodayWithdrawals(userId);
  const dailyTotal = todayWithdrawals.reduce((sum, w) => sum + w.amount, 0);
  
  if (dailyTotal + amount > limits.dailyLimit) {
    return {
      valid: false,
      reason: "daily_limit_exceeded",
      remaining: limits.dailyLimit - dailyTotal
    };
  }
  
  // Check monthly limit
  const monthlyWithdrawals = await getMonthlyWithdrawals(userId);
  const monthlyTotal = monthlyWithdrawals.reduce((sum, w) => sum + w.amount, 0);
  
  if (monthlyTotal + amount > limits.monthlyLimit) {
    return {
      valid: false,
      reason: "monthly_limit_exceeded",
      remaining: limits.monthlyLimit - monthlyTotal
    };
  }
  
  // Check minimum withdrawal amount
  if (amount < limits.minimumWithdrawal) {
    return {
      valid: false,
      reason: "below_minimum",
      minimum: limits.minimumWithdrawal
    };
  }
  
  // Check maximum single withdrawal
  if (amount > limits.maximumSingleWithdrawal) {
    return {
      valid: false,
      reason: "exceeds_maximum",
      maximum: limits.maximumSingleWithdrawal
    };
  }
  
  return { valid: true };
}
```

### Validate recipient account

Verify the recipient account is valid and active:


```typescript
async function validateRecipientAccount(walletType: string, receiver: string) {
  if (walletType === "Paypal") {
    // Validate PayPal email format
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(receiver)) {
      return {
        valid: false,
        reason: "invalid_email_format"
      };
    }
    
    // Check if account is verified (if you have this capability)
    const accountStatus = await checkPayPalAccountStatus(receiver);
    if (accountStatus.restricted) {
      return {
        valid: false,
        reason: "account_restricted"
      };
    }
  }
  
  if (walletType === "Venmo") {
    // Validate based on recipient type
    const recipientType = getVenmoRecipientType();
    
    if (recipientType === "Phone") {
      // Phone must be numeric
      const phoneRegex = /^\+?[0-9]+$/;
      if (!phoneRegex.test(receiver)) {
        return {
          valid: false,
          reason: "invalid_phone_format"
        };
      }
    }
    
    if (recipientType === "Email") {
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(receiver)) {
        return {
          valid: false,
          reason: "invalid_email_format"
        };
      }
    }
  }
  
  return { valid: true };
}
```

### Calculate fraud risk score

Assess the risk level of a payout request:


```typescript
async function calculatePayoutRiskScore(payoutData: PayoutData) {
  const factors = {
    // Check velocity - multiple payouts in short time
    velocityScore: await checkPayoutVelocity(payoutData.userId),
    
    // Check if payout destination is new
    destinationScore: await checkDestinationHistory(payoutData.userId, payoutData.receiver),
    
    // Check amount patterns
    amountScore: await analyzeAmountPatterns(payoutData.userId, payoutData.amount),
    
    // Check device/location consistency
    deviceScore: await checkDeviceConsistency(payoutData.userId, payoutData.deviceData),
    
    // Check time patterns
    timeScore: await checkTimePatterns(payoutData.userId)
  };
  
  // Calculate weighted average
  const weights = {
    velocityScore: 0.25,
    destinationScore: 0.20,
    amountScore: 0.20,
    deviceScore: 0.20,
    timeScore: 0.15
  };
  
  const totalScore = Object.entries(factors).reduce((sum, [key, score]) => {
    return sum + (score * weights[key as keyof typeof weights]);
  }, 0);
  
  return {
    score: totalScore,
    riskLevel: totalScore > 0.8 ? "HIGH" : totalScore > 0.5 ? "MEDIUM" : "LOW",
    factors: factors,
    requiresReview: totalScore > 0.7
  };
}
```

## Validation best practices

### Client-side validation

Always validate on the client side before submission:


```typescript
function validatePayoutBeforeSubmit() {
  const errors: string[] = [];
  
  // Validate amount
  const amount = getPayoutAmount();
  if (!amount || amount <= 0) {
    errors.push("Please enter a valid withdrawal amount.");
  }
  
  // Validate recipient is selected
  const recipientWallet = getSelectedWallet();
  if (!recipientWallet) {
    errors.push("Please select a payout method.");
  }
  
  // Validate wallet-specific requirements
  if (recipientWallet === "Venmo" && getCurrency() !== "USD") {
    errors.push("Venmo only supports USD withdrawals.");
  }
  
  if (errors.length > 0) {
    showValidationErrors(errors);
    return false;
  }
  
  return true;
}
```

### Display meaningful error messages

Map technical errors to user-friendly messages:


```typescript
const errorMessages: Record<string, { title: string; message: string; action: string }> = {
  "SDK0810": {
    title: "Invalid Amount",
    message: "The withdrawal amount is not valid.",
    action: "Please enter a valid number."
  },
  "SDK0811": {
    title: "Invalid Amount",
    message: "The withdrawal amount must be greater than zero.",
    action: "Please enter a positive amount."
  },
  "SDK0808": {
    title: "Invalid Email",
    message: "The PayPal email address is not valid.",
    action: "Please check your PayPal email and try again."
  },
  "SDK0904": {
    title: "Invalid Phone",
    message: "The phone number format is not valid.",
    action: "Please enter a valid phone number with only digits."
  },
  "SDK0905": {
    title: "Currency Not Supported",
    message: "Venmo only supports USD withdrawals.",
    action: "Please use PayPal for non-USD withdrawals."
  },
  "SDK1004": {
    title: "Note Too Long",
    message: "The payout note exceeds the maximum length.",
    action: "Notes longer than 4000 characters will be truncated."
  }
};

function getErrorMessage(errorCode: string) {
  return errorMessages[errorCode] || {
    title: "Error",
    message: "An unexpected error occurred.",
    action: "Please try again or contact support."
  };
}
```

### Log errors for debugging

Always log errors for troubleshooting:


```typescript
function logPayoutError(error: any, context: Record<string, any>) {
  const errorLog = {
    timestamp: new Date().toISOString(),
    errorCode: error.ErrorCode,
    errorMessage: error.message,
    userId: context.userId,
    payoutAmount: context.amount,
    payoutCurrency: context.currency,
    recipientWallet: context.recipientWallet,
    userAgent: navigator.userAgent,
    url: window.location.href
  };
  
  // Send to logging service
  sendToLoggingService("payout_error", errorLog);
  
  // Console log for development
  console.error("Payout error:", errorLog);
}
```