1.5 KiB
1.5 KiB
Architect — F-019 Promotions v1
Feature
F-019 adds server-side promo code validation and discount calculation. Discounts are recalculated from current cart totals and never trusted from the client.
Design
Module boundaries
Create project/src/modules/promotions/ with domain/application/infrastructure/api/tests. Promotions owns promo definitions and validation. Cart receives a public PromotionServicePort from the composition root and stores only the selected promo code, not discount amounts.
Data model
Add migration 014_promotions.js:
promotions_promotions: code, type (percent,fixed_amount), value, validity window, usage_limit, usage_count, active, timestamps.- Add
promo_code texttocart_carts.
Rules
- Percent discounts are basis points over current cart total.
- Fixed amount discounts are cents and capped at current cart total.
- Validity checks: active, starts_at <= now, ends_at >= now, usage_count < usage_limit when limit exists.
- No stacking in v1: one promo code per cart.
- Client discount fields are stripped/ignored by cart schemas.
API
- Admin
POST /promotionscreates promo codes. - Public/authenticated cart endpoint
POST /cart/promo-codestores a code after server validation. GET /cartrecalculates discount throughPromotionService.
Acceptance trace
- Valid promo: cart read applies recalculated server discount.
- Expired/exhausted: applying code returns HTTP 422 reason.
- Client discount ignored: schemas strip discount fields and cart stores no discount amount.