Skip to main content
Handbook/Harden/Page 92 · Accounts

Data, Users, and Payments

Share

Share this page

Pass it to someone who needs it.

Star on GitHub

Key takeaway: Add real accounts, data, and payments

Your demo runs fine on your laptop. A real app is different: it holds what people cannot afford to lose, their saved work, who they are, and their money. So it has to remember what they made after a restart, know them when they return, and charge them without ever mishandling a card. Two of those three you must never build yourself, and this chapter gets you all three plus the rules that keep them from ending your app.

10.3.1Store data that survives

A demo keeps its data in memory, so closing it erases everything. A real app must persist what people create: write it to a database that survives a restart, a deploy, and a crash. The chapter on modeling your data covers the shape that data takes.

Treat what you store as the most valuable thing you hold, because it is the one part you cannot regenerate. Keep a backup, a copy you can restore from, and never run a change that could erase it.

10.3.2Accounts: never roll your own auth

The moment people log in, you are doing authentication: proving each person is who they claim to be. It looks simple and is not. Password hashing, session tokens, password resets, two-factor, breach checks: each is a place to get security subtly wrong, and one mistake exposes every account at once.

So do not build it. Use an authentication provider, Auth0, Clerk, Supabase Auth, or your framework's own vetted library, whose whole job is getting this right and patching it as attacks change. You wire your app to it and never store a password yourself.

10.3.3Payments: never touch card numbers

Payments have a stricter rule: a raw card number must never reach your server, and you are not allowed to store one. Handling cards safely is governed by a standard called PCI, and meeting it yourself is a compliance project, not a feature.

So you never touch the card. A payments provider like Stripe gives you a form that sends the card straight from the user's browser to them. It hands back a token, a harmless reference you keep in place of the card. You charge the token; the provider carries the risk.

The card goes straight from the browser to Stripe; your server only ever holds the token.
DON'T let these reach your server a card number, its CVC or expiry a password you hashed yourself login tokens you invented DO store only safe references the auth provider's id for each user Stripe's token in place of the card

10.3.4Decide how you charge before you wire it

The safety rule is settled. The shape of the charge is still yours to pick, and each of these is different code:

  • One-time or subscription. A single charge and a recurring plan are separate objects at the provider, so decide before your agent writes either.
  • Hosted or embedded. The provider's own hosted checkout page handles far more edge cases than a form you embed. Take it unless you have a reason not to.
  • Direct or merchant of record. Paddle and Lemon Squeezy sell to your customer on your behalf and carry the worldwide sales-tax and VAT registration you would otherwise owe personally. Selling globally as one person, that is usually worth the bigger cut.
  • Whole cents, never a float. Store money as an integer count of the smallest unit. Fractions do not divide cleanly in binary, and a total built from decimals drifts by a cent.

Refunds and disputes are both certain, so wire those paths now rather than on the day the first one lands.

10.3.5Protect what users trust you with

The pattern is the same for all three: let specialists hold the dangerous parts. The database keeps the data, the auth provider holds identities, the payments provider holds cards, and your app stores only references to them. Give each the least access it needs, put anything sensitive behind encryption so it is unreadable without the key, and back up what you cannot regenerate.

This prompt hands your case to your agent:

Ready prompt
Act as a senior engineer wiring up what a real app needs beyond a demo: persisted data, user accounts, and payments. Read my data model, my privacy and cost targets, and my decision log first, so this fits limits I already set. Enforce two hard rules. Never roll our own authentication: choose a provider (Auth0, Clerk, Supabase Auth, or the framework's vetted library) and wire to it, so we never store a password. Never let a raw card number reach our server: use a payments provider (Stripe) so the card goes browser to provider and we store only its token. Put each provider behind its own adapter, like my other vendors. For each of the three, tell me what we store, what the specialist holds, and the least access it needs. Then list the backup and encryption steps for the data we cannot regenerate. For payments also decide: one-time or subscription, hosted checkout or embedded form, and direct or a merchant of record that carries global sales tax for me. Store money as integer minor units, and wire the refund and dispute paths from the start. Record each provider choice as one line in my decision log. If you need the full reasoning behind this step, read https://zalt.me/guides/vibe-coding/harden/data-accounts-and-payments My app: what it stores, who logs in, what they pay for:

Do this now: paste the prompt with your app's specifics and let your agent wire each of the three to a specialist. Then confirm your server holds references, never a password or a card.

Mahmoud Zalt

Mahmoud Zalt

Software engineer, 16+ yrs · built Sistava.com in 3 months, idea to production, using these methods

Resources
Star on GitHubContribute
Donate

Support my work

A small tip keeps the free work coming.

© 2026 Mahmoud Zalt. Free to read, not to republish.
Copyright & license