Data validation
Learn about built-in validation and implement additional scenarios.
Overview
The PayPal component includes comprehensive validation to ensure data integrity and compliance with PayPal's requirements. All built-in validation is performed before creating orders. If it fails, the SDK will throw a PaypalValidationException
with detailed error information.
You can also easily build custom validation, depending on your business needs.
Built-in validation
By default, the PayPal Component validates that:
- Fields marked as required are provided.
- Maximum length constraints are respected.
- All email, currency code, country code, and date fields are formatted properly.
- Valid values are provided for every enum.
Error codes
The PayPal component returns structured error codes for different validation failures:
Error Code | Description | Common Causes |
---|---|---|
REQUIRED_FIELD | Required field is missing | Missing mandatory configuration |
MAX_LENGTH_EXCEEDED | Field exceeds maximum length | Text too long for PayPal limits |
INVALID_LENGTH | Field has incorrect length | Currency/country code format |
INVALID_EMAIL_FORMAT | Email format is invalid | Malformed email address |
INVALID_DATE_FORMAT | Date format is invalid | Non-ISO 8601 date format |
INVALID_ENUM_VALUE | Invalid enum value | Unsupported option value |
INVALID_SHIPPING_PREFERENCE | Shipping preference mismatch | Wrong preference for shipping options |
NO_SELECTION_MADE | No shipping option selected | Missing selected option |
MULTIPLE_SELECTIONS_NOT_ALLOWED | Multiple shipping options selected | More than one selected |
CURRENCY_CODE_INVALID | Currency code inconsistency | Different currencies in same request |
Validation example
// Example of handling validation errors
const paypalConfig = {
// ... configuration
onSubmitError: (error) => {
if (error._validationResult) {
const { errors } = error._validationResult;
// Handle specific validation errors
if (errors['fundingData.paypal.payeeEmailAddress']) {
console.error('Invalid email address');
}
if (errors['amounts.currencyCode']) {
console.error('Invalid currency code');
}
// Display user-friendly error messages
Object.entries(errors).forEach(([field, errorInfo]) => {
console.error(`${field}: ${errorInfo.message}`);
});
}
}
};
Custom validation
Pre-authorisation validation
Run a validation before the PayPal authorisation to catch issues early and prevent failed payments.
const paypalComponent = pxpSdk.create('paypal-button', {
onApprove: async (data, actions) => {
try {
// 1. Business logic validation
const orderValidation = await validateOrder({
cartItems: getCartItems(),
customerLocation: getCustomerLocation(),
paymentAmount: getOrderTotal()
});
if (!orderValidation.valid) {
throw new Error(orderValidation.reason);
}
// 2. Security validation
const securityCheck = await performSecurityValidation({
customerIP: getClientIP(),
deviceFingerprint: getDeviceFingerprint(),
paymentHistory: getCustomerPaymentHistory()
});
if (securityCheck.riskLevel === 'HIGH') {
// Require additional verification
const verified = await requestAdditionalVerification();
if (!verified) {
throw new Error('Additional verification required');
}
}
// 3. Inventory validation
const inventoryCheck = await validateInventory(getCartItems());
if (!inventoryCheck.allAvailable) {
throw new Error('Some items are no longer available');
}
// 4. Regulatory compliance
const complianceCheck = await validateCompliance({
customerCountry: getCustomerCountry(),
orderAmount: getOrderTotal(),
productTypes: getProductTypes()
});
if (!complianceCheck.compliant) {
throw new Error('Order does not meet regulatory requirements');
}
// If all validations pass, proceed with authorisation
await processPayPalAuthorization(data);
} catch (error) {
console.error('Validation failed:', error);
showError(error.message);
}
}
});
Confirmation page validation
Run validation on the confirmation page before capturing funds, to ensure order integrity and prevent capture failures.
async function confirmAndCaptureOrder(orderID) {
try {
// 1. Re-validate order (things might have changed)
const currentOrderState = await validateCurrentOrderState(orderID);
if (!currentOrderState.valid) {
throw new Error('Order state has changed since authorisation');
}
// 2. Final inventory check
const finalInventoryCheck = await reserveInventory(orderID);
if (!finalInventoryCheck.success) {
throw new Error('Inventory no longer available');
}
// 3. Calculate final amounts (shipping, taxes, fees)
const finalCalculation = await calculateFinalAmounts(orderID);
// 4. Validate amount changes are within acceptable limits
const authData = getAuthorizationData(orderID);
const amountDifference = Math.abs(finalCalculation.total - authData.authorizedAmount);
const maxAllowedDifference = authData.authorizedAmount * 0.15; // 15% tolerance
if (amountDifference > maxAllowedDifference) {
throw new Error('Final amount differs too much from authorised amount');
}
// 5. Capture the payment
await captureAuthorizedPayment(orderID, finalCalculation.total);
} catch (error) {
handleCaptureValidationError(error);
}
}
Validate geographic restrictions
Check in real-time if any cart items are restricted in the customer's country to prevent compliance violations.
function validateGeographicRestrictions(customerCountry, cartItems) {
const restrictedItems = cartItems.filter(item =>
item.restrictions?.countries?.includes(customerCountry)
);
return {
valid: restrictedItems.length === 0,
restrictedItems: restrictedItems
};
}
Calculate a risk score
Calculate a comprehensive fraud risk score based on multiple behavioural and historical factors.
async function calculateFraudScore(transactionData) {
const factors = {
velocityScore: await checkPaymentVelocity(transactionData.customerEmail),
locationScore: await validateLocation(transactionData.ipAddress),
deviceScore: await analyzeDeviceFingerprint(transactionData.deviceData),
historyScore: await analyzePaymentHistory(transactionData.customerId)
};
const totalScore = Object.values(factors).reduce((sum, score) => sum + score, 0) / 4;
return {
score: totalScore,
riskLevel: totalScore > 0.8 ? 'HIGH' : totalScore > 0.5 ? 'MEDIUM' : 'LOW',
factors: factors
};
}
Updated 13 days ago