Recurring payments
The customer signs an authorization once. Your system decides when a renewal is due and asks P2Flux to execute it; the contract enforces one charge per period.
P2Flux has no scheduler and no database. It does not know when a renewal is due, who the customer is, or what happens after a failure. It executes one charge when you ask, and the contract enforces one charge per period.
How it works
The customer signs an EIP-712 authorization once, with an ordinary EVM wallet, naming exactly what may be taken: payer, recipient, token, amount, period, start, end and a salt. The contract enforces those terms. The amount cannot be raised, the period cannot be shortened, and the recipient cannot be changed after signing. A charge outside the signed window, or a second charge inside one period, reverts.
The customer also grants an ordinary ERC-20 allowance to the recurring contract — that is the switch they can flip to stop everything, from their own wallet, without asking anyone.
1. Create the terms
Technical terms in, setup token out. No customer or product fields exist here.
The response returns the setup_token, its expires_at (24 hours by default), the chain_id, the recurring contract, the formatted amount, and a salt.
Store the salt with the pending order. It is what distinguishes two setups whose price and period are identical — so when a capability comes back, you can prove it belongs to this order and not to someone else’s cheaper plan.
2. Customer authorizes
Send the customer to <checkout>/#/subscribe/<setup_token>. The checkout resolves the token server-side, shows the terms, takes the token approval if one is needed, and collects the EIP-712 signature.
Those are two different things. The approval is an on-chain transaction from the customer’s wallet, so it costs network gas in ETH; it is the only point in the subscription where they need native currency. The signature is not a transaction — it is signed in the wallet, costs nothing and touches no chain.
The signature is exchanged for a capability — the p2s2 token — which is what your system stores and charges against later. The checkout posts it to the opening page as p2flux.subscription.created with the capability in subscription.
Call status and compare the echoed terms — payer, recipient, token, amount, period, start, end, salt — against what you actually sold. A capability can be cryptographically valid and still be the wrong one.
If you build your own page instead of using the hosted checkout, the same two calls are public: /v1/subscriptions/resolve returns the terms and the EIP-712 scaffold, and /v1/subscriptions/finalize exchanges setup_token, payer and signature for the capability.
3. Charge each period
Attempt this period’s payment. Safe to retry.
Call it from your existing renewal job. There is no amount and no recipient in the request — both come from the permission the customer signed.
P2Flux submits the transaction and pays its ETH gas up front. That cost is converted to USDC and added on top of the amount, so the contract debits amount + gas reimbursement — the customer reimburses it in the stablecoin they already hold and needs no ETH at renewal time. Two ceilings apply and the lower wins: what the customer signed for, and a hard 0.05 USDC cap in the contract.
The two P2Flux fees are separate money and come out of the amount instead, so the merchant funds them. Full arithmetic in Fees & gas.
Charge results
A charge answers with a status and an action. The action is what your system should do about it, so you never have to hard-code the code table yourself.
The contract allows one charge per billing period, so repeating a call after a timeout or a crash returns ALREADY_CHARGED rather than charging twice. There is no idempotency key to manage — the period is the key.
The retry schedule is yours. P2Flux reports the technical result; how long you wait, and whether you dun the customer, is business policy.
Reconcile with status
Current state, read straight from the chain.
No stored state is consulted, because there is none. Use it to reconcile after downtime and to check the signed terms before activating anything.
Cancel and revoke
Cancellation belongs to the customer. P2Flux cannot revoke a wallet’s authority and does not pretend to — the API returns unsigned calldata, and the customer’s own wallet sends it.
The cancel token exists so the capability never has to reach a browser: it carries the fields needed to build revoke() and not the customer’s signature, so it cannot be charged with. Open <checkout>/#/cancel/<cancel_token> to give customers self-service cancellation.
Failed renewals
Nothing retries on its own. A failed charge spent no money and changed nothing on chain, so the correct response is entirely yours to choose — the action field tells you which kind of failure it was.
Refunds
Not currently available. Renewals settle directly to your wallet, so returning value is an ordinary transfer from the wallet that received it, made by you.