Koovira Dev Docs
20-modules/ecommerce/wallet-checkout-flow.md

COM-004 — Checkout with Wallet (end-to-end)

Validated against classs/CommerceOrdersModule.php::checkoutWithWallet() (:563) and the live schema on 2026-09-06.

This is the archetype flow and the answer to the Definition-of-Done example ("customer places an order and pays using wallet"). Every other paid flow in the platform should be documented in this same shape. Read ../../40-financial/central-finance-ledger.md alongside it.


Actors & preconditions

  • Actor: logged-in customer (customer_master, buyer_id > 0).
  • Gate: cart has ≥1 valid item; wallet has sufficient balance (wallet-first); COD only if

admin_global_settings.ecommerce.allow_cod = 1.

  • Entry: POST api/commerce/cart.php (checkout action) → CommerceOrdersModule::checkoutWithWallet($userId, $items, $shippingInput).

The flow (each hop cited)

Customer (mycart.php)
  │  POST items[] + shipping (address, phone, email, delivery_mode, payment_method, coupon)
  ▼
api/commerce/cart.php                         ← passes $_POST['items'] through
  ▼
CommerceOrdersModule::checkoutWithWallet()    :563
  1. Auth/guard         userId>0, items non-empty                         :565–570
  2. Shipping validation address req; phone = /^[0-9]{10}$/; email valid   :572–592
  3. Delivery mode      quick ⇒ requires drop_lat/drop_lng                 :582,593–595
  4. Payment method     default 'wallet'; 'cod' only if allow_cod=1        :597–609
  5. Normalize items    product_id/inventory_id + qty; drop invalid        :611–637
  6. Server-side price  from seller_inventory | products_master |          :646–748
                        product_variants — NEVER from client  ⚠ guard
     • publicOnly guard: unpublished/paused/suspended ⇒ refuse            :667–683
     • variant price from product_variants.price by id/uid ⇒ ₹1 exploit fix :692–748
  7. Build order lines  unit_price × qty ⇒ line_total; sum ⇒ subtotal      :750–764
  8. Routing (opt.)     CommerceRoutingEngine candidates; quick filter     :766–782
  9. Create order       INSERT orders_master (one row per line)            (creation pass)
 10. Status history     INSERT order_status_history                        insertOrderStatusHistory() :179
 11. Wallet debit       WalletModule debit ⇒ wallet_transactions          (wallet-first)
 12. Finance ledger     ensureOrderFinanceMaster + recordOrderFinanceEntry :399,462
                        + refreshOrderFinanceAggregates                    :510
 13. Commission         CommissionEngineModule ⇒ commission_transactions   (require_once :19)
 14. Delivery-trust     owner receiving link / OTP scaffolding             :292–393
 15. Notifications      buyer + seller (see notifications section)
  ▼
Response { success, order_uid/order_number, ... }

Tables written (validated)

StepTableWhat
9orders_masterone row per line item — buyer/seller/product, pricing, GST, status=created, payment_method, payment_status
10order_status_historyinitial status row
11wallet_transactionsdebit against wallet_accounts (buyer)
12kv_order_finance_mastermoney header keyed (ecommerce, orders_master, order_id)
12kv_order_finance_entriesorder_paid/customer/debit, platform-fee, merchant-credit lines
13commission_transactionsrecorded 5% platform take
8/14commerce_order_routing, order_delivery_linksfulfilment + delivery-trust

Money math (per line)

unit_price (from DB) × quantity = line_total; summed to subtotal; + gst_amount (+ cgst/sgst/igst) + shipping/delivery_charges + platform_fee − discount_amount ⇒ total_amount. Wallet debited total_amount; platform take (commission) recorded as a platform finance entry + commission_transactions; merchant net = gross − commission − fees, settled later.

Failure branches (validated messages)

CauseWhereResult
not logged in:565Please login to place order
empty cart / no valid items:568,:635Cart is empty / No valid items to checkout
bad address/phone/email:584–592field-specific message
quick delivery w/o location:593prompt to enable location or pick Regular
item no longer available:672–678An item in your cart is no longer available…
price ≤ 0:685Invalid price for product ID <n>
quick unavailable for item:780prompt to select Regular
wallet insufficientWalletModule debitdebit fails ⇒ order not paid (verify exact message in Wallet doc)

What could break if you change this (change-impact)

Wallet balance integrity, commission recorded exactly once, finance ledger keyed to ecommerce (not another module), the two money-integrity guards (server-side pricing, publicOnly). Re-run the cross-module isolation test (finance/commission must not attach to another module's order id).

Errors (feed the Error Center; prefix COM-E)

  • COM-E001 price ≤ 0 after server resolution (:685) — root cause: product has no valid

selling_price/variant price; inspect products_master.selling_price, product_variants.price.

  • COM-E002 item unavailable at checkout (:672) — root cause: product unpublished or seller

company suspended/de-verified after carting; inspect product activestatus/publish + company state.

  • COM-E003 quick delivery unresolvable (:780) — no eligible/opted-in seller within radius;

inspect commerce_order_routing, seller quick_delivery_enabled.

Re-validate

php -r 'require "config/baseconfig/config.php"; global $conn;
 print_r($conn->query("SHOW COLUMNS FROM orders_master")->fetchAll(PDO::FETCH_COLUMN));'
grep -n "function checkoutWithWallet" classs/CommerceOrdersModule.php