[grade=B] Make website billing production-ready
This commit is contained in:
@@ -1,54 +1,32 @@
|
||||
# DashCaddy License Server
|
||||
|
||||
Stripe-driven license automation for DashCaddy.
|
||||
Production billing and license fulfillment for purchases made on [dashcaddy.net](https://dashcaddy.net).
|
||||
|
||||
## Purpose
|
||||
Requires **Node.js 22.5 or newer** for the built-in `node:sqlite` transactional store.
|
||||
|
||||
This service is the billing and license orchestration layer for DashCaddy.
|
||||
It receives Stripe webhooks, maps purchases/subscriptions to license entitlements,
|
||||
and exposes license validation/deactivation endpoints for DashCaddy instances.
|
||||
## What it does
|
||||
|
||||
## Planned responsibilities
|
||||
- Creates Stripe Checkout sessions for one-time purchases and auto-renewing subscriptions.
|
||||
- Verifies signed Stripe webhooks with durable event idempotency.
|
||||
- Creates one stable DashCaddy license key per customer and extends that key on later purchases or renewals.
|
||||
- Tracks one-time and subscription paid-through components separately, so subscription failure cannot erase valid one-time access.
|
||||
- Emails the exact key accepted by the validation API and tracks each renewal email per Stripe invoice.
|
||||
- Keeps subscriptions active through their paid period; failed renewals receive a seven-day grace period.
|
||||
- Supports one-machine activation and deactivation.
|
||||
- Migrates the previous JSON store into a transactional SQLite database on first startup.
|
||||
|
||||
- Verify Stripe webhook signatures
|
||||
- Track customers, subscriptions, invoices, and purchases
|
||||
- Generate or extend DashCaddy licenses
|
||||
- Expose `/api/license/validate` for DashCaddy activation
|
||||
- Expose `/api/license/deactivate` for DashCaddy deactivation
|
||||
- Support renewals, expirations, cancellations, and grace periods
|
||||
## Plans
|
||||
|
||||
## Architecture
|
||||
| Plan | One-time / renewal amount | Subscription interval |
|
||||
|---|---:|---:|
|
||||
| `premium_30d` | $20 | 1 month |
|
||||
| `premium_90d` | $50 | 3 months |
|
||||
| `premium_180d` | $70 | 6 months |
|
||||
| `premium_365d` | $99 | 1 year |
|
||||
|
||||
- **Stripe** is billing truth
|
||||
- **License server database** is entitlement truth
|
||||
- **DashCaddy app** remains the consumer of license validation
|
||||
- Existing DashCaddy license logic should be reused, not reinvented
|
||||
Stripe remains the billing source of truth. SQLite is the entitlement and fulfillment source of truth.
|
||||
|
||||
## Next steps
|
||||
|
||||
1. Extract/reuse the current DashCaddy license key generation and verification logic
|
||||
2. Define DB schema for customers, licenses, activations, and Stripe mapping
|
||||
3. Implement webhook ingestion and event processing
|
||||
4. Implement validate/deactivate endpoints
|
||||
5. Add admin tooling for manual recovery and support workflows
|
||||
|
||||
|
||||
## Current implementation status
|
||||
|
||||
Implemented now:
|
||||
- Stripe Checkout session creation
|
||||
- Stripe webhook ingestion scaffold with subscription/license sync
|
||||
- File-backed persistence for customers, subscriptions, and licenses
|
||||
- License validation endpoint
|
||||
- License deactivation endpoint
|
||||
- One-machine-at-a-time activation enforcement
|
||||
|
||||
Still required before production:
|
||||
- durable database
|
||||
- email delivery for license keys
|
||||
- deployment on Contabo
|
||||
- Stripe webhook registration
|
||||
- end-to-end live checkout verification
|
||||
Server-managed keys use DashCaddy's HMAC-compatible code format for initial activation, but renewed expiry is authoritative online because a stable signed code cannot encode changing renewal dates. The DashCaddy client refreshes server-managed entitlements from the license server and does not create a new activation through offline fallback when that server is configured.
|
||||
|
||||
## Environment
|
||||
|
||||
@@ -59,16 +37,62 @@ DASHCADDY_WEBSITE_URL=https://dashcaddy.net
|
||||
STRIPE_SECRET_KEY=sk_live_...
|
||||
STRIPE_PUBLISHABLE_KEY=pk_live_...
|
||||
STRIPE_WEBHOOK_SECRET=whsec_...
|
||||
DASHCADDY_LICENSE_SECRET=64_hex_characters_shared_with_dashcaddy
|
||||
DATA_DIR=./data
|
||||
ADMIN_TOKEN=generate-a-long-random-token
|
||||
|
||||
SMTP_HOST=mail.sami-ahmed.net
|
||||
SMTP_PORT=587
|
||||
SMTP_SECURE=false
|
||||
SMTP_USERNAME=licenses@dashcaddy.net
|
||||
SMTP_PASSWORD=...
|
||||
SMTP_FROM=licenses@dashcaddy.net
|
||||
# Keep certificate verification enabled in production.
|
||||
SMTP_TLS_REJECT_UNAUTHORIZED=true
|
||||
```
|
||||
|
||||
## HTTP endpoints
|
||||
## Public HTTP endpoints
|
||||
|
||||
- `GET /health`
|
||||
- `GET /api/public/config`
|
||||
- `GET /api/public/plans`
|
||||
- `POST /api/checkout/session`
|
||||
- `POST /api/checkout/one-time`
|
||||
- `POST /api/checkout/subscription`
|
||||
- `GET /api/checkout/session/:sessionId`
|
||||
- `POST /api/stripe/webhook`
|
||||
- `POST /api/license/validate`
|
||||
- `POST /api/license/deactivate`
|
||||
- `GET /api/admin/debug/store`
|
||||
|
||||
`GET /api/admin/debug/store` requires `Authorization: Bearer $ADMIN_TOKEN` and is hidden with a 404 when no admin token is configured.
|
||||
|
||||
## Checkout request
|
||||
|
||||
```json
|
||||
{
|
||||
"planCode": "premium_30d",
|
||||
"customerEmail": "buyer@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
A successful checkout request returns a Stripe-hosted `url` and a `sessionId`. The website redirects to Stripe. After payment, Stripe calls the webhook, the license is created or extended, SMTP delivery is recorded, and the website polls `/api/checkout/session/:sessionId` to display the same key sent by email.
|
||||
|
||||
## Safety properties
|
||||
|
||||
- Checkout accepts only valid plan codes and email addresses.
|
||||
- Browser CORS is restricted to `dashcaddy.net` and `www.dashcaddy.net`.
|
||||
- Checkout creation is rate limited.
|
||||
- Webhook event IDs and payment intent IDs are idempotent.
|
||||
- Store changes use SQLite transactions and WAL durability.
|
||||
- Repeated checkout sessions are stored independently.
|
||||
- Admin customer/license data is not public.
|
||||
- SMTP certificate verification is enabled by default.
|
||||
|
||||
An email marked `delivered` means the configured SMTP provider accepted it; final inbox placement remains the receiving mail system's responsibility. The success page also displays the same valid key, so fulfillment does not depend on inbox delivery.
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
The tests cover CORS, invalid checkout input, admin isolation, exact-key lookup and validation, payment idempotency, rate limiting, durable webhook claims, repeat checkout lookup, cancellation, and payment-grace expiry.
|
||||
|
||||
Reference in New Issue
Block a user