Payment
Process payments through multiple methods (cash register, QR code, pay by link) with a flexible, event-driven widget.
For installation instructions, see Getting Started with Unify Widgets.
Basic Implementation
<unify-payment-widget
api-key="your-api-key"
></unify-payment-widget>const widget = document.querySelector('unify-payment-widget');
// Set the order object (format defined by the Unify API)
widget.order = { /* order data */ };
widget.addEventListener('payment-success', (event) => {
console.log('Payment complete via:', event.detail.method);
});Component Attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
api-key | String | Yes | API key for Unify requests |
demo | Boolean | No | Enable demo mode for testing |
link | String | Only for qrcode / paybylink | Payment link to encode in the QR code / share via the "Pay by Link" button. Unify does not process the payment itself for these two methods — the integrator (ecommerce / StoreApp) is responsible for generating this link and handling the payment flow behind it |
Component Properties
| Property | Type | Required | Description |
|---|---|---|---|
config | Object | No | Configuration object (see Config Options) |
order | Object | Yes | Order data to process. Format is defined by the Unify API — set this before calling selectPaymentMethod() |
Read-only state
These properties are reflected as HTML attributes and can be observed via JS or used in CSS selectors:
| Property / Attribute | Values | Description |
|---|---|---|
paymentMethod | '', 'cash-register', 'qrcode', 'paybylink' | Currently active payment method |
paymentState | 'idle', 'sending', 'success', 'error' | Current payment processing state |
/* Example: style the widget based on state */
unify-payment-widget[payment-state="success"] { ... }Methods
selectPaymentMethod(method)
selectPaymentMethod(method)Programmatically select and process a payment method.
widget.order = { /* order data */ };
await widget.selectPaymentMethod('cash-register');Parameters:
method(String):'cash-register','qrcode'or'paybylink'
Returns: Promise — resolves when processing completes, rejects on error.
resetPayment()
resetPayment()Reset the widget to its initial (idle) state.
widget.resetPayment();Config Options
document.querySelector('unify-payment-widget').config = {
enabledPaymentMethods: ['cash-register', 'qrcode', 'paybylink'],
cashRegisterPaymentMethodName: 'Pay at Store',
};Payment Methods & Labels
| Option | Default | Description |
|---|---|---|
enabledPaymentMethods | ['cash-register', 'qrcode'] | Array of enabled payment methods. 'paybylink' must be explicitly added — it's not enabled by default |
cashRegisterPaymentMethodName | "Pay at Cash Register" | Label for cash register option |
qrcodePaymentMethodName | "Pay with QR Code" | Label for QR code option |
paybylinkPaymentMethodName | "Pay by Link" | Label for the pay by link option |
payAtCashRegisterSuccessText | "Proceed to pay at your cash register." | Text shown after cash register selection |
QR Code
The QR code is generated entirely client-side (no network call, no external image/QR service) via qr-code-styling, rendered onto an internal <canvas> (used instead of the library's SVG output, which has a known bug with logos).
document.querySelector('unify-payment-widget').config = {
qrCodeModuleShape: 'rounded',
qrCodeMargin: 20,
};| Option | Default | Description |
|---|---|---|
qrCodeErrorCorrectionLevel | 'M' (or 'H' automatically when qrCodeLogoUrl is set) | Error correction level: 'L' (~7%), 'M' (~15%), 'Q' (~25%), 'H' (~30%). Higher levels tolerate more visual obstruction (e.g. a center logo) at the cost of a denser code |
qrCodeModuleShape | 'square' | Shape of the QR modules (and, to match, the 3 finder pattern "eyes"): 'square', 'rounded' or 'dot' |
qrCodeMargin | 40 | Quiet zone size around the code, in pixels at the library's internal 1000×1000 render size (scales with the code regardless of the module count, which varies with the link's length and error correction level) |
qrCodeLogoUrl | undefined | URL (or data URI) of a logo image rendered at the center of the code. When set, the modules underneath are excavated automatically |
qrCodeLogoSizeRatio | 0.2 | Logo size as a fraction of the code's width (0-0.3 recommended to keep the code scannable) |
Pay By Link
The widget always offers a "Send by SMS" and a "Send by Email" button — it doesn't send anything itself, it only reports the customer's choice via payment-link-channel-selected (see Events) for the integrator to act on.
document.querySelector('unify-payment-widget').config = {
paybylinkSmsButtonLabel: 'Text me the link',
};| Option | Default | Description |
|---|---|---|
paybylinkSmsButtonLabel | "Send by SMS" | Label of the "Send by SMS" button |
paybylinkEmailButtonLabel | "Send by Email" | Label of the "Send by Email" button |
Error Handling
| Option | Default | Description |
|---|---|---|
defaultErrorMessage | "An error occurred during payment processing." | Fallback error message |
showErrorDetails | false | Show detailed error information when available |
Styling
CSS Custom Properties (recommended)
All visual tokens (colors, sizes, spacing, fonts) are exposed as CSS custom properties prefixed with --unify-. This is the stable styling API.
Set them on the widget element or any ancestor:
unify-payment-widget {
--unify-payment-method-border: #333;
--unify-payment-method-bg: white;
--unify-payment-method-color: black;
--unify-payment-method-option-width: 117px;
--unify-payment-method-option-height: 100px;
--unify-pay-button-min-height: 400px;
--unify-pay-button-border: black;
--unify-pay-button-bg: transparent;
--unify-pay-button-color: black;
--unify-paybylink-button-bg: black;
--unify-paybylink-button-color: white;
--unify-paybylink-button-border-width: 0px;
--unify-paybylink-button-border-color: transparent;
--unify-paybylink-button-radius: 4px;
--unify-paybylink-button-hover-bg: #333;
--unify-paybylink-button-hover-color: white;
--unify-paybylink-button-hover-border-color: transparent;
--unify-qrcode-size: 200px;
--unify-qrcode-bg: white;
--unify-qrcode-color: black;
}To discover all available properties:
- Open browser DevTools
- Inspect the
<unify-payment-widget>element - Look at the Computed tab for all
--unify-*entries - Edit them live in the Styles panel to preview
CSS Parts
Structural elements can be targeted using ::part():
unify-payment-widget::part(payment-method-selector-option) {
border-radius: 8px;
}⚠️ Parts may change between versions. Use CSS custom properties where possible.
To find available parts, inspect the widget's shadow DOM in DevTools — every element with a part attribute is targetable from outside.
Events
payment-success
payment-successFired when payment is confirmed by Unify. Only applies to 'cash-register' — the only method Unify actually processes itself.
widget.addEventListener('payment-success', (event) => {
console.log('Method used:', event.detail.method);
});Detail shape:
{
method: string // 'cash-register'
}payment-link-ready
payment-link-readyFired for 'qrcode' / 'paybylink' once the QR code / link is generated and ready to show. This is not a payment confirmation — Unify doesn't process these two methods itself, the customer still has to open the link on their own device and complete payment there. Poll your own backend (or however you track completion of the payment behind link) to know when that actually happens.
widget.addEventListener('payment-link-ready', (event) => {
console.log('Ready to show:', event.detail.method);
});Detail shape:
{
method: string // 'qrcode' or 'paybylink'
}payment-link-channel-selected
payment-link-channel-selectedFired when the customer picks the "Send by SMS" or "Send by Email" button for the "Pay by Link" method. The widget doesn't send anything itself — the integrator is expected to show its own form (phone number / email) and send the link.
widget.addEventListener('payment-link-channel-selected', (event) => {
console.log('Send the link via', event.detail.channel, 'to order', event.detail.orderId);
});Detail shape:
{
channel: string, // 'sms' or 'email'
link: string,
orderId: string
}payment-error
payment-errorFired when payment processing encounters an error.
widget.addEventListener('payment-error', (event) => {
console.error('Payment failed:', event.detail.error);
});Detail shape:
{
error: string, // Error message
method: string // 'cash-register', 'qrcode' or 'paybylink'
}Examples
const widget = document.querySelector('unify-payment-widget');
widget.config = {
enabledPaymentMethods: ['cash-register', 'qrcode', 'paybylink'],
cashRegisterPaymentMethodName: 'Pay at Register',
qrcodePaymentMethodName: 'Scan to Pay',
paybylinkPaymentMethodName: 'Pay by Link',
qrCodeModuleShape: 'rounded',
};
// Required by the qrcode and paybylink methods — Unify does not generate or
// process the payment itself, the integrator provides this link.
widget.link = 'https://pay.example.com/checkout/BB4789507809478347';
widget.order = { /* order object from your Unify integration */ };
widget.addEventListener('payment-success', (event) => {
console.log(`Payment completed via ${event.detail.method}`);
});
widget.addEventListener('payment-link-ready', (event) => {
console.log(`${event.detail.method} link ready — waiting for the customer to pay`);
});
widget.addEventListener('payment-link-channel-selected', (event) => {
console.log('Send the link via', event.detail.channel, 'to order', event.detail.orderId);
});
widget.addEventListener('payment-error', (event) => {
console.error(`Payment failed: ${event.detail.error}`);
widget.resetPayment();
});Updated about 1 month ago