Skip to content
English - United Kingdom
  • There are no suggestions because the search field is empty.

Accepting Funds - PaidBy Payment Widget

Overview

This API doc explains how to integrate the PaidBy payment widget into a merchant website.

The payment widget is not opened with a hardcoded transaction ID. A merchant must first integrate with PaidBy API to create a payment transaction. PaidBy API returns a transaction_id & that transaction_id is then used to open a hosted payment widget.

High-level flow:

  1. Shopper selects product, service, deposit amount or checkout option on the merchant site.
  2. Merchant frontend sends checkout details to merchant backend.
  3. Merchant backend calls PaidBy API to create transaction.
  4. PaidBy API returns a transaction_id.
  5. Merchant frontend opens payment widget in a dialog.
  6. Merchant frontend loads widget URL using the returned transaction_id.
  7. Shopper completes a payment inside hosted PaidBy widget.
  8. Merchant confirms final payment status using backend status checks, callbacks or webhooks.

Integration architecture

Screenshot 2026-08-05 at 3.41.13 PM

Merchant backend should be responsible for calling PaidBy API.

Frontend should not directly create payment transactions if API credentials or sensitive merchant configuration are required.

The integration has 2 simple parts:

  1. Your backend creates a PaidBy payment transaction.
  2. Your frontend opens the PaidBy hosted payment widget using the returned transaction_id.

The widget must not be opened with a hardcoded transaction ID. A new transaction should be created for a shopper’s checkout, deposit, or payment attempt.

Environment URLs

PaidBy provides separate widget URLs for stage/testing and production.

Use the stage URL while building and testing the integration. Use the production URL only when a merchant account has been approved for live traffic.

 
 

Environment

Widget base URL

stage

https://paidby-stage.isxtech.com

production

https://paidby.isxtech.com

 
 

Stage widget URL format

https://paidby-stage.isxtech.com/landing/

Production widget URL format

https://paidby.isxtech.com/landing/

Do not use the stage widget URL in production.

Quick start

To open the PaidBy payment widget:

  1. Add a backend endpoint that creates a PaidBy transaction.
  2. Add the payment dialog HTML to your checkout page.
  3. Add the payment dialog CSS.
  4. Add the frontend JavaScript.
  5. Replace the placeholder values with your merchant configuration.

Step 1: Create a transaction using the PaidBy API

Before opening widget, a merchant must create a payment transaction through PaidBy API.

The exact PaidBy API endpoint, authentication method & request schema depends on merchant API integration. A typical request includes values such as:

Example merchant backend request

 
// Example only. Replace endpoint, authentication & request fields with PaidBy API specs for a merchant account. app.post('/api/create-paidby-transaction', async (req, res) => { const { amount, currency, merchantReference } = req.body; const response = await fetch('https://api.paidby.example.com/transactions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.PAIDBY_API_KEY}` }, body: JSON.stringify({ amount, currency, merchant_reference: merchantReference, parent_url: 'https://merchant.example.com' }) }); if (!response.ok) { return res.status(502).json({ error: 'Unable to create paidby transaction' }); } const data = await response.json(); res.json({ transaction_id: data.transaction_id }); });

Frontend should receive only data it needs to open a widget, typically transaction_id.

Step 2: Add the payment dialog URL

Add this HTML to the merchant checkout page where a shopper clicks Proceed to pay.

 
<!DOCTYPE html> <html> <head> <title>page title</title> </head> <body> <iframe title="PaidBy Payment Widget" loading="lazy" scrolling="yes" allow="payment" allow="camera; microphone" src="about:blank"> </iframe> </body> </html>

The iframe starts with: src="about:blank".

The iframe src should only be set after your backend successfully creates a transaction and returns a valid transaction_id.

Step 3: Add the payment dialog CSS

Add this CSS to your checkout page.

 
dialog#paidby-widget-dialog { padding: 0; border: none; border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,0.25); width: 100%; max-width: 1020px; } dialog#paidby-widget-dialog::backdrop { background: rgba(0,0,0,0.5); } dialog#paidby-widget-dialog iframe { width: 100%; height: 600px; border: none; display: block; } @media (max-width: 768px) { dialog#paidby-widget-dialog { margin: 0; border-radius: 0; width: 100vw; height: 100vh; max-width: 100vw; max-height: 100vh; } dialog#paidby-widget-dialog iframe { height: 100%; } }

Step 4: Add the frontend JavaScript

Add this JavaScript to the checkout page.

 
var WIDGET_DIALOG_ID = "paidby-widget-dialog"; function ensureWidgetDialog() { var dlg = document.getElementById(WIDGET_DIALOG_ID); if (dlg) return dlg; dlg = document.createElement("dialog"); dlg.id = WIDGET_DIALOG_ID; document.body.appendChild(dlg); return dlg; } export function startPaymentWidget({ txId, webappBaseUrl, parentUrl, onEvent }) { var dlg = ensureWidgetDialog(); var url = webappBaseUrl + "/" + encodeURIComponent(txId) + "?parentUrl=" + encodeURIComponent(parentUrl); var existing = dlg.querySelector("iframe"); if (existing) { existing.src = url; } else { var f = document.createElement("iframe"); f.src = url; f.allow = "clipboard-write"; dlg.appendChild(f); } var iframe = dlg.querySelector("iframe"); function handleMessage(event) { if (event.source === iframe.contentWindow) { if (onEvent) onEvent(event.data); if (event.data && event.data.event === "close") { dlg.close(); } } } window.addEventListener("message", handleMessage); dlg.addEventListener("close", function () { window.removeEventListener("message", handleMessage); }, { once: true }); function handleBackdropClick(e) { if (e.target === dlg) { iframe.contentWindow.postMessage({ event: "close" }, "*"); dlg.close(); } } dlg.addEventListener("click", handleBackdropClick); dlg.addEventListener("close", function () { dlg.removeEventListener("click", handleBackdropClick); }, { once: true }); dlg.showModal(); }

Step 5: Replace the example values

Before testing, replace the example values with your own merchant values.

 
 
 

Example value

Replace with

https://api.paidby.example.com/transactions

Your PaidBy transaction API endpoint.

process.env.PAIDBY_API_KEY

Your PaidBy API key or authentication method.

http://paidby-stage.isxtech.com

Stage widget base URL.

https://paidby.isxtech.com

Production widget base URL.

amount: 5000

The shopper payment amount.

currency: 'EUR'

The transaction currency.

merchant_reference: 'ORDER-12345'

Your order, invoice, deposit, or checkout reference.

parent_url: window.location.origin

The merchant site URL where the widget is opened.

 
 

Dialog size

The payment widget should be displayed as a wide desktop-style payment panel.

 

Desktop

 
 
 

Property

Recommended value

Dialog width

min(1060px, 100%)

Maximum dialog width

1020px

Widget height

600px

Minimum widget height

520px

Maximum dialog height

calc(100dvh - 44px)

Dialog border

0 / transparent

Dialog background

Transparent around the iframe

 
 

Mobile / small screens

 
 

Property

Recommended value

Dialog width

100%

Dialog padding

12px

Widget height

620px

Maximum widget height

100%

Minimum widget height

520px

Breakpoint

768px

 
 

A merchant site can style a dialog wrapper, backdrop, close button, sizing & iframe container.
A merchant site cannot directly style content inside a cross-origin iframe.

Backend & frontend

 

Merchant backend should:

  • Authenticate with PaidBy API.
  • Create transaction with correct amount, currency, merchant reference & shopper context.
  • Return transaction_id to merchant frontend.
  • Store merchant reference & PaidBy transaction reference for reconciliation.
  • Verify final payment status server-side.
  • Handle webhook notifications or payment status callbacks, if configured.
 

Merchant frontend should:

  • Request transaction from merchant backend.
  • Receive transaction_id.
  • Build hosted widget URL.
  • Open payment widget inside dialog.
  • Display user-friendly loading & error states.

Widget URL format

The payment widget URL uses this format: /landing/?parentUrl=

Stage example

http://paidby-stage.isxtech.com/landing/00ebadf9-1fbd-4afa-b05e-21119ada1cb0?parentUrl=https://merchant.example.com

Production example

https://paidby.isxtech.com/landing/00ebadf9-1fbd-4afa-b05e-21119ada1cb0?parentUrl=https://merchant.example.com

URL values

 
 
 

Value

Required

Description

paidby_widget_base_url

Yes

The PaidBy widget base URL for a selected environment. Use http://paidby-stage.isxtech.com for stage & https://paidby.isxtech.com for production.

transaction_id

Yes

The transaction identifier returned by the PaidBy API. This value is placed in the URL path.

parentUrl

Yes

The merchant site URL where the widget is opened. This should normally be the merchant origin, e.g., https://merchant.example.com.

 
 

Parent URL

parentUrl must contain the URL of the merchant site where the widget is opened.

Recommended value: const merchantSiteUrl = window.location.origin;.

This produces values such as: https://merchant.example.com.

If the full page URL is required by your integration, use: const merchantSiteUrl = window.location.href;.

Do not use https://example.com in production. It should only be used for placeholder examples or local/demo testing.

Important implementation rules

 
  • Create a new PaidBy transaction before opening the widget.
  • Do not hardcode a transaction ID.
  • Do not expose PaidBy API credentials in frontend code.
  • Keep the iframe src as about:blank until a valid transaction_id is returned.
  • Set the iframe src only after transaction creation succeeds.
  • URL-encode both transaction_id and parentUrl.
  • Disable the pay button while the transaction is being created.
  • Show a clear error message if transaction creation fails.
  • Verify the final payment status server-side before fulfilling goods or services.
  • Use the stage widget URL only for testing.
  • Use the production widget URL only for live traffic.

Accessibility requirements

 

Payment dialog should include:

  • role="dialog".
  • aria-modal="true".
  • allow="camera; microphone".
  • accessible title using aria-labelledby.
  • close button with aria-label="Close payment dialog".
  • escape-key support.
  • focus placement when the dialog opens.
  • background page scroll lock while the dialog is open.
 

Recommended behavior:

  • Open dialog → focus close button.
  • Close dialog → return focus to Proceed to pay button.
  • Escape key → close dialog.
  • Click backdrop → close dialog.

Cross-origin iframe styling

The PaidBy payment widget is loaded from a hosted external origin. A merchant site can style the:

  • Dialog backdrop.
  • Dialog size.
  • iframe size.
  • iframe wrapper.
  • Close button.
  • Merchant page around the widget.

A merchant site cannot directly style content inside a cross-origin iframe unless PaidBy exposes supported theme parameters or configuration options.

Recommended iframe attributes

 
<iframe title="PaidBy Payment Widget" loading="lazy" scrolling="yes" allow="payment" allow="camera; microphone" src="about:blank"> </iframe>

Note that allow="camera; microphone" is required.

If your PaidBy integration requires clipboard access for sharing payment links, use allow="payment; clipboard-write".

Production checklist

Before going live, confirm the following:

  • Merchant backend can authenticate with PaidBy API.
  • Transaction is created server-side before opening a widget.
  • A transaction_id is valid & not expired.
  • Correct merchant parentUrl is passed.
  • iframe is only loaded after a shopper initiates payment.
  • Dialog works on desktop & mobile.
  • Escape key & close button behavior are implemented.
  • Merchant site handles payment success, failure, cancellation & timeout states.
  • Webhooks or backend payment status checks are configured.
  • Production domain allowlisting is configured, if required.
  • Staging widget URL is replaced with production widget URL when going live.