Files
dashcaddy-license-server/IMPLEMENTATION_PLAN.md
2026-04-17 20:19:35 -07:00

233 lines
5.3 KiB
Markdown

# 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.
There is no Premium free trial. Premium is simply locked until a paid subscription activates a license.
### 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, with no trial period:
- `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