Skip to main content

Invoices

Invoice lifecycle​

  1. Create invoice.
  2. Build canonical payment intent.
  3. Execute payment (direct or relay).
  4. Track status and stream updates.
  5. Cancel or confirm fallback if needed.

Create invoice​

What this does: creates offchain invoice metadata and pricing context.

import { createApiClient } from '@pepay/x402flex';

const api = createApiClient({ baseUrl: 'https://api.bnbpay.org' });

const invoice = await api.invoices.create({
title: 'Order #4201',
merchantId: 'merchant-123',
merchantName: 'Runey',
amount: '25.00',
currencyToken: 'USDT',
network: 'bnbTestnet',
reference: 'order-4201',
});

Expected result: invoice.invoiceId to anchor the payment workflow.

Build payment intent​

What this does: returns API-aligned intent + derived IDs + signing metadata.

const built = await api.payments.buildIntent({
mode: 'minimal',
network: 'bnbTestnet',
merchant: '0x1111111111111111111111111111111111111111',
token: '0x55d398326f99059fF775485246999027B3197955',
amount: '25.00',
scheme: 'permit2',
invoiceId: invoice.invoiceId,
});

console.log({
paymentId: built.derived.paymentId,
resourceId: built.derived.resourceId,
intentHash: built.derived.intentHash,
});

Expected result: canonical paymentId/resourceId tied to invoice reference.

Execute payment​

What this does: submits a relay payment from built payloads.

const witness = {
schemeId: '0xc07338ac3a63d5933e6a5b2184602e306eb7c25700866301b635132c811546f1',
intentHash: built.derived.intentHash,
payer: '0x2222222222222222222222222222222222222222',
salt: built.input.salt,
};

const permit2 = {
permit: {
permitted: { token: built.input.token, amount: built.input.amountWei },
nonce: 1,
deadline: Math.floor(Date.now() / 1000) + 3600,
},
transferDetails: { to: '0xf14f56A54E0540768b7bC9877BDa7a3FB9e66E91', requestedAmount: built.input.amountWei },
signature: '0xPERMIT2_SIGNATURE',
};

await api.relay.payment({
network: 'bnbTestnet',
scheme: 'permit2',
intent: built.derived.intent,
witness,
witnessSignature: '0xWITNESS_SIGNATURE',
permit2,
reference: built.input.baseReference,
});

Expected result: relay returns transaction hash and payment ID.

Track status and stream events​

What this does: observes invoice updates via polling and SSE/WS streams.

const status = await api.invoices.status(invoice.invoiceId);
const sseUrl = api.invoices.streamSseUrl(invoice.invoiceId);
const wsUrl = api.invoices.streamWsUrl(invoice.invoiceId);

console.log(status.status, sseUrl, wsUrl);

Expected result: realtime invoice status transitions (pending -> paid etc.).

Cancel and confirm fallback​

What this does: handles operational exceptions.

await api.invoices.cancel(invoice.invoiceId);

await api.invoices.confirmPayment(invoice.invoiceId, {
txHash: '0x...',
paidBy: '0x2222222222222222222222222222222222222222',
paidAmount: '25.00',
paidToken: '0x55d398326f99059fF775485246999027B3197955',
});

Expected result: invoice reaches terminal state even when automatic linkage is delayed.

Production checklist​

  • Persist invoice ID, payment ID, resource ID, and reference.
  • Persist nonce for deterministic payment reproduction.
  • Recompute reference hash from final tagged reference string.
  • Add retries for temporary indexer lag.