Use XPay Web SDK v5 to render XPay Element and confirm a PaymentIntent in Vanilla JavaScript, React, or Next.js.
Before you begin
Create the PaymentIntent on the merchant backend. The frontend needs the returned pi_client_secret and encryptionKey, together with the publishable key and account ID supplied for client integration.
Never expose the merchant API key or API signature secret in frontend code.
Add the SDK
Load the test or production v5 script in the page or application layout:
<!-- Test -->
<script src="https://js.xstak.com/v5/xpay-stage.js"></script>
<!-- Production -->
<script src="https://js.xstak.com/v5/xpay.js"></script>React and Next.js integrations can install the matching v5 package:
Test package:
npm install @xstak/xpay-element-stage-v5Production package:
npm install @xstak/xpay-element-live-v5Use test and production artifacts consistently. Do not combine the test script with the production package or credentials.
Initialize Vanilla JavaScript
Provide a container:
<form id="payment-form">
<div id="xpay-element"></div>
<button id="pay-button" type="submit">Pay</button>
</form>Initialize the v5 XPay class with an options object and mount the element:
const xpay = new XPay({
publishableKey: "{{publishable_key}}",
accountId: "{{account_id}}"
});
xpay.element("#xpay-element", {});Initialize React or Next.js
Wrap the checkout component with XPay:
import { XPay } from "@xstak/xpay-element-stage-v5";
export function CheckoutPage({ paymentIntent }) {
return (
<XPay
xpay={{
publishableKey: "{{publishable_key}}",
accountId: "{{account_id}}"
}}
>
<CheckoutForm paymentIntent={paymentIntent} />
</XPay>
);
}Render PaymentElement inside the wrapper:
import {
PaymentElement,
useXPayClient
} from "@xstak/xpay-element-stage-v5";
function CheckoutForm({ paymentIntent }) {
const xpay = useXPayClient();
async function submitPayment() {
await xpay.confirmPayment({
paymentMethodType: "card",
clientSecret: paymentIntent.pi_client_secret,
customer: { name: "Ayesha Khan" },
encryptionKey: paymentIntent.encryptionKey
});
}
return (
<form
onSubmit={(event) => {
event.preventDefault();
submitPayment();
}}
>
<PaymentElement options={{}} />
<button type="submit">Pay</button>
</form>
);
}Use the production package in production imports.
Validate the form before submission
The Web SDK readiness callback represents form validity: all required payment fields contain valid input and the customer can submit the payment. Merchants that initially disable the payment button can enable it when the readiness callback reports a complete form, and disable it again if the form becomes incomplete.
The readiness callback is not a payment result. Continue to call confirmPayment() and reconcile the outcome through the verified webhook.
Confirm the payment
Web SDK v5 uses one options object for confirmPayment():
await xpay.confirmPayment({
paymentMethodType: "card",
clientSecret: paymentIntent.pi_client_secret,
customer: { name: "Ayesha Khan" },
encryptionKey: paymentIntent.encryptionKey
});Do not use the v4 positional-argument form in a v5 integration.
Handle errors and the asynchronous result
- Catch rejected SDK calls and show a customer-safe message.
- Prevent repeated submission while confirmation is in progress.
- Keep the customer in the flow when XPay requires authentication.
- Verify webhook signatures on the merchant backend.
- Use the PaymentIntent ID and current
pi_statusto reconcile the order.
The SDK result is not the delivery mechanism for reusable payment tokens. In a token flow, read the token from token.id in the verified webhook.
Test the integration
Use the test script, test package, and XPay test credentials together. Confirm that the element renders, the PaymentIntent can be submitted, customer authentication can complete, and the merchant backend accepts the verified webhook.
Next steps
- Accept your first payment.
- Configure displayed payment methods.
- PaymentIntent lifecycle and statuses.
- Collect a payment token with XPay Element.
- Authenticate a saved card with 3DS.
- Upgrade to XPay Web SDK v5.