feat(api): group buying with payment-time seat claims

Shop-owned activities on one SKU, concrete groups with an
open/successful/expired/cancelled lifecycle, and one paid membership row per
paid order. Checkout accepts a single-SKU quantity-1 intent, snapshots the group
price and identity on the pending order, and opens or references a group; a paid
seat is claimed only at payment, which locks the group and fills it exactly at
capacity.

Pending-payment cancellation restores SKU stock only and never rolls back paid
seats; an unpaid opener cancelling closes a still-empty group. Coupons are
refused on a group shop order, and the flash-sale exclusion is now enforced in
both directions because the activity table exists, which activates the guard
add-flash-sales shipped dormant. The activity column names follow the contract
recorded in this change's design.

Surfaces (shop-admin, mall) and seeding follow.
This commit is contained in:
2026-09-18 13:16:28 +00:00
parent 705cbe249a
commit a7bc476251
15 changed files with 1563 additions and 48 deletions
+2
View File
@@ -8,6 +8,7 @@ pub mod coupon;
pub mod currency;
pub mod flash_sale;
pub mod fulfillment;
pub mod group_buying;
pub mod health;
pub mod identity;
pub mod order;
@@ -30,6 +31,7 @@ pub fn api_router() -> Router<AppState> {
.merge(cart::router())
.merge(coupon::router())
.merge(flash_sale::router())
.merge(group_buying::router())
.merge(order::router())
.merge(points::router())
.merge(shop::router())