Skip to content

Cart and checkout

From a member's cart to a placed order. The cart lives in the users module; placing the order hands off to orders, which talks to the WMS, Biteship and DOKU.

How this page was verified

Read on 2026-09-15 from the backend cart service (src/users/services/members-carts.service.ts), the order service (src/orders/services/orders.service.ts), and the storefront /checkout/shipping flow.

The cart (backend)

A member has one cart (member_carts) with items (member_cart_items) and chosen shipping methods (member_cart_shipping_methods). Storefront resolvers (storefront-members-carts, all front*, guarded by JwtMemberAuthGuard):

  • frontMemberCart — read the cart.
  • frontUpdateCartItem, frontDeleteCartItem, frontAddCartItemToTemporaryCart (buy-now).
  • frontUpdateCartAddress — set the delivery address.
  • frontCouriers — courier options for the cart (backend calls Biteship rates). frontUpdateCartCourier — pick one.
  • frontValidateMemberCartCheckout — validate and place the order.

The add-to-cart error has a custom GraphQL formatError that returns message, code and an optional cartId.

Placing the order

frontValidateMemberCartCheckout in the cart service calls ordersService.createOrder(...). createOrder() (in src/orders/services/orders.service.ts) is the core flow:

  1. Save the orders row and its order_items and order_addresses, status pending_payment.
  2. wmsService.createOrder() — reserve the order in the WMS.
  3. deliveriesService.createDraftOrder() — create the Biteship draft for the chosen courier.
  4. Create the DOKU payment and return a paymentLinkUrl.

It emits order.completed-family events for member stats. Order status values: pending_payment, processing, completed, canceled, shipped (see Orders).

Checkout (storefront)

  • /checkout redirects to /checkout/shipping (src/app/checkout/shipping/page.tsx). A leave-guard (useCheckoutLeave) warns on navigating away.
  • Addresssrc/modules/shipping/components/address-selection-form.tsx, src/context/address-context.tsx, src/lib/address/.
  • Couriershipping-form.tsx uses frontCouriers(memberCartId) (name, code, service, price, duration) then frontUpdateCartCourier. Biteship is not named in the frontend; courier data comes from the backend.
  • Vouchervouchers-form.tsx, src/lib/vouchers/. See Promotions.
  • PayfrontValidateMemberCartCheckout returns a paymentLinkUrl; the DOKU Jokul Checkout script opens it (src/hooks/useDokuCheckoutPayment.tsx, sandbox vs production by NEXT_PUBLIC_DOKU_ENV). On close the user goes to /user/my-order?status=pending_payment. See Payment.

The cart has no /cart route — it is src/context/cart-context.tsx (add, update qty, delete, buy-now temp cart, voucher and discount math) surfaced as a slide-over.

Gotchas

  • Checkout is one backend call that does four external things. If placing an order fails on dev, it may be the WMS, Biteship or DOKU leg — and dev points at the simulator, which can force any of them to 500.
  • Payment window is DOKU_PAYMENT_DUE_DATE_MINUTE; the order expiry cron cancels unpaid orders after it.