Build a subscription
Recurring revenue, on stablecoins, with one customer signature.
Strimz subscriptions are pull-based. Your customer signs one transaction that lets the contract charge them a fixed amount on a fixed schedule. After that, the Strimz scheduler does the work every period. There are no re-prompts to deal with, no card-on-file expiry, and no "your bank declined the renewal" emails.
Define a plan
Plans are templates. They don't represent a single customer; they represent a recurring price the customer can subscribe to.
Customer subscribes
Send the customer to plan.checkoutUrl. They connect a wallet, see
"Subscribe, 20 USDC/month", and sign one message.
The message is an EIP-2612 Permit, a structured authorisation USDC
supports natively that grants the StrimzSubscriptions contract a
recurring allowance. Strimz's relayer takes that signed permit and
calls permitAndCreateSubscription in a single atomic transaction.
The permit is consumed and the subscription is created in the same
block. There is no approve-then-subscribe round trip.
The full security model lives on the meta-tx flow page: what the customer signs, why the relayer cannot move funds on its own, when the signature expires.
You receive subscription.created immediately and another webhook
each time a period charges (subscription.charged).
What happens at charge time
The Strimz scheduler runs a sweep every 15 minutes (configurable). It finds subscriptions whose nextChargeAt is in the past, atomically locks them via chargeLock, and enqueues a job per locked sub onto strimz.subscription.due.
The worker calls batchCharge(subscriptionIds, chargeAttemptIds) on the contract. Each entry has a chargeAttemptId = keccak256(subscriptionId, periodEndAt) so retries can't double-charge. The contract pulls USDC from the customer's wallet and emits SubscriptionCharged (or SubscriptionChargeSkipped with the reason). The indexer projects both into your DB.
Handle a failed charge
A failed charge moves the subscription to at_risk and fires subscription.charge_failed with the on-chain outcome:
outcome | What it means |
|---|---|
insufficient_funds | Customer's USDC balance is below the charge amount |
revoked_approval | Customer revoked your contract's allowance |
cancelled | Customer cancelled on-chain between your charge attempts (rare race) |
skipped | Period not yet due (defensive; should never reach you) |
If you've enabled the agent's recovery capability, the customer gets an email. After your configured grace window (24h / 48h / 72h), the sub flips to lapsed.
Cancel a subscription
There are two paths to cancellation.
Merchant-initiated. Say the customer's account was deleted on your side:
This marks the DB optimistically and enqueues an on-chain cancel.
The subscription.cancelled webhook fires once the on-chain tx
confirms.
Customer-initiated. The customer calls the contract directly
from their wallet. The indexer picks up SubscriptionCancelled and
flips the row.
Either way, the customer's wallet stops getting charged. Their
existing token allowance still exists. To revoke the allowance
fully, the customer calls approve(spender, 0) on the USDC
contract. Strimz does not auto-revoke because future plans might
use the same spender.
Trial periods
trialPeriodDays: 14 on the plan means the first charge is now + 14 days instead of now. The customer signs immediately so you know they've committed. Strimz holds off on taking their money for two weeks. Standard trial behaviour.
Pricing math
Strimz charges a per-transaction fee on every charge based on your tier. For a Starter (0.5%) merchant subscribing a customer to a 20 USDC monthly plan:
- Customer pays: 20.00 USDC
- Strimz fee: 0.10 USDC (0.5%)
- Merchant receives: 19.90 USDC
The fee is taken on-chain, in the same transaction, by StrimzPayments. There's no separate invoicing. Your dashboard's MRR view shows both gross and net.
