Payments and credits
Stripe Checkout, the signed webhook, and the credit wallet with its ledger.
Billing is Stripe Checkout, and what the user buys is credits they spend later inside your product. It is the model that fits a young SaaS best: it does not demand a subscription before the customer trusts you.
The path of a purchase
- The user picks a credit pack.
- Your server creates a Stripe Checkout session and sends them there.
- They pay on Stripe. Your application never sees the card, which spares you most of the compliance burden.
- Stripe notifies your webhook.
- The webhook checks the signature, records the payment and adds the credits.
The important part of step 5: credits are granted when the webhook says so, not when the browser returns to the thank-you page. Trust the browser's return and anyone who closes the tab early has paid and got nothing, while anyone who knows what they are doing can claim credits without paying.
The webhook
It lives at /api/webhook. That route and no other.
It does three things before touching anything:
- Verifies the signature with
STRIPE_WEBHOOK_SECRET. A notification without a valid signature is refused. Without this, anyone can grant themselves credits with a single request. - Is idempotent. Stripe retries when it is unsure you answered. If the same event arrives twice, it is recorded once. Without this, a retry doubles credits.
- Leaves a trail. Every event is logged, so you can audit later why an account holds the credits it holds.
The most expensive deployment mistake is pointing the webhook at another route, or moving the webhook to production and leaving the test signing secret. In both cases the customer pays and receives nothing, and you do not find out until they complain.
The wallet
Every user has a credit balance and a ledger: every top-up and every spend is recorded.
Storing only the balance is tempting and it is a mistake. When a customer writes saying "I lost credits", without the ledger you cannot answer them, only believe them or not. With the ledger, you open their history and tell them what happened.
That is why the support inbox shows credits and last purchase next to the ticket: the answer is already in front of you.
Spending credits
The model is generic on purpose: "this action costs N credits". You decide which actions and how much they are worth.
There is also a free trial system with anti-abuse protection, combining a browser fingerprint and the IP, so an anonymous visitor cannot consume endlessly by opening accounts.
What you have to configure
- Create your products and prices in Stripe and wire their IDs into the purchase flow.
- Adjust pack names, prices and benefits in
pricing.json, in both languages. - Decide which actions cost credits, and how many.
- Test with card
4242 4242 4242 4242in test mode, and confirm the credits actually arrive. It is the only way to know the webhook is right.
Tax
Stripe collects payment, but it does not by itself solve VAT or international sales tax. If you sell to consumers across several countries, look at whether a merchant of record who handles that is worth it for you. That is a business decision, not a code one, and it is better taken before the first sale than after.