Skip to main content

Widget v3 – How to Build

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>

Widget Purpose

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.

Initializing the Widget Manager

Using API V1

const env = {
apiKey: "YOUR_API_KEY",
entityId: "YOUR_ENTITY_ID",
environment: "sandbox", // or 'production'
};

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.

Creating Widget Instances

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"); //can be called whenever you want
  • 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, // false if you want to display our button in every case (allowing customisation)
}
}
},
interface: "PAY_BUTTON"
});
await payButton.mount("widget-button");

Example Implementation

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>
info

Once your widget is displayed correctly, move on to the payment validation step.

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 };