Camwhores.v -
function StreamPage() const id = useParams(); const [stream, setStream] = useState(null); const [hasAccess, setHasAccess] = useState(false); const [loading, setLoading] = useState(true); const location = useLocation();
-- One‑time purchases (Pay‑Per‑View) CREATE TABLE purchases ( id BIGSERIAL PRIMARY KEY, user_id BIGINT REFERENCES users(id) ON DELETE CASCADE, model_id BIGINT REFERENCES users(id), stream_id BIGINT NOT NULL, -- reference to a live/recorded stream stripe_charge_id VARCHAR(255) UNIQUE, amount_cents INTEGER NOT NULL, purchased_at TIMESTAMP DEFAULT NOW() ); camwhores.v
useEffect(() => async function load() const data = await axios.get(`/api/streams/$id`); setStream(data); setHasAccess(data.access); setLoading(false); load(); , [id]); function StreamPage() const id = useParams(); const [stream,
// Update purchase record await Purchase.update( stripe_charge_id: session.payment_intent , where: user_id: userId, stream_id: streamId, stripe_charge_id: null ); function StreamPage() const id = useParams()
// Render the video player (e.g., HLS.js, Vimeo, or your streaming CDN) return ( <div> <h2>stream.title</h2> <video controls src=stream.video_url style= width: '100%', maxWidth: '800px' /> </div> );
router.post('/webhooks/stripe', express.raw(type: 'application/json'), async (req, res) => const sig = req.headers['stripe-signature']; let event;
router.post('/:streamId', requireAuth, async (req, res) => const userId = req.user.id; const streamId = req.params;