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

PaidBy Payment Widget

Overview

Transaction creation and hosted payment widget integration

Use the PaidBy API to create an Open Banking transaction and receive a hosted payment URL. A successful request returns an authorization object containing a redirect_url. Load this URL directly in the hosted PaidBy payment widget.

⚠️ Do not construct the widget URL manually. Use the complete URL returned in redirect_url.

Create a transaction

POST /v1/authorization/

Creates a PaidBy Open Banking transaction. A successful request returns an authorization object containing a redirect_url. The merchant frontend loads this URL in the hosted PaidBy payment widget.

API endpoints

Environment

Method

Transaction endpoint

stage

POST

https://stage-gateway.isignth.is/v1/authorization/

production

POST

https://api.isxpay.com/gateway/service/v1/authorization/

Use the stage environment for development and testing. Use the production environment for approved live transactions.

These URLs are transaction-creation endpoints, not widget URLs. The complete widget URL is returned in the redirect_url response field.

Request headers

Header

Type

Requirement

Example

Description

From

String

Mandatory

YOUR_API_CLIENT_NAME

API client name assigned to the PaidBy integration.

Authorization

String

Mandatory

Bearer YOUR_API_TOKEN

Bearer token assigned to the PaidBy integration. The word Bearer must be included.

Content-Type

String

Mandatory

application/json

Indicates that the request body is JSON.

Use the exact From and Authorization values assigned to the merchant integration. Do not expose these values in frontend code.

Request example

cURL

curl "https://stage-gateway.isignth.is/v1/authorization/" \

--request POST \

--header "From: YOUR_API_CLIENT_NAME" \

--header "Authorization: Bearer YOUR_API_TOKEN" \

--header "Content-Type: application/json" \

--data '{

"workflow": "Test_Workflow",

"merchant": {

"id": "Test_Merchant",

"transaction_webhook_url": "https://merchant.example.com/webhooks/paidby",

"return_url": "https://merchant.example.com/payment/result"

},

"client": {

"first_name": "Shana",

"last_name": "Barrows",

"email": "shana.barrows@mail.com"

},

"transaction": {

"id": "Test_ID",

"amount": "100",

"currency": "EUR",

"reference": "Test_Ref"

},

"account_holder": {

"name": "Jane Smith",

"type": "business",

"iban": "DE89370400440532013000",

"bic": "COBADEFFXXX"

},

"account": {

"identifier": "Test_ID"

}

}'

For production, use: https://api.isxpay.com/gateway/service/v1/authorization/

JSON body

{

"workflow": "Test_Workflow",

"merchant": {

"id": "Test_Merchant",

"transaction_webhook_url": "https://merchant.example.com/webhooks/paidby",

"return_url": "https://merchant.example.com/payment/result"

},

"client": {

"first_name": "Shana",

"last_name": "Barrows",

"email": "shana.barrows@mail.com"

},

"transaction": {

"id": "Test_ID",

"amount": "100",

"currency": "EUR",

"reference": "Test_Ref"

},

"account_holder": {

"name": "Jane Smith",

"type": "business",

"iban": "DE89370400440532013000",

"bic": "COBADEFFXXX"

},

"account": {

"identifier": "Test_ID"

}

}

For GBP transactions, set transaction.currency to GBP and omit account_holder. The account_holder object is used only for applicable EUR flows.

Request parameters

Parameter

Type

Requirement

Example

Description

workflow

String

Mandatory

Test_Workflow

Workflow name assigned to the merchant integration.

merchant

Object

Mandatory

-

Merchant identification and callback configuration.

merchant.id

String

Mandatory

Test_Merchant

Merchant identifier assigned to the integration.

merchant.transaction_webhook_url

String

Mandatory

https://merchant.example.com/webhooks/paidby

HTTPS endpoint that receives transaction notifications.

merchant.return_url

String

Mandatory

https://merchant.example.com/payment/result

URL to which the customer is returned after the hosted flow.

client

Object

Mandatory

-

Customer information.

client.first_name

String

Mandatory

Shana

Customer first name.

client.last_name

String

Mandatory

Barrows

Customer last name.

client.email

String

Mandatory

shana.barrows@mail.com

Customer email address.

client.gender

String

Optional

-

Customer gender.

client.ip

String

Optional

-

Customer IP address.

client.mobile

String

Optional

-

Customer mobile number.

client.title

String

Optional

-

Customer title.

client.middle_name

String

Optional

-

Customer middle name.

client.dob

String

Optional

-

Customer date of birth.

client.citizen_country

String

Optional

-

Customer country of citizenship.

client.birth_country

String

Optional

-

Customer country of birth.

client.residential_address_street_number

String

Optional

-

Residential street number.

client.residential_address_street

String

Optional

-

Residential street.

client.residential_address_secondary

String

Optional

-

Secondary address information.

client.residential_address_city

String

Optional

-

Residential city.

client.residential_address_subdivision

String

Optional

-

Residential state or subdivision.

client.residential_address_postal_code

String

Optional

-

Residential postal code.

client.residential_address_country

String

Optional

-

Residential country.

transaction

Object

Mandatory

-

Payment transaction information.

transaction.id

String

Mandatory

Test_ID

Unique merchant transaction identifier.

transaction.amount

String

Mandatory

100

Amount in the lowest currency unit.

transaction.currency

String

Mandatory

EUR

EUR or GBP, according to the merchant contract.

transaction.reference

String

Mandatory

Test_Ref

Unique merchant transaction reference.

account

Object

Mandatory

-

Merchant-side customer account information.

account.identifier

String

Mandatory

Test_ID

Unique customer identifier assigned by the merchant.

account_holder

Object

Conditional

-

Bank-account details for applicable EUR transactions.

account_holder.name

String

Optional

Jane Smith

Name on the bank account.

account_holder.type

String

Optional

business

Type of bank account.

account_holder.iban

String

Conditional

DE89370400440532013000

Required when account_holder is included.

account_holder.bic

String

Conditional

COBADEFFXXX

Bank identifier code.

Amount formatting

The transaction.amount must be supplied in the lowest currency unit.

Payment amount

API value

EUR 1.00

100

EUR 10.00

1000

GBP 25.50

2550

Unique reference fields

Field

Requirement

transaction.id

Must be unique.

transaction.reference

Must be unique.

account.identifier

Must be unique and paired with the customer for future transactions.

Conditional account-holder fields

Rule

Description

EUR

account_holder may be supplied for applicable EUR flows.

GBP

Omit account_holder.

IBAN

When account_holder is included, account_holder.iban is mandatory.

Response example

When the transaction is created successfully, PaidBy returns an authorization object similar to:

JSON

{

"id": "e6988b4a-ed1c-4104-9caf-af6bb8769756",

"uid": "e6988b4a-ed1c-4104-9caf-af6bb8769756",

"secret": "159173ee-6309-4806-b499-d2598a2ed21e",

"context_uid": "e6988b4a-ed1c-4104-9caf-af6bb8769756",

"mode": "registration",

"original_message": {

"merchant_id": "Test_Merchant",

"transaction_id": "Test_ID",

"reference": "Test_Ref"

},

"transactions": "[]",

"state": "PENDING",

"compound_state": "PENDING.VALIDATED_TRANSACTION",

"redirect_url": "https://paidby-stage.isxtech.com/landing/e6988b4a-ed1c-4104-9caf-af6bb8769756"

}

The redirect_url field contains the complete hosted PaidBy widget URL. Load this value directly in the payment iframe.

Response attributes

Attribute

Type

Example

Description

id

String

e6988b4a-...

Unique authorization response identifier.

uid

String

e6988b4a-...

Unique authorization identifier.

secret

String

159173ee-...

Transaction secret used to validate notifications.

context_uid

String

e6988b4a-...

Context identifier associated with the transaction.

mode

String

registration

Authorization flow mode.

original_message

Object

-

Merchant and transaction information associated with the original request.

original_message.merchant_id

String

Test_Merchant

Merchant identifier from the request.

original_message.transaction_id

String

Test_ID

Transaction identifier from transaction.id.

original_message.reference

String

Test_Ref

Transaction reference from transaction.reference.

transactions

String or collection

[]

Transaction data returned by the API.

state

String

PENDING

Current transaction state.

compound_state

String

PENDING.VALIDATED_TRANSACTION

Detailed transaction state.

redirect_url

String

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

Complete hosted PaidBy widget URL.

Response handling

    • Use redirect_url exactly as returned.
    • Do not construct the widget URL from id, uid, or another response field.
    • Do not append values to redirect_url.
    • Do not treat the initial PENDING state as confirmation of payment.
    • Handle final payment status through the separate PaidBy Notifications documentation.

Open the payment widget

The merchant frontend requests a transaction from the merchant backend. The merchant backend calls PaidBy and returns the information required to open the widget.

Recommended merchant backend response

JSON

{

"id": "e6988b4a-ed1c-4104-9caf-af6bb8769756",

"state": "PENDING",

"compound_state": "PENDING.VALIDATED_TRANSACTION",

"redirect_url": "https://paidby-stage.isxtech.com/landing/e6988b4a-ed1c-4104-9caf-af6bb8769756"

}

Backend integration

The following examples demonstrate the same transaction-creation flow in Node.js, PHP, and Java.

Node.js

import express from "express";

const app = express();

app.use(express.json());

const endpoints = {

stage: "https://stage-gateway.isignth.is/v1/authorization/",

production: "https://api.isxpay.com/gateway/service/v1/authorization/"

};

app.post("/api/create-paidby-transaction", async (req, res) => {

const payload = {

workflow: process.env.PAIDBY_WORKFLOW,

merchant: {

id: process.env.PAIDBY_MERCHANT_ID,

transaction_webhook_url: process.env.PAIDBY_WEBHOOK_URL,

return_url: process.env.PAIDBY_RETURN_URL

},

client: req.body.client,

transaction: req.body.transaction,

account: req.body.account

};

if (req.body.transaction.currency === "EUR" && req.body.account_holder) {

payload.account_holder = req.body.account_holder;

}

const endpoint = process.env.PAIDBY_ENVIRONMENT === "production"

? endpoints.production : endpoints.stage;

const response = await fetch(endpoint, {

method: "POST",

headers: {

From: process.env.PAIDBY_API_CLIENT_NAME,

Authorization: `Bearer ${process.env.PAIDBY_API_TOKEN}`,

"Content-Type": "application/json"

},

body: JSON.stringify(payload)

});

const data = await response.json();

if (!response.ok || !data.redirect_url) {

return res.status(502).json({ error: "Unable to create PaidBy transaction" });

}

res.json({

id: data.id,

state: data.state,

compound_state: data.compound_state,

redirect_url: data.redirect_url

});

});

PHP

<?php

$endpoint = getenv('PAIDBY_ENVIRONMENT') === 'production'

? 'https://api.isxpay.com/gateway/service/v1/authorization/'

: 'https://stage-gateway.isignth.is/v1/authorization/';

$payload = [

'workflow' => getenv('PAIDBY_WORKFLOW'),

'merchant' => [

'id' => getenv('PAIDBY_MERCHANT_ID'),

'transaction_webhook_url' => getenv('PAIDBY_WEBHOOK_URL'),

'return_url' => getenv('PAIDBY_RETURN_URL')

],

'client' => $request['client'],

'transaction' => $request['transaction'],

'account' => $request['account']

];

if ($request['transaction']['currency'] === 'EUR' && isset($request['account_holder'])) {

$payload['account_holder'] = $request['account_holder'];

}

$ch = curl_init($endpoint);

curl_setopt_array($ch, [

CURLOPT_POST => true,

CURLOPT_RETURNTRANSFER => true,

CURLOPT_HTTPHEADER => [

'From: ' . getenv('PAIDBY_API_CLIENT_NAME'),

'Authorization: Bearer ' . getenv('PAIDBY_API_TOKEN'),

'Content-Type: application/json'

],

CURLOPT_POSTFIELDS => json_encode($payload)

]);

$response = curl_exec($ch);

$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);

curl_close($ch);

$data = json_decode($response, true);

if ($status < 200 || $status >= 300 || empty($data['redirect_url'])) {

http_response_code(502);

echo json_encode(['error' => 'Unable to create PaidBy transaction']);

exit;

}

echo json_encode([

'id' => $data['id'],

'state' => $data['state'],

'compound_state' => $data['compound_state'],

'redirect_url' => $data['redirect_url']

]);

Java

HttpClient client = HttpClient.newHttpClient();

ObjectMapper mapper = new ObjectMapper();

Map<String, Object> payload = new LinkedHashMap<>();

payload.put("workflow", System.getenv("PAIDBY_WORKFLOW"));

payload.put("merchant", Map.of(

"id", System.getenv("PAIDBY_MERCHANT_ID"),

"transaction_webhook_url", System.getenv("PAIDBY_WEBHOOK_URL"),

"return_url", System.getenv("PAIDBY_RETURN_URL")

));

payload.put("client", requestBody.get("client"));

payload.put("transaction", requestBody.get("transaction"));

payload.put("account", requestBody.get("account"));

Map<String, Object> transaction = (Map<String, Object>) requestBody.get("transaction");

if ("EUR".equals(transaction.get("currency")) && requestBody.containsKey("account_holder")) {

payload.put("account_holder", requestBody.get("account_holder"));

}

String endpoint = "production".equals(System.getenv("PAIDBY_ENVIRONMENT"))

? "https://api.isxpay.com/gateway/service/v1/authorization/"

: "https://stage-gateway.isignth.is/v1/authorization/";

HttpRequest request = HttpRequest.newBuilder(URI.create(endpoint))

.header("From", System.getenv("PAIDBY_API_CLIENT_NAME"))

.header("Authorization", "Bearer " + System.getenv("PAIDBY_API_TOKEN"))

.header("Content-Type", "application/json")

.POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(payload)))

.build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

Map<String, Object> data = mapper.readValue(response.body(), new TypeReference<>() {});

if (response.statusCode() < 200 || response.statusCode() >= 300 || data.get("redirect_url") == null) {

throw new IllegalStateException("Unable to create PaidBy transaction");

}

Frontend integration

HTML

<button id="paidby-pay-button" type="button">

Proceed to pay

</button>

<p id="paidby-error" role="alert"></p>

<dialog id="paidby-widget-dialog" aria-labelledby="paidby-widget-title">

<header class="paidby-dialog-header">

<h2 id="paidby-widget-title">Complete your payment</h2>

<button id="paidby-close-button" type="button" aria-label="Close payment dialog">×</button>

</header>

<iframe

id="paidby-widget-frame"

title="PaidBy Payment Widget"

loading="lazy"

scrolling="yes"

allow="payment"

allow="camera; microphone"

src="about:blank">

</iframe>

</dialog>

The iframe remains set to about:blank until transaction creation succeeds.

CSS

dialog#paidby-widget-dialog {

width: min(1020px, calc(100% - 32px));

max-width: 1020px;

max-height: calc(100dvh - 44px);

padding: 0;

border: 0;

border-radius: 10px;

overflow: hidden;

background: transparent;

box-shadow: 08px 32px rgba(0, 0, 0, 0.25);

}

dialog#paidby-widget-dialog::backdrop {

background: rgba(0, 0, 0, 0.5);

}

.paidby-dialog-header {

display: flex;

align-items: center;

justify-content: space-between;

padding: 12px 16px;

background: #fff;

}

#paidby-widget-frame {

display: block;

width: 100%;

height: 600px;

min-height: 520px;

border: 0;

background: #fff;

}

@media (max-width: 768px) {

dialog#paidby-widget-dialog {

width: 100vw;

height: 100dvh;

max-width: 100vw;

max-height: 100dvh;

margin: 0;

border-radius: 0;

}

#paidby-widget-frame {

height: calc(100dvh - 68px);

min-height: 0;

}

}

JavaScript

const payButton = document.getElementById("paidby-pay-button");

const dialog = document.getElementById("paidby-widget-dialog");

const iframe = document.getElementById("paidby-widget-frame");

const closeButton = document.getElementById("paidby-close-button");

const errorMessage = document.getElementById("paidby-error");

let lastFocusedElement = null;

async function openPaidByWidget(paymentDetails) {

payButton.disabled = true;

errorMessage.textContent = "";

try {

const response = await fetch("/api/create-paidby-transaction", {

method: "POST",

headers: { "Content-Type": "application/json" },

body: JSON.stringify(paymentDetails)

});

const data = await response.json();

if (!response.ok) throw new Error(data.error || "Unable to start payment");

if (!data.redirect_url) throw new Error("The response did not contain redirect_url");

lastFocusedElement = document.activeElement;

iframe.src = data.redirect_url;

dialog.showModal();

closeButton.focus();

} catch (error) {

errorMessage.textContent = error.message || "Unable to start payment";

} finally {

payButton.disabled = false;

}

}

closeButton.addEventListener("click", () => dialog.close());

dialog.addEventListener("click", event => {

if (event.target === dialog) dialog.close();

});

dialog.addEventListener("close", () => {

iframe.src = "about:blank";

if (lastFocusedElement) lastFocusedElement.focus();

});

Error handling

The merchant integration should handle:

    • Network failures.
    • Missing or invalid From values.
    • Missing or invalid Authorization values or Bearer prefix.
    • Invalid workflow or merchant IDs.
    • Missing mandatory request objects or fields.
    • Duplicate transaction identifiers or references.
    • Invalid amount formatting.
    • Unsupported currency.
    • Missing account.identifier.
    • Missing account_holder.iban when account_holder is supplied.
    • PaidBy API timeouts.
    • Non-JSON API responses.
    • Missing redirect_url.
    • Expired widget sessions.
    • Transaction cancellation, decline, or risk rejection.

HTTP response codes

PaidBy HTTP response codes are documented in the API Glossary. Refer to API Glossary - HTTP Codes.

Check the HTTP response status before processing the response body. Successful responses should also be checked for the expected fields, including redirect_url.

Payment notifications

Payment status notifications are documented separately from the transaction-creation API. Refer to PaidBy API Notifications.

The notifications documentation covers:

    • Notification payloads.
    • Authentication or verification.
    • Retry behaviour.
    • Merchant acknowledgement.
    • Final payment-status handling.

This guide covers only:

    • Creating the PaidBy transaction.
    • Receiving the redirect_url.
    • Opening the hosted PaidBy widget.

The initial state and compound_state returned when creating a transaction must not be treated as the final payment result.