diff options
| author | Ben Sima <ben@bsima.me> | 2025-11-09 20:32:34 -0500 |
|---|---|---|
| committer | Ben Sima <ben@bsima.me> | 2025-11-09 20:32:34 -0500 |
| commit | 14af38a21dc8e790b9ddc29e241784fd769dc3fc (patch) | |
| tree | e33ef9866f6448088cac4acd2ef9b48c32ca3990 /Biz/PodcastItLater/STRIPE_TESTING.md | |
| parent | 83d90f815447abc5447f6b0b4a978b2e8ce82894 (diff) | |
PodcastItLater: Add Stripe billing and mobile responsiveness
- Implemented complete Stripe integration (Billing.py)
- Checkout sessions for subscription upgrades - Billing portal for
subscription management - Webhook handling for subscription events
- Usage tracking with tier-based limits (free: 10, personal: 50,
pro: unlimited)
- Added billing page UI (BillingPage component)
- Current plan display with usage stats - Pricing cards for all
tiers with upgrade buttons - Manage subscription button for paid
users - Success/error messaging
- Database migrations for billing
- Added plan_tier, stripe_customer_id, stripe_subscription_id - Added
subscription_status, period dates, cancel_at_period_end - Created
stripe_events table for webhook idempotency - Added get_usage()
method for usage tracking
- Made UI mobile-friendly and responsive
- Added viewport meta tags to all pages - Replaced pages.HtmlPage
with raw html.html for meta tag control - Responsive button layouts
with flexbox wrapping - Responsive pricing cards (1 col mobile,
2 col tablet, 3 col desktop) - Touch-friendly forms and buttons
(44px minimum) - Responsive padding and containers - Admin tables
with horizontal scroll
- Added Stripe testing guide (STRIPE_TESTING.md)
- Fixed CSS bug in pricing cards (cardh-100 text rendering) - Updated
tasks: completed t-144e7lF, t-1pIV0ZF, t-1s8ADC0
Amp-Thread-ID:
https://ampcode.com/threads/T-42fd5fb3-3dc5-4cbc-a9a3-78db9e13187e
Co-authored-by: Amp <amp@ampcode.com>
Diffstat (limited to 'Biz/PodcastItLater/STRIPE_TESTING.md')
| -rw-r--r-- | Biz/PodcastItLater/STRIPE_TESTING.md | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/Biz/PodcastItLater/STRIPE_TESTING.md b/Biz/PodcastItLater/STRIPE_TESTING.md new file mode 100644 index 0000000..00910ec --- /dev/null +++ b/Biz/PodcastItLater/STRIPE_TESTING.md @@ -0,0 +1,366 @@ +# Stripe Testing Guide for PodcastItLater + +This guide covers end-to-end Stripe billing integration testing. + +## Prerequisites + +1. Stripe account (sign up at stripe.com) +2. Stripe CLI installed (`brew install stripe/stripe-cli/stripe` or download from stripe.com/docs/stripe-cli) + +## Setup Steps + +### 1. Get Stripe Test Mode API Keys + +1. Go to https://dashboard.stripe.com/test/apikeys +2. Copy your test mode keys: + - **Publishable key** (starts with `pk_test_`) + - **Secret key** (starts with `sk_test_`) + +### 2. Create Products and Prices in Stripe Dashboard + +#### Personal Plan ($9/month) +1. Go to https://dashboard.stripe.com/test/products +2. Click "+ Add product" +3. Fill in: + - Name: `PodcastItLater Personal` + - Description: `50 articles per month` + - Pricing model: `Standard pricing` + - Price: `$9.00` + - Billing period: `Monthly` + - Payment type: `Recurring` +4. Click "Save product" +5. **Copy the Price ID** (starts with `price_`) - you'll need this for `STRIPE_PRICE_ID_PERSONAL` + +#### Pro Plan ($29/month) +1. Repeat above steps with: + - Name: `PodcastItLater Pro` + - Description: `Unlimited articles` + - Price: `$29.00` + - Billing period: `Monthly` +2. **Copy the Price ID** - you'll need this for `STRIPE_PRICE_ID_PRO` + +### 3. Configure Environment Variables + +Create or update your `.envrc.local` file: + +```bash +# Stripe Test Mode Keys +export STRIPE_SECRET_KEY="sk_test_YOUR_SECRET_KEY_HERE" + +# Price IDs from Stripe dashboard +export STRIPE_PRICE_ID_PERSONAL="price_YOUR_PERSONAL_PRICE_ID" +export STRIPE_PRICE_ID_PRO="price_YOUR_PRO_PRICE_ID" + +# Other required vars (if not already set) +export BASE_URL="http://localhost:8000" +export SESSION_SECRET="dev-secret-key-for-testing" +export SECRET_KEY="dev-secret-key-for-magic-links" +export AREA="Test" # Important for test mode behavior +``` + +Reload environment: +```bash +direnv allow +``` + +### 4. Set Up Webhook Testing + +#### Option A: Stripe CLI (Recommended for local testing) + +1. Login to Stripe CLI: + ```bash + stripe login + ``` + +2. Start webhook forwarding: + ```bash + stripe listen --forward-to http://localhost:8000/stripe/webhook + ``` + +3. **Copy the webhook signing secret** shown in the output (starts with `whsec_`) + +4. Add to `.envrc.local`: + ```bash + export STRIPE_WEBHOOK_SECRET="whsec_YOUR_WEBHOOK_SECRET" + ``` + +5. Reload environment: + ```bash + direnv allow + ``` + +#### Option B: Deploy and Use Stripe Dashboard Webhooks + +1. Deploy to production or staging environment +2. Go to https://dashboard.stripe.com/test/webhooks +3. Click "+ Add endpoint" +4. Enter your webhook URL: `https://your-domain.com/stripe/webhook` +5. Select events to listen for: + - `checkout.session.completed` + - `customer.subscription.created` + - `customer.subscription.updated` + - `customer.subscription.deleted` + - `invoice.payment_failed` +6. Copy the signing secret and add to your environment + +### 5. Initialize Database + +```bash +# Make sure DATA_DIR is set (defaults to _/var/podcastitlater/) +export DATA_DIR="_/var/podcastitlater/" +mkdir -p $DATA_DIR + +# Start the web server to initialize database +bild --time 0 Biz/PodcastItLater/Web.py +python Biz/PodcastItLater/Web.py +``` + +## Testing the Complete Flow + +### Test 1: User Registration and Free Tier + +1. Start the web server: + ```bash + python Biz/PodcastItLater/Web.py + ``` + +2. Open http://localhost:8000 in your browser + +3. Login with `demo@example.com` (auto-approved in test mode) + +4. Verify: + - ✓ Logged in as demo@example.com + - ✓ Plan shows "Free" + - ✓ Billing button visible + +5. Click "Billing" button + +6. Verify billing page shows: + - ✓ Current Plan: Free + - ✓ Usage: 0 / 10 articles + - ✓ Period dates (current month) + - ✓ Three pricing cards (Free, Personal, Pro) + - ✓ "Upgrade" buttons on Personal and Pro plans + +### Test 2: Stripe Checkout Flow (Personal Plan) + +1. On billing page, click "Upgrade" button under Personal plan + +2. Verify redirected to Stripe Checkout page: + - ✓ Shows "PodcastItLater Personal" + - ✓ Shows $9.00/month + - ✓ Can enter test card details + +3. Use Stripe test card: + - Card number: `4242 4242 4242 4242` + - Expiry: Any future date (e.g., `12/34`) + - CVC: Any 3 digits (e.g., `123`) + - Email: Use same email as logged in user + +4. Complete checkout + +5. Verify: + - ✓ Redirected back to `/billing?status=success` + - ✓ Success message shown + - ✓ Plan updated to "Personal" (may take a few moments) + - ✓ Usage shows "0 / 50 articles" + - ✓ "Manage Subscription" button appears + - ✓ "Current Plan" badge on Personal plan + +### Test 3: Webhook Events + +Check your terminal running `stripe listen` to verify webhook events received: + +``` +✓ checkout.session.completed [evt_xxx] +✓ customer.subscription.created [evt_xxx] +✓ customer.subscription.updated [evt_xxx] +``` + +Check database to verify subscription data: + +```bash +sqlite3 _/var/podcastitlater/podcast.db +``` + +```sql +-- Check user subscription details +SELECT id, email, plan_tier, subscription_status, + stripe_customer_id, stripe_subscription_id +FROM users WHERE email = 'demo@example.com'; + +-- Should show: +-- plan_tier: personal +-- subscription_status: active +-- stripe_customer_id: cus_xxx +-- stripe_subscription_id: sub_xxx +``` + +### Test 4: Billing Portal (Manage Subscription) + +1. On billing page (now showing Personal plan), click "Manage Subscription" + +2. Verify redirected to Stripe Billing Portal: + - ✓ Shows current subscription: PodcastItLater Personal + - ✓ Can update payment method + - ✓ Can cancel subscription + - ✓ Can view invoices + +3. Test cancellation: + - Click "Cancel plan" + - Select cancellation option (e.g., "Cancel at end of period") + - Confirm + +4. Return to billing page + +5. Verify: + - ✓ Subscription shows as active but set to cancel + - ✓ Still can use service until period ends + +### Test 5: Usage Limits Enforcement + +1. Login as a free user (or create new user) + +2. Try to submit more than 10 articles in the current month + +3. On the 11th submission, verify: + - ✓ Error message shown: "You've reached your limit of 10 articles per period. Upgrade to continue." + - ✓ Submit button disabled or shows error + - ✓ Upgrade prompt shown + +4. Upgrade to Personal or Pro plan + +5. Verify: + - ✓ Can submit articles again + - ✓ New usage limit applies + +### Test 6: Subscription Upgrade Flow + +1. Start with Personal plan + +2. Go to billing page + +3. Click "Upgrade" on Pro plan + +4. Complete checkout with test card + +5. Verify: + - ✓ Plan upgraded to Pro + - ✓ Usage shows "0 / ∞ articles" + - ✓ Billing reflects pro-rated charges + +### Test 7: Payment Failure Handling + +1. Use Stripe test card that triggers payment failure: + - Card number: `4000 0000 0000 0341` (charge fails) + +2. After first payment succeeds, wait for next billing cycle or trigger failure manually + +3. Verify: + - ✓ Subscription status updates to "past_due" + - ✓ User still has access during grace period + - ✓ Webhook event processed: `invoice.payment_failed` + +### Test 8: Subscription Cancellation + +1. Cancel subscription from Billing Portal + +2. Wait for end of billing period OR manually expire in Stripe dashboard + +3. Verify: + - ✓ Webhook event: `customer.subscription.deleted` + - ✓ User downgraded to free tier + - ✓ Usage limit reset to 10 articles/month + - ✓ Stripe subscription data cleared + +## Common Test Cards + +| Card Number | Scenario | +|-------------|----------| +| 4242 4242 4242 4242 | Successful payment | +| 4000 0000 0000 0341 | Charge fails | +| 4000 0000 0000 9995 | Card declined | +| 4000 0025 0000 3155 | Requires authentication (3D Secure) | + +Full list: https://stripe.com/docs/testing#cards + +## Troubleshooting + +### Webhooks not received + +- Check Stripe CLI is running: `stripe listen --forward-to ...` +- Verify webhook secret matches in environment +- Check web server logs for webhook processing errors +- Verify web server is accessible at the forwarding URL + +### Database not updating + +- Check web server logs for errors +- Verify webhook events are being processed (check stripe_events table) +- Check database schema is up to date (run Web.py to trigger migrations) + +### Checkout session not creating + +- Verify STRIPE_SECRET_KEY is set and valid +- Check STRIPE_PRICE_ID_PERSONAL and STRIPE_PRICE_ID_PRO are correct +- Look for errors in web server logs +- Verify price IDs exist in Stripe dashboard + +### User not upgrading after checkout + +- Verify webhooks are being received and processed +- Check that customer email in checkout matches user email in database +- Look for errors in webhook processing logs +- Check stripe_events table for duplicate processing + +## Production Deployment + +Before going to production: + +1. Switch to live mode keys (remove `_test_` from keys) +2. Create products/prices in live mode Stripe dashboard +3. Set up live webhook endpoint in Stripe dashboard +4. Update STRIPE_WEBHOOK_SECRET to live mode secret +5. Set AREA=Live in production environment +6. Test with real payment methods (or use test mode in production at first) +7. Monitor webhook events and logs closely + +## Monitoring + +### Check Webhook Events in Database + +```sql +SELECT * FROM stripe_events +ORDER BY created_at DESC +LIMIT 10; +``` + +### Check Subscription States + +```sql +SELECT email, plan_tier, subscription_status, + current_period_start, current_period_end +FROM users +WHERE plan_tier != 'free'; +``` + +### Check Usage Stats + +```sql +SELECT u.email, u.plan_tier, COUNT(e.id) as articles_this_month +FROM users u +LEFT JOIN episodes e ON e.user_id = u.id + AND e.created_at >= date('now', 'start of month') +GROUP BY u.id, u.email, u.plan_tier; +``` + +## Next Steps + +After successful testing: + +1. Mark task t-1pIV0ZF as done +2. Update AGENTS.md with Stripe setup documentation +3. Create production deployment checklist +4. Set up error monitoring (Sentry) +5. Configure email notifications for payment failures +6. Add analytics for conversion tracking |
