Skip to content

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

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

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

Conversation

@waleedlatif1
Copy link
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
Copy link

cursor bot commented Mar 24, 2026

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
Copy link

vercel bot commented Mar 24, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Mar 24, 2026 7:51pm

Request Review

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 24, 2026

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

Filename Overview
apps/sim/app/api/v1/admin/referral-campaigns/route.ts Adds 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

@waleedlatif1
Copy link
Collaborator Author

@cursor review

@waleedlatif1
Copy link
Collaborator Author

@greptile

Copy link

@cursor cursor bot left a comment

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 staging Mar 24, 2026
12 checks passed
@waleedlatif1 waleedlatif1 deleted the waleedlatif1/coupon-duration-support branch March 24, 2026 20:04
Sign up for free to 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