fix(coupons): enforce start_date, deleted_at and per_user_limit in coupon validation - #1716
Open
MOHITKOURAV01 wants to merge 1 commit into
Open
Conversation
…lidateCoupon validateCoupon checked is_active, an end date, the global usage_limit and minimum_order_amount, and nothing else. Three columns the coupons table has carried since the baseline schema were never read: - start_date, so a campaign scheduled for next month discounted today - deleted_at, so a soft-deleted coupon kept working - per_user_limit, so a one-per-customer coupon was unlimited in practice The userId parameter was accepted and documented but never referenced. Adds the two date/lifecycle guards and a per-account redemption check backed by the coupon_usage ledger (promo_usage for promo_codes codes), which nothing in the codebase had ever written to. recordCouponUsage now files that ledger row; its options argument is optional, so existing three-argument callers are unaffected. Date parsing is deliberately lenient: an unparseable column yields null rather than a boundary that has already passed, so a malformed row cannot reject a live coupon. A missing ledger table is counted as zero redemptions rather than failing checkout, leaving the global usage_limit as the backstop. Closes AnthropicBots#1711
🔍 Quality Gate Report✅ All quality gates passed!
|
🤖 AI Code Review🔴 Score: 50/100 | AI review unavailable at this time. Automated AI review — a human maintainer will also review. |
|
@MOHITKOURAV01 is attempting to deploy a commit to the Bhuvansh's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
💡 Suggested reviewers based on relevant file history: @Aditya8369, @Pcmhacker-hero |
🟡 PR Health Score: 55/100This PR's health score is below the 75/100 threshold for a healthy label.
Improving these signals will help reviewers engage faster and raise your score. 💪 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this fixes
Closes #1711
validateCoupon()looked atis_active, an end date, the globalusage_limitandminimum_order_amount. Three columns thecouponstable has carried sincemigrations/0001_baseline_schema.sqlwere never read at all:start_date TIMESTAMP NOT NULLdeleted_at DATETIMEper_user_limit INT DEFAULT 1The
userIdargument was accepted and documented as "ID of the calling user" and then never referenced anywhere in the function body — which is exactly why the per-account cap was unenforceable.What changed
backend/services/couponService.jsstart_date/starts_at/valid_fromis still in the future. All three spellings are accepted because the coupons and promo_codes tables name the column differently.deleted_atis rejected as an invalid code. Deliberately reuses the "Invalid coupon code" wording rather than announcing that a coupon used to exist.userIdis supplied, prior redemptions are counted and checked againstper_user_limit(orusage_limit_per_useron a promo row). Counting readscoupon_usagefor a coupons-table code andpromo_usagefor a promo_codes code, so the two ledgers stay separate.coupon_usagehas existed since the baseline schema and nothing in the codebase ever inserted into it — without a row there is nothing for the cap to count.recordCouponUsage()now files one. Its newoptionsargument is optional, so the existing three-argument call inorder.service.jskeeps working with no change.Deliberate choices
parseDate()returnsnullfor junk rather than a date, so a malformedstart_datecannot make a live coupon look "not yet active". Rejecting a valid coupon is the worse failure here.per_user_limitmeans uncapped, not "nobody may use this". A mis-seeded row should not lock out every shopper.coupon_usagehas not been migrated yet, checkout still completes and the globalusage_limitremains the backstop — the alternative is a hard checkout failure on a schema gap.coupon_usage.user_idisNOT NULLand a guest has no stable identity to cap against, so no query is issued and no row is written.Tests
New
backend/tests/couponLifecycle.test.js, 21 cases across start-date handling, soft deletion, per-account limits (including the coupons-vs-promo table split and the guest path), ledger writes, backwards compatibility of the three-argument call, and date parsing. They run against a stub connection, so no MySQL is needed in CI.Verified the guards are meaningful: with the service reverted and only the tests applied, 11 of the 21 fail. With the fix in place all 21 pass.
Scope
backend/services/couponService.jsand one new test file. No route, schema or frontend changes — the call sites keep their current signatures.CI note
Syntax check is red on this branch, and on every open PR in the repo right now. The single failure is inherited from
main:No file this PR touches is implicated —
shop.jsis untouched here. It is tracked as #1696 with a fix open in #1701. Because Syntax check gates the rest of the workflow, Backend tests and Server boots reportskippingrather than running; both suites pass locally on this branch.The red Vercel check is the repo-wide
Authorization required to deploy(teambhuvanshs-projects) and is unrelated to this change.