Customer link lifecycle
Customer links let a POS operator share a checkout URL. The customer fills products/shipping (and optionally chats), then the store converts the submission into a POSOrder. Catalog links are a one-shot picker that creates a customer link already in Submitted.
Models & helpers: customerlink/models.py (backend-main/customerlink/models.py), customerlink/status.py (backend-main/customerlink/status.py). Public routes: customerlink/urls.py (backend-main/customerlink/urls.py). Checkout UI: printbazar-checkout/.
Status machine
| Status | Who sets it | Meaning |
|---|---|---|
Empty | POS create | Link issued; customer has not finished checkout |
Submitted | Customer submit (or catalog submit) | Shipping / payment screenshot / line data captured |
Order Placed | POS order create with customerLinkId | Converted to POSOrder; link stays for tracking/chat |
Archived | Manual archive, order archive sync, or restore inverse | Hidden from live lists; public APIs return 410 when appropriate |
stateDiagram-v2
[*] --> Empty: POS creates link
Empty --> Submitted: customer submit / step3
Empty --> Archived: archive unused
Submitted --> Order_Placed: POS places POSOrder
Submitted --> Archived: archive
Order_Placed --> Archived: order or link archive
Archived --> Empty: restore unconverted
Archived --> Submitted: restore unconverted
Archived --> Order_Placed: restore converted / with order
Apply transitions with apply_customer_link_status so archived_from_status / archived_at stay correct and SSE clients get status_change events.
Classic customer link flow
- Create (Empty) — authenticated POS
POST /api/customer-links/with customer name + products (CustomerLinksView(backend-main/customerlink/views.py)). Receiveslink_id,link_code, and checkout URL (CustomerLink.url). - Customer steps — public endpoints under
/api/checkout/<pos_slug>/<link_code>/:step1/step2/step3— progressive completion flagssubmit/—CustomerLinkSubmitSerializer(backend-main/customerlink/serializers.py) writes shipping + screenshot and setsSubmitted
- Order Placed — POS creates an order including
customerLinkId.POSOrderCreateSerializer(backend-main/pos/serializers.py) sets the link toOrder Placed, attachesPOSOrder.customer_link, and emits SSE (notify_link_status_change,notify_link_submission). - Fulfillment — subsequent order/workflow updates may surface to the checkout client as approval/status SSE while the link remains
Order Placed.
Catalog link variant
CatalogLink is a live catalog slice (categories / collections / products + optional prices). Public browse + POST …/catalog/<link_code>/submit/ (CatalogLinkSubmitView (backend-main/customerlink/catalog_views.py)):
- Rejects if already
consumed_at(410). - Creates
CustomerLinkwithsource=catalog, statusSubmitted, steps marked complete, line items + design snapshots. - Links
CatalogLink.customer_linkand marks consumed.
POS then places the order the same way (→ Order Placed).
Chat & SSE overview
Comments are CustomerLinkComment rows (Admin / Customer / System). REST:
- POS/admin:
/api/customer-links/<link_id>/comments/ - Public:
/api/checkout/<pos_slug>/<link_code>/comments/
Live updates use Server-Sent Events in customerlink/sse.py (backend-main/customerlink/sse.py) (in-memory fan-out; multi-process needs external pub/sub — Verify deploy story):
| Stream | Path | Audience |
|---|---|---|
| Per-link (auth) | /api/customer-links/<link_id>/comments/stream/ | POS / staff |
| Global link events | /api/customer-links/stream/ | POS board |
| Public per-link | /api/checkout/<pos_slug>/<link_code>/comments/stream/ | Checkout app |
Event types include roughly:
new_comment— chat messagestatus_change— Empty / Submitted / Order Placed / Archivedapproval_change— design-approval workflow mirrored for the customer UI
Helpers: notify_new_link_comment, notify_link_status_change, notify_link_approval_change, plus submission notify used after order conversion.
Security note: some SSE clients pass POS access_key as a query param (report.md §3.5). Prefer headers for new integrations.
Archive coupling with orders
When a converted order is archived, _sync_linked_customer_link archives the link. Restoring either side can restore the other via pos/archive.py (backend-main/pos/archive.py) (restore_archived_customer_link / restore_archived_order). Admin restore endpoint: /api/admin/customer-links/<link_id>/restore/.
Archived public checkout responses use archived_gone_response() (HTTP 410).
Related
- Domain model —
CustomerLink/CatalogLinkfields - Order lifecycle — payment gate into Processing after conversion
- Architecture overview — checkout ↔
/api/placement