The core objects for selling digital products. These exact terms recur in every lesson.
A thing you sell — described by name and metadata. It carries no price by itself.
e.g. "Pro Plan", "UI Kit download". One Product can have many Prices.
How much a Product costs and how it's billed. A Price is either one-time
or recurring (set via the recurring field). This single field is what
separates a download from a subscription.
$29 one-time → digital download. $9/month recurring → SaaS. Prices are immutable; create a new one to change pricing.
A persistent record of a buyer — email, saved payment methods, subscriptions, invoices. Optional for one-time sales; required for subscriptions (recurring charges need someone to bill).
Stripe's recommended way in. A pre-built, Stripe-hosted (or embedded) payment page you
create server-side with line items. Handles card UI, 3DS, receipts, and can create subscriptions for you.
Set mode: 'payment' for one-time, mode: 'subscription' for recurring.
You create it → send the buyer to its URL → they pay on Stripe's page.
The lower-level object that tracks a single payment through its lifecycle
(requires_payment_method → requires_action → succeeded).
Checkout creates one for you under the hood. Use it directly only when you build custom UI with Elements.
A no-code shareable URL backed by a Price. Same engine as Checkout, zero code. Useful as a fallback, not the focus of this mission.
A recurring agreement linking a Customer to one or more recurring Prices. Stripe automatically generates an Invoice each billing period and attempts payment.
Statuses: trialing, active, past_due, unpaid,
incomplete, canceled. Grant access while active or trialing.
A bill for a billing period: open → paid (or void). Each invoice has its own
PaymentIntent. invoice.paid is your "recurring payment succeeded → keep access on" signal.
A Stripe-hosted page where customers manage their own subscription — upgrade, cancel, update card, view invoices. Saves you building account-management UI.
Stripe POSTs an Event to your server's webhook endpoint when something happens. This — not the browser redirect — is how you reliably know a payment succeeded and should grant access.
Key events: checkout.session.completed (one-time fulfillment),
invoice.paid / invoice.payment_failed and
customer.subscription.updated/deleted (subscription lifecycle).
Verify every webhook with the signing secret (whsec_…) so you know it's really Stripe.
Make handling idempotent (dedupe on Event id) because events can arrive more than once and out of order.
Fully separate datasets and keys. Test keys (sk_test_… / pk_test_…) +
test cards (e.g. 4242 4242 4242 4242) let you build everything for free before flipping to
live keys (sk_live_…).
Secret key = server only. Publishable key = safe in the browser.