Defining accepts
Introduction
Section titled “Introduction”RequirePaymentOptions is the one object you pass to
requirePayment or
createPaymentGate. It declares what you
accept: which chain, which token, how much, and where the money lands. There are two forms —
a single-rail shorthand and a multi-rail accept[] — and you pass exactly one of them.
Single-rail shorthand
Section titled “Single-rail shorthand”The common case: name the chain, token, amount, and payTo at the top level. One rail,
one line.
import { requirePayment } from '@piprail/sdk'
requirePayment({ chain: 'base', token: 'USDC', amount: '0.10', payTo: '0xYourWallet' })token is required — every gate states exactly what it takes, so there’s never any doubt
whether a route wants USDC, USDT, or the native coin. Name a built-in symbol ('USDC',
'USDT'), use 'native' for the chain’s own coin, or pass a custom token by address. The
symbol is all you write; the SDK fills in the contract address and decimals.
requirePayment({ chain: 'bnb', token: 'USDT', amount: '1', payTo: '0xYourWallet' })requirePayment({ chain: 'base', token: 'native', amount: '0.001', payTo: '0xYourWallet' }) // ETHrequirePayment({ chain: 'solana', token: 'USDC', amount: '0.10', payTo: 'YourSolanaAddr' })amount is human units, given as a string — '0.10' is ten cents of a 6-decimal USDC, not
ten base units. The SDK scales it to base units against the token’s decimals, so you never write
100000. See Chains and tokens for the full token grammar and
custom-token descriptors per family.
Multi-rail — accept[]
Section titled “Multi-rail — accept[]”To offer several rails in one challenge, pass accept[]. The agent pays with whatever it
holds, on whichever of the offered chains it can settle.
import { requirePayment } from '@piprail/sdk'
const BASE_RPC = process.env.BASE_RPC // your own RPC per chain (public ones are rate-limited)const TRON_RPC = process.env.TRON_RPCconst SOL_RPC = process.env.SOL_RPC
requirePayment({ payTo: '0xYourWallet', accept: [ { chain: 'base', token: 'USDC', amount: '0.10', payTo: '0xYourWallet', rpcUrl: BASE_RPC }, { chain: 'tron', token: 'USDT', amount: '0.10', payTo: 'T…', rpcUrl: TRON_RPC }, { chain: 'solana', token: 'USDC', amount: '0.10', payTo: 'YourSolanaAddr', rpcUrl: SOL_RPC }, ],})The two forms are mutually exclusive — pass accept[] or top-level
chain/token/amount, never both. Passing both, or neither in full, throws a clear Error on
the first request (the gate resolves lazily, not at construction time) — under requirePayment
that error is forwarded to next(err).
Per-rail overrides and fallbacks
Section titled “Per-rail overrides and fallbacks”Each AcceptOption carries its own chain, token, and amount, and may override payTo and
rpcUrl for its chain. When omitted, both fall back to the top-level value.
| Field | Per-rail behaviour |
|---|---|
chain / token / amount | Required on every entry; describe that one rail. |
payTo | Falls back to the top-level payTo when omitted. |
rpcUrl | Falls back to the top-level rpcUrl when omitted. |
Each rail also resolves through its own driver with its own rpcUrl, so one throttled
public RPC can’t take down verification for the others. In production, set rpcUrl on every rail
— public endpoints are rate-limited. The rpcUrl is used server-side only; it is never leaked
into the challenge.
How the client picks
Section titled “How the client picks”A PipRailClient is bound to one chain — its own chain
plus wallet. When it receives a multi-rail challenge it picks the offered rail whose network it
supports and whose spend policy allows, pays that one, and
ignores the rest. With autoRoute on it pays the
cheapest settleable rail; planPayment() shows you the
ranking first. To compare cost across chains yourself, point one client per chain at the same URL
and compare their estimateCost() results.
On the gate side, verification selects the matching rail by network + asset and re-derives every checked field from its own trusted spec — a forged echo of the offered terms can’t redirect it. See Verifying payments.
Describing the charge
Section titled “Describing the charge”description is shown to the agent inside the challenge — a short human label for what the
payment buys.
requirePayment({ chain: 'base', token: 'USDC', amount: '0.10', payTo: '0xYourWallet', description: 'One market-data report',})Timing and confirmations
Section titled “Timing and confirmations”| Option | Default | Purpose |
|---|---|---|
maxTimeoutSeconds | 600 | Max age of an accepted payment, in seconds — how long a challenge stays valid. |
minConfirmations | 1 | Confirmations required on-chain before access is granted. |
requirePayment({ chain: 'base', token: 'USDC', amount: '0.10', payTo: '0xYourWallet', maxTimeoutSeconds: 120, // tighten the recency window minConfirmations: 2, // wait for two confirmations})A tighter maxTimeoutSeconds shrinks the replay window; pair it with a persistent
replay store for multi-instance deploys.
Further options
Section titled “Further options”The remaining RequirePaymentOptions fields are covered on their own pages: onPaid on
Receipts and onPaid, isUsed/markUsed on
Replay protection, exact on the
exact rail, and discovery on
Discover and register. The full type lives in the
API reference.