The Purse widget allows you to quickly build a secure and dynamic payment interface using your configured experiences.
Although the Purse API can be used directly, the Widget Library greatly simplifies frontend integration across multiple PSPs.
Installation
First, include the Purse Checkout script in your HTML page. This script loads all necessary components to initialize the checkout.
Using API V1
<script src="https://cdn.purse-sandbox.com/dropin/v3-latest/purse.js"></script>
Using API V2
You can recover the script from the widget url in the client session API response.
<script src="{widget_url}"></script>
The widget orchestrates one or more payment providers and handles rendering, session management, and submission.
Even if your experience only includes a single payment method, the widget will handle it cleanly and consistently.
You’ll use the WidgetManager
to build and mount the widgets associated with a payment session or wallet session.
Using API V1
const env = {
apiKey: "YOUR_API_KEY",
entityId: "YOUR_ENTITY_ID",
environment: "sandbox",
};
const manager = UpStreamPay.WidgetManager.buildForCredentials(env);
await manager.setPaymentSession(session);
Using API V2
const widgetManager = await UpStreamPay
.WidgetManager
.buildForWidgetData(clientSession.widget.data);
The widget.data
and widget.js_url
values are returned by the /v2/client-sessions
API.
Once the manager is initialized, create the widgets you need:
const payment = await manager.createWidget({
name: "my_unique_payment_widget_name",
interface: "PAYMENT",
});
const button = await manager.createWidget({
name: "my_unique_pay_button_widget_name",
interface: "PAY_BUTTON",
});
await payment.mount("widget-payment");
await button.mount("widget-button");
Rendered Output
- The
widget-payment
displays available methods:
The widget uses the id html artibutte to target its rendering place. Here we assume that the DOM contains an
element with the id widget-payment
const widget = await manager.createWidget({
interface: "SELECTION",
ui:{
layout:"GRID"
}
methods:(protocol)=>protocol.method !== "giftcard"
});
await widget.mount("widget-payment");
- The
widget-button
links to the payment widget and sends payment data:
The widget uses the ID HTML attribute to target its rendering place. Here, we assume that the DOM contains an
element with the id widget-button
const payButton = await manager.createWidget({
ui: {
translations: {
overrides: {
WIDGET_PAY_BUTTON_TEXT: "Proceed to payment"
}
},
stylesheet: {
overrides:{
"usp-pay-button-use-partner-ui":true,
}
}
},
interface: "PAY_BUTTON"
});
await payButton.mount("widget-button");
Example Implementation
- Widget API V1
- Widget API V2
let widgetManager;
let widgetPaymentContainer;
let widgetAmountContainer;
let widgetButtonContainer;
async function createWidgetManager() {
const credentials = {
apiKey: apiKey,
entityId: entityId,
environment: id,
};
widgetManager = UpStreamPay.WidgetManager.buildForCredentials(credentials);
await createPaymentWidget();
await createAmountWidget();
await createButtonWidget();
}
async function createButtonWidget() {
const button = await widgetManager.createWidget({ interface: 'PAY_BUTTON' });
await button.mount('widget-button');
}
async function createPaymentWidget() {
const widget = await widgetManager.createWidget({
interface: 'PAYMENT',
ui: {
layout: { name: 'ACCORDION' }
}
});
await widget.mount('widget-payment');
}
async function createAmountWidget() {
const widget = await widgetManager.createWidget({ interface: 'AMOUNT' });
await widget.mount('widget-amount');
}
<h1>Let's test Purse</h1>
<div class="main">
<div id="widget-payment" bind:this={widgetPaymentContainer} />
</div>
<div class="secondary">
<div id="widget-amount" bind:this={widgetAmountContainer} />
<div id="widget-button" bind:this={widgetButtonContainer} />
</div>
let widgetManager;
let widgetPaymentContainer;
let widgetAmountContainer;
let widgetButtonContainer;
async function createWidgetManager() {
const clientSession = "data-from-your-create-client-session";
widgetManager = await UpStreamPay.WidgetManager.buildForWidgetData(clientSession.widget.data);
await createPaymentWidget();
await createAmountWidget();
await createButtonWidget();
}
async function createButtonWidget() {
const button = await widgetManager.createWidget({ interface: 'PAY_BUTTON' });
await button.mount('widget-button');
}
async function createPaymentWidget() {
const widget = await widgetManager.createWidget({
interface: 'PAYMENT',
ui: {
layout: { name: 'ACCORDION' }
}
});
await widget.mount('widget-payment');
}
async function createAmountWidget() {
const widget = await widgetManager.createWidget({ interface: 'AMOUNT' });
await widget.mount('widget-amount');
}
<h1>Let's test Purse</h1>
<div class="main">
<div id="widget-payment" bind:this={widgetPaymentContainer} />
</div>
<div class="secondary">
<div id="widget-amount" bind:this={widgetAmountContainer} />
<div id="widget-button" bind:this={widgetButtonContainer} />
</div>
Callbacks overrides
Hook into the Validation Process
You may want to execute logic before or after payment validation (e.g. saving an order in your system).
You can pass preValidate
and postValidate
callbacks to the widget manager.
widgetManager = await UpStreamPay.WidgetManager.buildForWidgetData(widgetData, [], {
preValidate: async () => console.log('Prevalidate working'),
postValidate: async () => console.log('PostValidate working'),
});
- If
preValidate
throws an error or rejects, the validation is cancelled.
- If
postValidate
fails, the validation still proceeds normally.
Redirection Mode
By default, the widget will follow the redirection after the payment is validated. However, you can override this behavior by setting a redirectionHandler
callback in the buildFor...
method.
widgetManager.buildForCredentials({
hooks: {
redirectionHandler(redirectionParams: RedirectionHandlerParams) {
if (redirectionParams.type === 'redirection') {
window.location.href = redirectionParams.url;
} else if (redirectionParams.type === 'jsonFormSubmit') {
const form = document.createElement('form');
form.method = redirectionParams.method || 'POST';
form.action = redirectionParams.url;
if (redirectionParams.JSONPayload) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'payload';
input.value = redirectionParams.JSONPayload;
form.appendChild(input);
}
document.body.appendChild(form);
form.submit();
} else if (redirectionParams.type === 'htmlFormSubmit') {
const container = document.getElementById(redirectionParams.containerId);
if (container) {
const form = document.createElement('form');
form.method = 'POST';
form.action = redirectionParams.url;
form.innerHTML = redirectionParams.responseForm;
container.appendChild(form);
}
}
}
}
});
The following redirection types are supported:
type RedirectionHandlerParams =
| {
type: 'redirection';
url: string;
}
| {
type: 'jsonFormSubmit';
url: string;
JSONPayload?: string;
method?: 'GET' | 'POST';
}
| { type: 'htmlFormSubmit'; containerId: string; responseForm: any };