Promotion Module Backend Documentation
Promotion schema, digital eligibility constraints, runtime contracts, and file ownership map.
Promotion Module - Backend Documentation
1. Eligibility Model
Promotions enforce eligibility across two dimensions:
Cart Type (cartTypeEligibility)
"digital"— applies to digital, care_package, and mixed carts"physical"— applies to physical and mixed carts"both"— applies to any non-empty cart
Payment Method (paymentMethodEligibility)
"online"— applies when payment is not COD"cod"— applies only to physical carts via COD payment"any"— applies regardless of payment method
Mixed cart partial discount: For "digital" or "physical" coupons, only matching-kind items are included in eligibleSubtotal. Discount is computed on eligibleSubtotal, not cart.totalSp. "both" coupons always use cart.totalSp.
care_package treatment: Items with productKind = "care_package" are treated as digital in all eligibility checks.
Existing promotions: All existing cartTypeEligibility = "digital" and paymentMethodEligibility = "online" promotions continue to behave identically — default values unchanged.
2. Schema Overview
Primary schema files:
packages/db/src/schema/promotion/enums.tspackages/db/src/schema/promotion/promotion.tspackages/db/src/schema/promotion/promotion-scope.tspackages/db/src/schema/promotion/promotion-usage.ts
Core invariants:
promotion_usage_target_check:num_nonnulls(cart_id, order_id) >= 1promotion_scope_target_check:num_nonnulls(product_id, category_id, tag_id) = 1promotion_discount_value_non_negative:discount_value >= 0promotion_max_discount_cap_non_negative:max_discount_cap IS NULL OR max_discount_cap >= 0promotion_min_cart_value_non_negative:min_cart_value IS NULL OR min_cart_value >= 0
3. Core Backend Responsibilities
- create/update/disable/delete promotions (admin)
- customer coupon validate/apply/remove
- customer active-promotion browsing and usage-history retrieval
- scope-based applicability checks by product/category/tag
- usage-limit enforcement with stable code normalization
4. API Routes
Admin routes (/api/admin/promotions):
GET /GET /:idPOST /PATCH /:idDELETE /:idPOST /:id/disable
Customer routes (/api/promotions):
POST /applyPOST /removeGET /GET /my-usage
Mobile equivalents are composed under /api/mobile/promotions/....
5. Cache Keyspaces
| Surface | Prefix |
|---|---|
| Admin list | promotions:admin:list: |
| Admin detail | promotions:admin:detail: |
| Customer list | promotions:customer:list: |
| Promotion by code | promotions:code: |
All keys use deterministic segments and admin writes invalidate affected prefixes.
6. Idempotency & Integrity
- create path uses conflict-safe code uniqueness handling
- coupon usage writes remain transactionally consistent with cart coupon updates
- input code normalization is deterministic (
trim + uppercase) - invalid legacy usage rows are remediated or rejected before strict checks are enforced
7. Integration Notes
- customer promotion operations are tightly integrated with cart totals and applied-coupon fields
- promotion scope lookups integrate with catalog entities (product/category/tag)
- COD re-validation at order creation:
OrderService.createFromCart()re-validates payment method eligibility whencart.appliedPromotionIdis set andcouponDiscount > 0. If the promotion'spaymentMethodEligibilityconflicts with the order's payment method, the order is rejected withORDER_COUPON_PAYMENT_METHOD_MISMATCHbefore any inventory is reserved.
8. Runtime Configuration
Admin rate limit:
PROMOTION_ADMIN_RATE_LIMIT(default120)PROMOTION_ADMIN_RATE_WINDOW_SECONDS(default60)
Customer operation rate limit:
PROMOTION_CUSTOMER_OPERATION_RATE_LIMIT(default60)PROMOTION_CUSTOMER_RATE_WINDOW_SECONDS(default60)
Cache TTL:
PROMOTION_ADMIN_CACHE_TTL_SECONDS(default120)PROMOTION_CUSTOMER_CACHE_TTL_SECONDS(default120)
9. Error Handling
| HTTP | errorCode | Message |
|---|---|---|
| 400 | PROMOTION_USAGE_CONFLICT | Cannot extend end date for promotions that already have usage. |
| 400 | PROMOTION_USAGE_CONFLICT | Cannot change code for promotions that already have usage. |
| 400 | PROMOTION_USAGE_CONFLICT | Cannot delete promotion with usage history. |
| 400 | PROMOTION_INVALID_DATE_RANGE | Invalid startDate or endDate. |
| 400 | PROMOTION_INVALID_DATE_RANGE | endDate must be after startDate. |
| 400 | PROMOTION_DISCOUNT_INVALID | discountValue must be greater than zero. |
| 400 | PROMOTION_DISCOUNT_INVALID | maxDiscountCap must be greater than or equal to discountValue for percentage discounts. |
| 400 | PROMOTION_SCOPE_INVALID | One or more scoped IDs are invalid. |
| 400 | PROMOTION_NOT_APPLICABLE | Coupon not applicable to cart/product context. |
| 404 | PROMOTION_NOT_FOUND | Promotion not found. |
| 409 | PROMOTION_CODE_EXISTS | Promotion code already exists. |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests. Please try again later. |
| 400 | ORDER_COUPON_PAYMENT_METHOD_MISMATCH | Coupon is not valid for the selected payment method. |
10. Backend Diagram
11. Module Composition
PromotionModule- shared module- imports:
ConfigModule,DatabaseModule,RedisModule
- imports:
PromotionAdminModule- admin leaf- controller:
PromotionAdminController - service:
PromotionAdminService
- controller:
PromotionCustomerModule- customer leaf- controller:
PromotionCustomerController - service:
PromotionCustomerService
- controller:
12. File Map
apps/api/src/modules/promotion/admin/*apps/api/src/modules/promotion/customer/*apps/api/src/modules/promotion/__tests__/*packages/db/src/schema/promotion/enums.tspackages/db/src/schema/promotion/promotion.tspackages/db/src/schema/promotion/promotion-scope.tspackages/db/src/schema/promotion/promotion-usage.ts
13. Environment Variables
| Variable | Default | Description |
|---|---|---|
PROMOTION_ADMIN_RATE_LIMIT | 120 | Rate limit for admin promotion operations |
PROMOTION_ADMIN_RATE_WINDOW_SECONDS | 60 | Rate limit window for admin operations |
PROMOTION_CUSTOMER_OPERATION_RATE_LIMIT | 60 | Rate limit for customer promotion operations |
PROMOTION_CUSTOMER_RATE_WINDOW_SECONDS | 60 | Rate limit window for customer operations |
PROMOTION_ADMIN_CACHE_TTL_SECONDS | 120 | Cache TTL for admin promotion views |
PROMOTION_CUSTOMER_CACHE_TTL_SECONDS | 120 | Cache TTL for customer promotion views |
Time fields in this module are stored as timezone-aware values and should be handled as ISO-8601 instants by API consumers.
See Also
- Feature Guide: See Promotion - Feature List Section 6 (State Models) for promotion lifecycle and scope diagrams.
- API Reference: See Promotion - API & Integration Guide Section 7 (Endpoint Reference + Payload Cheatsheet) for API contracts.