Skip to main content

Getting Started

Overview

This guide walks you through the essential steps to build a fully functional Headless Checkout integration using the Purse SDK. Here is a high-level overview of the steps involved:

  1. Load the SDK : Include the script or import the module into your application.
  2. Initialize the Checkout : Use your API credentials or client session data to set up the checkout instance.
  3. Display Available Payment Methods : Dynamically list the payment methods retrieved from the SDK.
  4. Handle Method Selection : Render the appropriate form UI when a user selects a method.
  5. Validate and Submit Payment : Monitor form validity and handle the final submission.
Scope of This Guide

This guide focuses on primary payment methods (e.g. credit cards and digital wallets for example). To handle secondary methods like gift cards, see the Gift Cards guide.

Step 1 - Load the SDK

<script src="https://cdn.purse-sandbox.com/headless-checkout/latest/purse.umd.js"></script>
Information
Use the latest stable version for production.

Step 2 - Initialize the Checkout

Pass the widget.data from the client session to the createHeadlessCheckout function.

const checkout = await Purse.createHeadlessCheckout(clientSession.widget.data);

API V1 (legacy)

Or the PurseHeadlessCheckoutV1Params object with a paymentSession

const checkout = await Purse.createHeadlessCheckout({
apiKey: "YOUR_API_KEY",
entityId: "YOUR_ENTITY_ID",
environment: "sandbox",
paymentSession: "YOUR_SESSION"
});

Step 3 - Display Payment Methods

Let's create a simple UI to display the available payment methods.

HTML Structure

Let's create a simple UI to display the available payment methods.

<div id="root">
<!-- Payment methods list -->
<ul id="methods"></ul>
<!-- Payment form -->
<div id="form"></div>
<!-- Pay button -->
<button id="pay-button" disabled>Pay</button>
</div>

Retrieve and display the Payment Methods

Once the SDK is initialized, you can retrieve and display all available payment methods using the checkout.paymentMethods.subscribe() function.

const checkout = await Purse.createHeadlessCheckout(clientSession.widget.data);

function displayPaymentMethods(methods) {
const ul = document.getElementById("methods");
ul.innerHTML = "";
methods.forEach((pm) => {
const li = document.createElement("li");
li.textContent = pm.partner + " - " + pm.method;
ul.appendChild(li);
});
}

checkout.paymentMethods.subscribe(displayPaymentMethods);

Step 4 - Select and Render a Payment Method

Once the list of available payment methods is displayed, you'll need to handle the user selection and render the corresponding payment form.

This is done by attaching an event handler to each method. When selected, the corresponding PaymentElement will be created and mounted inside your container (e.g., a <div id="form">).

Example

// Add configuration (optional)
const paymentConfig = {
hostedForm: {
global: {
color: "#FF0000",
fontSize: "16px",
fontFamily: "Arial, sans-serif"
}
}
};

// Add selection handler to render the chosen payment form
function handlePaymentMethodSelect(paymentMethod) {
const form = document.getElementById("form");
form.innerHTML = "";
const partnerUI = paymentMethod.getPaymentElement(paymentConfig);
partnerUI.appendTo(form);
}

// Update the previous displayPaymentMethods to attach click listeners
function displayPaymentMethods(methods) {
const ul = document.getElementById("methods");
ul.innerHTML = "";
methods.forEach((pm) => {
const li = document.createElement("li");
li.textContent = `${pm.partner} - ${pm.method}`;
li.addEventListener("click", () => handlePaymentMethodSelect(pm));
ul.appendChild(li);
});
}

Deselect a Payment Method

To deselect a payment method, simply call the remove() method on the PaymentElement instance. This will automatically deselect the method.

function deselectPaymentMethod() {
// given you have a reference to the selected PaymentElement
selectedPaymentElement.remove();
}

Step 5 - Validate and Submit Payment

To ensure the payment form is complete before submission, use checkout.isPaymentFulfilled. This reactive variable tells you when the payment form is valid and complete.

checkout.isPaymentFulfilled.subscribe((isReady) => {
document.getElementById("pay-button").disabled = !isReady;
});

document.getElementById("pay-button").addEventListener("click", async () => {
try {
await checkout.submitPayment();
} catch (error) {
console.error(error);
}
});
Information
Your basic Headless Checkout is now operational!

Full Example

Get your widget.data from the client session and copy-paste the code below.

<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Purse Headless Checkout - Demo</title>
<!-- Tailwind for quick styling -->
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.purse-sandbox.com/headless-checkout/latest/purse.umd.js"></script>
<script>
async function initCheckout() {

const checkout = await Purse.createHeadlessCheckout("widget.data");
const payButton = document.getElementById("pay-button");
const paymentConfig = {
hostedForm: {
panPlaceholder: '1234 5678 9012 3456',
panInputLabel: 'Card number',
panRequiredError: 'Enter your card number.',
panFormatError: 'Enter a valid card number.',
panCannotBeEmptyError: 'Card number cannot be empty.',
panTooltipText: 'We only store a secure token, not your full card number.',

cvvPlaceholder: '123',
cvv4Placeholder: '1234',
cvvInputLabel: 'Security code',
cvv4InputLabel: 'Security code',
cvvRequiredError: 'Enter your card security code.',
cvvFormatError: 'Enter a valid security code.',
cvvCannotBeEmptyError: 'Security code cannot be empty.',
cvvTooltipText: 'The 3–4 digit code on the back (or front for Amex).',

expirationPlaceholder: 'MM/YY',
expirationInputLabel: 'Expiration date',
expirationRequiredError: 'Enter your card’s expiration date.',
expirationFormatError: 'Use the format MM/YY.',
expirationCannotBeEmptyError: 'Expiration date cannot be empty.',
expirationOutOfRangeError: 'Enter a valid future date.',
expirationTooltipText: 'Month and year when the card expires (MM/YY).',

holderPlaceholder: 'John Appleseed',
holderInputLabel: 'Cardholder name',
holderRequiredError: 'Enter the name as it appears on your card.',
holderFormatError: 'Enter a valid name.',
holderCannotBeEmptyError: 'Name cannot be empty.'
},
theme: {
global: {
color: "#0f172a",
fontSize: "16px",
fontFamily: "Inter, system-ui, Arial, sans-serif",
},
input: {
height: '48px',
border: '1px solid #d1d5db',
padding: '0 8px',
backgroundColor: '#ffffff',
':hover': {
borderColor: '#9ca3af',
},
':focus': {
borderColor: '#6366f1',
boxShadow: '0 0 0 4px rgba(99,102,241,0.15)',
},
'::placeholder': {
color: '#94a3b8',
},
}
}
};

function handlePaymentMethodSelect(paymentMethod) {
const form = document.getElementById("form");
form.innerHTML = ""; // Reset
const element = paymentMethod.getPaymentElement(paymentConfig);
element.appendTo(form);
}

function displayPaymentMethods(methods) {
const ul = document.getElementById("methods");
ul.innerHTML = "";

methods.forEach((pm) => {
const li = document.createElement("li");
li.className = "flex items-center justify-between rounded-md border border-gray-200 bg-white px-3 py-2 hover:border-indigo-300 hover:bg-indigo-50 cursor-pointer transition";
li.textContent = pm.partner + " - " + pm.method;
li.addEventListener("click", () => handlePaymentMethodSelect(pm));
ul.appendChild(li);
});
}

// Update Pay button state based on validation
checkout.isPaymentFulfilled.subscribe((isReady) => {
payButton.disabled = !isReady;
});

// Submit the payment
payButton.addEventListener("click", () => {
try {
checkout.submitPayment();
} catch (error) {
console.error(error);
}
});

// Init methods
checkout.paymentMethods.subscribe(displayPaymentMethods);
}

window.onload = initCheckout;
</script>
</head>

<body class="min-h-screen bg-gray-50 text-slate-800">
<div class="max-w-xl mx-auto p-6">
<h1 class="text-2xl font-semibold mb-4">Headless Checkout</h1>
<div id="root" class="space-y-4">
<ul id="methods" class="space-y-2"></ul>
<div id="form" class="border rounded-lg p-4 bg-white shadow-sm"></div>
<button id="pay-button" disabled
class="rounded-md bg-indigo-600 px-4 py-2 text-white font-medium disabled:bg-gray-300 disabled:text-gray-600 disabled:cursor-not-allowed hover:bg-indigo-700 transition-colors">
Pay
</button>
</div>
</div>
</body>
</html>

Where to go from here