Files
vmall/openspec/specs/order/spec.md
T
james cdfb1b06f2 chore(openspec): archive add-group-buying
Sync the group-buying capability plus the order, frontend-mall, and
frontend-shop-admin requirements into the main specs, and write the capability
Purpose the archive leaves as a placeholder. This closes the last of the five
planned marketing changes.
2026-09-18 13:22:47 +00:00

5.0 KiB

order Specification

Purpose

Checkout splitting a cart into per-shop orders, plus order lifecycle and ownership.

Requirements

Requirement: Checkout splits by shop

POST /api/orders/checkout SHALL create one order per distinct shop in the cart, in a single database transaction: stock decrement, order + item insert with price snapshots, cart clear, active flash-sale resolution when no group-buy intent applies to the SKU, requested group-buying intent validation, and requested coupon redemption when eligible. All amounts use the cart's SKU currencies converted into the buyer-chosen display currency at checkout time. A group-buy intent SHALL contain exactly one active activity SKU at quantity 1 and either an open group identifier or a request to open a group; the server SHALL snapshot the group price and group identity on the resulting pending-payment order. Group intent SHALL return 409 when that SKU is also eligible for an overlapping flash sale. For each remaining cart line without group intent the server SHALL resolve an eligible active flash-sale item as specified by flash-sales, snapshot the final unit price and activity identity, and charge standard price for ineligible quantity. The request MAY select at most one owned coupon per generated shop order; the server SHALL persist coupon_id, discount_minor, and post-discount total_minor only after ownership, shop, status, window, and threshold validation. The client SHALL NOT provide a discount amount. The server SHALL reject a coupon (409) for any shop order that applied flash-sale pricing or carries group-buy intent.

Scenario: two shops → two orders

  • WHEN the cart contains SKUs from shops A and B without group-buy intent
  • THEN two orders are created, each with only its shop's items, and the cart is empty

Scenario: insufficient stock

  • WHEN any line's qty exceeds SKU stock or applicable flash-sale stock
  • THEN the whole checkout returns 409 and no order is created and stock is unchanged

Scenario: active flash price applied

  • WHEN checkout contains a SKU with an active eligible flash-sale item and no group-buy intent applies to it
  • THEN the order item records the server-calculated flash-sale price and activity identity

Scenario: coupon rejected on a flash-priced shop order

  • WHEN the customer selects a coupon for a shop whose generated order applied flash-sale pricing on any line
  • THEN checkout returns 409 and creates no orders

Scenario: open a group at checkout

  • WHEN a customer checks out one eligible group-buying SKU at quantity 1 with open-group intent
  • THEN a pending-payment order and an open group identity are created with the server-resolved group price

Scenario: coupon rejected on a group shop order

  • WHEN the customer selects a coupon for the shop that carries group-buy intent
  • THEN checkout returns 409 and creates no orders

Scenario: coupon applies to one matching shop

  • WHEN the customer selects an eligible shop coupon and that shop's generated order has no flash-priced lines and no group-buy intent
  • THEN the order records that coupon and its server-calculated discount

Requirement: Order lifecycle

Status transitions SHALL be: pending_payment → paid → fulfilling → shipped → completed; cancellable only from pending_payment, which MUST restore SKU stock, any reserved flash-sale activity stock consumed by the order, and a redeemed coupon to claimed status when the order has one. Pending-payment cancellation SHALL NOT change group paid-member counts. When the cancelled order opened the group and paid member count is zero, the group SHALL become cancelled. Payment of an order with group-buying intent SHALL also validate and atomically claim an open group seat; full, expired, or cancelled groups SHALL return 409 and leave the order pending_payment.

Scenario: group payment wins final seat

  • WHEN payment fills the final available group seat
  • THEN the payment succeeds and the group state becomes successful

Scenario: cancel restores stock

  • WHEN a customer cancels a pending_payment group-intent order
  • THEN SKU stock increases by the ordered qty, status is cancelled, and no paid group membership is removed

Scenario: unpaid opener cancel closes an empty group

  • WHEN the opening pending_payment order is cancelled and the group has zero paid members
  • THEN the group status becomes cancelled and a later payment against that group returns 409

Scenario: cancel restores coupon

  • WHEN a customer cancels a pending_payment order with a redeemed coupon
  • THEN the coupon is detached from the order and becomes claimed

Scenario: illegal transition rejected

  • WHEN cancelling a paid order via the customer endpoint
  • THEN the API returns 409

Requirement: Order ownership

Customers SHALL see only their own orders; shop roles only their shop's orders; platform_admin sees all.

Scenario: cross-customer read denied

  • WHEN customer X requests customer Y's order id
  • THEN the API returns 404