The Payments API lets you list the payments collected for your project and mint
one-time deposit accounts that settle into a wallet top-up or a subscription. Depending
on the settlement rail, a deposit account is either a fiat bank account (bank
transfer in NGN) or a stablecoin deposit wallet (USDC or cNGN, on-chain).
How funding works
- Mint a deposit account with Create deposit account,
stating the purpose: a subscription (
planId + billingCycle) or a wallet
top-up (USD amount).
- The payer sends the exact
amountExpected to the account before expiryAt:
a bank transfer for FIAT, an on-chain token transfer for STABLECOIN.
- Settlement is automatic: the deposit is verified, an idempotent payment
record is written, and the purpose is applied: the subscription activates (with
product access granted) or the wallet balance is credited.
The step-by-step version, including per-rail payer instructions, lives in the
Fund and subscribe guide.
The payment object
The deposit account object
Returned by Create deposit account in the
data array, one entry per active provider. Each account is bound to a single expected
amount and expires.
The object is a discriminated union on kind: the keys present depend on the rail, and
you only ever receive the keys for that rail. Always branch on kind; do not assume a field
exists across both.
A FIAT account carries no on-chain keys (walletAddress, token, chainId, …) and a
STABLECOIN account carries no bank keys (accountNumber, bankName, …). The
irrelevant keys are absent, not null.
FIAT: bank transfer
STABLECOIN: on-chain deposit
For a cNGN deposit, show the payer the naira value, e.g.
Send ₦22,556.39 (22,556.39 cNGN). For USD-pegged tokens, show the token
amount, e.g. Send 15 USDC.
FX rate lock
Every amount in a deposit-account response is priced at a single NGN/USD rate,
returned as fxRate (USD per 1 NGN). The rate is locked for the life of the
account: when the deposit lands, settlement converts at the quoted rate, not
the live market rate, so what the payer owes and what gets credited can’t
drift inside the deposit window. After expiryAt the lock (and the account)
expires; request a new account to get a fresh rate.
Subscription upgrades are prorated
If the project already holds an active paid subscription to a different plan
of the same product, a subscription request is treated as an upgrade:
- The unused value of the current subscription (
current plan price × fraction of the billing period remaining) is credited against the new plan’s price.
- Only the difference is charged. All amounts in the response already reflect
the credit, priced at the locked
fxRate.
- When the payment settles, the new subscription starts a full fresh period,
the old subscription is cancelled, and its product access is replaced by the
new plan’s.
- Moving from a free plan to a paid plan of the same product works the same
way, just with no credit: full price is charged, and the free subscription
and its access grant are replaced on settlement.
- If the credit would cover the entire new price (a downgrade or near-equal
switch), the request is rejected: no-charge plan switches are not supported.
No special request field is needed: upgrades are detected server-side, and a
renewal is just a new request with the same planId.