Payments¶
Payments exist so the product can be free for most users and great for the users who pay for the extra surface. Tomoda's monetisation is a subscription billed through Stripe — checkout, billing portal, and webhook-driven state sync, all hosted on Stripe rather than reimplemented in-app.
The mechanics¶
Upgrading is two taps to a Stripe-hosted checkout. Managing the
subscription is one tap to the Stripe-hosted billing portal — we don't
reimplement the cancellation flow. The backend mirrors Stripe's source
of truth on the users row (subscription_status, subscription_plan,
subscription_end, stripe_customer_id) and consumes that mirror
wherever a feature needs to ask "is this user paying?".
What's shipped¶
| Surface | Notes |
|---|---|
POST /api/v1/auth/payment/create-checkout-session |
Mints a Stripe Checkout URL for monthly or yearly. |
POST /api/v1/auth/payment/create-portal-session |
Opens the Stripe-hosted billing portal. |
POST /api/v1/payment/webhook |
Stripe → backend webhook, signature-verified, drives subscription_status / subscription_plan / subscription_end on users. |
users.subscription_status, subscription_plan, subscription_end, stripe_customer_id |
Mirror of Stripe's source of truth. |
backend/scripts/create_stripe_prices.go |
One-shot price seeder per environment. |
See also¶
- Rules + state machine: Domains → Payments
- Backend implementation: Backend → Payments service