feat(billing): Convert referral bonuses from monthly recurring to one-time credits, and bump them from 250 -> 500#422
Merged
brandonkachen merged 12 commits intomainfrom Feb 4, 2026
Merged
Conversation
- Add referral_legacy to GrantType union and GrantTypeValues array - Update GRANT_PRIORITIES: referral_legacy=30 (consumed first, renews monthly), referral=50 (one-time, preserved longer) - Add consumption order comment for clarity
- Add is_legacy boolean column to referral table (default false) - Migration adds referral_legacy enum value to grant_type - Backfills existing referrals as is_legacy=true (grandfathered users) - Migrates existing credit_ledger referral grants with expiry to referral_legacy type
…rals - Rename calculateTotalReferralBonus to calculateTotalLegacyReferralBonus - Filter by is_legacy=true to only count grandfathered referrals - Use referral_legacy type for monthly renewal grants - Update test mocks to include referral_legacy in breakdown/principals
- Set expiresAt: null for new referral grants (never expire) - Set is_legacy: false for new referrals (new program) - Remove unnecessary user.next_quota_reset query - Consolidate duplicate grant code into reusable grantForUser helper
- Add referral_legacy to grantTypeInfo with emerald color and Legacy label - Update description: referral is now "One-time bonus from referrals" - Move referral from expiringTypes to nonExpiringTypes - Add referral_legacy to expiringTypes (renews monthly)
84d62b6 to
2fef96d
Compare
- Add is_legacy field to referrals API response - Update CreditsBadge to show "per month" for legacy, "(one-time)" for new - Add "(legacy)" label next to legacy referrals in the list - Change main description to "one-time bonus" instead of "per month" - Add defensive default for is_legacy in Zod schema
Resolves merge conflicts to integrate both features: - referral_legacy grant type for legacy recurring monthly referrals - subscription grant type from main for subscription-based credits Conflict resolution: - common/src/types/grant.ts: Added both referral_legacy and subscription to GrantType - packages/billing: Updated grant-credits.ts to use referral_legacy for monthly bonuses - web/src/app/profile: Updated UI components to display both grant types correctly - DB migrations: Renumbered our migration to 0039 to sequence after main (0036-0038)
…me referral grants
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.
Summary
This PR changes the referral program from granting recurring monthly credits to one-time bonus credits that never expire. Existing (legacy) recurring referral bonuses are grandfathered and will continue to renew monthly.
Changes
Core Feature
expires_at: nullinstead of the user's next quota reset dateis_legacycolumn: Tracks whether a referral grant is legacy (recurring monthly) or new (one-time)referral_legacygrant type: Separate grant type for grandfathered recurring referrals with priority 30 (consumed first)is_legacycolumn andreferral_legacyenum valueGrant Priority Changes
referral_legacy(priority 30): Consumed first, renews monthly for grandfathered usersreferral(priority 50): One-time bonus, never expires, preserved longerUI Updates
API Changes
/api/referralsnow returnsis_legacyfieldredeemReferralCodecreates grants withtype: 'referral',expiresAt: null,is_legacy: falsecalculateTotalLegacyReferralBonusonly counts referrals whereis_legacy = trueFiles Changed
common/src/types/grant.ts- Addedreferral_legacygrant typecommon/src/constants/grant-priorities.ts- Updated prioritiespackages/billing/src/grant-credits.ts- Usereferral_legacyfor monthly renewalpackages/internal/src/db/schema.ts- Addedis_legacycolumnweb/src/app/api/referrals/helpers.ts- One-time grants with null expiryweb/src/app/api/referrals/route.ts- Addedis_legacyto responseweb/src/app/profile/components/referrals-section.tsx- Badge & label updatesweb/src/app/profile/components/usage-display.tsx- Addedreferral_legacydisplay