Skip to main content

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​

StatusWho sets itMeaning
EmptyPOS createLink issued; customer has not finished checkout
SubmittedCustomer submit (or catalog submit)Shipping / payment screenshot / line data captured
Order PlacedPOS order create with customerLinkIdConverted to POSOrder; link stays for tracking/chat
ArchivedManual archive, order archive sync, or restore inverseHidden 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.

  1. Create (Empty) — authenticated POS POST /api/customer-links/ with customer name + products (CustomerLinksView (backend-main/customerlink/views.py)). Receives link_id, link_code, and checkout URL (CustomerLink.url).
  2. Customer steps — public endpoints under /api/checkout/<pos_slug>/<link_code>/:
    • step1 / step2 / step3 — progressive completion flags
    • submit/ — CustomerLinkSubmitSerializer (backend-main/customerlink/serializers.py) writes shipping + screenshot and sets Submitted
  3. Order Placed — POS creates an order including customerLinkId. POSOrderCreateSerializer (backend-main/pos/serializers.py) sets the link to Order Placed, attaches POSOrder.customer_link, and emits SSE (notify_link_status_change, notify_link_submission).
  4. Fulfillment — subsequent order/workflow updates may surface to the checkout client as approval/status SSE while the link remains Order Placed.

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 CustomerLink with source=catalog, status Submitted, steps marked complete, line items + design snapshots.
  • Links CatalogLink.customer_link and 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):

StreamPathAudience
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 message
  • status_change — Empty / Submitted / Order Placed / Archived
  • approval_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).