CronPay turns recurring stablecoin billing into a single React hook. No backend, no cron jobs, no custody — the protocol streams payments on-chain while your app just reads the balance.
Non-custodialBase L2Sepolia testnet
Why CronPay
Every subscription business rebuilds the same plumbing: a billing server, a scheduler, webhooks, retries, PCI paperwork. CronPay deletes all of it. You add one hook; the smart contract does the recurring work.
No server to run
Streams execute on-chain via Base L2 + Chainlink automation. Nothing to deploy or babysit.
One line to revenue
useRainStream() is the whole integration. Ship in an afternoon, not a sprint.
Never hold funds
Money moves wallet-to-wallet. CronPay meters the flow — it never takes custody.
How it works
A payer opens a stream to your merchant address. The protocol releases funds per second according to the rate. Your dApp subscribes to the stream and receives a live balance that ticks up in real time — the same "rain" you see in the dashboard console.
payer → stream (rate/sec) → your wallet → your dApp reads balance
Install
Add the React SDK to your Next.js project.
bash
npm install @cronpay/react
Quickstart
Import the hook and point it at your merchant address. That is the entire server-side of your billing.
TypeScriptapp/pricing/page.tsx
import { useRainStream } from'@cronpay/react';
const { startStream } = useRainStream({
merchant: "0xFcdC...7e92",
ratePerSecond: 0.00005,
token: "USDC",
});
Streams run autonomously — no cron job on your side.
No server required
There is no CronPay backend to configure. The hook talks directly to the contract at 0xFcdC…7e92 on Sepolia. Swap the address for mainnet when you launch.
Live data & callbacks
To reproduce the dashboard's live console in your own dApp, read the reactive balance value or subscribe to per-drip events. Both update every time the protocol releases funds.
TypeScript
const { balance } = useRainStream({
merchant: "0xFcdC...7e92",
token: "USDC",
onDrip: (e) => setTotal(e.total), // fires on every drip
onError: (err) => console.error(err),
});
// `balance` re-renders live — drop it straight into your JSX
return <span>${balance.toFixed(6)}</span>;
onDrip fires on each release; balance re-renders automatically.
What you get back
balance — reactive number, re-renders on every drip.
onDrip(e) — callback per release; e.total, e.amount, e.from.