Glossary

Stripe Checkout Session

A Stripe Checkout Session is a single, expiring instance of Stripe’s hosted payment page, created for one customer and one basket of line items, which may or may not go on to collect any money.

Also called: Stripe Checkout, checkout session, checkout.session, cs_

Definition

A Checkout Session is a page, not a payment. Stripe creates one when you want to send a customer somewhere to pay, it carries the line items and the configuration for that specific attempt, and it has a `url` that Stripe documents as "only present when the session is active".

The object that makes this concrete is `expires_at`, the timestamp at which the session will expire. Sessions are perishable by design. A customer who opens your checkout page and closes the tab leaves behind a real, queryable object with a real id, a real basket, and no money attached to it whatsoever.

It sits one level above the [PaymentIntent](/glossary/stripe-payment-intent). In `payment` mode the session holds the id of the PaymentIntent it created, and Stripe notes that you cannot confirm or cancel that PaymentIntent directly: to cancel, you expire the Checkout Session instead. The session drives the intent, the intent attempts the collection, and a charge is what records money moving.

So of the three, the Checkout Session is the object furthest from your books. It is worth understanding precisely because people reach for it as a sales number, and it is the one object in the chain that can exist in quantity with nothing behind it.

Key points

  • +A single instance of Stripe’s hosted payment page, created per customer per attempt.
  • +Has an `expires_at` timestamp. Abandoned sessions are real objects with no money attached.
  • +Two independent status fields: `status` (open, complete, expired) and `payment_status` (paid, unpaid, no_payment_required).
  • +`status: complete` does not mean paid. Stripe states payment processing may still be in progress.
  • +Runs in one of three modes: `payment`, `setup`, or `subscription`.
  • +Identified by an id beginning `cs_`. It produces a PaymentIntent (`pi_`), which produces a charge (`ch_`).

Two status fields, and why they are not the same question

The Checkout Session carries two separate status fields, and reading one as though it were the other is the most common mistake made with this object.

`status` describes the page. Stripe documents three values. `open` means "the checkout session is still in progress. Payment processing has not started". `complete` means "the checkout session is complete. Payment processing may still be in progress". `expired` means "the checkout session has expired. No further processing will occur".

`payment_status` describes the money, and takes one of `paid`, `unpaid`, or `no_payment_required`. Stripe defines `paid` as "the payment funds are available in your account" and `unpaid` as "the payment funds are not yet available in your account". `no_payment_required` covers `setup` mode sessions and sessions using a billing cycle anchor where payment is collected later.

The combination that matters is `complete` plus `unpaid`. The customer finished the page, the session is done, and the money has not landed. Stripe says as much in its own definition of `complete`. This is why Stripe attaches the fulfilment advice to the payment field rather than the page field: it says of `payment_status` that "you can use this value to decide when to fulfill your customer’s order". If you are deciding anything financial, the page status is not the field to read.

The abandoned session, and why it is not a number you can book

Every hosted checkout produces abandoned sessions. Someone clicks through, sees the total, and leaves. That session sits in your Stripe account as a complete object with line items and an amount, and none of it is revenue.

Stripe models the recovery of these deliberately. `after_expiration` configures what happens when a session expires, and a session created through a recovery flow carries `recovered_from`, the id of the original expired session that triggered it. That is a strong signal about how to read the object: Stripe expects sessions to be created, lost, and recreated, and it gives you the lineage to follow.

The accounting consequence is simple and worth stating flatly. Counting Checkout Sessions overstates sales, in some businesses dramatically. `amount_total` on a session is what the basket came to, not what anyone paid. The countable, bookable object is the charge underneath it, and a session that never produced a charge produced nothing your books should ever see.

This also cuts the other way for reconciliation. If a customer insists they paid and you cannot find the money, an expired session with their email on it is a useful thing to find. It tells you the attempt existed and did not complete, rather than leaving you hunting for a payment that was never made.

What the session creates, and what carries the numbers

In `payment` mode, the session produces a PaymentIntent, and Stripe stores its id on the session. In `subscription` mode it produces a Subscription. In `setup` mode it produces a SetupIntent and collects no money at all, which is the case `no_payment_required` exists for.

A session can also produce an invoice. The `invoice` field holds the id of the invoice created by the Checkout Session, if it exists, and `invoice_creation` configures whether that happens. This matters for bookkeeping shape more than most fields on the object: an invoice-producing Checkout Session lands in your books through the invoice path, and one without an invoice lands through the charge path.

None of these objects carry the numbers a bookkeeper actually needs. The gross amount, the [fee](/glossary/stripe-fee), the net, and the date the funds become available all live on the [balance transaction](/glossary/stripe-balance-transaction) attached to the charge, several objects downstream of the page the customer saw.

One field on the session is genuinely useful for reconciliation, though. `client_reference_id` is a string you set yourself, and Stripe describes it as something that "can be used to reconcile the Session with your internal systems". If you run an order id through it, you have a thread from your own records to the Stripe object.

How Acodei treats a Checkout Session

No Checkout Session event creates a QuickBooks record in Acodei. The sales path keys on charge events: `charge.succeeded` and `charge.captured` are what route to a job and produce a sales record, with the record type decided by your account settings. An abandoned or expired session therefore produces nothing in QuickBooks, which is the correct outcome rather than a gap, because no money moved.

Where the session does show up is product mapping, and this is the part worth knowing. Acodei’s description-based mapping reads from a different place depending on how the charge arrived. On a charge not linked to a Stripe invoice, it reads the charge or payment description first and the [PaymentIntent](/glossary/stripe-payment-intent) second. On a charge linked to a Stripe invoice, it reads the invoice line item description. And on a charge linked to a Stripe Checkout session, Acodei’s product documentation gives its own two-step order: the product description first, then the Checkout session line item description.

That third branch is why a Checkout-driven charge can map to a different QuickBooks product than an otherwise identical charge created another way. If you have description rules that fire correctly for your API-created charges and mysteriously do not fire for your hosted-checkout sales, this ordering is the first thing to check.

Metadata mapping does not have a Checkout branch. Acodei reads metadata for mapping in a fixed order: payment or charge, then PaymentIntent, then invoice, then subscription. The Checkout Session is not in that list. So metadata your platform writes to the session itself is not what Acodei matches on, and metadata written to the charge or the subscription is. That distinction catches people whose checkout integration attaches everything useful to the session and nothing to the objects underneath it.

Want to see this on your own Stripe data?

Start a free trial

Frequently asked questions

What is a Stripe Checkout Session?

It is a single instance of Stripe’s hosted payment page, created for one customer and one set of line items. It has its own id beginning `cs_`, an expiry timestamp, and a url that is only present while the session is active. It is a page with a lifetime, not a payment, and it may never collect any money.

Does a completed Checkout Session mean the customer paid?

No. `status: complete` refers to the page, and Stripe’s own definition notes that payment processing may still be in progress. The field that describes the money is `payment_status`, which is `paid`, `unpaid`, or `no_payment_required`. A session can be complete and unpaid at the same time, so `payment_status` is the field to read before anything financial happens.

What is the difference between a Checkout Session and a PaymentIntent?

The Checkout Session is the hosted page, the PaymentIntent is the attempt to collect. In payment mode the session creates the intent and stores its id, and Stripe notes you cannot confirm or cancel that intent directly: you expire the session instead. The chain runs hosted page, then collection attempt, then charge, and only the charge records money moving.

Do abandoned Checkout Sessions appear in QuickBooks?

No. Acodei’s sales records key on charge events, so a session that never produced a charge produces no QuickBooks record. That is the right result: an abandoned session has no money attached to it. It also means the count of Checkout Sessions in Stripe will always be higher than the count of sales in your books, and it should be.

Why does my description mapping rule not fire on Checkout sales?

Because Checkout-linked charges use their own lookup order. Acodei reads the product description first and the Checkout session line item description second, which is different from the non-invoice order (charge or payment description, then PaymentIntent) and from the invoice order (invoice line item description). A rule written against the text you see on a non-Checkout charge may simply not be looking at the same field.

Can I map on metadata I set on the Checkout Session?

Not for metadata mapping. Acodei reads mapping metadata from the payment or charge, then the PaymentIntent, then the invoice, then the subscription. The Checkout Session is not in that order. If your integration writes its useful metadata to the session, move it onto the charge or the subscription, or map on the Checkout session line item description instead.

What customers say about running Stripe through Acodei

Stripe Verified Partner BadgeQuickBooks Intuit Badge
If you're testing out all the different Stripe/QuickBooks integration apps right now, let me save you some time. This one is the best one by far.
RyanOwner at Indie Music Academy
Works well and is really helpful for massive transactions. The support is really fast and helpful. 100% recommended.
AndresCo-founder and CEO at Kanguro Collections and Reinsurance

Ready to try Acodei?

Connect Stripe to QuickBooks Online in minutes and let the fees, refunds, and payouts land where your accountant expects them.