On this page

Payment Rails

Use prepaid sessions, authorization holds and subscriptions, with fixed funding and payout terms.

Prepaid public budgets#

Payment rails hold public budgets for API usage, streaming, authorization/capture and recurring access. Once earnings settle, they become backed credits that cannot be reversed. Parties and request amounts remain visible. The rail authorizes payment; it does not prove service delivery. To bring settled credits into the private Pool, use a separately admitted payment-credit adapter action.

Amounts use native asset units. Session, payment and subscription IDs derive from payer and salt and can be used only once. Save the chain and rail address with the ID for each offchain request. Credits represent settled earnings or refunds. A provisional hold or unclaimed budget has not yet become transferable credits.

A private input, a public action and its returned note
A private input, a public action and its returned note

Cumulative sessions#

FuyuPaymentSessions fixes its asset and dispute period at deployment. openSession collects the full initial budget and fixes merchant and voucher signer. A zero signer uses the payer. The payer can top up until requesting closure. Vouchers specify total merchant earnings; claim pays only the increase over what was already claimed.

Anyone can relay a claim, and its credits go to the fixed merchant. Only the merchant can call claimAndRedeem, which redeems the newly claimed credits. Only the payer can call requestClose; this stops top-ups and starts the dispute window. Claims stop at closeAt. Anyone can call finalizeClose to credit the unused remainder to the payer, while finalizeCloseAndRedeem is payer-only.

solidity
function openSession(address _merchant, address _voucherSigner,
    uint256 _amount, bytes32 _salt) external returns (bytes32 sessionId);
function topUp(bytes32 _sessionId, uint256 _amount) external;
function voucherDigest(bytes32 _sessionId, uint256 _cumulativeAmount)
    public view returns (bytes32);
function claim(bytes32 _sessionId, uint256 _cumulativeAmount,
    bytes calldata _signature) external returns (uint256 settledAmount);
function requestClose(bytes32 _sessionId) external;
function finalizeClose(bytes32 _sessionId)
    external returns (uint256 refundedAmount);

Reserve a hold, then capture#

FuyuPaymentAuthorizations fixes merchant, optional operator, authorization signer, refund receiver and budget. A zero operator uses merchant, and a zero signer uses payer. Authorization signs paymentId, requestId, ceiling, validUntil and nonce. The merchant/operator must reserve the hold onchain before relying on the ceiling. Until then, cancellation or another hold may use the available budget.

reserve locks the full ceiling, consumes the request and nonce, and checks the signer once. A hold can last at most 30 days. Registered holds remain valid through payer cancellation or a later signer-policy revocation, until they are captured, voided or expired. Cancellation prevents new holds.

capture takes cumulativeAmount and credits only its increase. Repeating a target does not charge twice. finalCapture releases the unused remainder; capturing the full ceiling closes the hold even without that flag. Anyone can expire a hold after its deadline. Released funds return to available budget, or to the refund receiver's credits if the payment was cancelled.

solidity
function reserve(bytes32 paymentId, bytes32 requestId,
    uint256 ceiling, uint64 validUntil, uint256 nonce,
    bytes calldata signature) external;
function capture(bytes32 paymentId, bytes32 requestId,
    uint256 cumulativeAmount, bool finalCapture)
    external returns (uint256 capturedAmount, uint256 releasedAmount);
function expireHold(bytes32 paymentId, bytes32 requestId)
    external returns (uint256 releasedAmount);
function cancelPayment(bytes32 paymentId)
    external returns (uint256 refundAmount);

Prepaid subscriptions#

FuyuSubscriptionPayments fixes merchant, refund receiver, amountPerPeriod, periodSeconds, startAt, expiresAt, maxPeriods and budget. Creation collects the budget without charging a period. Anyone can charge the current calendar period once. Missed periods cannot be charged later.

Each charge mints merchant credits and updates paidUntil. Only the payer can cancel, which stops future charges and refunds remaining reserve. After expiry, anyone can call refundExpired for the fixed refund receiver. Charged credits stay with the merchant. A partial final period costs the full fixed amount, so use a period-boundary expiry if you want full periods.

currentPeriod returns the current index, start/end and chargeable flag. Simulate charge before submitting it, since another worker can charge first and cause PeriodAlreadyCharged. Your application decides how payment affects access, usually by reading finalized paidUntil.

Fund a deterministic escrow#

FuyuSessionFunding and FuyuPaymentFunding predict and deploy immutable escrows from fixed terms, chain and rail/asset identities. Anyone can deploy or activate a funded escrow without gaining signer or refund rights. Deploying the same terms returns the same escrow; activation can happen only once.

In a session escrow, sessionKey authorizes closure and refundReceiver receives refunds. The multi-rail escrow supports session, authorization and subscription kinds, with a budget, funding deadline and active expiry. Reaching active expiry allows a caller to trigger closure or cancellation, but a caller must send that transaction. Session dispute windows and registered holds keep the rights their rails provide.

Before funding, check the predicted address, rail and asset code, authority and refund terms. After activation, read the rail's opened ID. Recover uncredited assets or forward settled refund credits through the escrow functions. If activation times out, check its state before sending another budget.

Redeem credits and split payouts#

FuyuPaymentCreditLedger provides transferable credits backed by equal underlying units for authorization and subscription rails. Sessions have their own equivalent credit functions. redeem burns the caller's settled credits and sends the same number of underlying units to the receiver. Balance checks reject inexact transfers. Verify the backing asset address rather than relying on the credit symbol or decimals.

FuyuPaymentSplitter fixes its credit ledger, asset, beneficiaries, shares and payout mode. creditPending and assetPending show what each beneficiary is owed. Anyone can distribute, but only the recorded beneficiaries get paid. redeemCredits works only in asset payout mode and cannot select another recipient.

StateEvents and errors
Session earnings and closureVoucherClaimed, CloseRequested, SessionClosed; InvalidVoucher, ClaimWindowEnded, CloseNotReady
Hold capture or releaseHoldReserved, HoldCaptured, HoldVoided, HoldExpired; InsufficientAvailableBudget, AuthorizationExpired
Subscription billingPeriodCharged, SubscriptionCanceled, SubscriptionRefunded; PeriodAlreadyCharged, SubscriptionInactive
Escrow activation and refundActivated, RefundCreditsForwarded, AssetsRecovered; BindingChanged, FundingExpired, InsufficientFunding
Credit transfers and payoutsTransfer, CreditRedeemed, PaymentReleased; InexactTransfer, InsufficientBacking, WrongPayoutMode

Implement the merchant flow#

Save an operation record for every voucher, hold request or subscription period. Check signatures against the rail's digest function and read finalized settlement state before delivering irreversible service. For cumulative usage, track authorized earnings separately from settled earnings.

Stop accepting new session usage when closure starts. Reserve holds before committing billable work, and use finalized payment state for subscription access. Run a worker for close, hold expiry and refund forwarding if your product needs prompt completion. Your service still needs to account for usage honestly; the contracts enforce payment authority.