# Data validation

Learn about built-in validation and implement additional scenarios for Paze web.

## Overview

The Paze component validates configuration, identity data, and transaction parameters before and during the payment flow. Validation runs at multiple stages:

1. **Component initialisation:** Session configuration, currency, and client presentation settings
2. **Presentment:** Identity validation when `emailAddress` and/or `phoneNumber` are provided; dynamic mode requires both
3. **Checkout:** Amount, billing preferences, shipping countries, cobrand configuration, and checkout identity (phone takes precedence over email when both are set)
4. **Complete:** Transaction type, amount, and currency for the Paze `complete()` call
5. **Merchant callbacks:** Optional custom validation before checkout and merchant approval after checkout


If validation fails, the SDK throws a `BaseSdkException` with a structured error code in the `SDK12XX` range.

## Built-in validation

By default, the Paze component validates that:

* `allowedFundingTypes.wallets.paze.clientId` is present in the session.
* The transaction currency is `USD`.
* `clientName` is 50 characters or fewer.
* `siteName` (brand name) is 50 characters or fewer.
* `paymentDescription` (statement descriptor) is 25 characters or fewer.
* Email addresses are valid RFC 5322 format and 128 characters or fewer when provided.
* Phone numbers match `1` followed by 10 digits (11 characters total, no `+`) when provided.
* The transaction amount is greater than 0.
* `billingPreference` is one of `ALL`, `ZIP_COUNTRY`, or `NONE`.
* `acceptedShippingCountries` use valid ISO 3166-1 alpha-2 codes.
* Each cobrand entry has a `cobrandName`.
* For dynamic presentment, both `emailAddress` and `phoneNumber` are provided.
* When both `emailAddress` and `phoneNumber` are set, the SDK passes both to `canCheckout()` in dynamic mode, but sends only `phoneNumber` to Paze `checkout()`.


### Checkout identity precedence

Component identity (`emailAddress` / `phoneNumber`) is used for Paze presentment and checkout, not for Unity authorisation (that comes from `onGetShopper`). At checkout:

| Configuration | Sent to Paze `checkout()` |
|  --- | --- |
| Phone only | `phoneNumber` as `mobileNumber` |
| Email only | `emailAddress` |
| Both set | `phoneNumber` only (email omitted) |


In dynamic mode you must supply both fields for `canCheckout()` eligibility, even though checkout uses phone when both are present.

The Paze component returns structured error codes for validation failures:

| Error code | Description | Common causes |
|  --- | --- | --- |
| `SDK1202` | Missing `session.allowedFundingTypes.wallets.paze.clientId`. | Paze not configured in Unity Portal, or session created before configuration. |
| `SDK1203` | `clientName` exceeds 50 characters. | `clientName` on SDK initialisation is too long. |
| `SDK1204` | `siteName` exceeds 50 characters. | `siteName` on SDK initialisation is too long. |
| `SDK1205` | `paymentDescription` exceeds 25 characters. | Statement descriptor on component config is too long. |
| `SDK1207` | `canCheckout()` did not return an eligibility result. | Paze SDK returned an unexpected response during dynamic presentment. |
| `SDK1208` | Dynamic presentment requires both email and phone. | `buttonRenderMode: "dynamic"` without both identity fields. |
| `SDK1209` | Email address exceeds 128 characters. | `emailAddress` value is too long. |
| `SDK1210` | Email address format is invalid. | `emailAddress` is not a valid RFC 5322 address. |
| `SDK1211` | Phone number exceeds 15 characters. | `phoneNumber` value is too long. |
| `SDK1212` | Phone number format is invalid. | `phoneNumber` is not a US E.164 number without `+`. |
| `SDK1213` | Currency not supported. | Transaction currency is not `USD`. |
| `SDK1222` | `sessionId` exceeds 255 characters. | Optional component `sessionId` is too long. |
| `SDK1223` | Invalid `transactionType` for `complete()`. | Internal complete request is not `PURCHASE`. |
| `SDK1224` | `transactionOptions` is required. | Complete request is missing transaction options. |
| `SDK1225` | `transactionValue` is required. | Complete request is missing transaction value. |
| `SDK1226` | Invalid `billingPreference` on component config. | Value is not `ALL`, `ZIP_COUNTRY`, or `NONE`. |
| `SDK1227` | Invalid `billingPreference` in complete transaction options. | `transactionOptions.billingPreference` is not a supported value. |
| `SDK1228` | Invalid `payloadTypeIndicator`. | `transactionOptions.payloadTypeIndicator` is not `PAYMENT`. |
| `SDK1229` | Transaction amount must be greater than 0. | Zero or negative amount at checkout or in the `complete()` request. |
| `SDK1230` | Invalid transaction currency code. | `transactionCurrencyCode` is not a 3-letter ISO code. |
| `SDK1231` | Invalid shipping country codes. | `acceptedShippingCountries` contains non-ISO alpha-2 codes. |
| `SDK1232` | Cobrand name is required. | A cobrand entry is missing `cobrandName`. |


### Validation example

```typescript
import BaseSdkException from '@pxpio/web-components-sdk/src/types/sdkExceptions/BaseSdkException';

const pazeButton = pxpSdk.create('paze-button', {
  buttonRenderMode: 'dynamic',
  emailAddress: 'customer@example.com',
  phoneNumber: '15551234567',

  onError: (error: BaseSdkException) => {
    switch (error.ErrorCode) {
      case 'SDK1208':
        console.error('Both email and phone are required for dynamic presentment');
        break;
      case 'SDK1210':
        console.error('Invalid email format:', error.message);
        showError('Please enter a valid email address.');
        break;
      case 'SDK1212':
        console.error('Invalid phone format:', error.message);
        showError('Please enter a valid US phone number.');
        break;
      case 'SDK1213':
        console.error('Paze only supports USD');
        showError('Paze is not available for this currency.');
        break;
      default:
        console.error('Paze error:', error.ErrorCode, error.message);
    }
  }
});
```

## Custom validation

### Pre-checkout validation

Use `onCustomValidation` to run merchant-side checks before the Paze checkout popup opens. Return `false` to prevent checkout from starting.

```typescript
const pazeButton = pxpSdk.create('paze-button', {
  onCustomValidation: async () => {
    // Validate cart state before opening Paze
    if (!cartHasItems()) {
      showError('Your cart is empty.');
      return false;
    }

    if (!termsAccepted()) {
      showError('Please accept the terms and conditions.');
      return false;
    }

    return true;
  }
});
```

### Post-checkout merchant validation

Use `onCheckoutComplete` to inspect checkout data and approve or reject before the SDK calls `complete()`. Return `false` to treat the checkout as incomplete.

```typescript
const pazeButton = pxpSdk.create('paze-button', {
  onCheckoutComplete: async (result) => {
    const decoded = result.checkoutDecodedResponse;

    const isValid = await validateCheckoutOnBackend({
      sessionId: decoded?.sessionId,
      consumerEmail: decoded?.consumer?.emailAddress,
      cardLast4: decoded?.maskedCard?.panLastFour,
      cardNetwork: decoded?.maskedCard?.paymentCardNetwork
    });

    if (!isValid) {
      return false;
    }

    return true;
  },

  onCheckoutIncomplete: async (result) => {
    console.log('Checkout not completed:', result.reason);
    showMessage('Payment was not completed. Please try again.');
  }
});
```

### Pre-decryption validation

Use `onPreDecryption` to control whether the SDK decrypts the secured payload. Return `false` to handle decryption on your backend.

```typescript
const pazeButton = pxpSdk.create('paze-button', {
  onPreDecryption: async () => {
    // Return false for manual backend decryption
    return false;
  },

  onComplete: async (result) => {
    const securedPayload = result.completeDecodedResponse?.securedPayload;
    // Send securedPayload to your backend for decryption
    await fetch('/api/paze/decrypt', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ securedPayload })
    });
  }
});
```

### Pre-authorisation validation

Use `onPreAuthorisation` to provide risk screening data or abort the transaction before submission.

```typescript
const pazeButton = pxpSdk.create('paze-button', {
  onPreAuthorisation: async () => {
    // Return false to cancel authorisation
    if (fraudCheckFailed()) {
      return false;
    }

    // Optionally provide Kount risk screening data
    return {
      riskScreeningData: {
        performRiskScreening: true,
        userIp: '192.168.1.100',
        account: {
          id: 'shopper-123',
          creationDateTime: '2024-01-15T10:30:00.000Z'
        }
      }
    };
  }
});
```

## Identity validation rules

### Email address

| Rule | Requirement |
|  --- | --- |
| Format | Valid RFC 5322 email address |
| Maximum length | 128 characters |
| Static presentment | Optional; validated when provided |
| Dynamic presentment | Required alongside `phoneNumber` |


### Phone number

| Rule | Requirement |
|  --- | --- |
| Format | US E.164 without `+` prefix: `1` followed by 10 digits |
| Length | Exactly 11 characters (e.g. `15551234567`) |
| Maximum length | 15 characters (values longer than 15 throw `SDK1211`) |
| Static presentment | Optional; validated when provided |
| Dynamic presentment | Required alongside `emailAddress` |


Phone numbers must not include the `+` prefix or formatting characters. Use `15551234567`, not `+1 (555) 123-4567`.

## What's next?

- **[Configuration](/guides/checkout/components/web/paze/configuration):** View the full configuration reference.
- **[Events](/guides/checkout/components/web/paze/events):** Read about callback payloads and event data structures.
- **[Troubleshooting](/guides/checkout/components/web/paze/troubleshooting):** Diagnose validation and presentment issues.