โ† Pattern library

Commerce

Coupon / Promo Code

Lets users apply a discount code at checkout (percentage off, fixed amount, free shipping) with rules around usage limits and expiry.

๐ŸŒฑ

When to use this

For promotional campaigns, partner discounts, retention offers, or seasonal sales.

commercecoupondiscountpromotioncode
โœจ Built using these library patterns:
coupon

What I assumed

I made these guesses to fill gaps. Let me know if any are wrong.

    Flow diagram

    Step-by-step recipe

    Copy this and paste into Cursor, Claude Code, or v0.

    PATTERN: Coupon Application
    INPUT: code_string, cart_or_subscription
    OUTPUT: discount_applied | error_message
    
    SETUP_STEPS (admin creates coupon):
      1. Admin defines: code, discount type (%, fixed, shipping), amount
      2. Set rules: max uses (total + per user), expiry, min order value
      3. Set scope: applies to all/specific products/specific plans
    
    APPLY_STEPS (user enters code):
      1. User enters code in input field at cart or checkout
      2. Validate code: exists, not expired, not exhausted, not already used by this user
      3. IF invalid โ†’ show specific reason ("This code expired", "Already used", "Doesn't exist")
      4. Validate cart eligibility: meets min order value, has eligible items
      5. IF ineligible โ†’ show "Code requires $X minimum / specific items"
      6. Calculate discount amount
      7. Show updated total with line item: "Coupon SAVE10: -$10.00"
      8. Persist code on cart so it carries through to checkout
      9. On checkout, double-check validity (race condition protection)
      10. Apply at payment: pass discount to payment provider
      11. After successful payment: increment usage counter, mark per-user used
    
    ERROR_HANDLING:
      - Code typed wrong โ†’ trim whitespace, lowercase compare, show "Code 'X' not found"
      - Stacking attempt (multiple codes) โ†’ policy: allow stacking OR replace with newer (be explicit)
      - Last available use claimed by another user mid-checkout โ†’ show "Sorry, this code just sold out"
      - Discount makes total negative โ†’ cap at $0, never refund money
      - Provider rejects coupon โ†’ fall back to manual discount on order
    
    EXTENSION_POINTS:
      - First-purchase-only (composable_with: ["new-user"])
      - Subscription discount on first month (composable_with: ["subscription"])
      - Auto-applied discount based on URL param (UTM-driven)
    

    States โ€” how things change

    StateDescriptionTransitions
    No coupon appliedCart at full price
    • Code enteredโ†’Validating
    ValidatingChecking code against rules
    • Validโ†’Applied
    • Invalidโ†’No coupon applied
    AppliedDiscount visible on cart
    • Cart changedโ†’Validating
    • User removes codeโ†’No coupon applied
    • Checkout completedโ†’Used
    UsedCoupon consumed, counter incrementedterminal

    Easy-to-miss situations

    The kinds of edge cases that break demos.

    • What if a viral coupon code leaks publicly?

      high

      Massive unintended usage, budget blown.

      Suggested handling: Set hard total-uses limit on every coupon (default 1000). Set per-user limit (default 1). Monitor unusual usage spikes. Have a "burn coupon" admin action to instantly disable.

    • What if a user tries to stack 3 coupons?

      medium

      Without explicit policy, stacking creates massive discounts.

      Suggested handling: Default to one coupon per cart. Show "Replacing previous coupon X with Y" when applying second. Allow stacking only if explicitly enabled (e.g., welcome + free shipping).

    • What if the coupon makes the order total negative?

      medium

      $30 cart with $50 coupon โ€” do we owe the customer money?

      Suggested handling: Cap discount at order subtotal โ€” never go below $0. Show "Coupon -$30 (max for this order)" with explanation. Don't credit unused balance.

    • What if the user changes cart after applying coupon (removes the eligible item)?

      medium

      Coupon silently stops working, user confused at checkout.

      Suggested handling: Re-validate on every cart change. If newly ineligible, show clear toast: "Coupon SAVE10 no longer applies โ€” needs item X". Auto-remove from cart with notification.

    • What if a coupon was used but checkout failed (user retries)?

      medium

      Per-user limit hit but no successful purchase. Frustrating.

      Suggested handling: Only increment usage counter on successful payment, not on apply. Allow same code re-application on retry. Idempotency by user_id + code on success.

    Composes well with

    Combine these patterns when you need a richer flow.

    Build a flow starting from this pattern โ†’