Guide
Every layer in this guide builds on the last. By the end, you'll have a complete mental model of how Stripe works — so you can reason about payment systems instead of blindly following documentation.
Stripe is a middleman between credit cards and bank accounts. A customer's card is charged. The money lands in the business's Stripe balance — a virtual wallet Stripe holds for you. Periodically, Stripe moves that balance to the business's real bank account. That movement is called a payout.
Customer ────$50────► Stripe ────$48.55────► Business's Bank
keeps $1.45
(2.9% + $0.30)Stripe takes a processing fee on every charge. This is how they make money. The fee is deducted before money reaches the business's balance.
STRIPE
┌──── ─────────────┐
Customer pays │ │ Payout (automatic, free)
──────────────► │ Stripe Balance │ ────────────────────────► Bank Account
$50 │ $48.55 │ $48.55
└─────────────────┘
(fee already deducted)Standard payouts are free. Stripe already made its money on the processing fee. That's all Stripe does for a single business: charge cards, hold money, pay out to bank.
Before we dive deeper, let's see the whole landscape. When you strip away the branding and documentation, every business that accepts payments online faces three questions:
How does money move? — the infrastructure
How do buyers experience paying? — the interface
What am I selling? — the business model
Stripe's entire product surface maps to these three questions — plus a fourth layer for everything that happens after money moves.
STRIPE — THE COMPLETE MAP
┌─────────────────────────────────────────────────────────────┐
│ Layer 1: The Pipes (everyone needs this) │
│ │
│ Payment Intents → "Move money from A to B" │
│ Payment Methods → "How the money travels" │
│ Webhooks → "What happened" │
│ Customers → "Who's paying" │
└─────────────────────────────────────────────────────────────┘
▲ everything above is infrastructure
▼ everything below is your choice
┌─────────────────────────────────────────────────────────────┐
│ Layer 2: The Interface (how buyers pay) │
│ │
│ Payment Links → A URL (zero code) │
│ Checkout → Stripe-hosted page (low code) │
│ Elements → Embedded components (medium code) │
│ Custom UI → Raw API (full code) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Layer 3: The Business Model (what you sell) │
│ │
│ Products + Prices → One-time purchases │
│ Subscriptions → Recurring billing │
│ Connect → Multi-party / marketplace │
│ Invoicing → B2B billing │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Layer 4: After the Money Moves │
│ │
│ Disputes & Fraud → Chargebacks, Radar │
│ Refunds → Returning money │
│ Payouts → Stripe balance → your bank │
│ Tax & Reporting → Compliance │
└─────────────────────────────────────────────────────────────┘Layer 1 is infrastructure — every Stripe integration uses it. Layer 4 applies to everyone eventually. Layers 2 and 3 are where your decisions live. You choose an interface and a business model.
Where do I start?
What are you building?
│
├─► I sell things directly to customers
│ │
│ ├─► One-time purchases → Products + Prices (section 7)
│ └─► Recurring billing → Subscriptions (section 9)
│
└─► Others sell through my platform → Connect (section 10)
How should buyers experience payment?
│
├─► I just need a link to send people → Payment Links
├─► A hosted checkout page is fine → Checkout
├─► It must look like my brand → Elements
└─► I'll build everything from scratch → Custom UI (rarely needed)The rest of this article walks each layer from the ground up. At every point, you'll know exactly where you are on this map.
The core of Stripe's API is the Payment Intent. To understand why it exists, consider what happens when a customer pays:
Your server tells Stripe "I want to collect $50 from this customer"
The customer's bank might need to verify the charge (3D Secure, bank redirect)
Some time passes — milliseconds for cards, days for bank transfers
The charge succeeds or fails
That third step is why Payment Intents exist. Stripe's older API (the Charges API) assumed payments were synchronous — you called the API, got back success or failure, done. But the world changed. EU regulation (SCA/PSD2) requires 3D Secure for many card payments — a popup the customer must interact with. Bank debits take days to settle. Wallets have their own confirmation flows.
A charge is no longer a single API call. It's a process that unfolds over time. A Payment Intent represents that process — the intention to collect money, not the money itself.
The Payment Intent lifecycle:
create ──► requires_payment_method ──► requires_confirmation
│
▼
processing
│ │
▼ ▼
succeeded requires_action
│
▼
(customer completes
3D Secure / redirect)
│
▼
processing
│ │
▼ ▼
succeeded payment_failedThe mental model: a Payment Intent is a state machine. You create it, the customer confirms it, and then things happen asynchronously. You don't drive the outcome — you react to state transitions.
This is why webhooks (next section) are essential. The state might change minutes or days after you created the intent.
By default, a successful Payment Intent both authorizes and captures — the customer's card is charged and money moves immediately. But you can separate these steps:
stripe.paymentIntents.create({
amount: 5000,
currency: 'usd',
capture_method: 'manual', // authorize only, capture later
})Authorization places a hold on the customer's card without moving money. Capture completes the transfer. This matters for businesses where the final amount isn't known at checkout — hotels, car rentals, restaurants with tips — or where you need to confirm availability before charging.
The hold expires after 7 days (or 31 for some card types). If you don't capture, the customer's funds are released automatically.
Payment Intents are state machines that transition asynchronously. The question is: how does your server learn about those transitions?
The naive answer is polling — periodically call the API to check the status. This doesn't work for payments. You'd waste thousands of API calls checking statuses that haven't changed. If your poller crashes, you miss the transition entirely. And it doesn't scale: N pending payments multiplied by polling frequency equals too many API calls.
Stripe's answer is webhooks: when a state change happens, Stripe sends an HTTP POST to your server with the event details.
Customer ──► confirms 3D Secure ──► Stripe processes payment
│
▼
payment_intent.succeeded
│
POST to your server
│
▼
Your server updates order statusThree things to understand about webhooks:
Signature verification. Every webhook includes a cryptographic signature. You verify it using your webhook secret to ensure the event came from Stripe, not an attacker. Stripe's SDK handles this — one function call.
At-least-once delivery. Stripe guarantees it will deliver the event at least once. But it might deliver it more than once — network retries, timeouts, your server returning a 500. Your webhook handler must be idempotent: processing the same event twice must produce the same result as processing it once.
The events you care about. Stripe emits hundreds of event types. You subscribe only to what you need. payment_intent.succeeded tells you money was collected. payment_intent.payment_failed tells you a charge failed. checkout.session.completed tells you a Checkout page was completed. invoice.payment_failed tells you a subscription payment failed.
Webhooks are not optional. They are the primary mechanism for learning about payment outcomes. Every section after this assumes webhooks are wired up.
How does money actually travel from the customer to Stripe? Through a Payment Method — an abstraction that represents how someone pays.
The insight behind Payment Methods is uniformity. Whether the customer uses a Visa card, a bank debit, Apple Pay, or a local payment method like iDEAL, the flow through your code is identical:
Create a Payment Intent with an amount
Attach a Payment Method (or let the customer choose one)
Confirm the intent
Handle the outcome via webhooks
What changes is what happens behind the abstraction:
Your code (same flow for all)
│
Payment Intent + Payment Method
│
├─► Card (Visa, Mastercard, Amex)
│ Instant authorization. Funds available immediately.
│ Highest fees (~2.9% + $0.30). Most common.
│
├─► Bank Debit (ACH, SEPA)
│ Takes 3–5 business days to settle. Can fail after apparent success.
│ Lowest fees (~0.8%). Good for large amounts.
│
├─► Wallet (Apple Pay, Google Pay)
│ A card payment underneath, with better UX on mobile.
│ Same fees as cards. Higher conversion rates.
│
└─► Local Methods (iDEAL, Bancontact, Boleto...)
Region-specific. Required to do business in some markets.
Fees and timing vary by method.The practical consequence: adding a new payment method to your checkout is configuration, not code. Your Payment Intent flow doesn't change. You tell Stripe which methods to accept, and Stripe handles the differences.
So far, every payment is standalone. The customer enters their card, money moves, done. If they come back tomorrow, they enter their card again.
A Customer object in Stripe ties an identity to saved Payment Methods. The first time a customer pays, you create a Customer object and attach their Payment Method to it. On their next purchase, you create a Payment Intent with the saved Customer — Stripe charges the saved method without the customer re-entering their details.
Tokenization is what makes this safe. When a customer enters their card number, Stripe converts it into a token — a reference that represents the card without containing the actual number. You store the token. Stripe stores the card. The real card number never touches your server, which keeps you out of PCI compliance scope.
Sometimes you want to save a card without charging it — free trials, "add a payment method" flows, or future purchases. A SetupIntent does this: it runs the authentication flow (3D Secure if required), saves the Payment Method to the Customer, and charges nothing.
SetupIntent (save card, charge nothing)
│
▼
Customer object
│
├── Payment Method: visa ****4242
├── Payment Method: mastercard ****8210
└── Payment Method: bank account ****6789
│
▼
Future Payment Intents use these saved methods
(no card details re-entered)Up to this point, we've been specifying raw amounts: "charge $50." But most businesses sell things — products with names, descriptions, and prices. Stripe models this explicitly.
A Product is what you sell. It has a name, a description, and optionally images. It says nothing about price.
A Price is how much a product costs. One product can have multiple prices — monthly and yearly for the same subscription, different currencies, tiered pricing.
The critical distinction: a Price is either one-time or recurring. This is where the road forks:
Product: "Pro Plan"
│
├── Price: $10/month (recurring) ──► used by Subscriptions
├── Price: $100/year (recurring) ──► used by Subscriptions
└── Price: $200 once (one-time) ──► used by Checkout / Payment LinksWhy does Stripe model this? Because Products and Prices are shared across multiple surfaces. Checkout uses Prices to know what to charge. Subscriptions use recurring Prices for billing cycles. Invoices use Prices for line items. Payment Links use Prices to generate shareable URLs.
One catalog definition, used everywhere. Change the price in one place, every surface reflects it.
You've built the backend — Payment Intents, webhooks, Customer objects. Now the question: how does the buyer actually enter their payment details?
Stripe offers a spectrum from zero code to full control:
Zero code Full control
◄──────────────────────────────────────────────────────────────────►
Payment Links Checkout Elements Custom UI
│ │ │ │
A URL. Stripe-hosted Stripe Raw API calls.
Share it. page. Redirect components You build
Done. customers inside YOUR everything.
there. page. Your
design,
Stripe's
security.Payment Links — You create a link in the Stripe dashboard or via API. It points to a Stripe-hosted page with a product, a price, and a pay button. Share the link in an email, embed it on a page, post it on social media. No code required.
Checkout — A full-page payment experience hosted by Stripe. You redirect the customer there, they pay, Stripe redirects them back. Checkout handles payment method selection, address collection, 3D Secure, tax calculation, and receipts. You control what's being sold. Stripe controls the UI.
Elements — Individual UI components (card input, payment method selector, address form) that you embed inside your own page. They render in an iframe — the customer's card details never touch your server, keeping you PCI compliant. You control the layout and styling. Stripe handles the security.
Custom UI — You collect payment details entirely through your own UI and use the raw API. This requires full PCI compliance (SAQ D) and is almost never worth the overhead. Included here for completeness.
The decision:
"I need a payment page yesterday" — Payment Links
"I want a professional checkout without building it" — Checkout
"The payment experience must look like my brand" — Elements
Most businesses start with Checkout and move to Elements when they need more control over the experience.
One-time payments are straightforward — money moves once and you're done. Subscriptions add a time dimension: money moves repeatedly, on a schedule, until something changes.
A Subscription connects a Customer to one or more recurring Prices. Stripe creates an Invoice on each billing date, attempts to charge the customer's saved Payment Method, and emits events based on the outcome.
The subscription lifecycle:
create ──► active ──────────────────────────► canceled
│ ▲
│ payment fails │
▼ │
past_due ──► (retry succeeds) ──► active │
│ │
│ all retries exhausted │
▼ │
unpaid ──────────────────────────────────┘Billing cycles. Subscriptions charge on a fixed schedule — monthly, yearly, or custom. When a customer changes plans mid-cycle, Stripe prorates automatically: they get credit for the unused portion of the old plan and are charged the remaining portion of the new one.
Dunning. When a payment fails — expired card, insufficient funds — Stripe doesn't immediately cancel the subscription. It enters past_due and retries the charge on a schedule you configure (e.g., retry after 3, 5, and 7 days). Stripe can also email the customer to update their payment method. This process of recovering failed payments is called dunning.
Trials. A subscription can start with a free trial. No charge during the trial. When it ends, Stripe automatically creates and charges the first invoice. If the customer doesn't have a payment method yet, you use a SetupIntent to collect one before the trial ends.
Metered billing. Instead of a fixed price, you report usage after the fact. Stripe tallies the usage at the end of each billing cycle and generates an invoice for the total. This is for usage-based pricing — API calls, compute hours, messages sent.
A warning about complexity. Subscriptions are where Stripe's surface area explodes. Coupons, promotions, multi-item subscriptions, billing thresholds, subscription schedules, pause and resume, tax integration — the feature list is vast. Most businesses need a small fraction of it. Know which fraction before you build. Start with the simplest subscription model that works, and resist adding complexity until a real customer situation demands it.
Everything so far assumes a simple relationship: one business accepts money from customers. Connect exists for a different relationship — a platform that facilitates payments between customers and third-party sellers.
Think marketplaces, SaaS platforms that process payments for their users, or any business where the money ultimately belongs to someone other than the platform operator.
A customer pays $50 for a ticket. But the ticket belongs to a venue. The platform takes a commission. Who receives the money? When?
Customer ──── $50 ────► Platform ...but the ticket
is for a venue.
Who does this money
belong to?This is the reason Connect exists — to let platforms split money between multiple parties.
Every seller on your platform needs a Stripe account. Connect lets you create and manage these accounts. You control the experience through four independent levers:
Lever Options
───── ───────
Who pays Stripe's fees? Seller (cheaper at low volume)
Platform (negotiable at scale)
Who covers losses? Stripe absorbs negative balances
Platform absorbs negative balances
Who handles identity Stripe's hosted KYC forms (recommended)
verification? Your own forms (compliance burden)
What dashboard do Full Stripe dashboard
sellers see? Limited dashboard
None (your platform IS their experience)Three different plumbing arrangements for how money flows through a platform:
Direct charges
──────────────
Charge happens on the seller's Stripe account.
Platform takes an application fee.
Seller "owns" the charge.
Destination charges
───────────────────
Charge happens on the platform's account.
Money auto-routes to the seller.
Platform "owns" the charge.
Separate charges and transfers
──────────────────────────────
Platform charges first. Transfers to seller later.
Maximum control over timing.
Required when fulfillment is deferred.If there's a gap between payment and fulfillment — tickets bought weeks before an event, work delivered after payment — you need to control when the seller gets paid. This is deferred settlement: hold the funds until the value is delivered, then transfer.
This matters because of chargebacks. If you transfer money to the seller immediately and the customer disputes the charge 45 days later, the money is gone — recovering it from the seller is a legal problem, not a technical one. With deferred settlement, you still hold the funds when the dispute arrives.
Connect is a deep topic. The charge model, settlement timing, onboarding flows, and chargeback defense form an interconnected system. This section tells you what decisions you face. The implementation details — race condition protection, crash-safe settlements, error classification — deserve their own dedicated guide.
A chargeback is not a refund. A refund is when you return money voluntarily. A chargeback is when the customer's bank forcibly reverses the payment. The bank takes the money back. You don't get a choice.
Under Visa and Mastercard rules, customers can dispute charges for up to 120 days after a transaction. The reasons range from "I didn't authorize this" (fraud) to "I didn't receive the product" (fulfillment) to "this wasn't what I expected" (quality).
The dispute lifecycle:
Customer contacts bank ──► Bank reverses payment
│
Stripe debits your account
($amount + ~$15 dispute fee)
│
You submit evidence
(receipts, delivery proof, logs)
│
Bank reviews (60–90 days)
│
┌────┴────┐
▼ ▼
You win You lose
($amount ($amount + $15
+ $15 permanently
returned) gone)The dispute fee is always charged. Win or lose, Stripe debits roughly $15 per dispute. If you win, it's returned. But you're still out the time spent assembling evidence. Prevention is always cheaper than winning disputes.
Evidence matters. Banks follow evidence. Delivery confirmation, signed receipts, IP address logs, email correspondence, and proof of customer engagement dramatically increase win rates. The time to collect evidence is at the point of sale — not when the dispute arrives months later.
3D Secure shifts liability. When a payment completes 3D Secure authentication, liability for fraud-related chargebacks shifts from you to the customer's bank. The customer authenticated — the bank accepted the risk. This doesn't protect against "product not received" disputes, but it eliminates the most common category: unauthorized use.
Stripe's built-in fraud detection runs on every payment. It scores transactions using risk signals — card country vs. IP country, velocity patterns, known fraud indicators from Stripe's network of millions of businesses. You can configure rules: block payments above a risk threshold, require 3D Secure for medium-risk transactions, allow low-risk ones through automatically.
Radar isn't a separate product you opt into. It's running on every charge by default.
Money has flowed from customers into your Stripe balance. The last step: getting it to your real bank account.
A payout moves money from your Stripe balance to your bank account or debit card. By default, Stripe handles this automatically — it sends everything available in your balance on a schedule you choose.
Charges accumulate Payout (automatic)
┌──────────────────┐ ┌──────────────────┐
│ Stripe Balance │─────────►│ Your Bank │
│ $1,247.50 │ │ Account │
└──────────────────┘ └──────────────────┘
Schedule: daily, weekly, or monthly (you choose)
Timing: 2 business days (US), varies by countryTiming. Payouts are not instant by default. Stripe holds funds for a period — typically 2 business days in the US, up to 7 or more days for new accounts or in other countries. This rolling reserve protects against chargebacks and fraud. As your account establishes a track record, timing may improve.
Instant payouts. For an additional fee (typically 1% of the payout amount), you can get money in minutes instead of days. The money goes to an eligible debit card or bank account. Useful when cash flow timing matters more than the fee.
Payout failures. If your bank rejects a payout — wrong account number, closed account, name mismatch — the money returns to your Stripe balance. Stripe emits a payout.failed webhook. You fix the bank account details, and the next payout goes through.
Manual vs. automatic. By default, Stripe pays out everything available on your chosen schedule. You can switch to manual payouts for more control — holding funds for deferred settlement in a marketplace, for example. With manual payouts, money stays in your Stripe balance until you explicitly create a payout.
You now have the complete mental model. Every Stripe product maps to one of four layers: the pipes that move money, the interface buyers interact with, the business model you're operating, and the operations that happen after.
Start with the pipes — Payment Intents, webhooks, and Payment Methods are non-negotiable regardless of what you're building. Then pick your interface (Checkout for most, Elements for brand control) and your business model (one-time, subscription, or marketplace). The map tells you which sections to focus on. The layers tell you what order to build in.