Cart¶
Persist and sync the authenticated user’s shopping cart with the backend. Line items mirror order placement rules: each row is either a product or a variant, not both.
Frontend sync flow¶
-
On app load (logged in)
CallGET /api/orders/cart/me/and replace local cart state with the responseitems(and optionally showsubtotal). -
When the user changes the cart
Debounce updates, then callPUTorPOST/api/orders/cart/sync/with the full desireditemslist. The server replaces all cart items with the payload (idempotent full sync). -
After checkout
WhenPOST /api/orders/orders/succeeds, the backend clears that user’s cart. Refresh local state from the create response or callGET .../cart/me/again (cart will be empty). -
Explicit empty cart
OptionallyDELETE /api/orders/cart/clear/to remove all items without sending a fullitemsarray.
Endpoints¶
Creates an empty cart for the user if none exists yet.
Success (200):
{
"id": 1,
"user": 42,
"items": [
{
"id": 10,
"product": 5,
"variant": null,
"quantity": 2,
"product_name": "Vitamin C 1000mg",
"variant_description": "",
"barcode": "",
"unit_price": "20.00",
"line_total": "40.00"
},
{
"id": 11,
"product": null,
"variant": 3,
"quantity": 1,
"product_name": "Omega-3",
"variant_description": "Size: 60 caps",
"barcode": "1234567890",
"unit_price": "15.50",
"line_total": "15.50"
}
],
"subtotal": "55.50",
"created_at": "2026-04-20T08:00:00Z",
"updated_at": "2026-04-22T12:30:00Z"
}
unit_pricefollows the same rule as orders: variant override price, else product price.subtotalis the sum of line totals for all items.
Response fields (same object shape for GET /cart/me/, and for PUT|POST /cart/sync/ and DELETE /cart/clear/ success bodies):
| Field | Type | Notes |
|---|---|---|
id |
integer | Cart id |
user |
integer | Owner user id |
items |
array | Line items (see below) |
subtotal |
decimal / number | Sum of line totals |
created_at |
string (ISO 8601) | When the cart row was created |
updated_at |
string (ISO 8601) | Last update (used for abandoned-cart timing) |
Each element of items:
| Field | Type | Notes |
|---|---|---|
id |
integer | Cart line id |
product |
integer or null |
Product pk when line is product-level |
variant |
integer or null |
Variant pk when line is variant-level |
quantity |
integer | Quantity |
product_name |
string | Resolved product name |
variant_description |
string | Option text for variants; empty for product-only lines |
barcode |
string | Variant or product barcode snapshot |
unit_price |
decimal / number | Price per unit |
line_total |
decimal / number | unit_price × quantity |
Request body:
Rules:
- Each element must include
quantity(integer ≥ 1). - Include
productorvariant, not both. - Duplicate product or variant IDs in one request should be avoided; the database enforces one row per product/variant per cart—prefer merging quantities on the client before sync.
Success (200): same shape as GET .../cart/me/.
Syncing also resets abandoned-cart reminder tracking for that cart (see below).
Relation to orders¶
- Placing an order uses
POST /api/orders/orders/withitemsin the same product/variant shape as the cart sync payload. - After a successful order create, the user’s server-side cart is cleared automatically.
- See also:
api/orders.md
Abandoned cart reminders (ops)¶
The backend can email users who leave items in the cart without purchasing. Reminders are sent at 12 hours, then 24 hours, then 48 hours of cart inactivity (based on updated_at), then no further emails for that cart cycle.
- Template: set environment variable
ZEPTOMAIL_CART_REMINDER_TEMPLATE_KEYto your ZeptoMail template key. If unset, reminder sending is skipped. -
Background processing (django-background-tasks): reminder work runs in the DB-backed task queue, not inline in the web process.
- Install the package and add
background_tasktoINSTALLED_APPS(already included in this project). - Apply migrations (creates
background_tasktables):python src/manage.py migrate -
Once per environment (after deploy), schedule the repeating job:
Interval between runs defaults to 900 seconds (15 minutes); override with env
ABANDONED_CART_REMINDER_BG_INTERVAL_SECONDS. -
Run a worker continuously (Supervisor, systemd, etc.):
Optional: queue a single run without setting up repeat (still needs
process_tasks): - Install the package and add
-
Inspect queue (staff API): django-background-tasks stores work in the DB. JWT required; user must be staff. This reflects the task worker schedule (when jobs run), not ZeptoMail’s outbound mail queue.
List queued / due tasks
Query params (list only):
task_name(substring match ontask_name),queue(exact),limit(default 200, max 500). Response: JSON array of task objects (list) or one object (retrieve).Fields on each queued task object
Field Type Notes idinteger Task row id task_namestring Registered function path (e.g. orders.background_tasks.run_abandoned_cart_reminders_task)verbose_namestring or nullOptional display name run_atstring (ISO 8601) or nullNext scheduled execution time repeatinteger Repeat interval in seconds ( 0= no repeat)repeat_interval_labelstring Human label, e.g. never,hourly, orevery 900 secondsfor custom intervalsrepeat_untilstring (ISO 8601) or nullEnd of repeat window, if set queuestring or nullQueue name attemptsinteger How many times execution was attempted failed_atstring (ISO 8601) or nullSet when the task is marked failed in-queue last_error_previewstring Truncated error text (up to ~2000 chars + …)locked_atstring (ISO 8601) or nullWhen a worker claimed the task locked_bystring or nullWorker id (e.g. process id as string) locked_by_pid_runningboolean or nullWhether the locking process still appears alive statusstring One of: scheduled(run_atin the future),due(waiting for worker),running,failedseconds_until_runinteger or nullSeconds until run_atwhenstatusisscheduled; otherwisenullList completed task runs
Same query params as list:
task_name,queue,limit(default 100, max 500). Response: JSON array of completed task objects.Fields on each completed task object
Field Type Notes idinteger Completed task row id task_namestring Same meaning as queued tasks verbose_namestring or nullOptional display name run_atstring (ISO 8601) Recorded completion / run time repeatinteger Repeat interval in seconds at completion repeat_interval_labelstring Same as queued tasks repeat_untilstring (ISO 8601) or nullRepeat window end, if any queuestring or nullQueue name attemptsinteger Attempt count for that run failed_atstring (ISO 8601) or nullIf set, run ended in failure last_error_previewstring Truncated error text when failed locked_atstring (ISO 8601) or nullLast lock snapshot locked_bystring or nullLast worker id locked_by_pid_runningboolean or nullPID check on archived row statusstring succeedediffailed_atis null, elsefailed -
User activity: any successful
syncorclearupdates the cart and resets the reminder schedule for the next abandonment window.
ZeptoMail merge_info keys¶
The reminder email builder sends (among others): name, team, support_email, support_phone, current_year, reminder_hours ("12", "24", or "48"), item_count, subtotal, currency, cart_items (JSON array of { name, quantity, unit_price, line_total }), cart_items_html (pre-rendered table rows for the template), and cart_url (from env FRONTEND_CART_URL, or # if unset).
A ready-to-paste HTML layout lives at src/docs/email-templates/zeptomail-abandoned-cart-reminder.html. If ZeptoMail escapes {{cart_items_html}}, configure that merge field as HTML/raw per ZeptoMail’s docs.
Related docs¶
- Orders:
api/orders.md - Payments:
api/payments.md