diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..5706c51 --- /dev/null +++ b/IMPLEMENTATION_PLAN.md @@ -0,0 +1,231 @@ +# DashCaddy License Server Implementation Plan + +## Goal + +Connect live Stripe billing to DashCaddy Premium licensing without inventing a second license model. + +## Existing DashCaddy integration points + +The main DashCaddy app already has the core license hooks: +- `dashcaddy-api/license-keygen.js` +- `dashcaddy-api/license-manager.js` +- `dashcaddy-api/routes/license.js` + +The app already supports: +- `LICENSE_SERVER_URL` +- online validation at `/api/license/validate` +- online deactivation at `/api/license/deactivate` +- offline HMAC validation fallback +- premium feature checks for: + - `sso` + - `recipes` + - `swarm` + +## Locked product decisions + +### Free tier +Everything works without a license except premium-gated features. + +### Premium tier +Paid subscriptions unlock: +- `sso` +- `recipes` +- `swarm` + +### Stripe plans +- 1 month — $25 +- 3 months — $50 +- 6 months — $65 +- 12 months — $99 + +### Subscription policy +- recurring subscriptions +- 7-day grace period on failed payment +- cancel at period end +- one active machine at a time +- transfer requires deactivation from previous machine +- no public lifetime plan + +## Recommended system shape + +### Website (`dashcaddy.net`) +Responsibilities: +- show Premium pricing cards +- create Stripe Checkout sessions through the license server +- redirect to Stripe Checkout +- return users to success/cancel pages + +### License server (`dashcaddy-license-server`) +Responsibilities: +- create Stripe Checkout sessions +- ingest Stripe webhooks +- maintain customer/license/subscription state +- issue and renew DashCaddy-compatible licenses +- validate license activations +- enforce one-active-machine policy +- accept deactivation requests +- provide admin support tooling later + +### Stripe +Source of truth for: +- customers +- subscriptions +- invoices +- payment state +- cancellation state + +## Proposed API surface + +### Public endpoints for website +- `POST /api/checkout/session` +- `GET /api/public/plans` + +### DashCaddy app endpoints +- `POST /api/license/validate` +- `POST /api/license/deactivate` + +### Stripe webhook endpoint +- `POST /api/stripe/webhook` + +### Future admin endpoints +- `GET /api/admin/licenses/:code` +- `POST /api/admin/licenses/:code/override` +- `POST /api/admin/licenses/:code/reset-activation` + +## Data model + +### customers +- id +- email +- stripe_customer_id +- created_at +- updated_at + +### subscriptions +- id +- customer_id +- stripe_subscription_id +- stripe_price_id +- plan_code +- status +- current_period_start +- current_period_end +- cancel_at_period_end +- grace_until +- created_at +- updated_at + +### licenses +- id +- customer_id +- subscription_id +- license_code +- tier +- features_json +- status +- expires_at +- last_validated_at +- created_at +- updated_at + +### activations +- id +- license_id +- machine_fingerprint +- hostname +- platform +- arch +- cpu +- mac_hash +- activated_at +- deactivated_at +- status + +### stripe_events +- id +- stripe_event_id +- type +- processed_at +- payload_json + +## Stripe plan mapping + +One Premium product, four recurring prices: +- `premium_monthly` +- `premium_3month` +- `premium_6month` +- `premium_12month` + +All plans unlock the same Premium features. Only the billing interval differs. + +## Activation logic + +### Validation request from DashCaddy +Expected inputs: +- license code +- machine fingerprint +- machine metadata + +Validation rules: +1. license exists and is active +2. subscription is active or in grace period +3. license not expired +4. no active activation on a different machine +5. if first activation, bind to this machine +6. if same machine, refresh validation +7. if different machine while old activation still active, reject + +### Deactivation request +1. find active activation for license +2. mark activation deactivated +3. free license for reuse on another machine + +## Renewal logic + +On successful Stripe renewal: +- keep same customer/license relationship +- extend effective entitlement through subscription period +- license remains active continuously + +On payment failure: +- mark subscription past_due-like state +- set `grace_until = current time + 7 days` + +On cancellation: +- keep access until current_period_end +- then expire + +## Website integration plan + +The `dashcaddy.net` marketing site should be updated to: +- add Premium pricing section buttons +- each button POSTs to `/api/checkout/session` with a plan code +- redirect to returned Stripe Checkout URL +- success page explains license delivery/activation flow +- cancellation page returns user to pricing + +## Deployment target + +Deploy `dashcaddy-license-server` on Contabo. + +Initial env expected: +- `STRIPE_SECRET_KEY` +- `STRIPE_WEBHOOK_SECRET` +- `STRIPE_PUBLISHABLE_KEY` +- `APP_BASE_URL` +- `DASHCADDY_WEBSITE_URL` +- `DATABASE_URL` +- `LICENSE_SIGNING_SECRET` or reused DashCaddy-compatible secret path + +## Immediate next build steps + +1. Scaffold Node service for `dashcaddy-license-server` +2. Add Stripe SDK, Express, and SQLite/Postgres adapter layer +3. Build `/api/public/plans` +4. Build `/api/checkout/session` +5. Build `/api/stripe/webhook` +6. Port/reuse DashCaddy-compatible license generation/validation helpers +7. Build `/api/license/validate` +8. Build `/api/license/deactivate` +9. Wire pricing CTA buttons into `dashcaddy.net` +10. Test with Stripe test mode flow before flipping deployed env to live operation