Camwhores.v -

try event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET); catch (err) console.error('⚠️ Webhook signature verification failed.', err); return res.sendStatus(400);

// Handle subscription events similarly… camwhores.v

-- Streams (live or recorded) CREATE TABLE streams ( id BIGSERIAL PRIMARY KEY, model_id BIGINT REFERENCES users(id), title VARCHAR(255), is_live BOOLEAN DEFAULT FALSE, start_time TIMESTAMP, end_time TIMESTAMP, is_premium BOOLEAN DEFAULT FALSE, -- true = requires purchase/subscription price_cents INTEGER, -- optional PPV price thumbnail_url VARCHAR(255) ); | Method | URL | Description | Auth | |--------|-----|-------------|------| | GET /api/plans | List all subscription plans | Public | | POST /api/subscriptions | Create a Stripe Checkout session for a plan | Viewer (JWT) | | POST /api/webhooks/stripe | Handle subscription events ( invoice.payment_succeeded , customer.subscription.deleted ) | Stripe secret | | GET /api/streams/:id | Retrieve stream metadata; includes access flag | Viewer (JWT) | | POST /api/purchases/:streamId | Create a PPV checkout session for a specific stream | Viewer (JWT) | | GET /api/user/me | Current user profile + subscription status | Viewer/Model (JWT) | try event = stripe

if (!hasAccess && stream.is_premium) return ( <div> <h2>stream.title</h2> <p>This stream is premium. Purchase access to watch.</p> <button onClick=buyAccess> Buy for $(stream.price_cents / 100).toFixed(2) </button> </div> ); try event = stripe.webhooks.constructEvent(req.body

// routes/purchases.js const express = require('express'); const router = express.Router(); const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY); const requireAuth = require('../middleware/auth'); const Stream, Purchase = require('../models');