Skip to content

Implementation

Complete guide to integrating the Paze component into your application.

Overview

The Paze component provides a fast, secure payment experience using the Paze digital wallet. The component follows a simple three-step lifecycle:

  1. Initialise: Configure the SDK with your session and transaction data.
  2. Create and mount: Build the Paze button with your configuration and render it to your page.
  3. Handle callbacks: Respond to payment success, errors, and other events.

The component automatically handles Paze wallet presentment, checkout, token decryption, and transaction processing.

Backend verification is mandatory. Always verify payments on your backend before fulfilling orders. Frontend callbacks can be manipulated by malicious users.

Before you start

Complete Paze onboarding in the Unity Portal before integrating the component. You will need:

  • Paze enabled at merchant group and site level, with your Paze client ID saved in Paze Account Settings
  • PXP API credentials (client ID and authentication token) for session creation on your backend
  • HTTPS on your site (required for Paze; localhost is exempt for development)
  • A Paze merchant account linked to the Unity Portal

Set the SDK environment to test for UAT or live for production. The Web SDK loads the Paze Digital Wallet SDK for you; the script URL depends on your environment:

EnvironmentPaze SDK URL
testhttps://checkout.wallet.uat.earlywarning.io/web/resources/js/digitalwallet-sdk.js
livehttps://checkout.paze.com/web/resources/js/digitalwallet-sdk.js

Paze also requires USD currency and a card intent in your session and SDK config. We also recommend an onGetShopper callback on SDK initialisation. These are covered in the steps below.

If portal or session setup fails, see Portal and session setup.

Browser and device compatibility

Paze for Web has specific requirements for optimal functionality.

Supported browsers

  • Chrome 80+ on all platforms
  • Safari 13.1+ on macOS and iOS
  • Firefox 75+ on all platforms
  • Edge 80+ on Windows and macOS

Device requirements

  • Android devices: Android 7.0 (Nougat) or later
  • iOS devices: iOS 13.1 or later
  • Desktop: Windows 10+, macOS 10.14+, or Linux

Configuration requirements

  • The customer must have a Paze wallet account with saved payment methods.
  • The customer's email address must match their Paze wallet registration.

Step 1: Install the Web SDK library

Install the latest version of the Web SDK from the npm public registry. You'll need to have Node.js 22.x or higher.

npm i @pxpio/web-components-sdk

The Paze component is part of the main SDK package. Import PxpCheckout directly from @pxpio/web-components-sdk.

Step 2: Create a session on your backend

The Paze component requires a session from the PXP Sessions API. This must be done on your backend using HMAC authentication to keep your credentials secure.

Understanding HMAC authentication

Our platform uses HMAC (Hash-based Message Authentication Code) with SHA256 for authentication to ensure secure communication and data integrity. This method involves creating a signature by hashing your request data with a secret key, which must then be included in the HTTP headers of your API request.

Create an HMAC signature

To create the HMAC signature, you need to prepare a string that includes four parts concatenated together:

  • Timestamp: The current time in Unix seconds (UTC) (e.g., 1754701373)
  • Request ID: A unique GUID for this request (e.g., ce244054-b372-42c2-9102-f0d976db69f6)
  • Request path: The API endpoint path: api/v1/sessions
  • Request body: The complete JSON request body as a minified string

Example request body to minify:

{
  "merchant": "MERCHANT-1",
  "site": "SITE-1",
  "sessionTimeout": 120,
  "merchantTransactionId": "txn-123",
  "transactionMethod": {
    "intent": {
      "card": "Authorisation"
    }
  },
  "amounts": {
    "currencyCode": "USD",
    "transactionValue": 99.99
  },
  "allowTransaction": true
}

When creating the HMAC signature, the request body must be minified (no whitespace or formatting). The formatted JSON above is for readability only. Paze requires currencyCode to be USD.

Session request parameters

The session request accepts the following parameters:

ParameterDescription
merchant
string (≤ 20 characters)
required
Your unique merchant identifier, as assigned by PXP. You can find it in the Unity Portal, by going to Merchant setup > Merchants and checking the Merchant ID column.
site
string (≤ 20 characters)
required
Your unique site identifier, as assigned by PXP. You can find it in the Unity Portal, by going to Merchant setup > Sites and checking the Site ID column.
merchantTransactionId
string (≤ 50 characters)
required
A unique identifier of your choice that represents this transaction.
sessionTimeout
number
required
The duration of the session, in minutes.
transactionMethod
object
required
Details about the transaction method, including the intent for each payment type.
transactionMethod.intent
object
required
The transaction intent for each payment method.
transactionMethod.intent.card
string
required
The intent for Paze transactions.

Possible values:
  • Authorisation
  • Purchase
  • Verification
  • EstimatedAuthorisation
  • Payout
amounts
object
required
Details about the transaction amount.
amounts.currencyCode
string (3 characters)
required
The currency code associated with the transaction, in ISO 4217 format. Paze supports USD only.
amounts.transactionValue
number
required
The transaction amount. The numbers after the decimal will be zero padded if they are less than the expected currencyCode exponent. For example, USD 1 = USD 1.00. The transaction will be rejected if numbers after the decimal are greater than the expected currencyCode exponent (e.g., USD 1.234).
allowTransaction
boolean
Whether or not to proceed with the transaction.

Prepare and send your request

The HMAC signature requires combining your timestamp, request ID, request path, and request body. Here's the format:

String to hash: {timestamp}{requestId}{requestPath}{requestBody}

For example:

1754701373ce244054-b372-42c2-9102-f0d976db69f6api/v1/sessions{"merchant":"MERCHANT-1","site":"SITE-1","sessionTimeout":120,"merchantTransactionId":"txn-123","transactionMethod":{"intent":{"card":"Authorisation"}},"amounts":{"currencyCode":"USD","transactionValue":99.99},"allowTransaction":true}

Use your token value as the secret key to create an HMAC SHA256 hash of this string. The result will be a hex-encoded signature like:

1DE2DFC390D7CD746A972140F26846AFA81CF85F5A0BAABA95DBC95301795EA6

You can now put together your Authorization header. It follows this format: PXP-UST1 {tokenId}:{timestamp}:{signature}. For example:

PXP-UST1 9aac6071-38d0-4545-9d2f-15b936af6d7f:1754701373:1DE2DFC390D7CD746A972140F26846AFA81CF85F5A0BAABA95DBC95301795EA6

Lastly, send your request to the Sessions API. You'll need to add a request ID of your choice and include your client ID, which you can find in the Unity Portal.

Here's a full example of what your request might look like:

curl -i -X POST \
  'https://api-services.dev.pxp.io/api/v1/sessions' \
  -H 'Authorization: PXP-UST1 9aac6071-38d0-4545-9d2f-15b936af6d7f:1754701373:1DE2DFC390D7CD746A972140F26846AFA81CF85F5A0BAABA95DBC95301795EA6' \
  -H 'X-Request-Id: ce244054-b372-42c2-9102-f0d976db69f6' \
  -H 'X-Client-Id: f47ac10b-58cc-4372-a567-0e02b2c3d479' \
  -H 'Content-Type: application/json' \
  -d '{
  "merchant": "MERCHANT-1",
  "site": "SITE-1",
  "sessionTimeout": 120,
  "merchantTransactionId": "txn-123",
  "transactionMethod": {
    "intent": {
      "card": "Authorisation"
    }
  },
  "amounts": {
    "currencyCode": "USD",
    "transactionValue": 99.99
  },
  "allowTransaction": true
}'

Session response

If your request is successful, you'll receive a 200 response containing the session data. When Paze is enabled for your site, the response includes Paze configuration:

{
  "sessionId": "c5f0799b-0839-43ce-abc5-5b462a98f250",
  "hmacKey": "904bc42395d4af634e2fd48ee8c2c7f52955a1da97a3aa3d82957ff12980a7bb",
  "encryptionKey": "20d175a669ad3f8c195c9c283fc86155",
  "sessionExpiry": "2025-05-19T13:39:20.3843454Z",
  "allowedFundingTypes": {
    "cardSchemes": ["Visa", "Mastercard", "AmericanExpress"],
    "cards": [],
    "wallets": {
      "paze": {
        "clientId": "your-paze-client-id",
        "profileId": "optional-profile-id",
        "merchantCategoryCode": "category-code"
      }
    }
  }
}

Return the session response to your frontend together with the merchantTransactionId you sent in the session request. Your frontend must pass the same value to transactionData.merchantTransactionId when initialising the SDK.

The Paze configuration is automatically included in the session response when Paze is configured for your site in the Unity Portal. See Onboarding for portal setup steps.

Step 3: Initialise the SDK on your frontend

Import PxpCheckout from the SDK and initialise with your configuration.

import { PxpCheckout, IntentType } from '@pxpio/web-components-sdk';

// Get session data from your backend
const sessionData = await fetch('/api/sessions', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' }
}).then(response => response.json());

// Initialise the SDK
const merchantTransactionId = sessionData.merchantTransactionId ?? crypto.randomUUID();

const pxpSdk = PxpCheckout.initialize({
  environment: 'test',
  session: sessionData,
  ownerId: 'MERCHANT-1',
  ownerType: 'MerchantGroup',
  clientName: 'Your Merchant',
  siteName: 'Your Store',
  transactionData: {
    currency: 'USD',
    amount: 99.99,
    entryType: 'Ecom',
    intent: {
      card: IntentType.Authorisation
    },
    merchantTransactionId,
    merchantTransactionDate: () => new Date().toISOString()
  },
  kountDisabled: false, // OPTIONAL: Set to true to disable Kount fraud detection
  onGetShopper: () => Promise.resolve({ 
    id: 'shopper-123',
    email: 'customer@example.com',
    firstName: 'John',
    lastName: 'Doe'
  })
});

Use the same merchantTransactionId in your session request and in transactionData.merchantTransactionId. If these values differ, reconciliation, callbacks, and transaction lookups will not align.

Step 4: Create and configure the Paze button

Use the SDK's create() method to build the Paze button with your desired configuration:

import { PxpCheckout, IntentType, PazeCompleteResult } from '@pxpio/web-components-sdk';

const pazeButton = pxpSdk.create('paze-button', {
  buttonRenderMode: 'static',
  emailAddress: 'customer@example.com',
  phoneNumber: '15551234567',
  style: {
    color: 'pazeblue',
    shape: 'pill',
    label: 'check out with'
  },
  onPresentmentResolved: (isVisible) => {
    console.log('Paze button visible:', isVisible);
  },
  onCheckoutComplete: async (result) => {
    console.log('Paze checkout complete:', result);
    return true;
  },
  onComplete: async (result: PazeCompleteResult) => {
    console.log('Paze complete success', result);
  },
  onPreDecryption: async () => true,
  onPostDecryption: async (decryptedPayload) => {
    console.log('Decryption complete:', decryptedPayload.fundingData.cardNetwork);
  },
  onPostAuthorisation: async (data) => {
    // CRITICAL: Verify on backend before fulfilling order
    await verifyPaymentOnBackend(data.systemTransactionId);
  },
  onError: (error) => {
    console.error('Paze payment failed:', error);
  }
});

Configuration options

ParameterDescription
buttonRenderMode
enum
Controls button presentment. Defaults to "static".

Possible values:
  • "static"
  • "dynamic"
emailAddress
string
Optional shopper email address for identity and presentment. Required when buttonRenderMode is "dynamic".
phoneNumber
string
Optional phone number in US E.164 format without +. Required when buttonRenderMode is "dynamic".
paymentDescription
string
A statement descriptor forwarded to Paze. Maximum 25 characters.
billingPreference
enum
Billing address collection in the Paze popup. Defaults to "ALL".

Possible values:
  • "ALL"
  • "ZIP_COUNTRY"
  • "NONE"
confirmLaunch
boolean
When true, Paze may show a confirmation popup before checkout.
sessionId
string
Optional session ID echoed back by Paze. Defaults to the SDK session ID.
acceptedShippingCountries
string[]
The list of accepted shipping countries, in ISO 3166-1 alpha-2 format.
acceptedPaymentCardNetworks
string[]
Accepted card networks. Defaults to VISA, MASTERCARD, and DISCOVER.
cobrand
PazeCobrandConfig[]
Optional cobrand entries for partner branding in the Paze wallet.
enhancedTransactionData
object
Optional checkout metadata forwarded to Paze. See Configuration.
style.color
enum
The button colour theme.

Possible values:
  • white
  • whitewithoutline
  • midnightblack
  • pazeblue
style.shape
enum
The button shape.

Possible values:
  • default
  • rectangle
  • pill
style.label
enum
The button label text.

Possible values:
  • checkout
  • check out with
  • Donate with
style.disableMaxHeight
boolean
When true, disables the native max-height so the button fills its container.
performAVS
boolean
When true, includes billing address from decrypted payload in AVS fields when the SDK submits the transaction. Defaults to false. Not used when onPreDecryption returns false.
onCustomValidation
() => Promise<boolean>
Called before checkout opens. Return false to prevent checkout.

For the full list of component callbacks, see Events. For additional configuration examples, see Configuration.

Step 5: Mount the button to your page

Add a container element to your page where the Paze button will be rendered:

<div id="paze-container"></div>

Then call the mount() method to render the button:

pazeButton.mount('paze-container');

Step 6: Unmount the component

When your component unmounts (e.g., when navigating away or cleaning up), call the unmount() method:

return () => {
  pazeButton.unmount();
};

Step 7: Handle callbacks

Most Paze callbacks are optional. Configure them on the paze-button component to customise presentment, checkout approval, decryption, and authorisation handling.

We recommend implementing onGetShopper on PxpCheckout.initialize() (not on the button component). The SDK calls it when building the transaction request after decryption. The callback is optional in code, but most integrations need shopper data for authorisation:

const pxpSdk = PxpCheckout.initialize({
  // ... session and transactionData
  onGetShopper: () => Promise.resolve({
    id: 'shopper-123',
    email: 'customer@example.com',
    firstName: 'John',
    lastName: 'Doe'
  })
});

If you implement onCheckoutComplete, return true to approve checkout and proceed to complete(), decryption, and authorisation. Return false to cancel.

For all component callbacks, including onPresentmentResolved, onCheckoutComplete, onPreDecryption, onPostDecryption, and onPostAuthorisation, see Events.

Never trust frontend callbacks for order fulfilment. Always verify payments on your backend using webhooks or the Get transaction details API before fulfilling orders.

Step 8: Backend verification (critical)

Frontend callbacks can be manipulated by malicious users. You must verify all payments on your backend before fulfilling orders.

Use the same webhook and API verification patterns described in the card implementation guide.

Understanding the Paze payment flow

The Paze payment flow consists of several stages:

  1. Presentment: The SDK checks if the customer has a Paze wallet for the provided email address.
  2. Button click: The customer clicks the Paze button to initiate payment.
  3. Checkout: The customer completes checkout within the Paze wallet.
  4. Decryption: The Paze token is decrypted (SDK-managed or manual).
  5. Authorisation: The payment is authorised with the decrypted card data.
// Example showing all stages
const pazeButton = pxpSdk.create('paze-button', {
  buttonRenderMode: 'static',
  emailAddress: 'customer@example.com',
  
  onPresentmentResolved: (isVisible) => {
    console.log('Stage 1: Presentment resolved, visible:', isVisible);
  },
  
  onPazeButtonClicked: () => {
    console.log('Stage 2: Paze button clicked');
    trackEvent('paze_button_clicked');
  },
  
  onCheckoutComplete: async (result) => {
    console.log('Stage 3: Checkout complete');
    return true;
  },
  
  onPreDecryption: async () => {
    console.log('Stage 4a: Starting decryption');
    return true;
  },
  
  onPostDecryption: async (decryptedPayload) => {
    console.log('Stage 4b: Decryption complete');
  },
  
  onPreAuthorisation: async () => {
    console.log('Stage 5a: Preparing authorisation');
    return {
      riskScreeningData: {
        performRiskScreening: true,
        userIp: '192.168.1.100'
      }
    };
  },
  
  onPostAuthorisation: async (data) => {
    console.log('Stage 5b: Payment authorised:', data.systemTransactionId);
    await verifyPaymentOnBackend(data.systemTransactionId);
  }
});

Backend decryption

When you return false from onPreDecryption, the SDK skips built-in decryption and authorisation. Your backend must decrypt the secured payload and submit the scheme token transaction.

1. Configure the frontend

Return false from onPreDecryption and forward the secured payload to your backend in onComplete:

const pazeButton = pxpSdk.create('paze-button', {
  onCheckoutComplete: async () => true,

  onPreDecryption: async () => false,

  onComplete: async (result) => {
    const securedPayload = result.completeDecodedResponse?.securedPayload;

    await fetch('/api/paze/decrypt', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ securedPayload })
    });
  }
});

The onComplete callback receives a PazeCompleteResult like this:

{
  "completeResponse": "eyJhdWQiOi...",
  "completeDecodedResponse": {
    "payloadId": "5C9190F52C004d7cf1a2-d4a0-4d4d-0463-15f90ad0d801",
    "sessionId": "3247d172-1232-4175-9fa2-eabbec62950d",
    "securedPayload": "eyJhdWQHBmaW..."
  }
}

Pass completeDecodedResponse.securedPayload to your backend. Never call the PXP decrypt API directly from frontend code.

2. Decrypt the secured payload

From your backend, call the PXP decrypt-token endpoint using HMAC authentication. Use the same HMAC pattern as session creation.

This is the merchant backend endpoint (PXP-UST1). When onPreDecryption returns true or is omitted, the Web SDK decrypts internally using POST /api/v1/paze/decrypt-token with { "securedPayload": "..." } and session authentication. Use the wallets endpoint below only for manual backend decryption.

EnvironmentEndpoint
TestPOST https://api-services.dev.pxp.io/api/v1/wallets/Paze/decrypt-token
LivePOST https://api-services.pxp.io/api/v1/wallets/Paze/decrypt-token

Headers:

  • Content-Type: application/json
  • Authorization: PXP-UST1 <tokenId>:<timestamp>:<hmac>
  • X-Request-Id: <uuid>
  • X-Client-Id: <clientId>

Request body:

{
  "site": "your-site-id",
  "token": "eyJhdWQHBmaW..."
}

The token value is the securedPayload from onComplete.

Here's a sample response:

{
  "shopper": {
    "firstName": "Test",
    "lastName": "Wallet",
    "fullName": "Test Wallet",
    "emailAddress": "customer@example.com",
    "phoneCountryCode": "1",
    "phoneNumber": "6264645283",
    "countryCode": "US",
    "languageCode": "EN"
  },
  "billingAddress": {
    "addressLine1": null,
    "city": null,
    "state": null,
    "countryCode": "US",
    "postalCode": "67051"
  },
  "fundingData": {
    "networkToken": "<networkToken>",
    "expirationMonth": "08",
    "expirationYear": "2032",
    "accountReference": "V0010013023181487240991060052",
    "cardNetwork": "VISA",
    "tokenUsageType": "PURCHASE",
    "dynamicDataExpiration": "1781938231",
    "eci": "07",
    "schemeTokenCryptogram": "<cryptogram>"
  }
}

3. Submit the scheme token transaction

Use the decrypted fundingData to submit an authorisation through the PXP Create transaction API (POST /api/v1/transactions) from your backend. Authenticate with the same PXP-UST1 HMAC pattern as session creation.

Cryptogram and ECI handling

Decrypt pathWhere cryptogram / ECI go
SDK-managed (/api/v1/paze/decrypt-token)The gateway stores schemeTokenCryptogram and ECI in the session before the SDK submits the transaction. You do not pass them in the frontend transaction request.
Manual backend (/api/v1/wallets/Paze/decrypt-token)Map fields from the decrypt response into your transaction request: fundingData.schemeTokenCryptogramfundingData.card.schemeTokenCryptogram, and fundingData.ecithreeDSecureData.electronicCommerceIndicator when required by your integration.

Sample transaction request

Map decrypted Paze token data to a scheme-token card authorisation. Align merchantTransactionId with the value used at session creation and in SDK transactionData:

{
  "merchant": "MERCHANT-1",
  "site": "SITE-1",
  "merchantTransactionId": "txn-123",
  "merchantTransactionDate": "2025-06-17T10:30:00.000Z",
  "transactionMethod": {
    "intent": "Authorisation",
    "entryType": "Ecom",
    "fundingType": "Card"
  },
  "fundingData": {
    "card": {
      "schemeTokenNumber": "<networkToken from decrypt response>",
      "expiryMonth": "08",
      "expiryYear": "2032",
      "walletType": "Paze",
      "schemeTokenCryptogram": "<schemeTokenCryptogram from decrypt response>"
    }
  },
  "amounts": {
    "transaction": 99.99,
    "currencyCode": "USD"
  },
  "threeDSecureData": {
    "electronicCommerceIndicator": "07"
  },
  "shopper": {
    "id": "shopper-123",
    "email": "customer@example.com",
    "firstName": "John",
    "lastName": "Doe"
  },
  "addressVerification": {
    "countryCode": "US",
    "houseNumberOrName": "123 Main St",
    "postalCode": "67051"
  }
}

Include addressVerification only when you perform AVS on the backend. Populate shopper from your customer records or from the decrypt response shopper object.

After submission, verify the transaction on your backend before fulfilling the order. See Backend verification.

When using manual decryption, you are responsible for securely calling the decrypt API and submitting the transaction from your backend. The SDK doesn't proceed with authorisation.

Manual decryption example

End-to-end frontend example that delegates decryption and payment submission to your backend:

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

  onCheckoutComplete: async () => true,

  onPreDecryption: async () => false,

  onComplete: async (result) => {
    const securedPayload = result.completeDecodedResponse?.securedPayload;

    try {
      const paymentResult = await fetch('/api/paze/decrypt-and-pay', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ securedPayload })
      }).then(res => res.json());

      if (paymentResult.success) {
        window.location.href = '/order-confirmation';
      } else {
        showError('Unable to complete payment');
      }
    } catch (error) {
      console.error('Manual decryption failed:', error);
      showError('Unable to complete payment');
    }
  }
});

Your /api/paze/decrypt-and-pay backend route should call the decrypt-token endpoint, then submit the scheme token transaction using the decrypted fundingData.

Complete example

Here's a complete example showing Paze integration in a React component:

import { PxpCheckout, IntentType } from '@pxpio/web-components-sdk';
import { useEffect, useState } from 'react';

export default function PazePage() {
  const [sessionData, setSessionData] = useState<any>(null);
  const [customerEmail, setCustomerEmail] = useState('customer@example.com');

  const createSession = async () => {
    const merchantTransactionId = crypto.randomUUID();
    const createdSession = await fetch('/api/sessions', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        merchant: "MERCHANT-1",
        site: "SITE-1",
        sessionTimeout: 120,
        merchantTransactionId,
        transactionMethod: {
          intent: {
            card: "Authorisation"
          }
        },
        amounts: {
          currencyCode: "USD",
          transactionValue: 99.99
        },
        allowTransaction: true
      })
    }).then((response) => response.json());
    setSessionData({ ...createdSession, merchantTransactionId });
  };

  useEffect(() => {
    createSession();
  }, []);

  useEffect(() => {
    if (!sessionData) {
      return;
    }

    const pxpSdk = PxpCheckout.initialize({
      environment: 'test',
      session: sessionData,
      ownerId: 'MERCHANT_GROUP_1',
      ownerType: 'MerchantGroup',
      clientName: 'Your Merchant',
      siteName: 'Your Store',
      transactionData: {
        currency: 'USD',
        amount: 99.99,
        entryType: 'Ecom',
        intent: {
          card: IntentType.Authorisation,
        },
        merchantTransactionId: sessionData.merchantTransactionId,
        merchantTransactionDate: () => new Date().toISOString(),
      },
      kountDisabled: false,
      onGetShopper: async () => {
        return {
          id: 'shopper-123',
          email: customerEmail,
          firstName: 'John',
          lastName: 'Doe',
        };
      },
    });

    const pazeButton = pxpSdk.create('paze-button', {
      buttonRenderMode: 'dynamic',
      emailAddress: customerEmail,
      phoneNumber: '15551234567',
      billingPreference: 'ALL',
      acceptedPaymentCardNetworks: ['VISA', 'MASTERCARD'],
      performAVS: true,
      style: {
        color: 'pazeblue',
        shape: 'pill',
        label: 'check out with'
      },
      onInit: () => {
        console.log('Paze component initialised');
      },
      onPresentmentResolved: (isVisible) => {
        console.log('Paze button visible:', isVisible);
        if (!isVisible) {
          console.log('Shopper not eligible for Paze');
        }
      },
      onPazeButtonClicked: () => {
        console.log('Paze button clicked');
        trackEvent('paze_button_clicked');
      },
      onCheckoutComplete: async (result) => {
        console.log('Paze checkout complete');
        return true;
      },
      onCheckoutIncomplete: async (result) => {
        console.log('Paze checkout cancelled:', result.reason);
      },
      onPreDecryption: async () => true,
      onPostDecryption: async (decryptedPayload) => {
        console.log('Token decrypted:', decryptedPayload.fundingData.cardNetwork);
      },
      onPreAuthorisation: async () => ({
        riskScreeningData: {
          performRiskScreening: true,
          userIp: '192.168.1.100',
          account: {
            id: 'shopper-123',
            creationDateTime: '2024-01-15T10:30:00.000Z'
          }
        }
      }),
      onPostAuthorisation: async (data) => {
        if (data.state === 'Authorised') {
          const verified = await fetch('/api/verify-payment', {
            method: 'POST',
            body: JSON.stringify({
              systemTransactionId: data.systemTransactionId,
              merchantTransactionId: data.merchantTransactionId
            })
          }).then(r => r.json());
          
          if (verified.success) {
            window.location.href = `/success?orderId=${verified.orderId}`;
          }
        } else {
          console.error('Payment not authorised:', data.state);
        }
      },
      onSubmitError: async (submitError) => {
        console.error('Payment submission error:', submitError);
      },
      onError: (error) => {
        console.error('Paze error:', error);
      }
    });

    pazeButton.mount('paze-container');

    return () => {
      pazeButton.unmount();
    };
  }, [sessionData, customerEmail]);

  return (
    <div>
      <h1>Checkout with Paze</h1>
      <div>
        <label htmlFor="email">Email address:</label>
        <input
          id="email"
          type="email"
          value={customerEmail}
          onChange={(e) => setCustomerEmail(e.target.value)}
          placeholder="customer@example.com"
        />
      </div>
      <div id="paze-container"></div>
    </div>
  );
}

What's next?

Now that you've integrated Paze, here are some recommended next steps:

  • Configuration: Learn how to customise the Paze button and behaviour.
  • Events: Explore all available callbacks and event handling.
  • Analytics: Track Paze payment metrics and performance.
  • Configure webhooks: Set up server-side webhook handling for reliable payment verification.