Add billing + Stripe + portal handover doc (developer mode chat capture)
This commit is contained in:
parent
7d0355bd88
commit
49cea9a7a9
196
SCRATCH/billing-stripe-handover-apr11-2026.md
Normal file
196
SCRATCH/billing-stripe-handover-apr11-2026.md
Normal file
@ -0,0 +1,196 @@
|
||||
# Billing + Stripe Handover (Apr 11, 2026)
|
||||
|
||||
## Context
|
||||
This document captures the current billing integration state between:
|
||||
- zpack-api
|
||||
- zpack-portal
|
||||
- Stripe (sandbox/test mode)
|
||||
|
||||
This was developed during a developer-mode session where chat history is not persisted, so this doc acts as the continuity handoff.
|
||||
|
||||
---
|
||||
|
||||
## Current Status
|
||||
|
||||
### Stripe
|
||||
- Stripe sandbox/test mode configured
|
||||
- Products + recurring prices created
|
||||
- Checkout sessions successfully redirect to Stripe
|
||||
- Test checkout completed using Stripe test card
|
||||
- Stripe dashboard confirms:
|
||||
- checkout.session.completed
|
||||
- customer created
|
||||
- subscription created
|
||||
- invoice/payment succeeded (test)
|
||||
|
||||
### API (zpack-api)
|
||||
Working:
|
||||
- `ensureStripeCustomer()` persists `stripeCustomerId`
|
||||
- `/api/billing/checkout` successfully creates Stripe Checkout session
|
||||
- Billing foundation fields exist in Prisma:
|
||||
- `stripeCustomerId`
|
||||
- `subscriptionStatus`
|
||||
- `plan`
|
||||
|
||||
Not yet complete:
|
||||
- Webhook delivery not working (API not publicly reachable)
|
||||
- As a result:
|
||||
- `subscriptionStatus` is NOT being updated
|
||||
- `plan` is NOT being updated
|
||||
|
||||
Root cause:
|
||||
- Stripe cannot reach `/api/billing/webhook`
|
||||
- This is NOT a checkout issue
|
||||
- This is a webhook reachability issue
|
||||
|
||||
### Portal (zpack-portal)
|
||||
Working:
|
||||
- Billing page renders correctly for unsubscribed users
|
||||
- Plan selection UI present
|
||||
- Checkout button calls API
|
||||
- Redirect to Stripe works
|
||||
|
||||
Partially complete:
|
||||
- Needs final alignment for:
|
||||
- `trialing` state
|
||||
- `active` state
|
||||
- billing-exempt/admin display
|
||||
|
||||
---
|
||||
|
||||
## Key Architectural Decisions
|
||||
|
||||
### Billing Model
|
||||
- Users can register without paying
|
||||
- Subscription is optional until resource enforcement is implemented
|
||||
- Future plan: soft gate (likely 1 free server, then require subscription)
|
||||
|
||||
### Stripe Integration Strategy
|
||||
- Use Stripe Checkout (NOT custom payment form)
|
||||
- Use Stripe Billing Portal for management
|
||||
- Use webhooks to update DB state
|
||||
- DB remains source of truth for access decisions
|
||||
|
||||
### Metadata Usage
|
||||
Stripe objects should include:
|
||||
- `metadata.userId`
|
||||
- optionally `metadata.plan`
|
||||
|
||||
This is used for safer webhook reconciliation.
|
||||
|
||||
---
|
||||
|
||||
## Current Blocker
|
||||
|
||||
### Webhook Delivery
|
||||
|
||||
Problem:
|
||||
- API is not publicly reachable
|
||||
- Stripe cannot POST webhook events
|
||||
|
||||
Observed behavior:
|
||||
- `stripeCustomerId` set (checkout path)
|
||||
- `subscriptionStatus` remains null
|
||||
- `plan` remains null
|
||||
|
||||
---
|
||||
|
||||
## Immediate Next Steps
|
||||
|
||||
### Option A (Dev Testing - Recommended)
|
||||
Use Stripe CLI forwarding:
|
||||
|
||||
```bash
|
||||
stripe listen --forward-to http://10.60.0.18:4000/api/billing/webhook
|
||||
```
|
||||
|
||||
Then:
|
||||
- copy CLI-provided `whsec_...`
|
||||
- set as `STRIPE_WEBHOOK_SECRET`
|
||||
- restart API
|
||||
- re-run test checkout
|
||||
|
||||
Expected result:
|
||||
- webhook hits API
|
||||
- DB updates correctly
|
||||
|
||||
---
|
||||
|
||||
### Option B (Staging/Prod Path)
|
||||
|
||||
Expose only webhook endpoint via public domain:
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
https://billing.zerolaghub.com/api/billing/webhook
|
||||
```
|
||||
|
||||
Requirements:
|
||||
- real reverse proxy or tunnel (NOT DNS redirect)
|
||||
- HTTPS
|
||||
- Stripe signature verification enabled
|
||||
|
||||
---
|
||||
|
||||
## Important Notes
|
||||
|
||||
### Namecheap Redirect
|
||||
- simple DNS/URL redirect is NOT sufficient
|
||||
- Stripe requires direct POST endpoint
|
||||
- must use proxy/tunnel instead
|
||||
|
||||
### Stripe CLI
|
||||
- does NOT need to run on API VM
|
||||
- but easiest if it does
|
||||
- must be able to reach internal API IP
|
||||
|
||||
### Test Cards
|
||||
Use Stripe test card:
|
||||
|
||||
```
|
||||
4242 4242 4242 4242
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Known Good
|
||||
|
||||
- Checkout flow: WORKING
|
||||
- Customer creation: WORKING
|
||||
- Portal integration: WORKING enough for testing
|
||||
|
||||
## Known Broken
|
||||
|
||||
- Webhook delivery: NOT working
|
||||
- Subscription state persistence: NOT working
|
||||
|
||||
---
|
||||
|
||||
## Next Session Starting Point
|
||||
|
||||
1. Get Stripe CLI installed and running
|
||||
2. Verify webhook hits API
|
||||
3. Confirm DB updates:
|
||||
- `subscriptionStatus`
|
||||
- `plan`
|
||||
4. Refresh portal billing page
|
||||
5. Verify UI reflects new state
|
||||
|
||||
---
|
||||
|
||||
## Future Work (Post-Webhook)
|
||||
|
||||
- Implement trial handling (`trialing` state)
|
||||
- Add quota enforcement (soft gate)
|
||||
- Improve billing UI states
|
||||
- Add billing portal button behavior
|
||||
- Consider exposing webhook endpoint cleanly for production
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The system is effectively complete except for webhook reachability.
|
||||
|
||||
Once webhooks are flowing, billing becomes fully functional end-to-end.
|
||||
Loading…
Reference in New Issue
Block a user