Task flow · Office Hours seat
Office Hours seat
A visitor buys a payable tier or joins a free waitlist for Office Hours sessions.
Flow
flowchart TD
A["Visitor opens /ressources/office-hours/"] --> B["Browse tiers and sessions"]
B --> C["Open tier / session detail"]
C --> D{"Product type?"}
D -- "office_hours" --> E{"Payable price?"}
E -- Yes --> F["Render checkout form"]
F --> G["POST /paiement/produit/<slug>"]
G --> H["Same flow as product purchase"]
E -- No --> I["Render waitlist form"]
I --> J["POST registration"]
J --> K["Store WebinarInterest + assign cohort"]
D -- "cohort_access" --> L["Render lead form"]
L --> M["POST cohort application"]
M --> N["Store lead; no payment"]
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. |
| Settlement authority | Only the signed Stripe webhook writes PAID. |
| No card data on server | Stripe hosts the checkout page. |
| Throttling | Max 10 pending payments per email per hour. |
Validation and recovery
| Condition | System response |
|---|---|
| Product has no payable price | The checkout form is not rendered; waitlist form appears instead. |
| Email is throttled | Error message; redirect back to Office Hours 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 the corresponding Entitlement row. The buyer receives a Stripe receipt by email and a personal delivery link.