Kucatoo Pay — central billing for the Kucatoo product family Help

How Kucatoo Pay works

Kucatoo Pay is the central billing hub for the Kucatoo product family (Coder, Cloud, Stores). Each person has one Stripe customer across all products — one checkout, one customer portal, one bill. Card details are always entered on Stripe's hosted pages, never in this app.

For your customers

Page / actionWhat happens
/ — pricing page Cards per product with plan badges. Customer enters their email and clicks Subscribe → the app POSTs /api/checkout → browser is redirected to Stripe Checkout. After paying, Stripe sends them back to the product site ({product}.kucatoo.ai/billing/return).
Managing a subscription Done in the Stripe Customer Portal (cancel, switch plans, update card, download invoices). Product sites broker portal sessions server-to-server via POST /api/portal with their per-product API key (the endpoint is key-gated, like the entitlement API). There is no account page in this app — identity is email → Stripe Customer.
/legal/terms|privacy|refund Shared legal pages, linked from every product site's footer.

Behind the scenes

Running it locally

start.bat          # Windows — uses .venv if present
./start.sh         # Linux/macOS

Serves on http://127.0.0.1:8081 (override with the PORT env var) and opens the browser. The SQLite database (kucatoo_pay.db) and its tables are created automatically on first start. Tests: pytest tests/ -q — Stripe is mocked, no keys needed.

Configuration (.env)

VariableNotes
STRIPE_SECRET_KEY sk_test_… for dev, sk_live_… for prod. Empty → API returns 503 "Stripe is not configured".
STRIPE_WEBHOOK_SECRET whsec_… from the Stripe dashboard (or from stripe listen locally).
PRODUCT_PRICES JSON map product → plan → Stripe price_… id. Products without a configured price show "Pricing is being finalized" on the landing page.
ENTITLEMENT_API_KEYS Comma-separated product=key pairs; each product site uses its own key (403 on mismatch).
ALLOWED_ORIGINS CORS + allowed return_url origins (open-redirect defense).
DATABASE_URL Default sqlite:///./kucatoo_pay.db; Postgres in production.
ENVIRONMENT development (default) or production. In production the app refuses to boot on SQLite and expects the schema to be pre-applied via python -m app.migrate (the Fly release_command).
STRIPE_API_VERSION Pinned Stripe API version used for every SDK call; bump deliberately.
ALERT_WEBHOOK_URL Optional. When a provision hook fails, a redacted JSON alert (no emails, no payloads) is POSTed here, best-effort. Empty disables alerting.

Testing payments

Gotchas