<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Purse Headless Checkout - Demo</title>
<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 = "";
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);
});
}
checkout.isPaymentFulfilled.subscribe((isReady) => {
payButton.disabled = !isReady;
});
payButton.addEventListener("click", () => {
try {
checkout.submitPayment();
} catch (error) {
console.error(error);
}
});
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>