From 3bb68c97a97fd45fc38be739013611fcfa1176f0 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 09:07:21 +0000 Subject: [PATCH] fix(kilo-pass): remove grandfathered second-month 50% bonus promo The limited-time two-month promo ended on 2026-05-07. Active subscribers in their second month were still seeing a 50% bonus displayed on the Kilo Pass bar instead of the standard streak ramp bonus. Remove the second-month 50% grandfathered promo from computeMonthlyCadenceBonusPercent so month 2 now uses the standard ramp formula (e.g. 10% for streak=2). Clean up the now-unused constants, subscriptionStartedAtIso parameter, isTwoMonthPromoOfferActive helper, showSecondMonthPromo UI props, and all related test fixtures. --- .../billing/PlanSelectionDialog.tsx | 13 -- .../claw/components/billing/WelcomePage.tsx | 13 -- .../profile/ProfileKiloPassSection.tsx | 10 -- .../KiloPassActiveSubscriptionCard.logic.ts | 1 - .../kilo-pass/KiloPassBonusRampDialog.tsx | 34 +----- .../kilo-pass/KiloPassSubscribeCard.tsx | 16 +-- .../profile/kilo-pass/KiloPassTierCard.tsx | 21 +--- .../kilo-pass/KiloPassDetail.tsx | 15 --- .../subscriptions/kilo-pass/KiloPassGroup.tsx | 10 -- apps/web/src/lib/kilo-pass/bonus.test.ts | 86 ++----------- apps/web/src/lib/kilo-pass/bonus.ts | 22 +--- apps/web/src/lib/kilo-pass/constants.ts | 7 -- .../kilo-pass/usage-triggered-bonus.test.ts | 115 +----------------- .../lib/kilo-pass/usage-triggered-bonus.ts | 1 - .../usage-triggered-bonus.unit.test.ts | 9 +- apps/web/src/routers/kilo-pass-router.test.ts | 97 ++++----------- apps/web/src/routers/kilo-pass-router.ts | 13 +- 17 files changed, 53 insertions(+), 430 deletions(-) diff --git a/apps/web/src/app/(app)/claw/components/billing/PlanSelectionDialog.tsx b/apps/web/src/app/(app)/claw/components/billing/PlanSelectionDialog.tsx index b7e5388276..de4858b1ac 100644 --- a/apps/web/src/app/(app)/claw/components/billing/PlanSelectionDialog.tsx +++ b/apps/web/src/app/(app)/claw/components/billing/PlanSelectionDialog.tsx @@ -20,8 +20,6 @@ import { type KiloClawSignupDisplay, type KiloPassUpsellActivationPreview, } from './billing-types'; -import { KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF } from '@/lib/kilo-pass/constants'; -import { dayjs } from '@/lib/kilo-pass/dayjs'; type Cadence = 'monthly' | 'yearly'; type Tier = '19' | '49' | '199'; @@ -293,8 +291,6 @@ function HostingOnlyPlanCard({ function CreditsHowItWorks() { const [open, setOpen] = useState(false); - const showTwoMonthPromo = dayjs().utc().isBefore(KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF); - return (
- {showTwoMonthPromo ? ( -
- - - First-time subscribers receive 50% free - bonus credits for the first two months. - -
- ) : null} )} diff --git a/apps/web/src/app/(app)/claw/components/billing/WelcomePage.tsx b/apps/web/src/app/(app)/claw/components/billing/WelcomePage.tsx index 18e981cf29..f3f9a812e7 100644 --- a/apps/web/src/app/(app)/claw/components/billing/WelcomePage.tsx +++ b/apps/web/src/app/(app)/claw/components/billing/WelcomePage.tsx @@ -19,8 +19,6 @@ import { type KiloClawSignupDisplay, type KiloPassUpsellActivationPreview, } from './billing-types'; -import { KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF } from '@/lib/kilo-pass/constants'; -import { dayjs } from '@/lib/kilo-pass/dayjs'; type Cadence = 'monthly' | 'yearly'; type Tier = '19' | '49' | '199'; @@ -286,8 +284,6 @@ function HostingOnlyPlanCard({ function CreditsHowItWorks() { const [open, setOpen] = useState(false); - const showTwoMonthPromo = dayjs().utc().isBefore(KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF); - return (
- {showTwoMonthPromo ? ( -
- - - First-time subscribers receive 50% free - bonus credits for the first two months. - -
- ) : null} )} diff --git a/apps/web/src/components/profile/ProfileKiloPassSection.tsx b/apps/web/src/components/profile/ProfileKiloPassSection.tsx index 2c777fdd4a..42655da1b2 100644 --- a/apps/web/src/components/profile/ProfileKiloPassSection.tsx +++ b/apps/web/src/components/profile/ProfileKiloPassSection.tsx @@ -11,14 +11,6 @@ import { KiloPassLoadingCard } from '@/components/profile/kilo-pass/KiloPassLoad import { KiloPassSubscribeCard } from '@/components/profile/kilo-pass/KiloPassSubscribeCard'; import { isStripeSubscriptionEnded } from '@/lib/kilo-pass/stripe-subscription-status'; import { recommendKiloPassTierFromAverageMonthlyUsageUsd } from '@/lib/kilo-pass/recommend-tier'; -import { KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF } from '@/lib/kilo-pass/constants'; -import { dayjs } from '@/lib/kilo-pass/dayjs'; - -function getShowKiloPassTwoMonthPromo(showFirstMonthPromo: boolean): boolean { - return ( - showFirstMonthPromo && dayjs().utc().isBefore(KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF) - ); -} export function ProfileKiloPassSection() { const trpc = useTRPC(); @@ -65,7 +57,6 @@ export function ProfileKiloPassSection() { if (!activeSubscription) { const pending = checkoutMutation.isPending; const showFirstMonthPromo = query.data.isEligibleForFirstMonthPromo; - const showSecondMonthPromo = getShowKiloPassTwoMonthPromo(showFirstMonthPromo); const averageMonthlyUsageUsd = averageMonthlyUsageQuery.data?.averageMonthlyUsageUsd; const recommendedTier = typeof averageMonthlyUsageUsd === 'number' @@ -78,7 +69,6 @@ export function ProfileKiloPassSection() { setCadence={setCadence} pending={pending} showFirstMonthPromo={showFirstMonthPromo} - showSecondMonthPromo={showSecondMonthPromo} recommendedTier={recommendedTier} onSelectTier={tier => checkoutMutation.mutate({ tier, cadence })} /> diff --git a/apps/web/src/components/profile/kilo-pass/KiloPassActiveSubscriptionCard.logic.ts b/apps/web/src/components/profile/kilo-pass/KiloPassActiveSubscriptionCard.logic.ts index 3527b7ce7a..c1d60e68b5 100644 --- a/apps/web/src/components/profile/kilo-pass/KiloPassActiveSubscriptionCard.logic.ts +++ b/apps/web/src/components/profile/kilo-pass/KiloPassActiveSubscriptionCard.logic.ts @@ -130,7 +130,6 @@ function computeRefillRowModel(params: { tier: baseTier, streakMonths: Math.max(1, params.subscription.currentStreakMonths + 1), isFirstTimeSubscriberEver: params.subscription.isFirstTimeSubscriberEver, - subscriptionStartedAtIso: params.subscription.startedAt, }) : null; if (typeof bonusUsd !== 'number' || bonusUsd <= 0) return null; diff --git a/apps/web/src/components/profile/kilo-pass/KiloPassBonusRampDialog.tsx b/apps/web/src/components/profile/kilo-pass/KiloPassBonusRampDialog.tsx index adc9715ae3..2633000146 100644 --- a/apps/web/src/components/profile/kilo-pass/KiloPassBonusRampDialog.tsx +++ b/apps/web/src/components/profile/kilo-pass/KiloPassBonusRampDialog.tsx @@ -26,29 +26,12 @@ const clampMonth = (month: number) => Math.min(12, Math.max(1, Math.round(month) export function KiloPassBonusRampDialog(props: { tier: KiloPassTier; showFirstMonthPromo?: boolean; - showSecondMonthPromo?: boolean; streakMonths?: number; showSlider?: boolean; - subscriptionStartedAtIso?: string; }) { - const { - tier, - showFirstMonthPromo = false, - showSecondMonthPromo = false, - streakMonths, - showSlider = true, - subscriptionStartedAtIso, - } = props; + const { tier, showFirstMonthPromo = false, streakMonths, showSlider = true } = props; const [open, setOpen] = useState(false); - const fallbackSubscriptionStartedAtIso = useMemo(() => { - const now = new Date(); - now.setSeconds(0, 0); - return now.toISOString(); - }, []); - - const resolvedSubscriptionStartedAtIso = - subscriptionStartedAtIso ?? fallbackSubscriptionStartedAtIso; const resolvedMonth = typeof streakMonths === 'number' && !Number.isNaN(streakMonths) ? clampMonth(streakMonths) : 1; const [sliderMonth, setSliderMonth] = useState(resolvedMonth); @@ -60,7 +43,6 @@ export function KiloPassBonusRampDialog(props: { tier, streakMonths: effectiveMonth, isFirstTimeSubscriberEver: showFirstMonthPromo, - subscriptionStartedAtIso: resolvedSubscriptionStartedAtIso, }); const totalBonusUsd = useMemo( @@ -71,18 +53,10 @@ export function KiloPassBonusRampDialog(props: { tier, streakMonths: index + 1, isFirstTimeSubscriberEver: showFirstMonthPromo, - subscriptionStartedAtIso: resolvedSubscriptionStartedAtIso, }) ).reduce((total, percent) => total + config.monthlyPriceUsd * percent, 0) : 0, - [ - config.monthlyPriceUsd, - effectiveMonth, - showFirstMonthPromo, - showSlider, - resolvedSubscriptionStartedAtIso, - tier, - ] + [config.monthlyPriceUsd, effectiveMonth, showFirstMonthPromo, showSlider, tier] ); return ( @@ -172,9 +146,7 @@ export function KiloPassBonusRampDialog(props: { {showPromoCallout && (
- As a new subscriber, your{' '} - first {showSecondMonthPromo ? '2 paid months' : 'paid month'} get a - one-time promo of{' '} + As a new subscriber, your first paid month gets a one-time promo of{' '} +{formatPercent(KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT)} free bonus credits {' '} diff --git a/apps/web/src/components/profile/kilo-pass/KiloPassSubscribeCard.tsx b/apps/web/src/components/profile/kilo-pass/KiloPassSubscribeCard.tsx index 6c3e06f1ff..c3ce7f78bc 100644 --- a/apps/web/src/components/profile/kilo-pass/KiloPassSubscribeCard.tsx +++ b/apps/web/src/components/profile/kilo-pass/KiloPassSubscribeCard.tsx @@ -7,12 +7,8 @@ import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { KiloPassCadence } from '@/lib/kilo-pass/enums'; import type { KiloPassTier } from '@/lib/kilo-pass/enums'; -import { - KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF, - KILO_PASS_TIER_CONFIG, -} from '@/lib/kilo-pass/constants'; +import { KILO_PASS_TIER_CONFIG } from '@/lib/kilo-pass/constants'; import { cn } from '@/lib/utils'; -import { formatIsoDateString_UsaDateOnlyFormat } from '@/lib/utils'; import { KiloPassTierCard } from './KiloPassTierCard'; @@ -21,7 +17,6 @@ export function KiloPassSubscribeCard(props: { setCadence: (cadence: KiloPassCadence) => void; pending: boolean; showFirstMonthPromo?: boolean; - showSecondMonthPromo: boolean; showHeader?: boolean; className?: string; contentClassName?: string; @@ -33,7 +28,6 @@ export function KiloPassSubscribeCard(props: { setCadence, pending, showFirstMonthPromo = false, - showSecondMonthPromo, showHeader = true, className, contentClassName, @@ -43,14 +37,9 @@ export function KiloPassSubscribeCard(props: { const tiers = Object.keys(KILO_PASS_TIER_CONFIG) as KiloPassTier[]; - const promoCutoffLabel = formatIsoDateString_UsaDateOnlyFormat( - KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF.toISOString() - ); const monthlyPromoDescription = cadence === KiloPassCadence.Monthly && showFirstMonthPromo - ? showSecondMonthPromo - ? `First-time subscribers receive 50% free bonus credits for the first two months when they start before ${promoCutoffLabel}.` - : 'First-time subscribers receive 50% free bonus credits for the first month.' + ? 'First-time subscribers receive 50% free bonus credits for the first month.' : null; const cadenceOptions = [ { value: KiloPassCadence.Monthly, label: 'Monthly' }, @@ -121,7 +110,6 @@ export function KiloPassSubscribeCard(props: { cadence={cadence} pending={pending} showFirstMonthPromo={showFirstMonthPromo} - showSecondMonthPromo={showSecondMonthPromo} isRecommended={recommendedTier != null && tier === recommendedTier} onSelect={onSelectTier} /> diff --git a/apps/web/src/components/profile/kilo-pass/KiloPassTierCard.tsx b/apps/web/src/components/profile/kilo-pass/KiloPassTierCard.tsx index b352cdbd77..192d9f0fed 100644 --- a/apps/web/src/components/profile/kilo-pass/KiloPassTierCard.tsx +++ b/apps/web/src/components/profile/kilo-pass/KiloPassTierCard.tsx @@ -24,19 +24,10 @@ export function KiloPassTierCard(props: { cadence: KiloPassCadence; pending: boolean; showFirstMonthPromo?: boolean; - showSecondMonthPromo?: boolean; isRecommended: boolean; onSelect: (tier: KiloPassTier) => void; }) { - const { - tier, - cadence, - pending, - showFirstMonthPromo = false, - showSecondMonthPromo = false, - isRecommended, - onSelect, - } = props; + const { tier, cadence, pending, showFirstMonthPromo = false, isRecommended, onSelect } = props; const config = KILO_PASS_TIER_CONFIG[tier]; const handleSelect = () => { if (pending) return; @@ -99,17 +90,13 @@ export function KiloPassTierCard(props: { {' '} free bonus credits - +
{showFirstMonthPromo && (
- {showSecondMonthPromo ? 'First 2 months:' : 'First month:'} + - {formatPercent(KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT)} free bonus credits + First month: +{formatPercent(KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT)} free bonus + credits
)} diff --git a/apps/web/src/components/subscriptions/kilo-pass/KiloPassDetail.tsx b/apps/web/src/components/subscriptions/kilo-pass/KiloPassDetail.tsx index c2352203f6..5de6cb6ad7 100644 --- a/apps/web/src/components/subscriptions/kilo-pass/KiloPassDetail.tsx +++ b/apps/web/src/components/subscriptions/kilo-pass/KiloPassDetail.tsx @@ -110,23 +110,10 @@ export function KiloPassDetail() { tier: subscription.tier, streakMonths: Math.max(1, subscription.currentStreakMonths), isFirstTimeSubscriberEver: subscription.isFirstTimeSubscriberEver, - subscriptionStartedAtIso: subscription.startedAt, }); return promoPercent === KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT; }, [subscription]); - const showSecondMonthPromoInDialog = useMemo(() => { - if (!subscription || subscription.cadence !== 'monthly') return false; - if (subscription.currentStreakMonths > 2) return false; - const month2Percent = computeMonthlyCadenceBonusPercent({ - tier: subscription.tier, - streakMonths: 2, - isFirstTimeSubscriberEver: subscription.isFirstTimeSubscriberEver, - subscriptionStartedAtIso: subscription.startedAt, - }); - return month2Percent === KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT; - }, [subscription]); - async function refreshData() { await Promise.all([ queryClient.invalidateQueries({ queryKey: trpc.kiloPass.getState.queryKey() }), @@ -262,9 +249,7 @@ export function KiloPassDetail() { diff --git a/apps/web/src/components/subscriptions/kilo-pass/KiloPassGroup.tsx b/apps/web/src/components/subscriptions/kilo-pass/KiloPassGroup.tsx index 1fed8b70d8..c1ccd417e1 100644 --- a/apps/web/src/components/subscriptions/kilo-pass/KiloPassGroup.tsx +++ b/apps/web/src/components/subscriptions/kilo-pass/KiloPassGroup.tsx @@ -5,8 +5,6 @@ import { useState } from 'react'; import { toast } from 'sonner'; import { Crown } from 'lucide-react'; import { useTRPC } from '@/lib/trpc/utils'; -import { KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF } from '@/lib/kilo-pass/constants'; -import { dayjs } from '@/lib/kilo-pass/dayjs'; import { KiloPassCadence } from '@/lib/kilo-pass/enums'; import type { KiloPassTier } from '@/lib/kilo-pass/enums'; import { recommendKiloPassTierFromAverageMonthlyUsageUsd } from '@/lib/kilo-pass/recommend-tier'; @@ -27,12 +25,6 @@ import { getKiloPassSubscriptionDisplayModel, } from './KiloPassDetail.logic'; -function getShowKiloPassTwoMonthPromo(showFirstMonthPromo: boolean): boolean { - return ( - showFirstMonthPromo && dayjs().utc().isBefore(KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF) - ); -} - export function KiloPassGroup({ showTerminal, accordionValue, @@ -73,7 +65,6 @@ export function KiloPassGroup({ } const showFirstMonthPromo = query.data?.isEligibleForFirstMonthPromo ?? false; - const showSecondMonthPromo = getShowKiloPassTwoMonthPromo(showFirstMonthPromo); const averageMonthlyUsageUsd = averageMonthlyUsageQuery.data?.averageMonthlyUsageUsd; const recommendedTier = typeof averageMonthlyUsageUsd === 'number' @@ -134,7 +125,6 @@ export function KiloPassGroup({ setCadence={setCadence} pending={checkout.isPending} showFirstMonthPromo={showFirstMonthPromo} - showSecondMonthPromo={showSecondMonthPromo} recommendedTier={recommendedTier} onSelectTier={tier => void startCheckout(tier)} showHeader={false} diff --git a/apps/web/src/lib/kilo-pass/bonus.test.ts b/apps/web/src/lib/kilo-pass/bonus.test.ts index 42803933b3..b398990f49 100644 --- a/apps/web/src/lib/kilo-pass/bonus.test.ts +++ b/apps/web/src/lib/kilo-pass/bonus.test.ts @@ -8,7 +8,6 @@ import { import { KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT, - KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF, KILO_PASS_MONTHLY_RAMP_BASE_BONUS_PERCENT, KILO_PASS_MONTHLY_RAMP_CAP_BONUS_PERCENT, KILO_PASS_MONTHLY_RAMP_STEP_BONUS_PERCENT, @@ -165,37 +164,35 @@ describe('kilo pass bonus utilities', () => { }); describe('computeMonthlyCadenceBonusPercent', () => { - it('keeps the second-month grandfather cutoff at midnight May 7 UTC', () => { - expect(KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF.toISOString()).toBe( - '2026-05-07T00:00:00.000Z' - ); - }); - - it('applies the 50% promo for streak months 1 and 2 when eligible (strictly before cutoff)', () => { + it('applies the 50% promo for streak month 1 when first-time subscriber', () => { expect( computeMonthlyCadenceBonusPercent({ tier: KiloPassTier.Tier19, streakMonths: 1, isFirstTimeSubscriberEver: true, - subscriptionStartedAtIso: '2026-01-26T23:59:59.000Z', }) ).toBeCloseTo(KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT); + }); + it('uses the standard ramp for streak month 2 even for first-time subscribers', () => { expect( computeMonthlyCadenceBonusPercent({ tier: KiloPassTier.Tier19, streakMonths: 2, isFirstTimeSubscriberEver: true, - subscriptionStartedAtIso: '2026-01-26T23:59:59.000Z', }) - ).toBeCloseTo(KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT); + ).toBeCloseTo( + KILO_PASS_TIER_CONFIG.tier_19.monthlyBaseBonusPercent + + KILO_PASS_TIER_CONFIG.tier_19.monthlyStepBonusPercent * 1 + ); + }); + it('uses the standard ramp for streak month 3 for first-time subscribers', () => { expect( computeMonthlyCadenceBonusPercent({ tier: KiloPassTier.Tier19, streakMonths: 3, isFirstTimeSubscriberEver: true, - subscriptionStartedAtIso: '2026-01-26T23:59:59.000Z', }) ).toBeCloseTo( KILO_PASS_TIER_CONFIG.tier_19.monthlyBaseBonusPercent + @@ -203,19 +200,6 @@ describe('kilo pass bonus utilities', () => { ); }); - it('applies the first-month promo for first-time subscribers after the grandfather cutoff', () => { - expect( - computeMonthlyCadenceBonusPercent({ - tier: KiloPassTier.Tier19, - streakMonths: 1, - isFirstTimeSubscriberEver: true, - subscriptionStartedAtIso: new Date( - KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF.valueOf() + 1 - ).toISOString(), - }) - ).toBeCloseTo(KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT); - }); - it('does not apply the override when isFirstTimeSubscriberEver is false', () => { expect( computeMonthlyCadenceBonusPercent({ @@ -227,58 +211,6 @@ describe('kilo pass bonus utilities', () => { }); }); - describe('computeMonthlyCadenceBonusPercent (promo cutoff behavior)', () => { - const tier = KiloPassTier.Tier49; - - const computeFallback = (params: { - streakMonths: number; - isFirstTimeSubscriberEver: boolean; - }): number => { - return computeMonthlyCadenceBonusPercent({ - tier, - streakMonths: params.streakMonths, - isFirstTimeSubscriberEver: params.isFirstTimeSubscriberEver, - subscriptionStartedAtIso: KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF.toISOString(), - }); - }; - - it('applies the first-month promo at the second-month grandfather cutoff', () => { - expect( - computeMonthlyCadenceBonusPercent({ - tier, - streakMonths: 1, - isFirstTimeSubscriberEver: true, - subscriptionStartedAtIso: KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF.toISOString(), - }) - ).toBe(KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT); - }); - - it('does not apply the second-month promo at the grandfather cutoff', () => { - expect( - computeMonthlyCadenceBonusPercent({ - tier, - streakMonths: 2, - isFirstTimeSubscriberEver: true, - subscriptionStartedAtIso: KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF.toISOString(), - }) - ).toBeCloseTo( - KILO_PASS_TIER_CONFIG.tier_49.monthlyBaseBonusPercent + - KILO_PASS_TIER_CONFIG.tier_49.monthlyStepBonusPercent - ); - }); - - it('does not apply promo when isFirstTimeSubscriberEver is false', () => { - expect( - computeMonthlyCadenceBonusPercent({ - tier, - streakMonths: 1, - isFirstTimeSubscriberEver: false, - subscriptionStartedAtIso: '2026-01-26T23:59:59.000Z', - }) - ).toBe(computeFallback({ streakMonths: 1, isFirstTimeSubscriberEver: false })); - }); - }); - describe('computeYearlyCadenceMonthlyBonusUsd', () => { it('returns half of monthly price as monthly bonus USD', () => { expect(computeYearlyCadenceMonthlyBonusUsd(KiloPassTier.Tier19)).toBe( diff --git a/apps/web/src/lib/kilo-pass/bonus.ts b/apps/web/src/lib/kilo-pass/bonus.ts index 2d66e5c889..02a9d677ab 100644 --- a/apps/web/src/lib/kilo-pass/bonus.ts +++ b/apps/web/src/lib/kilo-pass/bonus.ts @@ -1,12 +1,9 @@ import { KiloPassCadence, type KiloPassTier } from '@/lib/kilo-pass/enums'; import { KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT, - KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_BONUS_PERCENT, - KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF, KILO_PASS_TIER_CONFIG, KILO_PASS_YEARLY_MONTHLY_BONUS_PERCENT, } from '@/lib/kilo-pass/constants'; -import { dayjs } from '@/lib/kilo-pass/dayjs'; export const getMonthlyPriceUsd = (tier: KiloPassTier): number => { return KILO_PASS_TIER_CONFIG[tier].monthlyPriceUsd; @@ -28,9 +25,8 @@ export const computeMonthlyCadenceBonusPercent = (params: { tier: KiloPassTier; streakMonths: number; isFirstTimeSubscriberEver: boolean; - subscriptionStartedAtIso?: string | null; }): number => { - const { tier, streakMonths, isFirstTimeSubscriberEver, subscriptionStartedAtIso } = params; + const { tier, streakMonths, isFirstTimeSubscriberEver } = params; if (streakMonths < 1) { throw new Error('streakMonths must be >= 1'); @@ -40,22 +36,6 @@ export const computeMonthlyCadenceBonusPercent = (params: { return KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT; } - // Limited-time grandfathered promo: first-time subscribers who started strictly before the - // cutoff keep the 50% bonus for streak month 2. - if (streakMonths === 2 && isFirstTimeSubscriberEver) { - const startedAt = subscriptionStartedAtIso ?? null; - if (startedAt != null) { - const startedAtUtc = dayjs(startedAt).utc(); - - if ( - startedAtUtc.isValid() && - startedAtUtc.isBefore(KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF) - ) { - return KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_BONUS_PERCENT; - } - } - } - const config = KILO_PASS_TIER_CONFIG[tier]; const nMinus1 = streakMonths - 1; const uncapped = config.monthlyBaseBonusPercent + config.monthlyStepBonusPercent * nMinus1; diff --git a/apps/web/src/lib/kilo-pass/constants.ts b/apps/web/src/lib/kilo-pass/constants.ts index 720573548f..87405fc7fd 100644 --- a/apps/web/src/lib/kilo-pass/constants.ts +++ b/apps/web/src/lib/kilo-pass/constants.ts @@ -1,4 +1,3 @@ -import { dayjs } from '@/lib/kilo-pass/dayjs'; export { KILO_PASS_MONTHLY_RAMP_BASE_BONUS_PERCENT, KILO_PASS_MONTHLY_RAMP_CAP_BONUS_PERCENT, @@ -8,9 +7,3 @@ export { } from '@kilocode/worker-utils/kilo-pass-bonus-projection'; export const KILO_PASS_FIRST_MONTH_PROMO_BONUS_PERCENT = 0.5; - -// First-time subscribers receive a 50% bonus for month 2 only if they started -// strictly before this grandfather cutoff. Month 1 remains 50% for new subscribers. -export const KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF = dayjs('2026-05-07T00:00:00Z').utc(); - -export const KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_BONUS_PERCENT = 0.5; diff --git a/apps/web/src/lib/kilo-pass/usage-triggered-bonus.test.ts b/apps/web/src/lib/kilo-pass/usage-triggered-bonus.test.ts index f4c9d30d28..f38926e4b9 100644 --- a/apps/web/src/lib/kilo-pass/usage-triggered-bonus.test.ts +++ b/apps/web/src/lib/kilo-pass/usage-triggered-bonus.test.ts @@ -19,7 +19,6 @@ import { KiloPassWelcomePromoEligibilityReason, } from '@/lib/kilo-pass/enums'; import { computeMonthlyCadenceBonusPercent, getMonthlyPriceUsd } from '@/lib/kilo-pass/bonus'; -import { KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF } from '@/lib/kilo-pass/constants'; import { maybeIssueKiloPassBonusFromUsageThreshold } from '@/lib/kilo-pass/usage-triggered-bonus'; import { and, eq } from 'drizzle-orm'; @@ -129,10 +128,7 @@ describe('maybeIssueKiloPassBonusFromUsageThreshold', () => { stripeInvoiceId: 'inv_test_monthly', currentStreakMonths: 2, nextYearlyIssueAt: null, - // Ensure this test remains a "regular ramp" case, not eligible for the month-2 grandfathered promo. - startedAtIso: new Date( - KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF.valueOf() + 1 - ).toISOString(), + startedAtIso: '2026-01-01T00:00:00.000Z', }); await maybeIssueKiloPassBonusFromUsageThreshold({ @@ -162,107 +158,7 @@ describe('maybeIssueKiloPassBonusFromUsageThreshold', () => { expect(userRow?.kilo_pass_threshold).toBeNull(); }); - test('monthly: first-2-months promo eligible => 50% bonus (tier_49, streak=2)', async () => { - const user = await insertTestUser({ - microdollars_used: 55_000_000, - kilo_pass_threshold: 49_000_000, - }); - - const { issuanceId } = await seedBaseIssuance({ - kiloUserId: user.id, - cadence: KiloPassCadence.Monthly, - tier: KiloPassTier.Tier49, - issueMonth: '2026-02-01', - stripeInvoiceId: 'inv_test_monthly_month2_grandfathered_eligible', - currentStreakMonths: 2, - nextYearlyIssueAt: null, - startedAtIso: '2026-01-26T23:59:59.000Z', - }); - - await maybeIssueKiloPassBonusFromUsageThreshold({ - kiloUserId: user.id, - nowIso: new Date('2026-02-15T00:00:00.000Z').toISOString(), - db, - }); - - const bonusItem = await db.query.kilo_pass_issuance_items.findFirst({ - where: and( - eq(kilo_pass_issuance_items.kilo_pass_issuance_id, issuanceId), - eq(kilo_pass_issuance_items.kind, KiloPassIssuanceItemKind.Bonus) - ), - }); - expect(bonusItem).toBeTruthy(); - - const bonusTx = await db.query.credit_transactions.findFirst({ - where: eq(credit_transactions.id, bonusItem?.credit_transaction_id ?? ''), - }); - // tier_49 monthly price is $49, 50% => $24.50. - expect(bonusTx?.amount_microdollars).toBe(24_500_000); - - const auditRows = await db - .select({ payload: kilo_pass_audit_log.payload_json }) - .from(kilo_pass_audit_log) - .where( - and( - eq(kilo_pass_audit_log.action, KiloPassAuditLogAction.BonusCreditsIssued), - eq(kilo_pass_audit_log.related_monthly_issuance_id, issuanceId) - ) - ); - - const payload = auditRows[0]?.payload ?? null; - expect(isRecord(payload)).toBe(true); - if (!isRecord(payload)) - throw new Error('Expected bonus issuance audit payload to be an object'); - - const decision = payload.monthlyBonusDecision; - expect(isRecord(decision)).toBe(true); - if (!isRecord(decision)) { - throw new Error('Expected audit payload to include monthlyBonusDecision object'); - } - - expect(decision.streakMonths).toBe(2); - expect(decision.issueMonth).toBe('2026-02-01'); - }); - - test('monthly: first-2-months promo ineligible at cutoff => ramp applies (not 50%)', async () => { - const user = await insertTestUser({ - microdollars_used: 55_000_000, - kilo_pass_threshold: 49_000_000, - }); - - const { issuanceId } = await seedBaseIssuance({ - kiloUserId: user.id, - cadence: KiloPassCadence.Monthly, - tier: KiloPassTier.Tier49, - issueMonth: '2026-02-01', - stripeInvoiceId: 'inv_test_monthly_month2_grandfathered_ineligible_cutoff', - currentStreakMonths: 2, - nextYearlyIssueAt: null, - startedAtIso: KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF.toISOString(), - }); - - await maybeIssueKiloPassBonusFromUsageThreshold({ - kiloUserId: user.id, - nowIso: new Date('2026-02-15T00:00:00.000Z').toISOString(), - db, - }); - - const bonusItem = await db.query.kilo_pass_issuance_items.findFirst({ - where: and( - eq(kilo_pass_issuance_items.kilo_pass_issuance_id, issuanceId), - eq(kilo_pass_issuance_items.kind, KiloPassIssuanceItemKind.Bonus) - ), - }); - expect(bonusItem).toBeTruthy(); - - const bonusTx = await db.query.credit_transactions.findFirst({ - where: eq(credit_transactions.id, bonusItem?.credit_transaction_id ?? ''), - }); - // tier_49 at streak=2 => base 5% + step 5% * 1 = 10% of $49.00 = $4.90. - expect(bonusTx?.amount_microdollars).toBe(4_900_000); - }); - - test('monthly: first-2-months promo started AFTER cutoff => ramp applies (not 50%)', async () => { + test('monthly: standard ramp applies for streak month 2 (no longer a promo)', async () => { const user = await insertTestUser({ microdollars_used: 55_000_000, kilo_pass_threshold: 49_000_000, @@ -270,16 +166,14 @@ describe('maybeIssueKiloPassBonusFromUsageThreshold', () => { const tier = KiloPassTier.Tier49; const streakMonths = 2; - const startedAtIso = new Date( - KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF.valueOf() + 1 - ).toISOString(); + const startedAtIso = '2026-01-01T00:00:00.000Z'; const { issuanceId } = await seedBaseIssuance({ kiloUserId: user.id, cadence: KiloPassCadence.Monthly, tier, issueMonth: '2026-02-01', - stripeInvoiceId: 'inv_test_monthly_month2_grandfathered_ineligible_after_cutoff', + stripeInvoiceId: 'inv_test_monthly_month2_ramp', currentStreakMonths: streakMonths, nextYearlyIssueAt: null, startedAtIso, @@ -314,7 +208,6 @@ describe('maybeIssueKiloPassBonusFromUsageThreshold', () => { ); expect(bonusTx?.amount_microdollars).toBe(expectedBonusMicrodollars); - expect(bonusTx?.amount_microdollars).not.toBe(24_500_000); const auditRows = await db .select({ payload: kilo_pass_audit_log.payload_json }) diff --git a/apps/web/src/lib/kilo-pass/usage-triggered-bonus.ts b/apps/web/src/lib/kilo-pass/usage-triggered-bonus.ts index b7c8f84ccc..ee1e3074a2 100644 --- a/apps/web/src/lib/kilo-pass/usage-triggered-bonus.ts +++ b/apps/web/src/lib/kilo-pass/usage-triggered-bonus.ts @@ -64,7 +64,6 @@ export function computeUsageTriggeredMonthlyBonusDecision(params: { tier: params.tier, streakMonths, isFirstTimeSubscriberEver: isEligibleForFirstMonthPromo, - subscriptionStartedAtIso: params.startedAtIso, }); const shouldIssueFirstMonthPromo = bonusPercentApplied === 0.5 && streakMonths <= 2; diff --git a/apps/web/src/lib/kilo-pass/usage-triggered-bonus.unit.test.ts b/apps/web/src/lib/kilo-pass/usage-triggered-bonus.unit.test.ts index 2038e456e9..36f1a086ef 100644 --- a/apps/web/src/lib/kilo-pass/usage-triggered-bonus.unit.test.ts +++ b/apps/web/src/lib/kilo-pass/usage-triggered-bonus.unit.test.ts @@ -5,7 +5,6 @@ import { computeUsageTriggeredMonthlyBonusDecision, computeUsageTriggeredYearlyIssueMonth, } from '@/lib/kilo-pass/usage-triggered-bonus'; -import { KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF } from '@/lib/kilo-pass/constants'; describe('usage-triggered-bonus (unit)', () => { describe('computeUsageTriggeredMonthlyBonusDecision', () => { @@ -31,9 +30,7 @@ describe('usage-triggered-bonus (unit)', () => { test('eligible promo => shouldIssueFirstMonthPromo=true, bonusKind=promo-50pct, and promo description', () => { const d = computeUsageTriggeredMonthlyBonusDecision({ tier: KiloPassTier.Tier19, - startedAtIso: new Date( - KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF.valueOf() - 1 - ).toISOString(), + startedAtIso: '2026-01-01T00:00:00.000Z', currentStreakMonths: 1, isFirstTimeSubscriberEver: true, issueMonth: '2026-01-01', @@ -116,10 +113,10 @@ describe('usage-triggered-bonus (unit)', () => { } ); - test('ineligible at promo cutoff => uses ramp (not 50%) and bonusKind=monthly-ramp', () => { + test('streak month 2 first-time subscriber uses ramp (not 50%) and bonusKind=monthly-ramp', () => { const d = computeUsageTriggeredMonthlyBonusDecision({ tier: KiloPassTier.Tier49, - startedAtIso: KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF.toISOString(), + startedAtIso: '2026-01-01T00:00:00.000Z', currentStreakMonths: 2, isFirstTimeSubscriberEver: true, issueMonth: '2026-02-01', diff --git a/apps/web/src/routers/kilo-pass-router.test.ts b/apps/web/src/routers/kilo-pass-router.test.ts index 4cfdd6784b..ab9ae41a32 100644 --- a/apps/web/src/routers/kilo-pass-router.test.ts +++ b/apps/web/src/routers/kilo-pass-router.test.ts @@ -29,10 +29,6 @@ import { computeYearlyCadenceMonthlyBonusUsd, getMonthlyPriceUsd, } from '@/lib/kilo-pass/bonus'; -import { - KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_BONUS_PERCENT, - KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF, -} from '@/lib/kilo-pass/constants'; import { insertTestUser } from '@/tests/helpers/user.helper'; import type { insertMicrodollarUsageWithDailyRollup as insertMicrodollarUsageWithDailyRollupType } from '@/tests/helpers/microdollar-usage.helper'; @@ -854,7 +850,6 @@ describe('kiloPassRouter', () => { tier: KiloPassTier.Tier19, streakMonths: 1, isFirstTimeSubscriberEver: true, - subscriptionStartedAtIso: '2026-01-01T00:00:00.000Z', }); const currentBonusUsd = Math.round(baseAmountUsd * currentBonusPercent * 100) / 100; @@ -1102,13 +1097,13 @@ describe('kiloPassRouter', () => { ); }); - it('keeps App Store month-2 grandfather bonus after a post-cutoff renewal', async () => { + it('computes App Store month-2 bonus using standard ramp (no 50% grandfather promo)', async () => { freezeKiloPassClock('2026-06-15T00:00:00.000Z'); const user = await insertTestUser({ - google_user_email: 'kilo-pass-get-state-app-store-grandfathered-renewal@example.com', + google_user_email: 'kilo-pass-get-state-app-store-month2-ramp@example.com', }); - const providerSubscriptionId = 'orig_get_state_app_store_grandfathered_renewal'; + const providerSubscriptionId = 'orig_get_state_app_store_month2_ramp'; const renewalExpiresAt = '2026-07-01T00:00:00.000Z'; const { id: subscriptionId } = await insertSubscription({ kiloUserId: user.id, @@ -1129,7 +1124,7 @@ describe('kiloPassRouter', () => { payment_provider: KiloPassPaymentProvider.AppStore, product_id: 'kilo_pass_tier_19_monthly', provider_subscription_id: providerSubscriptionId, - provider_transaction_id: 'tx_get_state_app_store_grandfathered_initial', + provider_transaction_id: 'tx_get_state_app_store_month2_ramp_initial', provider_original_transaction_id: providerSubscriptionId, app_account_token: user.app_store_account_token, environment: 'Sandbox', @@ -1143,7 +1138,7 @@ describe('kiloPassRouter', () => { payment_provider: KiloPassPaymentProvider.AppStore, product_id: 'kilo_pass_tier_19_monthly', provider_subscription_id: providerSubscriptionId, - provider_transaction_id: 'tx_get_state_app_store_grandfathered_renewal', + provider_transaction_id: 'tx_get_state_app_store_month2_ramp_renewal', provider_original_transaction_id: providerSubscriptionId, app_account_token: user.app_store_account_token, environment: 'Sandbox', @@ -1157,10 +1152,12 @@ describe('kiloPassRouter', () => { const result = await caller.kiloPass.getState(); const baseAmountUsd = getMonthlyPriceUsd(KiloPassTier.Tier19); - const expectedCurrentBonusUsd = - Math.round( - Math.round(baseAmountUsd * 100) * KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_BONUS_PERCENT - ) / 100; + const expectedPercent = computeMonthlyCadenceBonusPercent({ + tier: KiloPassTier.Tier19, + streakMonths: 2, + isFirstTimeSubscriberEver: true, + }); + const expectedCurrentBonusUsd = Math.round(baseAmountUsd * expectedPercent * 100) / 100; expect(result.subscription).toEqual( expect.objectContaining({ @@ -1173,12 +1170,12 @@ describe('kiloPassRouter', () => { ); }); - it('predicts monthly nextBonusCreditsUsd as 50% for promo month 2 (streak=1 -> predicted=2)', async () => { + it('predicts monthly nextBonusCreditsUsd using ramp for month 2 (streak=1 -> predicted=2)', async () => { const stripeMock = getStripeMock(); const currentPeriodEndSeconds = 1_700_123_456; const currentPeriodStartSeconds = currentPeriodEndSeconds - 2_592_000; stripeMock.subscriptions.retrieve.mockResolvedValue({ - id: 'sub_test_monthly_grandfathered_month2_next', + id: 'sub_test_monthly_month2_next', status: 'active', items: { data: [ @@ -1191,12 +1188,12 @@ describe('kiloPassRouter', () => { }); const user = await insertTestUser({ - google_user_email: 'kilo-pass-get-state-monthly-grandfathered-month2-next@example.com', + google_user_email: 'kilo-pass-get-state-monthly-month2-next@example.com', }); await insertSubscription({ kiloUserId: user.id, - stripeSubscriptionId: 'sub_test_monthly_grandfathered_month2_next', + stripeSubscriptionId: 'sub_test_monthly_month2_next', tier: KiloPassTier.Tier19, cadence: KiloPassCadence.Monthly, status: 'active', @@ -1208,19 +1205,23 @@ describe('kiloPassRouter', () => { const result = await caller.kiloPass.getState(); const baseAmountUsd = getMonthlyPriceUsd(KiloPassTier.Tier19); - const baseCents = Math.round(baseAmountUsd * 100); + const expectedNextPercent = computeMonthlyCadenceBonusPercent({ + tier: KiloPassTier.Tier19, + streakMonths: 2, + isFirstTimeSubscriberEver: true, + }); const expectedNextBonusUsd = - Math.round(baseCents * KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_BONUS_PERCENT) / 100; + Math.round(Math.round(baseAmountUsd * 100) * expectedNextPercent) / 100; expect(result.subscription?.nextBonusCreditsUsd).toBe(expectedNextBonusUsd); }); - it('computes monthly currentPeriodBonusCreditsUsd as 50% for promo month 2 (streak=2)', async () => { + it('computes monthly currentPeriodBonusCreditsUsd using ramp for month 2 (streak=2)', async () => { const stripeMock = getStripeMock(); const currentPeriodEndSeconds = 1_700_123_456; const currentPeriodStartSeconds = currentPeriodEndSeconds - 2_592_000; stripeMock.subscriptions.retrieve.mockResolvedValue({ - id: 'sub_test_monthly_grandfathered_month2_current', + id: 'sub_test_monthly_month2_current', status: 'active', items: { data: [ @@ -1233,12 +1234,12 @@ describe('kiloPassRouter', () => { }); const user = await insertTestUser({ - google_user_email: 'kilo-pass-get-state-monthly-grandfathered-month2-current@example.com', + google_user_email: 'kilo-pass-get-state-monthly-month2-current@example.com', }); await insertSubscription({ kiloUserId: user.id, - stripeSubscriptionId: 'sub_test_monthly_grandfathered_month2_current', + stripeSubscriptionId: 'sub_test_monthly_month2_current', tier: KiloPassTier.Tier19, cadence: KiloPassCadence.Monthly, status: 'active', @@ -1249,49 +1250,6 @@ describe('kiloPassRouter', () => { const caller = await createCallerForUser(user.id); const result = await caller.kiloPass.getState(); - const baseAmountUsd = getMonthlyPriceUsd(KiloPassTier.Tier19); - const expectedCurrentBonusUsd = - Math.round( - Math.round(baseAmountUsd * 100) * KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_BONUS_PERCENT - ) / 100; - - expect(result.subscription?.currentPeriodBonusCreditsUsd).toBe(expectedCurrentBonusUsd); - }); - - it('does not apply 50% month-2 promo when started_at is at/after the cutoff (streak=2)', async () => { - const stripeMock = getStripeMock(); - const currentPeriodEndSeconds = 1_700_123_456; - const currentPeriodStartSeconds = currentPeriodEndSeconds - 2_592_000; - stripeMock.subscriptions.retrieve.mockResolvedValue({ - id: 'sub_test_monthly_grandfathered_month2_cutoff_ineligible', - status: 'active', - items: { - data: [ - { - current_period_end: currentPeriodEndSeconds, - current_period_start: currentPeriodStartSeconds, - }, - ], - }, - }); - - const user = await insertTestUser({ - google_user_email: 'kilo-pass-get-state-monthly-grandfathered-month2-cutoff@example.com', - }); - - await insertSubscription({ - kiloUserId: user.id, - stripeSubscriptionId: 'sub_test_monthly_grandfathered_month2_cutoff_ineligible', - tier: KiloPassTier.Tier19, - cadence: KiloPassCadence.Monthly, - status: 'active', - currentStreakMonths: 2, - startedAt: KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF.toISOString(), - }); - - const caller = await createCallerForUser(user.id); - const result = await caller.kiloPass.getState(); - const baseAmountUsd = getMonthlyPriceUsd(KiloPassTier.Tier19); const expectedPercent = computeMonthlyCadenceBonusPercent({ tier: KiloPassTier.Tier19, @@ -1301,11 +1259,6 @@ describe('kiloPassRouter', () => { const expectedCurrentBonusUsd = Math.round(baseAmountUsd * expectedPercent * 100) / 100; expect(result.subscription?.currentPeriodBonusCreditsUsd).toBe(expectedCurrentBonusUsd); - expect(result.subscription?.currentPeriodBonusCreditsUsd).not.toBe( - Math.round( - Math.round(baseAmountUsd * 100) * KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_BONUS_PERCENT - ) / 100 - ); }); it('keeps month 3+ bonus ramp unchanged even for grandfathered subscriptions (streak=3)', async () => { diff --git a/apps/web/src/routers/kilo-pass-router.ts b/apps/web/src/routers/kilo-pass-router.ts index 35882048b7..bda7633df3 100644 --- a/apps/web/src/routers/kilo-pass-router.ts +++ b/apps/web/src/routers/kilo-pass-router.ts @@ -38,10 +38,7 @@ import { KiloPassError } from '@/lib/kilo-pass/errors'; import { isStripeSubscriptionEnded } from '@/lib/kilo-pass/stripe-subscription-status'; import { releaseScheduledChangeForSubscription } from '@/lib/kilo-pass/scheduled-change-release'; import { appendKiloPassAuditLog } from '@/lib/kilo-pass/issuance'; -import { - KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF, - KILO_PASS_TIER_CONFIG, -} from '@/lib/kilo-pass/constants'; +import { KILO_PASS_TIER_CONFIG } from '@/lib/kilo-pass/constants'; import { fromMicrodollars } from '@/lib/utils'; import { timedUsageQuery } from '@/lib/usage-query'; import { @@ -231,10 +228,6 @@ function mapAppStoreCompletionError(error: unknown, userId: string): TRPCError { }); } -function isTwoMonthPromoOfferActive(): boolean { - return dayjs().utc().isBefore(KILO_PASS_MONTHLY_FIRST_2_MONTHS_PROMO_CUTOFF); -} - function roundToCents(usd: number): number { return Math.round(usd * 100) / 100; } @@ -275,7 +268,6 @@ function getNextKiloPassBonusCreditsUsd(params: { tier: params.subscription.tier, streakMonths: predictedStreakMonths, isFirstTimeSubscriberEver: params.isFirstTimeSubscriberEver, - subscriptionStartedAtIso: params.subscription.startedAt, }); const baseCents = Math.round(params.baseAmountUsd * 100); @@ -297,7 +289,6 @@ function getCurrentKiloPassBonusCreditsUsd(params: { tier: params.subscription.tier, streakMonths, isFirstTimeSubscriberEver: params.isFirstTimeSubscriberEver, - subscriptionStartedAtIso: params.subscription.startedAt, }); const cents = Math.round(params.baseAmountUsd * bonusPercentApplied * 100); return cents / 100; @@ -826,7 +817,7 @@ export const kiloPassRouter = createTRPCRouter({ getState: baseProcedure.output(GetStateOutputSchema).query(async ({ ctx }) => { const subscriptionBase = await getKiloPassStateForUser(db, ctx.user.id); if (!subscriptionBase) { - return { subscription: null, isEligibleForFirstMonthPromo: isTwoMonthPromoOfferActive() }; + return { subscription: null, isEligibleForFirstMonthPromo: false }; } if (subscriptionBase.paymentProvider !== KiloPassPaymentProvider.Stripe) {