2026-11-16
Crypto Payment Gateway for SaaS: USDC Plans and Renewals

The request tends to sound small: let us pay the $99 plan in stablecoins. The work behind it is mostly about the parts around the payment, because a SaaS bill isn't a one-off purchase. It renews and changes plan, and it switches access on and off.
One charge per invoice
Start from your billing system, not the gateway's. You already have invoices, or at least a record of "customer X owes $99 for November". Each of those becomes one charge:
const charge = await klap.charges.create({amount: 99.0,expiresIn: 3600,acceptedPayments: [{ token: 'USDC', network: 'base' },{ token: 'USDC', network: 'polygon' },{ token: 'USDT', network: 'polygon' },],externalRef: invoice.id,idempotencyKey: `invoice_${invoice.id}_attempt_${invoice.attempt}`,})
externalRef puts your invoice ID on every event, so you never match a payment by amount and timestamp. idempotencyKey makes a retried request return the same charge instead of creating a second one, which matters the first time your job runner retries after a timeout. The charge lives for at most an hour. If the customer comes back tomorrow, you create a new one for the same invoice.
The customer pays a little more than 99, because the network gas for paying you out is added on top. Does USDC have gas fees? explains where that number comes from and what to show on the checkout.
Who draws the payment screen
There are three options. You can send the customer to a hosted page, build the screen yourself on the API, or use components in between. For SaaS the deciding factor is usually where billing already lives in your app. If you have a billing settings page with a "Pay invoice" button, rendering the charge there with @klappay/checkout-kit keeps the customer inside your product. If billing happens over email, a payment link per invoice is less work. Hosted checkout vs payment API compares them in more detail.
Access follows the webhook, never the browser
The page that showed the QR code doesn't get to decide the customer paid. Your backend does, from a signed charge.confirmed event:
if (event.event === 'charge.confirmed') {await markInvoicePaid(event.data.externalRef)await extendSubscription(event.data.externalRef)}
Webhooks arrive at least once, so extendSubscription has to be safe to run twice. A handler that extends the plan by a month on every delivery gives away a free month each time a retry lands. Idempotent payment webhook handlers shows how to put the guard in the database.
Keep charge.confirmed and charge.settled apart, too. Confirmed means the customer's transfer landed. Settled means the payout reached your wallet a little later. For unlocking a workspace, confirmed is the right moment. How crypto payment webhooks work has the full event list.
Customers also send the wrong amount. A charge that expires short of the total ends as underpaid, and one that receives too much fires charge.overpaid. Decide before launch whether $98.50 on a $99 invoice unlocks the plan. Underpayments and overpayments covers the options.
Renewals: a wallet isn't a card on file
This is where crypto billing is most different from cards. When a card is on file, you charge it on the renewal date and the customer finds out from their statement. A self-custody wallet can't be charged. The customer has to send the money each time, or sign a token allowance that lets a contract pull it, and an open-ended allowance is a standing withdrawal permission most customers shouldn't grant.
So the practical renewal flow for most SaaS products is a reminder with a fresh charge:
- A few days before the renewal date, create the invoice and send an email with a link to pay it.
- Keep the plan active through a short grace period after the date.
- If the charge expires unpaid, send one more reminder before downgrading the account.
On the API, that schedule is yours: the invoice job, the emails and the grace period, built on top of charges. Designing a USDC recurring billing API covers the state machine and the allowance-based alternative, if your customers use smart wallets that support scoped permissions.
If you'd rather not build it, Klap Link has recurring links that run this exact flow. Three days before each renewal it emails the subscriber an invoice to pay from their wallet. An unpaid invoice marks the subscription past due on its due date and cancels it seven days later. Nothing is ever pulled from the wallet, and the payer approves every payment.
Upgrades and proration stay in your billing code too. Work out what the customer owes for the rest of the period, create a charge for that, and switch the plan on charge.confirmed. The gateway only needs to know the amount.
Who holds the money
A custodial gateway takes the customer's payment into its own account and owes you a balance. That's convenient until a payout is delayed or an account is reviewed, and for a subscription business it means a month of revenue can sit in someone else's ledger.
With direct on-chain settlement, each charge pays out to your wallet from its own contract, which can only pay the recipients it was created with. Klap detects the payment and reports it. It never holds a balance for you. The cost of that is that your wallet's keys are your problem, and self-custody crypto payments covers what that means in practice.
If you resell through agencies or pay a referral partner, the charge can also split between up to five wallets, so the partner's share never passes through your account.
What a stablecoin gateway won't do
It's worth being clear about the gaps before you commit, because some of them are deal breakers for some SaaS businesses.
- No fiat. Klap has no bank payouts. If your accounting needs dollars in a bank account, you'll pair it with an off-ramp or an exchange account.
- No automatic debits. As above, a wallet isn't a card on file, so every renewal is a payment the customer approves.
- No tax or invoicing engine. Your invoice PDF, VAT handling and receipts come from your own billing stack.
- No customer screening. KYC and sanctions checks on your customers stay with you, as they do today.
- No per-request billing. A live charge has a $0.10 minimum and its own payout, so API calls priced in fractions of a cent don't fit. How AI agents pay with stablecoins covers the protocols built for that.
If several of those matter to you, a custodial option like Stripe's stablecoin payments may fit better, and Stripe USDC vs a non-custodial gateway compares the two directly.
Klap charges 0.5% per settled payment, with no monthly or setup fee, and you choose whether you or the customer pays it. You can try every one of the states above, including underpaid, overpaid and expired charges, in the sandbox without real funds, and how to test crypto payments shows how.