Skip to content

feat(billing): add appliesTo plan restriction for coupon codes - #3744

Merged
waleedlatif1 merged 2 commits into
stagingfrom
waleedlatif1/coupon-duration-support
Mar 24, 2026
Merged

feat(billing): add appliesTo plan restriction for coupon codes#3744
waleedlatif1 merged 2 commits into
stagingfrom
waleedlatif1/coupon-duration-support

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Added appliesTo field to POST /api/v1/admin/referral-campaigns to restrict coupons to specific Stripe products
  • Supports broad categories (pro, team) and specific plan names (pro_6000, pro_25000, team_6000, team_25000)
  • Resolves plan names to Stripe product IDs via price lookups, sets coupon.applies_to.products
  • GET response includes appliesToProductIds for existing coupons

Type of Change

  • New feature

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@cursor

cursorBot commented Mar 24, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Moderate risk because it changes how Stripe coupons are created by adding product-scoped restrictions and introduces runtime Stripe price lookups that can fail if plan/price configuration is incorrect.

Overview
Admin POST /api/v1/admin/referral-campaigns now accepts an optional appliesTo array (e.g. pro, team, or specific tiers) to restrict created Stripe coupons to specific products via coupon.applies_to.products.

The endpoint validates appliesTo, resolves the selected plans’ Stripe product IDs by looking up configured plan price IDs (getPlans() + stripe.prices.retrieve), and fails the request if products can’t be resolved. GET responses now include appliesToProductIds to surface existing coupon restrictions.

Written by Cursor Bugbot for commit 903ff2d. Configure here.

@vercel

vercelBot commented Mar 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
docsSkippedSkippedMar 24, 2026 7:51pm

Request Review

@greptile-apps

greptile-appsBot commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extends the POST /api/v1/admin/referral-campaigns endpoint with an optional appliesTo field that restricts generated Stripe coupons to specific product tiers (pro, team, or specific plan names like pro_6000). It resolves plan names to Stripe product IDs by reading the billing plan registry and calling stripe.prices.retrieve per price ID, then passes the resulting product IDs to coupon.applies_to.products. The GET response is updated to expose appliesToProductIds from existing coupons.

Key implementation details:

  • Input validation correctly rejects unknown values and empty arrays using the VALID_APPLIES_TO constant
  • Broad matchers ('pro', 'team') are resolved via isPro/isTeam helpers, matching all tier suffixes (e.g. pro_6000, pro_25000)
  • Promise.allSettled is used and any single failure aborts the whole request (fixed from the previous review cycle), preventing under-restricted coupons
  • Product IDs are deduplicated via a Set before being passed to Stripe
  • Minor: when resolveProductIds throws due to a Stripe API error, the outer catch block logs and returns "Failed to create promotion code" — technically accurate but misleading since no coupon was created at that point

Confidence Score: 4/5

  • This PR is safe to merge; the critical partial-failure bug from the previous review cycle is resolved and the new feature is well-validated.
  • The previous P0 concern (silent partial failure creating under-restricted coupons) was addressed by switching to Promise.allSettled with a hard-fail on any rejected promise. Input validation is thorough, product ID deduplication is correct, and the admin-only scope limits blast radius. The only remaining item is a minor misleading error message in the catch block, which is a non-blocking style issue.
  • No files require special attention — the single changed file is well-structured and the implementation is correct.

Important Files Changed

FilenameOverview
apps/sim/app/api/v1/admin/referral-campaigns/route.tsAdds appliesTo field to the POST handler, resolving plan names to Stripe product IDs via resolveProductIds. Input validation, partial-failure handling (Promise.allSettled), and product ID deduplication are all correct. A minor issue: when resolveProductIds throws, the outer catch logs "Failed to create promotion code" even though no coupon was created yet, which is misleading.

Sequence Diagram

sequenceDiagram
participant Admin
participant POST Handler
participant resolveProductIds
participant getPlans
participant Stripe
Admin->>POST Handler: POST /api/v1/admin/referral-campaigns\n{ appliesTo: ['pro'] }
POST Handler->>POST Handler: Validate appliesTo values
POST Handler->>resolveProductIds: stripe, ['pro']
resolveProductIds->>getPlans: getPlans()
getPlans-->>resolveProductIds: [pro_6000, pro_25000, ...]
resolveProductIds->>resolveProductIds: Match plans via isPro/isTeam\nCollect priceIds
resolveProductIds->>Stripe: Promise.allSettled(prices.retrieve × N)
Stripe-->>resolveProductIds: price objects (or failures)
resolveProductIds->>resolveProductIds: Any failures? → throw Error
resolveProductIds-->>POST Handler: unique productIds[]
POST Handler->>Stripe: coupons.create({ applies_to: { products } })
Stripe-->>POST Handler: coupon
POST Handler->>Stripe: promotionCodes.create({ coupon })
Stripe-->>POST Handler: promoCode
POST Handler-->>Admin: 200 { appliesToProductIds, ... }
Loading

Reviews (2): Last reviewed commit: "fix(billing): fail coupon creation on pa..." | Re-trigger Greptile

Comment threadapps/sim/app/api/v1/admin/referral-campaigns/route.ts Outdated
Comment threadapps/sim/app/api/v1/admin/referral-campaigns/route.ts
Comment threadapps/sim/app/api/v1/admin/referral-campaigns/route.ts
@waleedlatif1

Copy link
Copy Markdown
CollaboratorAuthor

@cursor review

@waleedlatif1

Copy link
Copy Markdown
CollaboratorAuthor

@greptile

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

@waleedlatif1
waleedlatif1 merged commit 77eafab into stagingMar 24, 2026
12 checks passed
@waleedlatif1
waleedlatif1 deleted the waleedlatif1/coupon-duration-support branch March 24, 2026 20:04
waleedlatif1 added a commit that referenced this pull request Apr 4, 2026
* feat(billing): add appliesTo plan restriction for coupon codes
* fix(billing): fail coupon creation on partial product resolution
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@waleedlatif1