Create a PaymentIntent on your backend, collect card details with XPay Element, confirm the payment with Web SDK v5, and reconcile the result through your webhook endpoint.
What you'll build
This quickstart creates one PKR card payment. You will:
- Create a PaymentIntent from the merchant backend.
- Return the required client values to the frontend.
- Render XPay Element.
- Confirm the payment.
- Verify the final result through a webhook.
Before you begin
You need XPay test credentials, a merchant backend, a Web frontend, and a public HTTPS webhook endpoint. Complete API authentication and webhook integration before using this flow outside a local test.
1. Create a PaymentIntent
From the merchant backend, send an authenticated request to:
POST {{base_url}}/public/v1/payment/intentThe response is shortened to the values needed by the frontend:
{
"success": true,
"data": {
"_id": "xpay_pi_example",
"pi_client_secret": "xpay_pi_example_cs_example",
"pi_status": "requires_payment_method",
"encryptionKey": "{{encryption_key}}"
}
}Store the PaymentIntent ID and metadata.order_reference on the merchant backend. Return only pi_client_secret and encryptionKey to the trusted checkout frontend.
2. Render XPay Element
Load the Web SDK v5 test script and provide a container for XPay Element:
<script src="https://js.xstak.com/v5/xpay-stage.js"></script>
<form id="payment-form">
<div id="xpay-element"></div>
<button type="submit">Pay PKR 500</button>
<p id="payment-message" role="status"></p>
</form>Initialize XPay with publishable client credentials and mount the element:
const xpay = new XPay({
publishableKey: "{{publishable_key}}",
accountId: "{{account_id}}"
});
xpay.element("#xpay-element", {});
const paymentIntentResponse = await fetch(
"/api/xpay/payment-intents",
{ method: "POST" }
);
const paymentIntent = await paymentIntentResponse.json();
function showMessage(message) {
document.querySelector("#payment-message").textContent = message;
}In this example, /api/xpay/payment-intents is the merchant backend route that performs Step 1 and returns the shortened data object. Do not place the API key or API signature secret in frontend code.
3. Confirm the payment
Use the values returned by your backend when you call confirmPayment():
const form = document.querySelector("#payment-form");
form.addEventListener("submit", async (event) => {
event.preventDefault();
try {
await xpay.confirmPayment({
paymentMethodType: "card",
clientSecret: paymentIntent.pi_client_secret,
customer: { name: "Ayesha Khan" },
encryptionKey: paymentIntent.encryptionKey
});
showMessage("Payment submitted. Confirming the final result…");
} catch (error) {
showMessage(error.message);
}
});XPay Element handles the sensitive card fields. The merchant frontend must not read or submit those values.
4. Verify the result
Use the SDK outcome to update the checkout interface, but use the verified webhook and current PaymentIntent state for backend fulfillment. A successful captured payment has pi_status set to succeeded.
Test the full path by confirming that:
- The PaymentIntent ID is associated with
order-1001. - The customer completes any required authentication.
- The webhook signature is valid.
- The final PaymentIntent status is stored against the order.
- Fulfillment occurs only after the merchant backend accepts the successful result.
What to do next
- Read PaymentIntent lifecycle and statuses.
- Learn how to capture a payment later.
- Learn how to collect a reusable payment token.
- See the Create Payment Intent API for optional request fields and the Retrieve Payment Intent API for backend reconciliation.