01
The problem
Stablecoins settle enormous volume. Almost none of it happens at a checkout page. The money is already on-chain; what is missing is a way to spend it where people actually shop.
Four things block that path:
- The buyer needs a gas token. Paying $10 in USDC requires holding ETH first. That single requirement eliminates most non-technical buyers before they begin.
- The merchant must pick a chain. Every choice is a separate integration, and every new chain is new work.
- The checkout feels foreign. A generic Connect Wallet button dropped into someone else’s storefront looks like nothing else around it.
- Merchants fear losing control of their money. An intermediary holding funds is a risk many will not take.
02
Design goals
Four constraints shaped every decision below. They are listed in priority order, and where they conflicted, the higher one won.
Funds move in one transaction
A payment either settles completely or does not happen. There is no intermediate state where money sits in the contract by default.
The buyer needs one token
The token being spent. Not a second one to move the first.
One address everywhere
Adding a chain must not change a merchant’s integration.
Exact amounts only
No overpayment, no underpayment, no partial settlement. Payment reconciliation is hard enough without them.
03
Architecture
Payit has one contract, PaymentRouter, deployed at an identical address on every supported chain. That address is produced by CREATE2, which derives it from the salt, the compiled bytecode, and the constructor arguments — never from the chain ID.
The consequence is a property merchants can rely on: the same address on Ethereum, Base, and Robinhood Chain. It also imposes a permanent constraint on us. The owner address, the signer address, the salt, and the compiler version are locked from the first deployment onward. Changing any of them moves the address on all three chains at once.
| Chain | Stablecoin | Gas sponsored |
|---|---|---|
| Robinhood Chain | USDG | Yes |
| Base | USDC | Yes |
| Ethereum | USDC | No |
Gas is not sponsored on Ethereum because sponsoring it there costs more than most payments are worth. We would rather say where it works than promise it everywhere and quietly cap it.
The contract is immutable. There is no proxy and no upgrade path. A critical bug is fixed by deploying a V2 and pointing the backend at the new address — not by mutating the contract holding the money. This costs flexibility and buys the guarantee that the code a merchant audited is the code that will run.
04
Settlement
Every payment is authorised by a payment intent: a structured message signed off-chain using EIP-712 and verified on-chain before any funds move. The intent fixes the merchant, the token, the exact amount, the fee, an expiry, and an optional escrow deadline.
The signature is what makes the amount trustworthy. A buyer cannot pay a different merchant, a different amount, or a different fee than the one that was signed — tampering with any field invalidates the signature and the transaction reverts.
The EIP-712 domain separator includes the chain ID. An intent signed for one chain is therefore invalid on another. This matters precisely because the contract address is identical everywhere: without it, a signature captured on one chain could be replayed on the next.
Each intentId can be paid exactly once. Replaying a successful payment reverts.
05
Gasless payment
Requiring a buyer to hold ETH in order to spend USDC is the single largest source of abandonment in crypto checkout. Payit removes the requirement in two steps.
First, EIP-2612 permit. The buyer signs an approval instead of sending one, so approving and paying happen in a single transaction rather than two. USDC supports permit on Ethereum and Base; USDG supports it on Robinhood Chain, which we verified on-chain rather than assuming.
Second, gas sponsorship. On Base and Robinhood Chain, the transaction fee is paid on the buyer’s behalf. The buyer holds the stablecoin and nothing else.
06
Buyer protection
By default, funds move from buyer to merchant in the same transaction and the contract holds nothing.
Merchants who sell goods that ship can opt into buyer protection. When enabled, the intent carries an escrow deadline and the contract holds the payment until it is released or refunded. Two properties bound that hold:
- It has a fixed end date. The deadline is part of the signed intent, so it is visible to the buyer before paying and cannot be extended afterwards.
- Refunds require a separate authority. The address that can refund escrow is deliberately distinct from the payment signer, so a leaked signing key cannot drain held funds.
07
Confirming a payment
An order is marked paid only after the payment is read back from the chain. Webhooks are treated as a hint about where to look, never as proof — a forged webhook cannot mark an order paid, because the confirmation path does not trust it.
A reconciler scans each chain on a fixed interval and catches payments whose notification never arrived. It also detects the reverse case: if a transaction disappears in a reorg, the order is moved back rather than left incorrectly settled.
Both paths share an idempotency key, so a payment seen twice produces exactly one state change.
08
Custody and trust
Payit does not hold funds by default. It does hold them while an escrow is open. Both statements are true, and the second is the reason we do not describe the system as non-custodial without qualification.
What the contract owner can do:
- Rotate the intent signer
- Change the fee recipient and the refund arbiter
- Allow or disallow a token
- Pause payments
What the contract owner cannot do:
- Move funds that have already settled to a merchant
- Redirect a payment to an address other than the signed merchant
- Alter the amount a buyer pays or a merchant receives
- Take an escrowed payment for itself
Native ETH is deliberately not accepted. The payment function is non-payable, because without a native settlement path any ETH sent in would be stuck permanently in an immutable contract with no withdrawal function.
09
Fees
0.8%
per transaction
$10
minimum order
That is the entire pricing model. No monthly fee, no tiers, no negotiation required to see a number.
The fee is part of the signed intent and is split inside the same transaction, so a merchant can verify what they received against what was signed. The contract rejects any intent whose fee would consume the whole payment: the merchant always receives something.
The $10 minimum exists because below it, network fees take an unreasonable share of the payment.
10
Status and limits
PaymentRouter is deployed on Robinhood Chain. The address is public and can be verified independently:
0x4c816E3276ac5Cd0B46a85C3eCA06fcfc6102189
Base and Ethereum are not deployed yet. Because the address does not depend on the chain ID, they can follow later and land on the same address.
Payit is not taking real payments yet. An independent security audit is the gate we set for ourselves before any real money moves, and we have not cleared it. Contract ownership has also not yet been transferred to a multisig. Until both are done, this system should be treated as unaudited software.
The checkout widget is live at usepay.it.com/checkout, and the browser SDK that embeds it is published as @payit/checkout. Integrating takes two values from your server: an order id and a client token.
Deliberate V1 limitations:
- Stablecoins only — no native ETH, no volatile tokens
- Exact amounts only — no overpayment or partial payment
- Batch payments are capped and must share a single chain
- The contract is immutable; fixes ship as a new deployment