Oliver Wolfson
ServicesProjectsContact

Development Services

SaaS apps · AI systems · MVP builds · Technical consulting

Services·Blog
© 2025 O. Wolf. All rights reserved.
Stripe Invoices & Payment Intents: The Beating Heart of Billing
A deeply readable explanation of Stripe invoices and payment intents—the core financial engine behind subscriptions and payments.
November 24, 2025•O. Wolfson

Why Invoices Decide Everything — and Why Developers Should Treat Them with Respect

If you want to understand Stripe at a deep, almost medical level, you must understand invoices and payment intents. These two objects form the cardiovascular system of Stripe billing. Subscriptions, Checkout, and payment methods are important—but invoices and payment intents are the machinery that actually moves money through the system.

Without a clear mental model for how these two interact, developers often misinterpret errors, grant access too early, or chase down mysterious “payment failed” bugs that turn out to be completely predictable once you understand the underlying process.

This article is your calm, readable tour of Stripe’s billing engine, written in a way that even a screen reader can read aloud like a financial bedtime story.


Invoices: The Financial Event That Drives Everything

In Stripe, money does not magically transfer from customer to business. A subscription does not charge on its own. A Checkout session does not finalize the billing state.

Instead, Stripe relies on invoices.

An invoice represents a financial obligation. It states:

  • what the customer owes,
  • when they owe it,
  • and which payment method Stripe should use.

It’s not just a PDF or an email receipt. It’s the actual brain of the billing system.

Invoices are created for:

  • recurring subscription charges
  • prorations
  • trials ending
  • upgrades/downgrades
  • usage-based charges
  • extra seats
  • off-schedule billing (e.g., mid-month add-ons)

Every meaningful action that involves charging a customer eventually turns into an invoice.


Invoice Lifecycle: The Important States (in Human Language)

Here’s the true lifecycle, stripped of jargon:

1. Draft

Stripe is preparing the invoice. Nothing has been attempted yet.

2. Open

Stripe says, “This invoice must now be paid.” A payment intent is created.

3. Paid

The charge succeeded. Money moved. Stripe is pleased.

4. Past Due

Stripe attempted payment, but it failed. You have entered the realm of retries and bank mysteries.

5. Uncollectible or Voided

The invoice is not going to be paid. You either gave up or cancelled it intentionally.

This lifecycle is critical for understanding how access control should be updated. Most developers ignore these states—and then wonder why customers have access when their card failed two weeks ago.


Payment Intents: The Actual Attempt to Move Money

If invoices are the brain, payment intents are the hands.

A payment intent is:

  • the actual attempt to charge a card
  • the thing that can succeed or fail
  • the object that handles authentication (like 3D Secure)
  • the state machine that tracks retry logic

A single invoice may reference a single payment intent, which can have multiple attempts depending on fallback or authentication.

A payment intent can be:

  • requires_payment_method
  • requires_confirmation
  • requires_action
  • processing
  • succeeded
  • canceled

Most billing bugs arise because developers assume a payment intent becomes succeeded immediately. In reality, it might require hours, retries, or customer intervention.


Why Subscriptions Depend Entirely on Invoices

Stripe subscriptions do not charge customers directly.
They merely schedule invoices.

At every billing interval:

  1. A subscription generates an invoice.
  2. The invoice creates a payment intent.
  3. Stripe attempts to charge the payment method.
  4. Stripe emits events telling you what actually happened.
  5. Your system updates access accordingly.

This is why you should never trust subscription status without confirming the invoice events.


Trials, Upgrades, Downgrades — All Powered by Invoices

Even non-financial events like upgrading mid-cycle depend on invoices.

For example:

Upgrades

Stripe prorates the remaining time and generates an immediate invoice for the difference.

Downgrades

Stripe modifies future invoices, and may produce prorated credits.

Trials ending

Stripe creates the first invoice and attempts payment.

The invoice is always the authoritative source of truth for whether money was collected, scheduled, or declined.


Why Invoices Are the Key to Access Control

A common mistake:

“The subscription object says active, so the user gets access!”

Unfortunately, subscriptions can be “active” even when the invoice fails.
This usually happens during:

  • trial conversions
  • billing cycles involving 3D Secure
  • payment method failures
  • asynchronous retries

The correct approach is:

  • If invoice.paid: grant access
  • If invoice.payment_failed: restrict access
  • If customer.subscription.deleted: remove access

This prevents the infamous “free premium plan” bug.


Webhooks: Where Invoice Truth Arrives

Stripe uses webhooks to announce the financial outcome of invoices and payment intents.

The most important events are:

  • invoice.paid
  • invoice.payment_failed
  • payment_intent.succeeded
  • payment_intent.payment_failed
  • invoice.finalized

If you ignore these webhooks—or process them incorrectly—your billing system will be permanently confused.


Real-World Example: A Successful Charge

Let’s walk through what actually happens, step by step.

  1. Subscription renews.
  2. Stripe generates an invoice.
  3. Invoice generates a payment intent.
  4. Payment intent attempts charge.
  5. Bank approves.
  6. Stripe marks payment intent as succeeded.
  7. Stripe marks invoice as paid.
  8. Stripe emits invoice.paid webhook.
  9. Your app updates the user’s access.

Only the last step is your responsibility.
Stripe handles the financial machinery.


Real-World Example: A Failed Charge

Now the other scenario:

  1. Subscription renews.
  2. Stripe generates an invoice.
  3. Payment intent attempts charge.
  4. Bank declines.
  5. Stripe marks invoice as past_due.
  6. Stripe schedules retries.
  7. Stripe sends invoice.payment_failed.
  8. Your app restricts access.

If the customer updates their card:

  1. Stripe retries payment.
  2. If successful, Stripe emits invoice.paid.
  3. Access is restored.

Billing becomes predictable when you anchor everything on invoices and payment intents.


Closing Thoughts

Invoices and payment intents are the beating heart of Stripe’s system. They are the definitive record of what the customer owes and whether money was successfully collected.

Once you understand:

  • invoice lifecycle
  • payment intent states
  • how subscriptions depend on invoices
  • the role of webhooks in conveying truth

Stripe becomes dramatically easier to reason about.

Most teams try to understand billing by staring at subscriptions.
Professionals understand billing by following the invoices.

If you grasp this principle, the rest of Stripe billing will fall neatly into place, and your system will become stable, predictable, and refreshingly uneventful—exactly what a billing system should be.