0fra

Core concepts

The mental model behind 0fra — platforms, merchants, fees, and ledger.

Three layers, three wallets

Every payment touches exactly three parties on-chain. Each is a distinct wallet address.

LayerWhoWallet variableCut
L00fra (us)service_walletservice_fee_bps (set by 0fra)
L1Your platformplatform_walletplatform_fee_bps (set by you)
L2Sub-merchantmerchant_walletWhatever's left, minus their reserve

bps (basis points) is 1/10000. 100 bps = 1%, 1000 bps = 10%.

The split formula

const serviceFee  = gross * serviceFeeBps  / 10_000;
const platformFee = gross * platformFeeBps / 10_000;
const merchantNet = gross - serviceFee - platformFee;

const reserveHold       = merchantNet * reserveBps / 10_000;
const merchantAvailable = merchantNet - reserveHold;

The on-chain PaymentRouter.payOrder() runs the same arithmetic and reverts if the values you submit don't match the registries. You can't tamper with the math.

Reserves

A reserve is a percentage of every payment held back from the merchant for a configurable period — used to cover refunds without bouncing tx through wallets.

  • Set per-merchant on creation (reserve_bps), with a platform-wide default
  • Reserves accrue in a 0fra-controlled vault, indexed by (merchant_id, token, chain_id)
  • Refund flow draws from the reserve first; if insufficient, the platform absorbs and rolls a negative balance for the merchant

Identity

TermStripe analog0fra equivalent
Accountacct_…platform.id (uuid)
Connected acctAccountmerchant.id (uuid)
Payment intentpi_…order.id (uuid)
Chargech_…payment_attempt (per tx_hash)
Checkout sess.cs_…cs_live_… token
API keysk_live_…Same. Plus pk_live_… publishable.
Webhook eventevt_…Webhook delivery (per attempt)

How payments actually move

The dotted-line on the right represents a single atomic transaction. Either everything happens or nothing does.

Account state machine

order:
  pending_payment ──► confirmed ──► refunded / partially_refunded
                  └─► expired

merchant:
  pending_review ──► active ──► frozen

invitation:
  pending ──► completed
          └─► expired

What 0fra keeps off-chain

  • A ledger of every dollar that passed through (ledger_entries table)
  • The idempotency cache so retries are safe
  • Webhook delivery queue with HMAC signatures and exponential retries
  • API keys (only their SHA-256 hash) and session cookies for the dashboard

What's never off-chain: the actual transfer. That always lives on Base.

On this page