feat(api): flash sales resolved server-side at checkout

Shop-owned timed sessions carry SKU activity items with their own reserved
inventory and per-customer limit. Checkout resolves eligibility on the server,
locks candidate items by primary key after the SKU locks, and splits a cart line
into an activity-priced item plus a standard-priced remainder, so every unit
price snapshot is honest and a customer cannot exceed the limit.

Reserved activity stock and SKU stock decrement together under guards, and a
pending-payment cancellation restores both plus any redeemed coupon. Coupons are
rejected on a shop order that applied activity pricing, and a SKU cannot join
two overlapping enabled sessions.

The overlap check against group buying is present but dormant: that capability
lands later, so the check activates only once its table exists.

Surfaces (shop-admin, mall) and seeding follow.
This commit is contained in:
2026-09-18 12:47:46 +00:00
parent 82c1e8e4bb
commit 393e7f7209
12 changed files with 1521 additions and 36 deletions
+2
View File
@@ -6,6 +6,7 @@ pub mod catalog;
pub mod content;
pub mod coupon;
pub mod currency;
pub mod flash_sale;
pub mod fulfillment;
pub mod health;
pub mod identity;
@@ -28,6 +29,7 @@ pub fn api_router() -> Router<AppState> {
.merge(content::router())
.merge(cart::router())
.merge(coupon::router())
.merge(flash_sale::router())
.merge(order::router())
.merge(points::router())
.merge(shop::router())