Follow our walkthrough to get Checkout Drop-in running in minutes.
Before you start, make sure you have:
- Node.js 22.x or higher (for Node.js backend) or .NET SDK (for .NET backend) installed on your computer
- Your API credentials from the Unity Portal
To get started, 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-sdkDrop-in needs a session from the PXP API. This must happen on your backend using HMAC authentication.
Store your credentials securely
Set up your API credentials as environment variables — never hardcode them in your application.
Create the HMAC signature function
This function generates a secure authentication hash by combining your token ID, timestamp, request ID, request path, and request body, then hashing with your token value using HMAC SHA256.
Build the session request body
Create a request with your merchant details and transaction information. The request body must be minified (no whitespace) for the HMAC signature.
Send the session creation request
POST to https://api-services.pxp.io/api/v1/sessions with your authentication headers and request body.
Return the session data to your frontend
The API returns sessionId, hmacKey, encryptionKey, and allowedFundingTypes. Pass this entire response to your frontend.
import crypto from 'crypto';
// Your API credentials from the Unity Portal
const TOKEN_ID = '9aac6071-38d0-4545-9d2f-15b936af6d7f'; // Replace this with your token ID
const TOKEN_VALUE = 'your-token-value-here'; // Replace this with your token value
const CLIENT_ID = 'f47ac10b-58cc-4372-a567-0e02b2c3d479'; // Replace this with your client ID
/**
* Creates an HMAC signature for authenticating API requests
*/
function createHmacSignature(tokenId, timestamp, requestId, requestPath, requestBody, tokenValue) {
// Combine all parts with colons
const stringToHash = `${tokenId}:${timestamp}:${requestId}:${requestPath}:${requestBody}`;
// Create HMAC SHA256 hash using the token value as the secret
const hmac = crypto.createHmac('sha256', tokenValue);
hmac.update(stringToHash);
// Return the hash as a hex string
return hmac.digest('hex').toUpperCase();
}
/**
* Creates a new checkout session
*/
export async function createSession(merchantId, siteId, amount, currency) {
// Generate unique IDs for this request
const timestamp = Math.floor(Date.now() / 1000);
const requestId = crypto.randomUUID();
const merchantTransactionId = crypto.randomUUID();
// Build the request body
const requestBody = {
merchant: merchantId, // Replace with your merchant ID
site: siteId, // Replace with your site ID
sessionTimeout: 120,
merchantTransactionId: merchantTransactionId, // Replace with a unique transaction ID
transactionMethod: {
intent: {
card: 'Authorisation',
paypal: 'Purchase'
}
},
amounts: {
currencyCode: currency,
transactionValue: amount
},
allowTransaction: true,
serviceType: 'CheckoutDropIn'
};
// Minify the request body (no whitespace)
const requestBodyString = JSON.stringify(requestBody);
const requestPath = 'api/v1/sessions';
// Create the HMAC signature
const signature = createHmacSignature(
TOKEN_ID,
timestamp,
requestId,
requestPath,
requestBodyString,
TOKEN_VALUE
);
// Build the Authorization header
const authHeader = `${TOKEN_ID}:${timestamp}:${requestId}:${signature}`;
// Send the request
const response = await fetch('https://api-services.pxp.io/api/v1/sessions', {
method: 'POST',
headers: {
'Authorization': authHeader,
'X-Request-Id': requestId,
'X-Client-Id': CLIENT_ID,
'Content-Type': 'application/json'
},
body: requestBodyString
});
if (!response.ok) {
throw new Error(`Session creation failed: ${response.statusText}`);
}
const sessionData = await response.json();
return {
...sessionData,
merchantTransactionId
};
}Import the required dependencies
Import CheckoutDropIn, IntentType, and the necessary types from the Web SDK.
Set up your React component
Create a React component that will host the Checkout Drop-in interface.
Fetch the session data from your backend
Call your backend endpoint to get the session data you created in the previous steps.
Initialise Checkout Drop-in
Configure Drop-in with your environment, session data, and transaction details.
Configure the transaction data
Specify the currency, amount, entry type, and payment intents for each payment method.
Provide a shopper identifier
Implement the onGetShopper callback to provide shopper information. This is required for Card-on-File functionality.
Handle successful payments
Implement the onSuccess callback to handle successful payments. Always verify payments on your backend before fulfilling orders — frontend callbacks can be manipulated.
Handle payment errors
Implement the onError callback to handle payment failures and display appropriate error messages.
Mount Drop-in to the page
Call the create() method with your container element ID to render the payment interface.
Add a container element to your page
Return JSX that includes a container div where Checkout Drop-in will mount itself.
import CheckoutDropIn from '@pxpio/web-components-sdk/src/checkoutDropIn/CheckoutDropIn';
import IntentType from '@pxpio/web-components-sdk/src/basePxpCheckout/types/IntentType';
import PaymentMethod from '@pxpio/web-components-sdk/src/components/checkoutDropInComponents/types/PaymentMethod';
import { BaseSubmitResult } from '@pxpio/web-components-sdk/src/checkoutDropIn/types/BaseSubmitResult';
import BaseSdkException from '@pxpio/web-components-sdk/src/types/sdkExceptions/BaseSdkException';
import { useEffect } from 'react';
export default function CheckoutPage() {
useEffect(() => {
initialiseCheckoutDropIn();
}, []);
async function initialiseCheckoutDropIn() {
// 1. Get the session data from your backend
const sessionData = await fetch('/api/create-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
merchantId: 'MERCHANT-1', // Replace with your merchant ID
siteId: 'SITE-1', // Replace with your site ID
amount: 25.00,
currency: 'USD'
})
}).then(response => response.json());
// 2. Initialise Checkout Drop-in
const checkoutDropIn = CheckoutDropIn.initialize({
environment: 'test',
session: sessionData,
ownerId: 'MERCHANT-1',
ownerType: 'MerchantGroup',
transactionData: {
currency: 'USD',
amount: 25.00,
entryType: 'Ecom',
intent: {
card: IntentType.Authorisation,
paypal: IntentType.Purchase
},
merchantTransactionId: sessionData.merchantTransactionId,
merchantTransactionDate: () => new Date().toISOString()
},
onGetShopper: () => Promise.resolve({ id: 'shopper-123' }),
onBeforeSubmit: async (paymentMethod: PaymentMethod) => {
console.log('Payment method selected:', paymentMethod);
return true;
},
onSubmit: (paymentMethod: PaymentMethod) => {
console.log('Payment being processed:', paymentMethod);
},
onSuccess: async (result: BaseSubmitResult) => {
// CRITICAL: Do NOT fulfill order here!
// Frontend callbacks can be manipulated by malicious users.
// Always verify payment on your backend first.
console.log('Payment successful:', result.systemTransactionId);
// Verify payment on your backend
const verified = await fetch('/api/verify-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
systemTransactionId: result.systemTransactionId,
merchantTransactionId: result.merchantTransactionId,
amount: 25.00
})
}).then(r => r.json());
if (verified.success) {
window.location.href = `/success?orderId=${verified.orderId}`;
} else {
alert('Payment verification failed. Please contact support.');
}
},
onError: (error: BaseSdkException) => {
console.error('Payment failed:', error);
alert(`Payment failed: ${error.message}`);
}
});
// 3. Mount the drop-in to your page
await checkoutDropIn.create('checkout-drop-in-container');
}
return (
<div>
<h1>Complete Your Purchase</h1>
<div id="checkout-drop-in-container"></div>
</div>
);
}
When a payment succeeds, the onSuccess callback fires with transaction details. However, you must always verify the payment on your backend before fulfilling orders.
Configure webhooks in the Unity Portal to receive real-time payment notifications on your backend.
Create the webhook endpoint
Set up an endpoint at /webhooks/pxp to receive payment notifications from Unity.
Process webhook events
Loop through the events array and filter for Transaction events.
Check payment state
Verify the transaction state is Authorised or Captured before processing.
Prevent duplicate processing
Check if you've already processed this transaction using systemTransactionId.
Verify transaction details
Match the merchantTransactionId, amount, and currency against your order records.
Fulfill the order
If verification passes, fulfill the order and mark the transaction as processed.
Respond to webhook
Always return { state: 'Success' } to acknowledge receipt, even if processing failed.
You can also verify payments using the Transactions API to query transaction status directly. See the Integration guide for details.
// Webhook endpoint to receive payment notifications
// Configure this URL in the Unity Portal under Webhooks
app.post('/webhooks/pxp', async (req, res) => {
const events = req.body;
// Process each webhook event
for (const event of events) {
if (event.eventCategory === 'Transaction') {
const txn = event.eventData;
// Check if payment was successful
if (txn.state === 'Authorised' || txn.state === 'Captured') {
// Prevent duplicate processing
const alreadyProcessed = await isTransactionProcessed(txn.systemTransactionId);
if (alreadyProcessed) {
continue;
}
// Verify transaction details match your records
const expectedOrder = await getOrderByMerchantTransactionId(txn.merchantTransactionId);
if (expectedOrder &&
txn.amounts.transactionValue === expectedOrder.amount &&
txn.amounts.currencyCode === expectedOrder.currency) {
// Payment verified - fulfill the order
await fulfillOrder(txn.merchantTransactionId);
// Mark as processed to prevent duplicates
await markTransactionProcessed(txn.systemTransactionId);
console.log(`Order ${txn.merchantTransactionId} fulfilled successfully`);
} else {
console.error('Transaction verification failed - amount or currency mismatch');
}
}
}
}
// Always return success to acknowledge receipt
res.json({ state: 'Success' });
});
That's it! You now have a working Checkout Drop-in integration.
Now that you have Drop-in running, here are the recommended next steps:
- Customise the look and feel to match your brand
- Set up backend verification for Web to verify payments before fulfilling orders
- Add optional callbacks for Web to enhance the user experience with validation and loading states