docs: update subscription feature documentation - M7 (refs #55)
Some checks failed
Deploy to Staging / Build Images (pull_request) Successful in 6m58s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 28s
Deploy to Staging / Verify Staging (pull_request) Failing after 17s
Deploy to Staging / Notify Staging Ready (pull_request) Has been skipped
Deploy to Staging / Notify Staging Failure (pull_request) Successful in 6s
Some checks failed
Deploy to Staging / Build Images (pull_request) Successful in 6m58s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 28s
Deploy to Staging / Verify Staging (pull_request) Failing after 17s
Deploy to Staging / Notify Staging Ready (pull_request) Has been skipped
Deploy to Staging / Notify Staging Failure (pull_request) Successful in 6s
This commit is contained in:
55
backend/src/features/subscriptions/CLAUDE.md
Normal file
55
backend/src/features/subscriptions/CLAUDE.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# backend/src/features/subscriptions/
|
||||
|
||||
Stripe payment integration for subscription tiers and donations.
|
||||
|
||||
## Files
|
||||
|
||||
| File | What | When to read |
|
||||
| ---- | ---- | ------------ |
|
||||
| `README.md` | Feature overview with architecture diagram | Understanding subscription flow |
|
||||
|
||||
## Subdirectories
|
||||
|
||||
| Directory | What | When to read |
|
||||
| --------- | ---- | ------------ |
|
||||
| `api/` | HTTP controllers and routes | API endpoint changes |
|
||||
| `domain/` | Services and type definitions | Business logic changes |
|
||||
| `data/` | Repository for database operations | Database queries |
|
||||
| `external/stripe/` | Stripe API client wrapper | Stripe integration |
|
||||
| `migrations/` | Database schema | Schema changes |
|
||||
| `jobs/` | Scheduled background jobs | Grace period processing |
|
||||
|
||||
## Key Patterns
|
||||
|
||||
- Repository mapRow() converts snake_case to camelCase
|
||||
- Webhook idempotency via stripe_event_id unique constraint
|
||||
- Tier sync to user_profiles.subscription_tier on changes
|
||||
- Grace period: 30 days after payment failure
|
||||
- Vehicle selections for tier downgrades (not deleted, just gated)
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Subscriptions (Authenticated)
|
||||
- GET /api/subscriptions - Current subscription
|
||||
- POST /api/subscriptions/checkout - Create subscription
|
||||
- POST /api/subscriptions/cancel - Schedule cancellation
|
||||
- POST /api/subscriptions/reactivate - Cancel pending cancellation
|
||||
- POST /api/subscriptions/downgrade - Downgrade with vehicle selection
|
||||
- PUT /api/subscriptions/payment-method - Update payment
|
||||
- GET /api/subscriptions/invoices - Billing history
|
||||
|
||||
### Donations (Authenticated)
|
||||
- POST /api/donations - Create payment intent
|
||||
- GET /api/donations - Donation history
|
||||
|
||||
### Webhooks (Public)
|
||||
- POST /api/webhooks/stripe - Stripe webhook (signature verified)
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- STRIPE_SECRET_KEY
|
||||
- STRIPE_WEBHOOK_SECRET
|
||||
- STRIPE_PRO_MONTHLY_PRICE_ID
|
||||
- STRIPE_PRO_YEARLY_PRICE_ID
|
||||
- STRIPE_ENTERPRISE_MONTHLY_PRICE_ID
|
||||
- STRIPE_ENTERPRISE_YEARLY_PRICE_ID
|
||||
@@ -2,104 +2,181 @@
|
||||
|
||||
Stripe integration for subscription management, donations, and tier-based vehicle limits.
|
||||
|
||||
## Milestone 1: Core Infrastructure (COMPLETE)
|
||||
## Architecture
|
||||
|
||||
### Database Schema
|
||||
- `subscriptions` - User subscription records with Stripe integration
|
||||
- `subscription_events` - Webhook event logging for idempotency
|
||||
- `donations` - One-time payment tracking
|
||||
- `tier_vehicle_selections` - User vehicle selections during tier downgrades
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Frontend (React) │
|
||||
│ SubscriptionPage / SubscriptionMobileScreen │
|
||||
│ - TierCard, PaymentMethodForm, BillingHistory │
|
||||
│ - VehicleSelectionDialog, DowngradeFlow, DonationSection │
|
||||
└──────────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────────▼──────────────────────────────────────┐
|
||||
│ Backend API │
|
||||
│ /api/subscriptions/* - Subscription management │
|
||||
│ /api/donations/* - One-time donations │
|
||||
│ /api/webhooks/stripe - Stripe webhook (public) │
|
||||
└──────────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────┼─────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────────────┐ ┌────────────┐ ┌─────────────────┐
|
||||
│ Subscriptions │ │ Stripe │ │ User Profile │
|
||||
│ Service │ │ Client │ │ Repository │
|
||||
│ │ │ │ │ (tier sync) │
|
||||
└────────┬────────┘ └─────┬──────┘ └─────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────┐ ┌────────────┐
|
||||
│ Subscriptions │ │ Stripe │
|
||||
│ Repository │ │ API │
|
||||
└────────┬────────┘ └────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ PostgreSQL │
|
||||
│ subscriptions, subscription_events, donations, │
|
||||
│ tier_vehicle_selections │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Type Definitions
|
||||
- **Domain Types** (`domain/subscriptions.types.ts`): Core business entities and request/response types
|
||||
- **Stripe Types** (`external/stripe/stripe.types.ts`): Simplified Stripe API response types
|
||||
## Database Schema
|
||||
|
||||
### Data Access
|
||||
- **Repository** (`data/subscriptions.repository.ts`): Database operations with proper snake_case to camelCase mapping
|
||||
- Subscription CRUD operations
|
||||
- Event logging with idempotency checks
|
||||
- Donation tracking
|
||||
- Vehicle selection management
|
||||
### subscriptions
|
||||
Main subscription data linked to user profile.
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| id | UUID | Primary key |
|
||||
| user_id | VARCHAR(255) | FK to user_profiles.auth0_sub |
|
||||
| stripe_customer_id | VARCHAR(255) | Stripe customer ID |
|
||||
| stripe_subscription_id | VARCHAR(255) | Stripe subscription ID (nullable for free) |
|
||||
| tier | subscription_tier | free, pro, enterprise |
|
||||
| billing_cycle | billing_cycle | monthly, yearly |
|
||||
| status | subscription_status | active, past_due, canceled, unpaid |
|
||||
| current_period_start | TIMESTAMP | Billing period start |
|
||||
| current_period_end | TIMESTAMP | Billing period end |
|
||||
| grace_period_end | TIMESTAMP | Grace period expiry (30 days after payment failure) |
|
||||
| cancel_at_period_end | BOOLEAN | Pending cancellation flag |
|
||||
|
||||
### External Integration
|
||||
- **Stripe Client** (`external/stripe/stripe.client.ts`): Stripe API wrapper with error handling
|
||||
- Customer creation
|
||||
- Subscription lifecycle management
|
||||
- Payment intent creation for donations
|
||||
- Webhook event verification
|
||||
- Payment method updates
|
||||
### subscription_events
|
||||
Webhook event log for idempotency.
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| id | UUID | Primary key |
|
||||
| subscription_id | UUID | FK to subscriptions |
|
||||
| stripe_event_id | VARCHAR(255) | UNIQUE, prevents duplicate processing |
|
||||
| event_type | VARCHAR(100) | Stripe event type |
|
||||
| payload | JSONB | Full event payload |
|
||||
|
||||
## Environment Variables
|
||||
### donations
|
||||
One-time payment records.
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| id | UUID | Primary key |
|
||||
| user_id | VARCHAR(255) | FK to user_profiles.auth0_sub |
|
||||
| stripe_payment_intent_id | VARCHAR(255) | UNIQUE |
|
||||
| amount_cents | INTEGER | Amount in cents |
|
||||
| currency | VARCHAR(3) | Currency code (default: usd) |
|
||||
| status | donation_status | pending, succeeded, failed, canceled |
|
||||
|
||||
Required for Stripe integration:
|
||||
- `STRIPE_SECRET_KEY` - Stripe API secret key
|
||||
- `STRIPE_WEBHOOK_SECRET` - Stripe webhook signing secret
|
||||
### tier_vehicle_selections
|
||||
Tracks which vehicles user selected to keep during tier downgrade.
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| id | UUID | Primary key |
|
||||
| user_id | VARCHAR(255) | FK to user_profiles.auth0_sub |
|
||||
| vehicle_id | UUID | FK to vehicles |
|
||||
| selected_at | TIMESTAMP | Selection timestamp |
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Subscription Management (Authenticated)
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | /api/subscriptions | Get current subscription |
|
||||
| POST | /api/subscriptions/checkout | Create Stripe subscription |
|
||||
| POST | /api/subscriptions/cancel | Schedule cancellation at period end |
|
||||
| POST | /api/subscriptions/reactivate | Cancel pending cancellation |
|
||||
| POST | /api/subscriptions/downgrade | Downgrade with vehicle selection |
|
||||
| PUT | /api/subscriptions/payment-method | Update payment method |
|
||||
| GET | /api/subscriptions/invoices | Get billing history |
|
||||
|
||||
### Donations (Authenticated)
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| POST | /api/donations | Create donation payment intent |
|
||||
| GET | /api/donations | Get donation history |
|
||||
|
||||
### Webhooks (Public)
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| POST | /api/webhooks/stripe | Stripe webhook (signature verified) |
|
||||
|
||||
## Webhook Events Handled
|
||||
|
||||
| Event | Action |
|
||||
|-------|--------|
|
||||
| customer.subscription.created | Update subscription with Stripe subscription ID |
|
||||
| customer.subscription.updated | Sync status, tier, period dates |
|
||||
| customer.subscription.deleted | Mark canceled, downgrade to free tier |
|
||||
| invoice.payment_succeeded | Clear grace period, mark active |
|
||||
| invoice.payment_failed | Set 30-day grace period |
|
||||
| payment_intent.succeeded | Mark donation as succeeded |
|
||||
|
||||
## Subscription Tiers
|
||||
|
||||
Defined in `user_profiles.subscription_tier` enum:
|
||||
- `free` - Limited features (default)
|
||||
- `pro` - Enhanced features
|
||||
- `enterprise` - Full features
|
||||
| Tier | Price | Vehicle Limit | Features |
|
||||
|------|-------|---------------|----------|
|
||||
| Free | $0 | 2 | Basic tracking, standard reports |
|
||||
| Pro | $1.99/mo or $19.99/yr | 5 | VIN decoding, OCR, API access |
|
||||
| Enterprise | $4.99/mo or $49.99/yr | Unlimited | All features, priority support |
|
||||
|
||||
## Billing Cycles
|
||||
## Grace Period
|
||||
|
||||
- `monthly` - Monthly billing
|
||||
- `yearly` - Annual billing
|
||||
When payment fails:
|
||||
1. Subscription status set to `past_due`
|
||||
2. Grace period set to 30 days from failure
|
||||
3. Email notifications sent: immediate, day 23, day 29
|
||||
4. Daily job at 2:30 AM checks expired grace periods
|
||||
5. Expired subscriptions downgraded to free tier
|
||||
|
||||
## Subscription Status
|
||||
## Downgrade Flow
|
||||
|
||||
- `active` - Subscription is active
|
||||
- `past_due` - Payment failed, in grace period
|
||||
- `canceled` - Subscription canceled
|
||||
- `unpaid` - Payment failed, grace period expired
|
||||
When user downgrades to a tier with fewer vehicle allowance:
|
||||
1. Check if current vehicle count > target tier limit
|
||||
2. If yes, show VehicleSelectionDialog
|
||||
3. User selects which vehicles to keep
|
||||
4. Unselected vehicles become tier-gated (hidden, not deleted)
|
||||
5. Selections saved to tier_vehicle_selections table
|
||||
6. On upgrade, all vehicles become accessible again
|
||||
|
||||
## Milestone 2: Service Layer + Webhook Endpoint (COMPLETE)
|
||||
## Environment Variables
|
||||
|
||||
### Service Layer
|
||||
- **SubscriptionsService** (`domain/subscriptions.service.ts`): Business logic for subscription management
|
||||
- Get current subscription for user
|
||||
- Create new subscription (Stripe customer + free tier record)
|
||||
- Upgrade subscription (create Stripe subscription with payment method)
|
||||
- Cancel subscription (schedule for end of period)
|
||||
- Reactivate subscription (remove pending cancellation)
|
||||
- Handle Stripe webhook events with idempotency
|
||||
|
||||
### Webhook Processing
|
||||
- **Webhook Events Handled**:
|
||||
- `customer.subscription.created` - Update subscription record with Stripe subscription ID
|
||||
- `customer.subscription.updated` - Update status, tier, period dates
|
||||
- `customer.subscription.deleted` - Mark as canceled, downgrade to free tier
|
||||
- `invoice.payment_succeeded` - Clear grace period, mark active
|
||||
- `invoice.payment_failed` - Set 30-day grace period
|
||||
|
||||
### API Endpoints
|
||||
- **WebhooksController** (`api/webhooks.controller.ts`): Webhook event handler
|
||||
- **Routes** (`api/webhooks.routes.ts`): PUBLIC endpoint with rawBody support
|
||||
- POST /api/webhooks/stripe - Stripe webhook receiver (no JWT auth, signature verified)
|
||||
|
||||
### Integration
|
||||
- Syncs subscription tier changes to `user_profiles.subscription_tier` via UserProfileRepository
|
||||
- Uses environment variables for Stripe price IDs (PRO/ENTERPRISE, MONTHLY/YEARLY)
|
||||
|
||||
## Next Steps (Future Milestones)
|
||||
|
||||
- M3: API endpoints for subscription management (user-facing CRUD)
|
||||
- M4: Frontend integration and subscription UI
|
||||
- M5: Testing and documentation
|
||||
|
||||
## Database Migration
|
||||
|
||||
Run migrations:
|
||||
```bash
|
||||
npm run migrate:feature subscriptions
|
||||
```
|
||||
STRIPE_SECRET_KEY=sk_live_...
|
||||
STRIPE_WEBHOOK_SECRET=whsec_...
|
||||
STRIPE_PRO_MONTHLY_PRICE_ID=price_...
|
||||
STRIPE_PRO_YEARLY_PRICE_ID=price_...
|
||||
STRIPE_ENTERPRISE_MONTHLY_PRICE_ID=price_...
|
||||
STRIPE_ENTERPRISE_YEARLY_PRICE_ID=price_...
|
||||
```
|
||||
|
||||
## Architecture Notes
|
||||
## Files
|
||||
|
||||
- All database columns use snake_case
|
||||
- All TypeScript properties use camelCase
|
||||
- Repository `mapRow()` methods handle case conversion
|
||||
- Prepared statements used for all queries (no string concatenation)
|
||||
- Comprehensive error logging with structured logger
|
||||
- Webhook idempotency via `stripe_event_id` unique constraint
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| migrations/001_subscriptions_tables.sql | Database schema |
|
||||
| domain/subscriptions.types.ts | TypeScript interfaces |
|
||||
| domain/subscriptions.service.ts | Business logic |
|
||||
| domain/donations.service.ts | Donation logic |
|
||||
| data/subscriptions.repository.ts | Database operations |
|
||||
| external/stripe/stripe.client.ts | Stripe API wrapper |
|
||||
| external/stripe/stripe.types.ts | Stripe type definitions |
|
||||
| api/subscriptions.controller.ts | HTTP handlers |
|
||||
| api/subscriptions.routes.ts | Authenticated routes |
|
||||
| api/donations.controller.ts | Donation handlers |
|
||||
| api/donations.routes.ts | Donation routes |
|
||||
| api/webhooks.controller.ts | Webhook handler |
|
||||
| api/webhooks.routes.ts | Public webhook endpoint |
|
||||
| jobs/grace-period.job.ts | Daily grace period expiration job |
|
||||
|
||||
@@ -4,20 +4,21 @@ Frontend UI for subscription management with Stripe integration.
|
||||
|
||||
## Overview
|
||||
|
||||
Provides subscription tier management, payment method updates, and billing history viewing.
|
||||
Provides subscription tier management, payment method updates, billing history, donations, and vehicle selection during downgrade flow.
|
||||
|
||||
## Components
|
||||
|
||||
### TierCard
|
||||
Displays subscription plan with:
|
||||
- Plan name and pricing (monthly/yearly)
|
||||
Subscription plan display with:
|
||||
- Plan name and pricing (monthly/yearly toggle)
|
||||
- Feature list
|
||||
- Current plan indicator
|
||||
- Upgrade/downgrade button
|
||||
|
||||
### PaymentMethodForm
|
||||
Stripe Elements integration for:
|
||||
- Credit card input with validation
|
||||
- Credit card input with CardElement
|
||||
- Real-time validation
|
||||
- Payment method creation
|
||||
- Error handling
|
||||
|
||||
@@ -27,85 +28,130 @@ Invoice list with:
|
||||
- PDF download links
|
||||
- MUI Table component
|
||||
|
||||
### VehicleSelectionDialog
|
||||
Vehicle selection during downgrade:
|
||||
- Checkbox list for each vehicle
|
||||
- Counter showing selected vs allowed
|
||||
- Warning about tier-gated vehicles
|
||||
- Validation preventing over-selection
|
||||
|
||||
### DowngradeFlow
|
||||
Orchestrates downgrade process:
|
||||
- Checks vehicle count vs target tier limit
|
||||
- Shows VehicleSelectionDialog if needed
|
||||
- Submits vehicle selections to backend
|
||||
|
||||
### DonationSection / DonationSectionMobile
|
||||
One-time donation form:
|
||||
- Free-form amount input (no presets)
|
||||
- $0.50 minimum (Stripe limit)
|
||||
- Stripe CardElement for payment
|
||||
- Donation history table
|
||||
- Success feedback
|
||||
|
||||
## Pages
|
||||
|
||||
### SubscriptionPage (Desktop)
|
||||
- Current plan card with status
|
||||
- Three-column tier cards layout
|
||||
- Payment method section
|
||||
MUI-based layout:
|
||||
- Current plan card with status badges
|
||||
- Three-column tier comparison
|
||||
- Monthly/yearly toggle
|
||||
- Payment method modal
|
||||
- Billing history table
|
||||
- Material-UI components
|
||||
- Donation section
|
||||
|
||||
### SubscriptionMobileScreen (Mobile)
|
||||
Tailwind-based layout:
|
||||
- GlassCard styling
|
||||
- Stacked card layout
|
||||
- Touch-friendly buttons (44px min)
|
||||
- Tailwind styling
|
||||
- GlassCard components
|
||||
- Modal payment forms
|
||||
|
||||
## API Integration
|
||||
|
||||
All endpoints are in `/subscriptions`:
|
||||
- GET `/subscriptions` - Current subscription
|
||||
- POST `/subscriptions/checkout` - Upgrade subscription
|
||||
- POST `/subscriptions/cancel` - Cancel subscription
|
||||
- POST `/subscriptions/reactivate` - Reactivate subscription
|
||||
- PUT `/subscriptions/payment-method` - Update payment method
|
||||
- GET `/subscriptions/invoices` - Invoice history
|
||||
### Subscriptions
|
||||
| Endpoint | Method | Hook |
|
||||
|----------|--------|------|
|
||||
| /api/subscriptions | GET | useSubscription() |
|
||||
| /api/subscriptions/checkout | POST | useCheckout() |
|
||||
| /api/subscriptions/cancel | POST | useCancelSubscription() |
|
||||
| /api/subscriptions/reactivate | POST | useReactivateSubscription() |
|
||||
| /api/subscriptions/downgrade | POST | useDowngrade() |
|
||||
| /api/subscriptions/payment-method | PUT | useUpdatePaymentMethod() |
|
||||
| /api/subscriptions/invoices | GET | useInvoices() |
|
||||
|
||||
### Donations
|
||||
| Endpoint | Method | Hook |
|
||||
|----------|--------|------|
|
||||
| /api/donations | POST | useCreateDonation() |
|
||||
| /api/donations | GET | useDonations() |
|
||||
|
||||
## Hooks
|
||||
|
||||
- `useSubscription()` - Fetch current subscription
|
||||
- `useCheckout()` - Upgrade subscription
|
||||
- `useCancelSubscription()` - Cancel subscription
|
||||
- `useReactivateSubscription()` - Reactivate subscription
|
||||
- `useInvoices()` - Fetch invoice history
|
||||
| Hook | Purpose |
|
||||
|------|---------|
|
||||
| useSubscription() | Fetch current subscription |
|
||||
| useCheckout() | Upgrade subscription |
|
||||
| useCancelSubscription() | Cancel subscription |
|
||||
| useReactivateSubscription() | Reactivate subscription |
|
||||
| useDowngrade() | Downgrade with vehicle selection |
|
||||
| useInvoices() | Fetch billing history |
|
||||
| useCreateDonation() | Create donation payment |
|
||||
| useDonations() | Fetch donation history |
|
||||
|
||||
## Environment Setup
|
||||
## Environment Variables
|
||||
|
||||
Required environment variable:
|
||||
```bash
|
||||
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_...
|
||||
```
|
||||
|
||||
## Subscription Tiers
|
||||
|
||||
### Free
|
||||
### Free ($0)
|
||||
- 2 vehicles
|
||||
- Basic tracking
|
||||
- Standard reports
|
||||
- Price: $0
|
||||
|
||||
### Pro
|
||||
### Pro ($1.99/month or $19.99/year)
|
||||
- Up to 5 vehicles
|
||||
- VIN decoding
|
||||
- OCR functionality
|
||||
- API access
|
||||
- Price: $1.99/month or $19.99/year
|
||||
|
||||
### Enterprise
|
||||
### Enterprise ($4.99/month or $49.99/year)
|
||||
- Unlimited vehicles
|
||||
- All Pro features
|
||||
- Priority support
|
||||
- Price: $4.99/month or $49.99/year
|
||||
|
||||
## Mobile Navigation
|
||||
## Routing
|
||||
|
||||
Add subscription screen to settings navigation:
|
||||
```typescript
|
||||
navigateToScreen('Subscription')
|
||||
```
|
||||
- Desktop: `/garage/settings/subscription`
|
||||
- Mobile: `navigateToScreen('Subscription')`
|
||||
|
||||
## Desktop Routing
|
||||
## Files
|
||||
|
||||
Route: `/garage/settings/subscription`
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| types/subscription.types.ts | TypeScript interfaces |
|
||||
| api/subscription.api.ts | API client calls |
|
||||
| hooks/useSubscription.ts | React Query hooks |
|
||||
| constants/plans.ts | Tier configuration |
|
||||
| components/TierCard.tsx | Plan display card |
|
||||
| components/PaymentMethodForm.tsx | Stripe Elements form |
|
||||
| components/BillingHistory.tsx | Invoice table |
|
||||
| components/VehicleSelectionDialog.tsx | Vehicle selection modal |
|
||||
| components/DowngradeFlow.tsx | Downgrade orchestrator |
|
||||
| components/DonationSection.tsx | Desktop donation UI |
|
||||
| components/DonationSectionMobile.tsx | Mobile donation UI |
|
||||
| pages/SubscriptionPage.tsx | Desktop page |
|
||||
| mobile/SubscriptionMobileScreen.tsx | Mobile screen |
|
||||
|
||||
## Testing
|
||||
|
||||
Test subscription flow:
|
||||
1. View current plan
|
||||
2. Toggle monthly/yearly billing
|
||||
3. Select upgrade tier
|
||||
4. Enter payment method
|
||||
5. Complete checkout
|
||||
6. Verify subscription update
|
||||
7. View billing history
|
||||
3. Upgrade: Select tier, enter payment, complete checkout
|
||||
4. Downgrade: Select vehicles to keep if over limit
|
||||
5. Cancel/reactivate subscription
|
||||
6. Make a donation
|
||||
7. View billing and donation history
|
||||
|
||||
Reference in New Issue
Block a user