Skip to main content

Hooks

Hooks let you tap into the payment flow to add custom behavior like loading states, analytics tracking, or custom 3D Secure handling.

const checkout = await Purse.createHeadlessCheckout('your-session-data', {
onBeforeValidate: async (splits) => { /* your code */ },
onAfterValidate: async () => { /* your code */ },
redirectionHandler: async (data) => { /* your code */ }
});
SDK Reference

For complete type definitions, see PurseHeadlessCheckoutHooks interface.

Show a Loading Indicator During Payment

Use onBeforeValidate and onAfterValidate to control UI state.

const checkout = await Purse.createHeadlessCheckout('your-session-data', {
onBeforeValidate: async () => {
// Disable submit button and show loader
document.querySelector('#pay-button').disabled = true;
document.querySelector('#loader').classList.remove('hidden');
},

onAfterValidate: async () => {
// Hide loader (payment succeeded)
document.querySelector('#loader').classList.add('hidden');
}
});
Payment is Already Complete

When onAfterValidate runs, the payment has already been processed. Errors here won't stop the payment.

Track Payment Events in Analytics

Send payment events to your analytics platform.

const checkout = await Purse.createHeadlessCheckout('your-session-data', {
onBeforeValidate: async (splits) => {
// Track payment start
gtag('event', 'begin_checkout', {
value: splits.reduce((sum, s) => sum + s.amount, 0) / 100,
currency: 'EUR'
});
},

onAfterValidate: async () => {
// Track payment success
gtag('event', 'purchase', { transaction_id: 'your-transaction-id' });
}
});

Validate Data Before Submitting

Block the payment if validation fails.

const checkout = await Purse.createHeadlessCheckout('your-session-data', {
onBeforeValidate: async () => {
// Check terms acceptance
const termsAccepted = document.querySelector('#terms-checkbox').checked;
if (!termsAccepted) {
throw new Error('Please accept terms and conditions');
}
}
});
Error Handling

Throwing an error in onBeforeValidate stops the payment. The API call won't be made.

Handle 3D Secure Authentication

Customize how 3D Secure authentication is displayed to users.

Default Behavior (Full Page Redirect)

const checkout = await Purse.createHeadlessCheckout('your-session-data', {
redirectionHandler: async ({ url, type, method, JSONPayload }) => {
if (type === 'jsonFormSubmit') {
// Create and auto-submit form (full page redirect)
const form = document.createElement('form');
form.method = method;
form.action = url;

const data = JSON.parse(JSONPayload || '{}');
Object.entries(data).forEach(([key, value]) => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = String(value);
form.appendChild(input);
});

document.body.appendChild(form);
form.submit();
}
}
});

Custom Behavior (Modal or iFrame)

const checkout = await Purse.createHeadlessCheckout('your-session-data', {
redirectionHandler: async ({ url, type }) => {
if (type === 'redirection') {
// Open 3DS in a centered popup window
const width = 500;
const height = 600;
const left = (screen.width - width) / 2;
const top = (screen.height - height) / 2;

window.open(
url,
'3DS Authentication',
`width=${width},height=${height},left=${left},top=${top}`
);
}
}
});
3D Secure May Interrupt

If 3D Secure is required, redirectionHandler runs instead and the user must complete authentication first. Plan your redirect flow accordingly.

Complete Example

Here's a full integration combining loading states, analytics, and 3D Secure handling:

const checkout = await Purse.createHeadlessCheckout('your-session-data', {
// Before payment: Validate and prepare UI
onBeforeValidate: async (splits) => {
// Validate terms acceptance
if (!document.querySelector('#terms').checked) {
throw new Error('Please accept terms and conditions');
}

// Update UI
document.querySelector('#pay-button').disabled = true;
document.querySelector('#loader').classList.remove('hidden');

// Track event
gtag('event', 'begin_checkout', {
value: splits.reduce((sum, s) => sum + s.amount, 0) / 100
});
},

// After payment: Cleanup and track success
onAfterValidate: async () => {
document.querySelector('#loader').classList.add('hidden');
gtag('event', 'purchase');
},

// Handle 3DS if required
redirectionHandler: async ({ url, type, method, JSONPayload }) => {
if (type === 'jsonFormSubmit') {
const form = document.createElement('form');
form.method = method;
form.action = url;

const data = JSON.parse(JSONPayload || '{}');
Object.entries(data).forEach(([key, value]) => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = String(value);
form.appendChild(input);
});

document.body.appendChild(form);
form.submit();
}
}
}
);

Best Practices

  • Keep hooks fast: They run during the payment flow. Avoid slow operations.
  • Use async/await: Always return promises from hooks.
  • Validate in onBeforeValidate: This is your last chance to stop a payment.
  • Handle errors gracefully: In onAfterValidate, remember the payment already succeeded.

Hook Reference

HookWhenBlocks PaymentUse For
onBeforeValidateBefore API call✅ YesValidation, loading states, analytics
onAfterValidateAfter payment succeeds❌ NoCleanup, success tracking
redirectionHandlerWhen 3DS needed❌ NoCustom 3DS UI

See PurseHeadlessCheckoutHooks for parameter types and RedirectionHandlerParams for redirection types.