ARYZE Open Finance

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.

.env
ARYZE_CLIENT_SECRET=cs_live_...
ARYZE_API_BASE=https://payment-api.openfinance.aryze.io

2. 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.

Your backend (e.g. Express)
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 });
});
Your frontend
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.

app/checkout/return/page.tsx
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.

On this page