Commerce Journey
Overview
Section titled “Overview”A product enters the catalog through the data import, Matrix serves it to the storefront listing and product page, Checkout turns a cart into an order, a payment provider settles it, and the operator sees the order in the CMS. Matrix, Volkanos and the other product names are explained on Concepts.
| Step | Module | Endpoint | Storefront route |
|---|---|---|---|
| Catalog | PIM, filled by the data import | none (the import writes PIM, Matrix reads it) | none |
| Listing | Matrix | GET /api/matrix/v2/{channel_idx}/products/?category=<url_key>&include=full&language=¤cy=&country= | /catalog/<url_key> |
| Product page | Matrix | GET /api/matrix/v2/{channel_idx}/products/?url_key=<slug>&include=full | /product/<url_key> |
| Cart | Checkout | POST /api/checkout/v2/{channel_idx}/carts/, PATCH /api/checkout/v2/{channel_idx}/carts/{cart_id}/items/ | ”Add to cart” on the product page |
| Checkout | Checkout | PATCH .../carts/{cart_id}/addresses/, GET .../carts/{cart_id}/shipping-methods/, PATCH .../carts/{cart_id}/shipping/, GET .../carts/{cart_id}/payment-methods/, PATCH .../carts/{cart_id}/payment/ | /checkout |
| Payment and order | Checkout, payment SDKs | POST /api/checkout/v2/{channel_idx}/orders/ | redirect_url of the provider, or /checkout/success?ref=<pretty id> |
| Order in the CMS | Checkout (admin API) | GET /api/checkout/v2/admin/{channel_idx}/orders/ | CMS Orders panel |
... stands for /api/checkout/v2/{channel_idx}. Every storefront path above is in the published
Storefront API spec; the admin orders path comes from the checkout module’s admin URLs.
Architecture
Section titled “Architecture”The storefront calls Matrix and Checkout, the CMS calls Checkout, and Checkout hands the payment to a provider SDK:
Inside the backend, the catalog, the prices and the stock reach the Matrix read model:
Modules and What Each Contributes
Section titled “Modules and What Each Contributes”| Module | Contributes | Docs |
|---|---|---|
| pim | products, categories, attributes, images | docs |
| pim-csv | the CSV import that fills PIM | Data Import |
| pricemanager | price lists, the Price model, the currency of each sale channel | docs |
| qms | quantities, the XRAY pull and the push into django_checkout.Stock | docs, checkout sync |
| matrix | the storefront read model: listing, search, prices, stock, omnibus endpoints | docs |
| checkout | carts, addresses, shipping and payment methods, orders, channel API keys, provider callbacks | repo |
| omnibus | the lowest previous price required by the Omnibus rule, served as GET /api/matrix/v2/{channel_idx}/omnibus/ | docs |
| agreements | consents attached to an order, POST /api/agreements/v2/{channel_idx}/orders/{order_id}/agreements/ | repo |
| accounts | customer login and address book; its endpoints are v1, so they are outside the published spec | docs |
| payment SDKs | PayU, PayPal, Autopay, Przelewy24 and Paynow clients (entirius-py-payu-sdk, entirius-py-paypal-sdk, entirius-py-autopay-sdk, entirius-py-przelewy24-sdk, entirius-py-paynow-sdk); hard dependencies of checkout | checkout pyproject.toml |
Configuration
Section titled “Configuration”What must exist before the first order:
- A channel in every module of the table. The data import creates them.
- Prices in the channel currency (pricemanager) and quantities (qms, pushed into checkout stock).
- At least one shipping method and one payment method per channel:
django_checkout.shippingmethodanddjango_checkout.paymentmethodrecords. The Emporium test package fixtures ship the codeseurope-standardandbanktransfer. Both are edited in Django admin. - A channel API key: a
django_checkout.apikeyrecord bound to the channel. The storefront sends it as theX-API-KEYheader;ChannelAPIKeyPermission(api/v2/permissions.py) refuses a key of another channel. Edited in Django admin.
Orders are read in the CMS under the Orders entry of the navigation (/checkout-orders/orders); Review an Order walks through the panel.
Backend and API Contract
Section titled “Backend and API Contract”Every checkout call below carries the X-API-KEY header of the channel.
Listing and product page
Section titled “Listing and product page”GET /api/matrix/v2/{channel_idx}/products/ with the query parameters category, url_key, sku, include,
on_stock, page, page_size, language, currency, country. Free text goes to the separate
/api/matrix/v2/{channel_idx}/search/ endpoint.
The response is a PaginatedResponse: has_next_page, page, page_size, results, meta. Each result is a
ProductCore (schemas/responses/product.py in matrix) with sku, product_type, url_key, name,
main_image, on_stock, categories and price, whose fields include gross, net, final_gross and
has_special_price. The product page sends url_key=<slug>&include=full and takes the first result.
POST /api/checkout/v2/{channel_idx}/carts/ with items[].sku, items[].quantity, currency_code and the
optional language_code and country_code. The response is a CartV2Response: cart_id, cart_status,
validation_status, total_gross, items, shipping_method, payment_method.
PATCH /api/checkout/v2/{channel_idx}/carts/{cart_id}/items/ replaces the full item list.
Addresses
Section titled “Addresses”PATCH /api/checkout/v2/{channel_idx}/carts/{cart_id}/addresses/ with billing_address and shipping_address
(null means the same as billing). An AddressInput requires firstname, lastname, street, city, postcode,
country_code, telephone and dialling_code.
Shipping and payment
Section titled “Shipping and payment”GET /api/checkout/v2/{channel_idx}/carts/{cart_id}/shipping-methods/ lists the methods for the cart;
PATCH /api/checkout/v2/{channel_idx}/carts/{cart_id}/shipping/ selects one by code, with delivery_point for
pickup methods. GET /api/checkout/v2/{channel_idx}/carts/{cart_id}/payment-methods/ and
PATCH /api/checkout/v2/{channel_idx}/carts/{cart_id}/payment/ work the same way: code, plus bank_id, card
and save_card for gateways that need them.
POST /api/checkout/v2/{channel_idx}/orders/ with cart_id in the body. The published spec carries no request
body for this call; the field comes from OrderCreateView (api/v2/views/order_views.py), which answers 400
without it. The response is an OrderCreateResponse: order_id, order_pretty_id, order_status,
redirect_url, split_orders_pretty_ids.
Payment settlement
Section titled “Payment settlement”A redirect_url sends the customer to the provider. The provider calls back on the checkout notify/<provider>/
paths; those are v1 and outside the published spec. banktransfer needs no redirect: the order is created as UNPAID
and the storefront goes straight to /checkout/success?ref=<order_pretty_id>.
Operator view
Section titled “Operator view”GET /api/checkout/v2/admin/{channel_idx}/orders/, orders/{uid}/ and orders/{uid}/status/ under the same
prefix. JWT authentication and a staff user (IsAdminUser), no X-API-KEY.
Frontend
Section titled “Frontend”The storefront (Next.js) keeps every path in API/api.routes.ts:
API_PRODUCTS_ROUTE: the listing query inapp/[customer_country]/(catalog)/catalog/[catalog_url_key]/api.query.tssendscategory=<catalog_url_key>withinclude=fulland readshas_next_page,page,page_size; the product query inapp/[customer_country]/(product)/product/[product_url_key]/api.query.tssendsurl_keywithinclude=full. Both pass results throughutils/NORMALIZERS/product.normalizer.API_CART_ITEMS_ROUTE,API_CART_ADDRESS_ROUTE,API_CART_SHIPPING_ROUTE,API_CART_SHIPPING_SELECT_ROUTE,API_CART_PAYMENT_ROUTE,API_CART_PAYMENT_SELECT_ROUTEandAPI_CART_ORDERS_ROUTE: the cart and checkout calls, each sent with the channelX-API-KEY.
The checkout at /checkout (app/[customer_country]/checkout/) walks the steps in the order the Playwright test
takes them: address form, shipping, payment, review, Place order, then /checkout/success?ref=<pretty id> with
the heading “Order placed”.
In the CMS, the Orders panel loads the grid with GET_Orders (src/api/orders/api.js), which calls
/api/checkout/v2/admin/{channel_idx}/orders/.
Testing
Section titled “Testing”The path is proven by the Emporium test package (entirius-test-package-emporium):
| Proof | Tag | What it covers |
|---|---|---|
features/checkout/order_flow.feature | @checkout @order-flow @v2 | guest cart with ENT-C003, addresses, shipping europe-standard, payment banktransfer, cart valid, order unpaid with a pretty id |
features/matrix_v2/*.feature | @matrix-v2 | listing, filters, search, product detail, stock, prices, omnibus |
e2e/storefront/test_order_flow.py | Playwright | /catalog/office-chairs, product ENT-C003, add to cart, checkout, Order placed |
e2e/cms/test_order_visible.py | Playwright | an order placed through the checkout API, then visible in the CMS Orders grid |
Running them on a local stack: Quickstart.
Extending
Section titled “Extending”A new product field, end to end. Add the field to the PIM feature set, run fill-read-model so Matrix copies it
into the read model, and expose it on ProductCore in matrix schemas/responses/product.py. The storefront then
maps it in utils/NORMALIZERS/product.normalizer, which every product query already passes through. A field that
skips the read model refill stays empty on the storefront even though PIM holds it.
A new payment method. Create a django_checkout.paymentmethod record for the channel with the provider and a
code; GET .../carts/{cart_id}/payment-methods/ returns it from then on. Gateway providers use the SDK checkout
already depends on, so the provider credentials are the remaining step. A provider without an SDK in checkout’s
pyproject.toml needs a new SDK package and a callback view in checkout before a record can point at it.
Load your own catalog: Data Import.