Complete guide to integrating the Paze component into your application.
The Paze component provides a fast, secure payment experience using the Paze digital wallet. The component follows a simple three-step lifecycle:
- Initialise: Configure the SDK with your session and transaction data.
- Create and mount: Build the Paze button with your configuration and render it to your page.
- 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.
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;
localhostis 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:
| Environment | Paze SDK URL |
|---|---|
test | https://checkout.wallet.uat.earlywarning.io/web/resources/js/digitalwallet-sdk.js |
live | https://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.
Paze for Web has specific requirements for optimal functionality.
- Chrome 80+ on all platforms
- Safari 13.1+ on macOS and iOS
- Firefox 75+ on all platforms
- Edge 80+ on Windows and macOS
- Android devices: Android 7.0 (Nougat) or later
- iOS devices: iOS 13.1 or later
- Desktop: Windows 10+, macOS 10.14+, or Linux
- The customer must have a Paze wallet account with saved payment methods.
- The customer's email address must match their Paze wallet registration.
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-sdkThe Paze component is part of the main SDK package. Import PxpCheckout directly from @pxpio/web-components-sdk.
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.
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.
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.
The session request accepts the following parameters:
| Parameter | Description |
|---|---|
merchantstring (≤ 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. |
sitestring (≤ 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. |
merchantTransactionIdstring (≤ 50 characters) required | A unique identifier of your choice that represents this transaction. |
sessionTimeoutnumber required | The duration of the session, in minutes. |
transactionMethodobject required | Details about the transaction method, including the intent for each payment type. |
transactionMethod.intentobject required | The transaction intent for each payment method. |
transactionMethod.intent.cardstring required | The intent for Paze transactions. Possible values:
|
amountsobject required | Details about the transaction amount. |
amounts.currencyCodestring (3 characters) required | The currency code associated with the transaction, in ISO 4217 format. Paze supports USD only. |
amounts.transactionValuenumber 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). |
allowTransactionboolean | Whether or not to proceed with the transaction. |
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:
1DE2DFC390D7CD746A972140F26846AFA81CF85F5A0BAABA95DBC95301795EA6You 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:1DE2DFC390D7CD746A972140F26846AFA81CF85F5A0BAABA95DBC95301795EA6Lastly, 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
}'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.
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.
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);
}
});| Parameter | Description |
|---|---|
buttonRenderModeenum | Controls button presentment. Defaults to "static".Possible values:
|
emailAddressstring | Optional shopper email address for identity and presentment. Required when buttonRenderMode is "dynamic". |
phoneNumberstring | Optional phone number in US E.164 format without +. Required when buttonRenderMode is "dynamic". |
paymentDescriptionstring | A statement descriptor forwarded to Paze. Maximum 25 characters. |
billingPreferenceenum | Billing address collection in the Paze popup. Defaults to "ALL".Possible values:
|
confirmLaunchboolean | When true, Paze may show a confirmation popup before checkout. |
sessionIdstring | Optional session ID echoed back by Paze. Defaults to the SDK session ID. |
acceptedShippingCountriesstring[] | The list of accepted shipping countries, in ISO 3166-1 alpha-2 format. |
acceptedPaymentCardNetworksstring[] | Accepted card networks. Defaults to VISA, MASTERCARD, and DISCOVER. |
cobrandPazeCobrandConfig[] | Optional cobrand entries for partner branding in the Paze wallet. |
enhancedTransactionDataobject | Optional checkout metadata forwarded to Paze. See Configuration. |
style.colorenum | The button colour theme. Possible values:
|
style.shapeenum | The button shape. Possible values:
|
style.labelenum | The button label text. Possible values:
|
style.disableMaxHeightboolean | When true, disables the native max-height so the button fills its container. |
performAVSboolean | 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.
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');When your component unmounts (e.g., when navigating away or cleaning up), call the unmount() method:
return () => {
pazeButton.unmount();
};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.
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.
The Paze payment flow consists of several stages:
- Presentment: The SDK checks if the customer has a Paze wallet for the provided email address.
- Button click: The customer clicks the Paze button to initiate payment.
- Checkout: The customer completes checkout within the Paze wallet.
- Decryption: The Paze token is decrypted (SDK-managed or manual).
- 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);
}
});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.
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.
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.
| Environment | Endpoint |
|---|---|
| Test | POST https://api-services.dev.pxp.io/api/v1/wallets/Paze/decrypt-token |
| Live | POST https://api-services.pxp.io/api/v1/wallets/Paze/decrypt-token |
Headers:
Content-Type: application/jsonAuthorization: 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>"
}
}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.
| Decrypt path | Where 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.schemeTokenCryptogram → fundingData.card.schemeTokenCryptogram, and fundingData.eci → threeDSecureData.electronicCommerceIndicator when required by your integration. |
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.
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.
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>
);
}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.