Accept a payment
Create a payment, redirect the customer to their bank, and finalise the order on return.
This guide shows the full happy path for a single payment: your server calls
/v1/payments/initiate, the customer completes the bank transfer through the
ARYZE-hosted flow, and your return URL receives them when they're done.
The customer's bank does the actual authentication and authorisation. Your server never sees bank credentials — you only receive a transaction status.
1. Set up credentials on your backend
All Payment API calls must come from your server. The client secret never reaches the browser.
ARYZE_CLIENT_SECRET=cs_live_...
ARYZE_API_BASE=https://payment-api.openfinance.aryze.io2. Create a payment when the customer checks out
Get an access token
Tokens are valid for 60 minutes. Cache the token on your backend and reuse it until it's close to expiry — don't request a fresh one on every payment.
async function getAccessToken(): Promise<string> {
const res = await fetch(`${process.env.ARYZE_API_BASE}/v1/auth/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'client_credentials',
client_secret: process.env.ARYZE_CLIENT_SECRET,
}),
});
if (!res.ok) throw new Error(`Auth failed: ${res.status}`);
const { access_token } = await res.json();
return access_token;
}Initiate the payment
Pass a unique reference (your internal order ID) and the URL you want the
customer returned to. Amounts are decimal numbers in the payment currency —
100.50 means 100 euros and 50 cents, not 10050 minor units.
async function createPayment(orderId: string, amount: number) {
const token = await getAccessToken();
const res = await fetch(
`${process.env.ARYZE_API_BASE}/v1/payments/initiate`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
redirectUrl: `https://yourapp.com/checkout/return?order=${orderId}`,
amount,
currencyCode: 'EUR',
reference: orderId,
metadata: JSON.stringify({ orderId, source: 'web-checkout' }),
}),
},
);
if (!res.ok) {
const { error } = await res.json().catch(() => ({ error: res.statusText }));
throw new Error(`Payment initiation failed: ${error}`);
}
return res.json();
}Redirect to the flow URL
The response contains a flowUrl — the hosted page where the customer picks
their bank and approves the transfer. Expose your own checkout route on your
backend, return flowUrl to your frontend, and redirect. /api/checkout here
is your server route, not an ARYZE endpoint.
app.post('/api/checkout', async (req, res) => {
const payment = await createPayment(req.body.orderId, req.body.amount);
res.json({ flowUrl: payment.flowUrl, transactionId: payment.transactionId });
});const { flowUrl } = await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({ orderId, amount }),
headers: { 'Content-Type': 'application/json' },
}).then((r) => r.json());
window.location.href = flowUrl;3. Handle the return URL
After the customer finishes in their banking app, they're redirected to the
redirectUrl you provided. Do not trust the return URL alone to mark the
order as paid — the authoritative signal is the webhook. Use the return URL
only to render a "thanks" page and poll for status.
export default async function ReturnPage({
searchParams,
}: {
searchParams: { order?: string };
}) {
const order = searchParams.order;
const status = await getOrderStatus(order); // your DB, updated by webhook
if (status === 'paid') return <Thanks order={order} />;
if (status === 'failed') return <PaymentFailed order={order} />;
return <StillProcessing order={order} />; // polls every few seconds
}4. Finalise the order from the webhook
The webhook handler is where you actually mark the order as paid. See Consume webhooks for retry semantics and idempotency.
Field rules to know
Prop
Type
Pick a reference that is unique per order after sanitisation — if two
different order IDs collapse to the same 20-character alphanumeric string, you
won't be able to tell them apart in your bank statement.