Task flow · Product purchase
Product purchase
A visitor buys a framework, a seat, or a membership from the catalogue and settles through Stripe Checkout.
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
| State | Set by | Next |
|---|---|---|
| PENDING | View creates the row | → PROCESSING (checkout started) |
| PROCESSING | View after Stripe session created | → PAID (webhook) or FAILED (provider error) |
| PAID | Webhook only | → settled; entitlements granted |
| CANCELLED | Buyer action on Stripe | → terminal; row stays open |
| FAILED | Provider outage or invalid signature | → terminal; owner may retry |
| EXPIRED | Time or terms window | → terminal; owner must reissue |
Security and integrity rules
| Rule | Enforcement |
|---|---|
| Amount authority | Server reads ProductPrice.amount_eur; browser input is never charged. |
| Terms freeze | terms_version is stamped at payment creation; later CGV updates do not affect open payments. |
| Settlement authority | Only the signed Stripe webhook writes PAID. |
| CSRF exemption | The webhook exempts CSRF because it authenticates by Stripe signature. |
| Throttling | Max 10 pending payments per email per hour. |
| Reference secrecy | Payment references are unguessable. |
| No card data on server | Stripe hosts the checkout page. |
| Robots exclusion | /paiement/ is blocked by robots.txt. |
Validation and recovery
| Condition | System response |
|---|---|
| Stripe is not configured | 503 with a plain-language notice. No checkout session is created. |
| Product has no payable price | The checkout form is not rendered; existing CTAs remain. |
| Email is throttled | Error message; redirect back to product page. No row is created. |
| Webhook signature is invalid | 400; nothing is written. Stripe retries. |
| Payment is already settled | Redirect 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.