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