Skip to content

fix: discounts on update#864

Merged
johnyeocx merged 2 commits intomainfrom
fix/discounts-on-update
Mar 2, 2026
Merged

fix: discounts on update#864
johnyeocx merged 2 commits intomainfrom
fix/discounts-on-update

Conversation

@johnyeocx
Copy link
Collaborator

@johnyeocx johnyeocx commented Mar 2, 2026

Summary by cubic

Preserves existing Stripe discounts across subscription updates and plan switches by sending the correct params, and ensures checkout sessions only use coupon/promo code. Prevents new discount creation and duration resets.

  • Bug Fixes

    • Map discounts for updates using {discount: id} when present, else {promotion_code}/{coupon}; add stripeDiscountsToCheckoutParams and wire it into buildStripeCheckoutSessionAction.
    • Preserve discount identity and original start/end across immediate upgrades (including multi-hop), scheduled switches and phase transitions, cancel/uncancel, and downgrades; handle promo-code and deleted-coupon rollover cases.
  • Tests

    • Immediate switch: discount ID and end date stay the same across upgrades, including pro→premium→ultra.
    • Scheduled switch: discounts persist through schedule creation, cycle advance, replacements, and multiple discounts; upgrade-cancelled schedules keep discounts.
    • Edge cases: repeating coupon duration preserved, deleted-coupon downgrade succeeds, and checkout-applied promo codes persist when downgrading to free.
    • Update flow: cancel then uncancel keeps the same discount ID and end date.

Written for commit 50f0257. Summary will update on new commits.

Greptile Summary

This PR fixes a critical bug in discount handling during subscription updates. Previously, when customers upgraded/downgraded between products or performed cancel/uncancel operations, their discount durations were being reset instead of preserved.

Key Changes

Bug fixes:

  • Fixed discount parameter mapping to preserve existing discount IDs and durations during subscription updates by using { discount: id } instead of { coupon: id } for existing discounts
  • Separated checkout session discount handling (which doesn't support discount IDs) into a new stripeDiscountsToCheckoutParams function

Improvements:

  • Added comprehensive test coverage for discount preservation across multiple scenarios:
    • Immediate product switches (pro → premium)
    • Scheduled product switches with phase transitions
    • Cancel/uncancel operations
    • Edge cases including deleted coupons and repeating coupon duration preservation

The fix ensures that if a customer has a 3-month discount and uses 1 month, they retain only 2 months remaining after product changes - rather than getting a fresh 3-month discount.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The fix is well-isolated, logically sound, and addresses a clear bug in discount handling. The implementation correctly differentiates between subscription updates (which support preserving existing discounts via { discount: id }) and checkout sessions (which only support { coupon } or { promotion_code }). Type safety ensures source.coupon always exists on discount objects, and comprehensive test coverage validates all critical scenarios including edge cases like deleted coupons and phase transitions.
  • No files require special attention

Important Files Changed

Filename Overview
server/src/internal/billing/v2/providers/stripe/utils/discounts/stripeDiscountsToParams.ts Split discount parameter mapping into two functions - stripeDiscountsToParams for subscription updates (preserves existing discount IDs) and stripeDiscountsToCheckoutParams for checkout sessions (only supports coupon/promo code). Core fix that prevents discount duration from being reset.
server/src/internal/billing/v2/providers/stripe/actionBuilders/buildStripeCheckoutSessionAction.ts Updated to use stripeDiscountsToCheckoutParams instead of stripeDiscountsToParams, correctly handling checkout session discount format restrictions.
server/tests/integration/billing/attach/immediate-switch/immediate-switch-discounts.test.ts New test file covering discount preservation during immediate product upgrades (pro to premium). Verifies discount ID and end timestamp remain unchanged.
server/tests/integration/billing/attach/scheduled-switch/discounts/scheduled-switch-discounts-edge.test.ts New test file covering edge cases: repeating coupon duration preservation across phase transitions, handling deleted coupons, and promo code preservation during downgrades.

Last reviewed commit: 50f0257

@johnyeocx johnyeocx requested a review from ay-rod as a code owner March 2, 2026 19:46
@vercel
Copy link

vercel bot commented Mar 2, 2026

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

Project Deployment Actions Updated (UTC)
autumn-vite Ready Ready Preview, Comment Mar 2, 2026 7:47pm

Request Review

@johnyeocx johnyeocx merged commit f38e29f into main Mar 2, 2026
7 of 9 checks passed
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 8 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

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