Shop It Docs
Developer Resourcespromotion

Promotion Module Feature List

Promotion admin/customer features, eligibility model, state behavior, and cache/rate-limit contracts.

Promotion Module - Feature List

1. Feature Overview

Promotion module manages coupon lifecycle and customer coupon operations for physical-order commerce flows.

Eligibility is intentionally constrained to:

  • cartTypeEligibility = physical
  • paymentMethodEligibility = online

2. Route Ownership and Surfaces

SurfaceRoute prefixOwner
Admin API/api/admin/promotionsPromotionAdminController
Customer API/api/promotionsPromotionCustomerController
Mobile-composed customer API/api/mobile/promotionsMobileModule composition

3. Admin Feature Matrix

CapabilityEndpointAuthZ
List promotionsGET /api/admin/promotionsPromotions_READ
Promotion detailGET /api/admin/promotions/:idPromotions_READ
Create promotionPOST /api/admin/promotionsPromotions_CREATE
Update promotionPATCH /api/admin/promotions/:idPromotions_UPDATE
Delete promotionDELETE /api/admin/promotions/:idPromotions_DELETE
Disable promotionPOST /api/admin/promotions/:id/disablePromotions_UPDATE

4. Customer/Mobile Feature Matrix

CapabilityEndpointRuntime mode
Apply coupon to active cartPOST /api/promotions/apply (without productId)validate + mutate cart
Validate coupon for productPOST /api/promotions/apply (with productId)validate only
Remove coupon from cartPOST /api/promotions/removemutate cart
List active promotionsGET /api/promotionsread
List customer usage historyGET /api/promotions/my-usageread

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

5. Key Business Rules

  • coupon input is normalized (trim + uppercase) before lookup
  • promotions enforce date-window validity and discount constraints
  • scope targeting uses typed nullable FKs (product_id, category_id, tag_id) with exactly one target per scope row
  • usage rows require at least one redemption anchor (cart_id or order_id)
  • unresolved invalid legacy usage rows are rejected (no silent drift)

6. State Models

6.1 Promotion lifecycle state

6.2 Coupon operation state

6.3 Apply coupon paths

POST /promotions/apply has two valid paths:

  • cart path (productId omitted): coupon is validated and cart totals are updated
  • product validation path (productId provided): returns validation payload and does not mutate cart

7. Caching Strategy

Keyspaces:

  • promotions:admin:list:
  • promotions:admin:detail:
  • promotions:customer:list:
  • promotions:code:

Behavior:

  • deterministic key construction via CacheKeyUtil.build(...)
  • prefix invalidation after admin mutations (invalidatePattern(prefix*))
  • customer reads reuse cached code + active-list payloads

8. Rate Limiting

Promotion rate-limit envs:

  • PROMOTION_ADMIN_RATE_LIMIT
  • PROMOTION_ADMIN_RATE_WINDOW_SECONDS
  • PROMOTION_CUSTOMER_OPERATION_RATE_LIMIT
  • PROMOTION_CUSTOMER_RATE_WINDOW_SECONDS

Runtime behavior:

  • Redis sorted-set sliding windows per action + actor key
  • UUIDv7 request markers in rate-limit windows
  • threshold breach throws RateLimitExceededException (RATE_LIMIT_EXCEEDED)

9. Queue/Worker Features

Promotion module does not own BullMQ queues or background workers.

Operational notes:

  • Coupon validation and application happen synchronously in request flow.
  • There is no promotion-specific async job contract in packages/jobs for this module.

10. Error UX Mapping

ScenarioAPI behaviorRecommended UI
Promotion not found404 PROMOTION_NOT_FOUNDShow "Coupon not found" toast
Code already exists409 PROMOTION_CODE_EXISTSShow "Code already exists" error
Not applicable to cart400 PROMOTION_NOT_APPLICABLEShow "Coupon not valid for this cart"
Invalid date range400 PROMOTION_INVALID_DATE_RANGEShow "Invalid coupon dates"
Invalid discount400 PROMOTION_DISCOUNT_INVALIDShow discount validation error
Invalid scope400 PROMOTION_SCOPE_INVALIDShow "Invalid coupon targets"
Usage conflict400 PROMOTION_USAGE_CONFLICTShow "Cannot modify used coupon"
Rate limit exceeded429 RATE_LIMIT_EXCEEDEDShow "Please wait" with backoff

11. Data Flow

12. Release/QA Checklist

  • Admin CRUD/disable routes enforce Promotions_* permissions.
  • /promotions/apply supports both cart-apply and product-validate modes.
  • Scope target invariant (product/category/tag) allows exactly one target per row.
  • Usage target invariant (cart_id or order_id) is enforced.
  • Promotion code uniqueness conflicts return PROMOTION_CODE_EXISTS.
  • Admin write paths invalidate admin/detail/code cache prefixes.
  • Customer/admin operation limits enforce configured window thresholds.

13. Integration Flows

13.1 Apply Coupon Flow

A customer applies a coupon code to their cart or validates it for a product.

  1. Customer calls POST /api/promotions/apply (or POST /api/mobile/promotions/apply) with { code: "SAVE20" }.
  2. System normalizes code: trim() + uppercase() to "SAVE20".
  3. System looks up promotion by normalized code.
  4. System validates promotion exists, is active, and current date is within startDate and endDate.
  5. System validates discount constraints: min purchase amount, max discount cap.
  6. If productId provided: return validation payload only, do not mutate cart.
  7. If no productId: system validates cart contents are eligible (cartTypeEligibility = physical, paymentMethodEligibility = online).
  8. System evaluates scope targeting: checks if cart products match any promotion scope (product, category, or tag).
  9. If applicable: system updates cart's appliedPromotionId, appliedPromotionCode, appliedDiscount.
  10. System invalidates cart cache cart:user:{userId} and promotion code cache.
  11. Customer fetches updated cart to see discounted total.

13.2 Admin Create Promotion Flow

An admin creates a new promotion coupon with targeting and constraints.

  1. Admin calls POST /api/admin/promotions with promotion details.
  2. System validates: code uniqueness (returns 409 if duplicate), date range, discount value.
  3. System creates promotion record with status active (or schedules activation).
  4. For each scope target (product, category, or tag), system inserts promotion_scope row with exactly one target.
  5. System caches promotion under promotions:admin:detail:{id} and promotions:code:{normalizedCode}.
  6. Admin can later call POST /api/admin/promotions/:id/disable to deactivate the promotion.
  7. Active promotions appear in customer GET /api/promotions list.
  8. Customer usage creates promotion_usage rows linking to cart_id or order_id.

Time fields in this module are stored as timezone-aware values and should be handled as ISO-8601 instants by API consumers.


See Also