Shop It Docs
Developer Resourcespromotion

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.ts
  • packages/db/src/schema/promotion/promotion.ts
  • packages/db/src/schema/promotion/promotion-scope.ts
  • packages/db/src/schema/promotion/promotion-usage.ts

Core invariants:

  • promotion_usage_target_check: num_nonnulls(cart_id, order_id) >= 1
  • promotion_scope_target_check: num_nonnulls(product_id, category_id, tag_id) = 1
  • promotion_discount_value_non_negative: discount_value >= 0
  • promotion_max_discount_cap_non_negative: max_discount_cap IS NULL OR max_discount_cap >= 0
  • promotion_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 /:id
  • POST /
  • PATCH /:id
  • DELETE /:id
  • POST /:id/disable

Customer routes (/api/promotions):

  • POST /apply
  • POST /remove
  • GET /
  • GET /my-usage

Mobile equivalents are composed under /api/mobile/promotions/....

5. Cache Keyspaces

SurfacePrefix
Admin listpromotions:admin:list:
Admin detailpromotions:admin:detail:
Customer listpromotions:customer:list:
Promotion by codepromotions: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 when cart.appliedPromotionId is set and couponDiscount > 0. If the promotion's paymentMethodEligibility conflicts with the order's payment method, the order is rejected with ORDER_COUPON_PAYMENT_METHOD_MISMATCH before any inventory is reserved.

8. Runtime Configuration

Admin rate limit:

  • PROMOTION_ADMIN_RATE_LIMIT (default 120)
  • PROMOTION_ADMIN_RATE_WINDOW_SECONDS (default 60)

Customer operation rate limit:

  • PROMOTION_CUSTOMER_OPERATION_RATE_LIMIT (default 60)
  • PROMOTION_CUSTOMER_RATE_WINDOW_SECONDS (default 60)

Cache TTL:

  • PROMOTION_ADMIN_CACHE_TTL_SECONDS (default 120)
  • PROMOTION_CUSTOMER_CACHE_TTL_SECONDS (default 120)

9. Error Handling

HTTPerrorCodeMessage
400PROMOTION_USAGE_CONFLICTCannot extend end date for promotions that already have usage.
400PROMOTION_USAGE_CONFLICTCannot change code for promotions that already have usage.
400PROMOTION_USAGE_CONFLICTCannot delete promotion with usage history.
400PROMOTION_INVALID_DATE_RANGEInvalid startDate or endDate.
400PROMOTION_INVALID_DATE_RANGEendDate must be after startDate.
400PROMOTION_DISCOUNT_INVALIDdiscountValue must be greater than zero.
400PROMOTION_DISCOUNT_INVALIDmaxDiscountCap must be greater than or equal to discountValue for percentage discounts.
400PROMOTION_SCOPE_INVALIDOne or more scoped IDs are invalid.
400PROMOTION_NOT_APPLICABLECoupon not applicable to cart/product context.
404PROMOTION_NOT_FOUNDPromotion not found.
409PROMOTION_CODE_EXISTSPromotion code already exists.
429RATE_LIMIT_EXCEEDEDToo many requests. Please try again later.
400ORDER_COUPON_PAYMENT_METHOD_MISMATCHCoupon is not valid for the selected payment method.

10. Backend Diagram

11. Module Composition

  • PromotionModule - shared module
    • imports: ConfigModule, DatabaseModule, RedisModule
  • PromotionAdminModule - admin leaf
    • controller: PromotionAdminController
    • service: PromotionAdminService
  • PromotionCustomerModule - customer leaf
    • controller: PromotionCustomerController
    • service: PromotionCustomerService

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.ts
  • packages/db/src/schema/promotion/promotion.ts
  • packages/db/src/schema/promotion/promotion-scope.ts
  • packages/db/src/schema/promotion/promotion-usage.ts

13. Environment Variables

VariableDefaultDescription
PROMOTION_ADMIN_RATE_LIMIT120Rate limit for admin promotion operations
PROMOTION_ADMIN_RATE_WINDOW_SECONDS60Rate limit window for admin operations
PROMOTION_CUSTOMER_OPERATION_RATE_LIMIT60Rate limit for customer promotion operations
PROMOTION_CUSTOMER_RATE_WINDOW_SECONDS60Rate limit window for customer operations
PROMOTION_ADMIN_CACHE_TTL_SECONDS120Cache TTL for admin promotion views
PROMOTION_CUSTOMER_CACHE_TTL_SECONDS120Cache 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