Volley / Templates / Stripe Checkout
Payments templateStripe Checkout sequence diagram
The whole hosted Checkout flow, from the Buy button to fulfilment, with the session request, both redirects and the checkout.session.completed webhook on their arrows.
Click any arrow in the editor to see its request and response as a table or the raw body. Change the hosts, ids and fields to match your integration, then share a link or embed it in your README.
Step by step
- The browser asks your server for a checkout. It sends the price and quantity, never an amount the customer could edit.
- Your server creates a Checkout Session. A form-encoded
POST /v1/checkout/sessionswithmode,line_items,success_urlandcancel_url. Stripe answers with the session, including itsurl. - The browser goes to Stripe. A 303 redirect to the session URL, where the customer pays on Stripe's page.
- Stripe sends the customer back to your
success_url, with the session id filled in for{CHECKOUT_SESSION_ID}. - Stripe calls your webhook.
checkout.session.completedarrives with aStripe-Signatureheader. Verify it, fulfil the order and answer 200.
Easy to get wrong
- Fulfil from the webhook, not the success page. Customers close tabs, and the redirect can fail.
- Webhooks can arrive more than once and out of order. Fulfil once per session id.
- Verify the signature against the raw request body. Parsing the JSON first changes the bytes and the check fails.
The source
The whole diagram is this plain text. Copy it into your repo, or open it in the editor.
stripe-checkout.volley
title: Stripe Checkout with a fulfilment webhook
order: Customer, Browser, Server, Stripe
Customer -> Browser: Click **Buy now**
Browser -> Server: POST /checkout
in: Cart
```json
{ "price": "price_1QAbc2Def3", "quantity": 1 }
```
Server -> Stripe: POST /v1/checkout/sessions
in: Session request (form encoded)
mode: payment
line_items[0][price]: price_1QAbc2Def3
line_items[0][quantity]: 1
success_url: https://shop.example.com/success?session_id={CHECKOUT_SESSION_ID}
cancel_url: https://shop.example.com/cart
Stripe --> Server: 200 OK
out: Checkout Session
```json
{
"id": "cs_test_a1B2c3D4",
"object": "checkout.session",
"status": "open",
"payment_status": "unpaid",
"url": "https://checkout.stripe.com/c/pay/cs_test_a1B2c3D4"
}
```
Server --> Browser: 303 to Stripe Checkout
out: Redirect
Location: https://checkout.stripe.com/c/pay/cs_test_a1B2c3D4
Browser -> Stripe: GET /c/pay/cs_test_a1B2c3D4
...: {fas-credit-card} Customer enters a card and pays
Stripe --> Browser: 303 to the success URL
out: Redirect
Location: https://shop.example.com/success?session_id=cs_test_a1B2c3D4
Browser -> Server: GET /success?session_id=cs_test_a1B2c3D4
Server --> Browser: Thank-you page
note: Do not fulfil the order here. The customer can close the tab before this page loads.
=: {fab-stripe} Fulfilment
Stripe -> Server: POST /webhooks/stripe (checkout.session.completed)
in: Event
```http
POST /webhooks/stripe HTTP/1.1
Content-Type: application/json
Stripe-Signature: t=1760608800,v1=<redacted>
{
"id": "evt_1QAbc9",
"type": "checkout.session.completed",
"data": {
"object": {
"id": "cs_test_a1B2c3D4",
"payment_status": "paid",
"amount_total": 4900,
"currency": "usd",
"customer_details": { "email": "ada@example.com" }
}
}
}
```
Server -> Server: Verify the signature and fulfil the order
note: Verify `Stripe-Signature` with the endpoint's signing secret, then fulfil once per session id. Stripe can deliver the same event more than once.
Server --> Stripe: 200 OK