Skip to content

Conversation

@pieterbeulque
Copy link
Contributor

Issue #8714.
Replaced #8776.

Original PR description:

The original issue was that discounts applied to existing subscriptions mid-billing-cycle were immediately considered expired and never actually applied. The root cause was that is_repetition_expired() was using the subscription start date to calculate discount expiration, which doesn't work when a discount is added after the subscription started.

The fix involved tracking when a discount is first actually applied to a billing entry. For this I added a new discount_applied_at field on the Subscription model. This field is null when a discount is added but hasn't been used yet, and gets set during cycle() when the discount is first applied to a billing entry. The is_repetition_expired() method now uses this field to determine if "once" discounts have been used up and when "repeating" discount durations should start counting. This PR also fixes subscription.amount to only reflect the discounted price after the discount is actually applied, not immediately when added mid-cycle.

@vercel
Copy link

vercel bot commented Jan 6, 2026

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

2 Skipped Deployments
Project Deployment Review Updated (UTC)
polar Ignored Ignored Preview Jan 8, 2026 3:30pm
polar-sandbox Ignored Ignored Preview Jan 8, 2026 3:30pm

Copy link
Member

@frankie567 frankie567 left a comment

Choose a reason for hiding this comment

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

Great, makes a lot of sense :) Good to go!!

claude and others added 4 commits January 8, 2026 15:46
The discount expiration logic was using subscription.started_at as the
reference point, which caused discounts applied mid-subscription to
expire immediately (since started_at could be months in the past).

This commit:
- Adds discount_applied_at field to track when a discount was first
  applied to a billing cycle
- Updates is_repetition_expired() to use discount_applied_at instead
  of subscription.started_at
- Sets discount_applied_at at checkout for non-trial subscriptions
- Sets discount_applied_at on first cycle() for mid-subscription
  discount applications
- Updates subscription.amount immediately when discount is assigned
  (already handled by existing event listener)
Verifies that when a discount is applied at checkout with a trial period:
- discount_applied_at remains null during the trial
- discount_applied_at is set on the first billing cycle after trial ends
- The discount duration is counted from the first billing cycle, not trial start
Populates discount_applied_at for existing subscriptions by finding
the first order that used each subscription's current discount.
@pieterbeulque pieterbeulque force-pushed the claude/refactor-pr-changes-Am84s branch from a9dc25a to 77b3c29 Compare January 8, 2026 14:46
@github-actions
Copy link
Contributor

github-actions bot commented Jan 8, 2026

⚠️ Migration Isolation Check Failed

This PR contains database migrations along with other code changes. To ensure safe deployments, please split this into separate PRs,
or verify that your changes will not break. Keep in mind that the API is deployed before the workers.

  1. Migration PR: Only model changes and the migration file
  2. Code PR: All other changes (can be merged after migration PR)

Files that should be in a separate PR:

  • server/polar/subscription/service.py
  • server/tests/discount/test_service.py
  • server/tests/fixtures/random_objects.py
  • server/tests/subscription/test_service.py

Why?

Migrations are deployed separately and run before code changes. Mixing them can cause deployment issues if the new code depends on the migration.

@pieterbeulque
Copy link
Contributor Author

Split in #8826 and #8827

@pieterbeulque pieterbeulque deleted the claude/refactor-pr-changes-Am84s branch January 9, 2026 11:13
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.

4 participants