One drop-in button
Add <pay-button> and point it at your create endpoint. Your API key stays on your server; the browser only ever sees a payment id.
ZyroPay adds pay by bank to your checkout with one API call and one button. Your customer approves in their banking app, their bank pays your account directly, and a signed webhook tells your server the order is paid. We never hold your money.
payment.completed · signedDrop the ZyroPay button into your page. It opens a hosted checkout where the customer picks their country and bank, confirms the amount, and approves in their banking app. You never build a bank list or a consent screen.
Add <pay-button> and point it at your create endpoint. Your API key stays on your server; the browser only ever sees a payment id.
The payer's bank pays your registered account directly. ZyroPay orchestrates the approval and never pools or holds funds.
On a computer the checkout shows a QR code. The customer approves on their phone and the desktop page completes by itself.
With resume on, the button reconnects to the payment when the customer comes back from their bank, so your thank-you screen still appears.
Ask for INSTANT and DOMESTIC together. Payments use the instant rail whenever the customer's bank supports it and fall back to standard otherwise.
The hosted checkout and receipts page offer a language switch, and the bank list is filtered to banks that can take the payment.
Your customer chooses their country, then their bank from a list filtered to banks that support the payment. They confirm the amount and your business name, then continue to their bank to approve.
Sign in to the merchant portal to see your payments as they move from created to completed. Filter by status or rail, and match each one to your order by its reference.
CREATED to COMPLETED, with REJECTED, FAILED and CANCELLED kept distinct.| Reference | Status | Rail | Amount |
|---|---|---|---|
| ORDER-1001 | Completed | Instant | £49.99 |
| ORDER-1000 | Awaiting authorisation | — | £120.00 |
| ORDER-0999 | Completed | Domestic | €86.40 |
| ORDER-0998 | Rejected | Instant | £19.99 |
| ORDER-0997 | Cancelled | — | £64.50 |
Read two sections, copy the reference implementation, and you're done. No SDK to install: it's plain HTTPS and JSON, so any language works.
POST the amount in minor units and your order reference, with your API key and an Idempotency-Key.
Load pay.js and add <pay-button>. It calls your endpoint and opens the checkout.
Verify the HMAC signature, then mark the order paid once. Browser events are for the thank-you screen only.
// POST /api/checkout (your backend)
app.post("/api/checkout", async (req, res) => {
const order = await db.getOrder(req.body.orderId);
const r = await fetch("https://pay.zyropay.net/api/merchant/payment/requests", {
method: "POST",
headers: {
"x-api-key": process.env.ZYROPAY_API_KEY,
"Idempotency-Key": order.id,
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: { minorUnits: order.minorUnits, currency: "GBP" },
reference: order.id,
}),
});
const payment = await r.json();
res.json({ id: payment.id }); // only the id goes to the browser
});
<script src="https://pay.zyropay.net/pay.js"></script>
<pay-button
create-url="/api/checkout"
amount="49.99" currency="GBP" ref="ORDER-1001"
resume>
</pay-button>
<script>
document.querySelector("pay-button")
.addEventListener("payment:settled", (e) => showThankYou(e.detail.paymentId));
</script>
// The only place an order is marked paid
app.post("/webhooks/zyropay", express.raw({ type: "application/json" }), async (req, res) => {
if (!verify(req.body, req.get("x-openpay-signature"), process.env.ZYROPAY_WEBHOOK_SECRET))
return res.sendStatus(401);
const evt = JSON.parse(req.body.toString("utf8"));
res.sendStatus(200); // acknowledge fast, then work
if (evt.event === "payment.completed")
await db.fulfilOnce(evt.paymentId, evt.reference); // idempotent
});
curl -X POST https://pay.zyropay.net/api/merchant/payment/requests \
-H "x-api-key: sk_live_xxx" \
-H "Idempotency-Key: ORDER-1001" \
-H "Content-Type: application/json" \
-d '{
"amount": { "minorUnits": 4999, "currency": "GBP" },
"reference": "ORDER-1001"
}'
A payment request has no payee field. Every payment settles to the bank account registered for you at onboarding, and only your operator can change it. Each credential does one job, so losing one exposes as little as possible.
| Credential | Lives in | Can do |
|---|---|---|
| sk_live_… | Your server | Create and read payments |
| pay_… | The browser | View and pay one payment |
| dash_… | Merchant portal | View payments, read-only |
| webhookSecret | Your server | Verify our webhooks |
ZyroPay initiates payments through Yapily's open banking network. Your customer picks their country, and the checkout shows only banks that support the payment you asked for.
Bank availability changes as banks update their open banking services. Ask us about a specific bank or market.
Try the full flow in the sandbox today. Once onboarding is complete, we register your settlement account and issue your live API key, webhook secret and portal token.