Skip to main content

The path

1

Sign in with a wallet

SIWE only. No email, no password. The merchants row is created on first sign-in, and the merchant wallet is screened for sanctions at this point.
2

Attest a payout recipient

Call setRecipient on MerchantRegistry from the wallet that will own the settlement. This is ownerless and msg.sender-scoped, so nobody else can attest on the merchant’s behalf.Optional in the sense that effectiveRecipient is identity-default — an unattested merchant resolves to themselves — but an explicit attestation is what makes the anchor check meaningful.
3

Deploy the pool, per chain

The merchant signs the prepared factory transaction from the dashboard and pays native gas. Roughly 0.07onEthereum,0.07 on Ethereum, **29.37 on TRON**.No contract change is needed for this: the pool address is CREATE2-deterministic and its init code commits the recipients, so anyone can deploy it.
4

Get the embed snippet

SDK keys and the iframe snippet unlock only once the pool has code on-chain.
5

Pin the settlement anchor

Put the payout wallet in the embed on the merchant’s own site. This is the root of the client-side verification chain and the thing a database compromise cannot reach.

Why deployment gates integration

The gate sits at integration, not at signup and not at checkout. A merchant can create an account and explore freely, but cannot ship anything until their pool exists.
Two reasons:
  • It stops an unproven account from spending platform money. The keeper previously deployed pools lazily on its own gas at first use. Ten thousand fake accounts would cost 99onBaseand99 on Base and **288,200 on TRON**. The cost is not the point on EVM; the unauthenticated ability to make the platform spend is.
  • A live integration always has a settlement destination. No customer can pay into a configuration that was never finished.
This gate is not a security requirement. The client-side trust verification works fine against an undeployed pool — predictPoolAddress is pure CREATE2 arithmetic and the load-bearing chain read is against the registry, not the pool. Do not justify the gate on security grounds; that claim would be wrong.

Rail availability per chain

The deposit rail works before deployment because a sweep into an undeployed pool is a plain ERC-20 transfer that becomes distributable the moment the pool exists. The connect rail does not, because deposit() is a contract call that reverts against an address with no code.
Keep a keeper-side rescue path for a customer payment that lands while a pool is somehow still undeployed. Never strand a real payment over a merchant’s unfinished setup step.

Configuration

A single Generate modal produces a hosted, single-use payment link: price and currency, optional title, accepted tokens, networks, and an optional expiry defaulting to one hour.
Known issues in the shipped version: single-use is not enforced server-side, so a settled link keeps opening a working checkout; URL parameters can override the fixed amount because the checkout page reads editable parameters rather than the resolved link row; and comma-locale amounts parse as NaN.

What the customer sees

A single-panel wizard: currency → network → rail → pay. Copy is de-jargoned — no “pool”, “rail”, “forwarder”, or “deposit” in customer-facing text. Amounts display to at most two decimals, like fiat. The decided fix is to round the quoted token amount at quote time rather than only at render, so a customer who types exactly what they see is not technically off by the difference.