Task flow · Product purchase

Product purchase

A visitor buys a framework, a seat, or a membership from the catalogue and settles through Stripe Checkout.

Status Implemented Last reviewed 2026-08-06 All task flows →

Flow

flowchart TD
      A["Visitor opens product detail"] --> B{"Payable price?"}
      B -- No --> C["Show CTAs only"]
      B -- Yes --> D["Render checkout organism"]
      D --> E["Visitor selects plan + identity + consent"]
      E --> F["POST /paiement/produit/<slug>"]
      F --> G{"Stripe configured?"}
      G -- No --> H["503 unavailable"]
      G -- Yes --> I{"Throttle ok?"}
      I -- No --> J["Error: too many pending"]
      I -- Yes --> K["Create Payment row
freeze terms version"] K --> L["Create Stripe Checkout session"] L --> M["Redirect to Stripe hosted page"] M --> N{"Buyer action"} N -- "Pays" --> O["Stripe redirects to /retour/"] N -- "Cancels" --> P["Stripe redirects to /annule/"] O --> Q{"Webhook arrived?"} Q -- No --> R["Show: confirmation en cours"] Q -- Yes --> S["Show: paid + entitlements"] P --> T["Show: nothing was charged"] S --> U["Grant entitlements"] U --> V["Open next instalment if plan has one"]

Payment states and transitions

StateSet byNext
PENDINGView creates the row→ PROCESSING (checkout started)
PROCESSINGView after Stripe session created→ PAID (webhook) or FAILED (provider error)
PAIDWebhook only→ settled; entitlements granted
CANCELLEDBuyer action on Stripe→ terminal; row stays open
FAILEDProvider outage or invalid signature→ terminal; owner may retry
EXPIREDTime or terms window→ terminal; owner must reissue

Security and integrity rules

RuleEnforcement
Amount authorityServer reads ProductPrice.amount_eur; browser input is never charged.
Terms freezeterms_version is stamped at payment creation; later CGV updates do not affect open payments.
Settlement authorityOnly the signed Stripe webhook writes PAID.
CSRF exemptionThe webhook exempts CSRF because it authenticates by Stripe signature.
ThrottlingMax 10 pending payments per email per hour.
Reference secrecyPayment references are unguessable.
No card data on serverStripe hosts the checkout page.
Robots exclusion/paiement/ is blocked by robots.txt.

Validation and recovery

ConditionSystem response
Stripe is not configured503 with a plain-language notice. No checkout session is created.
Product has no payable priceThe checkout form is not rendered; existing CTAs remain.
Email is throttledError message; redirect back to product page. No row is created.
Webhook signature is invalid400; nothing is written. Stripe retries.
Payment is already settledRedirect to the return page; no second charge is created.

Entitlement and delivery

When the webhook records a settled payment, grant_entitlements() creates Entitlement rows for every product purchased. One entitlement is minted per (payment, product) pair.

The buyer receives:

  • a Stripe receipt by email;
  • a personal delivery link (/acces/<token>/) that grants download or session access;
  • for instalment plans, a second payment link opened automatically by the webhook once the first settles.

Subscriptions additionally receive a link to the Stripe customer portal for cancellation, card updates, and invoice history.