-
Notifications
You must be signed in to change notification settings - Fork 332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch premium plan #284
Switch premium plan #284
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Warning Rate limit exceeded@elie222 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 53 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (2)
WalkthroughThis pull request introduces comprehensive changes to the premium subscription management system across multiple files. The modifications enhance the user experience for switching premium plans, improve logging and error handling, and add new functionality for tracking subscription changes. The changes span the web application's frontend, backend API, and utility functions, focusing on seamless premium tier transitions and better user feedback. Changes
Sequence DiagramsequenceDiagram
participant User
participant Pricing
participant PremiumAction
participant LemonSqueezyAPI
participant Webhook
User->>Pricing: Click Switch Plan
Pricing->>PremiumAction: switchPremiumPlanAction
PremiumAction->>LemonSqueezyAPI: switchPremiumPlan
LemonSqueezyAPI-->>Webhook: Trigger subscription_plan_changed
Webhook->>Webhook: Process subscription change
Pricing->>User: Show Toast Notification
Possibly related PRs
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (4)
apps/web/app/api/lemon-squeezy/webhook/route.ts (2)
153-188
: Refactored subscription creation workflow
Delegating the core creation logic tohandleSubscriptionCreated
streamlines the code. Consider checking and loggingPromise.allSettled
partial rejections for better observability.
190-226
: NewsubscriptionPlanChanged
function
This is similar in structure tosubscriptionCreated
. If there is significant overlap, consider extracting common logic to reduce duplication.apps/web/app/(app)/premium/config.ts (1)
192-218
: Consider a lookup table or consistent type checks, plus improved error handling.The switch-case is straightforward. You could also leverage a lookup table (or dictionary) keyed by environment variable to reduce duplication. Additionally, ensure that the thrown error is caught or handled in upstream calls to avoid unhandled promise rejections.
apps/web/app/(app)/premium/Pricing.tsx (1)
260-284
: Handle plan switching with user feedback.Using
toast.promise
is an elegant approach to surface loading, success, and error states. Consider adding a fallback or additional error context ifswitchPremiumPlanAction
rejects unexpectedly (such as network/time-out issues).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/web/app/(app)/premium/Pricing.tsx
(5 hunks)apps/web/app/(app)/premium/config.ts
(2 hunks)apps/web/app/api/lemon-squeezy/api.ts
(3 hunks)apps/web/app/api/lemon-squeezy/webhook/route.ts
(6 hunks)apps/web/app/api/lemon-squeezy/webhook/types.ts
(1 hunks)apps/web/utils/actions/premium.ts
(2 hunks)apps/web/utils/premium/server.ts
(1 hunks)packages/loops/src/loops.ts
(1 hunks)
🔇 Additional comments (22)
apps/web/app/api/lemon-squeezy/webhook/route.ts (7)
15-19
: New import of switchedPremiumPlan
These imports from @inboxzero/loops
provide additional event functionality.
21-23
: Important imports for tier resolution and logging
You now have getSubscriptionTier
and a scoped logger, ensuring clearer subscription tier handling and better traceability.
84-94
: Added handling for subscription_plan_changed
This block correctly checks for userId
, logs the error if missing, and delegates processing to subscriptionPlanChanged
.
102-102
: Forwarding variantId
in subscriptionCancelled
Passing variantId
helps in accurately identifying the plan variant for cancellation.
112-112
: Consistent variantId
usage on payment failure
Including variantId
for payment failures ensures uniform cancellation logic.
Line range hint 228-255
: Centralized handleSubscriptionCreated
Good approach: centralizing the upgrade logic and automatically deriving the tier using getSubscriptionTier
.
360-374
: Enhanced subscriptionCancelled
Introducing variantId
and skipping cancellation when the database record is absent properly handles changed-plan scenarios.
apps/web/app/api/lemon-squeezy/api.ts (6)
9-9
: Imported updateSubscription
Enables the ability to update subscription plans.
11-11
: Added createScopedLogger
import
Maintains a custom logger context for “Lemon Squeezy” operations.
13-13
: Initializing scoped logger
Tracks API calls under a coherent “Lemon Squeezy” label.
29-29
: Logging in updateSubscriptionItemQuantity
Improves traceability for subscription item changes.
46-46
: Logging license key activation
Provides a clearer audit trail for license activation events.
50-57
: New switchPremiumPlan
function
Updates a subscription with a given variant, enabling plan switching.
packages/loops/src/loops.ts (1)
58-71
: Added switchedPremiumPlan
Allows tracking plan switches within Loops to maintain robust analytics and event history.
apps/web/app/api/lemon-squeezy/webhook/types.ts (1)
18-19
: Included subscription_plan_changed
in EventName
Ensures strongly typed event handling for plan-change scenarios.
apps/web/utils/premium/server.ts (1)
74-97
: Extended cancelPremium
to accept variantId
Checks for a matching premium record before proceeding, which gracefully handles the case when the user switches plans.
apps/web/app/(app)/premium/config.ts (1)
45-54
: Ensure environment variables are defined and correctly typed.
These new mappings retrieve numeric IDs from environment variables. If any of these are missing or not valid integers, the app might behave unpredictably. Consider validating these variables, or at least logging a warning if they’re missing.
✅ Verification successful
Let me check the environment variable definitions in the codebase.
Environment variables are properly validated with default values
The environment variables used in tierToVariantId
are properly validated using @t3-oss/env-nextjs
with Zod schema validation. Looking at apps/web/env.ts
, all variant IDs are:
- Coerced to numbers using
z.coerce.number()
- Have a default value of 0 using
.default(0)
This means:
- Type safety is enforced through Zod validation
- Missing values will default to 0 instead of undefined
- The app won't crash due to missing environment variables
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Validate presence of environment variables used in tierToVariantId
# Checking that they are set and numeric.
vars=(
"NEXT_PUBLIC_BASIC_MONTHLY_VARIANT_ID"
"NEXT_PUBLIC_BASIC_ANNUALLY_VARIANT_ID"
"NEXT_PUBLIC_PRO_MONTHLY_VARIANT_ID"
"NEXT_PUBLIC_PRO_ANNUALLY_VARIANT_ID"
"NEXT_PUBLIC_BUSINESS_MONTHLY_VARIANT_ID"
"NEXT_PUBLIC_BUSINESS_ANNUALLY_VARIANT_ID"
"NEXT_PUBLIC_COPILOT_MONTHLY_VARIANT_ID"
"NEXT_PUBLIC_LIFETIME_VARIANT_ID"
)
for var in "${vars[@]}"; do
val=$(printenv "$var")
if [[ -z "$val" ]]; then
echo "Missing: $var"
elif ! [[ "$val" =~ ^[0-9]+$ ]]; then
echo "Not numeric: $var=$val"
fi
done
Length of output: 2704
Script:
#!/bin/bash
# Check for environment variable definitions in common configuration files
fd -t f "env|.env" -x cat {} \; 2>/dev/null
# Also check if there's any validation logic for these variables
rg -l "NEXT_PUBLIC.*VARIANT_ID"
Length of output: 10879
apps/web/utils/actions/premium.ts (1)
14-21
: Imports appear correct.
These import statements cleanly bring in dependencies, and the references align with the new code changes. No further actions needed.
apps/web/app/(app)/premium/Pricing.tsx (4)
4-4
: Toast import successfully integrated for better UX feedback.
Import of toast
from "sonner" is a nice addition to give visible feedback to users during plan changes.
27-28
: Imports are consistent with newly introduced actions.
Bringing switchPremiumPlanAction
and isActionError
aligns with the new premium plan switching logic. Looks good.
177-177
: Clarity in comparing current vs. selected plan.
const isCurrentPlan = tier.tiers[frequency.value] === premiumTier;
succinctly checks if the user’s plan matches. This is straightforward and readable.
293-297
: Excellent user-friendly plan status messaging.
Differentiating text based on whether it’s the current plan or a switch action helps users understand their subscription state more clearly.
apps/web/utils/actions/premium.ts
Outdated
export const switchPremiumPlanAction = withActionInstrumentation( | ||
"switchPremiumPlan", | ||
async (premiumTier: PremiumTier) => { | ||
const session = await auth(); | ||
if (!session?.user.id) return { error: "Not logged in" }; | ||
|
||
const user = await prisma.user.findUnique({ | ||
where: { id: session.user.id }, | ||
select: { | ||
premium: { | ||
select: { lemonSqueezySubscriptionId: true }, | ||
}, | ||
}, | ||
}); | ||
|
||
if (!user) return { error: "User not found" }; | ||
if (!user.premium?.lemonSqueezySubscriptionId) | ||
return { error: "You do not have a premium subscription" }; | ||
|
||
const variantId = tierToVariantId[premiumTier]; | ||
|
||
await switchPremiumPlan(user.premium.lemonSqueezySubscriptionId, variantId); | ||
}, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Validate subscription ID and handle switch errors gracefully.
This new switchPremiumPlanAction
function checks if the user is premium, retrieves the subscription, and calls switchPremiumPlan
. Consider:
- Handling any thrown error from
switchPremiumPlan
. - Verifying that
variantId
is valid to avoid runtime issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/web/app/(app)/premium/config.ts (1)
202-216
: Enhance utility functions with better error handling and type safetyThe utility functions could benefit from improved error handling and type safety:
- Error messages could be more descriptive by including available options.
- Input validation could prevent runtime errors.
- TypeScript types could be more strict.
Apply this diff to enhance the functions:
+type GetSubscriptionTierParams = { + variantId: number | undefined | null; +}; + export function getSubscriptionTier({ variantId, -}: { - variantId: number; -}): PremiumTier { +}: GetSubscriptionTierParams): PremiumTier { + if (!variantId) { + throw new Error('Variant ID is required'); + } const tier = variantIdToTier[variantId]; - if (!tier) throw new Error(`Unknown variant id: ${variantId}`); + if (!tier) { + const availableIds = Object.keys(variantIdToTier).join(', '); + throw new Error( + `Unknown variant id: ${variantId}. Available ids: ${availableIds}` + ); + } return tier; } -export function getVariantId({ tier }: { tier: PremiumTier }): number { +type GetVariantIdParams = { + tier: PremiumTier | undefined | null; +}; + +export function getVariantId({ tier }: GetVariantIdParams): number { + if (!tier) { + throw new Error('Premium tier is required'); + } const variantId = tierToVariantId[tier]; - if (!variantId) throw new Error(`Unknown tier: ${tier}`); + if (!variantId) { + const availableTiers = Object.values(PremiumTier).join(', '); + throw new Error( + `Unknown tier: ${tier}. Available tiers: ${availableTiers}` + ); + } return variantId; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/app/(app)/premium/config.ts
(2 hunks)apps/web/utils/actions/premium.ts
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/utils/actions/premium.ts
🔇 Additional comments (1)
apps/web/app/(app)/premium/config.ts (1)
Line range hint 45-216
: Verify the usage of tier mapping functions
Since these functions are used across multiple files for premium plan switching, let's verify their usage and potential impact.
✅ Verification successful
The tier mapping implementation is consistent and properly handles all tiers
The verification shows that:
- The
PremiumTier
enum in the schema includes all tiers includingLIFETIME
- The
tierToVariantId
mapping correctly includes theLIFETIME
tier with its variant ID - The tier mapping functions
getSubscriptionTier
andgetVariantId
are used consistently in:- Premium plan switching (
premium.ts
) - Webhook handling (
webhook/route.ts
)
- Premium plan switching (
- The tier ranking system in
premium/index.ts
properly includes all tiers withLIFETIME
having the highest rank (8)
The implementation is complete and consistent across the codebase, with proper error handling for unknown tiers and variant IDs.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the usage of tier mapping functions and potential issues
# Check for usage of getSubscriptionTier and getVariantId
echo "Checking function usage..."
rg -A 2 "getSubscriptionTier|getVariantId" --type typescript
# Check for LIFETIME tier handling
echo "Checking LIFETIME tier handling..."
rg -A 2 "PremiumTier\.LIFETIME|\"LIFETIME\"" --type typescript
# Check for potential error handling of these functions
echo "Checking error handling..."
rg "try.*catch.*getSubscriptionTier|try.*catch.*getVariantId" --type typescript
Length of output: 517
Script:
#!/bin/bash
# Description: Verify the usage of tier mapping functions and potential issues
# Check for usage of getSubscriptionTier and getVariantId
echo "Checking function usage..."
rg "getSubscriptionTier|getVariantId"
# Check for LIFETIME tier handling
echo "Checking LIFETIME tier handling..."
rg "PremiumTier\.LIFETIME|\"LIFETIME\""
# Check for potential error handling of these functions
echo "Checking error handling..."
rg "try.*catch.*getSubscriptionTier|try.*catch.*getVariantId"
# Check for PremiumTier enum definition
echo "Checking PremiumTier enum..."
rg "enum PremiumTier"
Length of output: 2183
Script:
#!/bin/bash
# Let's check the PremiumTier enum definition and its values
echo "Checking PremiumTier enum definition..."
rg -A 10 "enum PremiumTier" apps/web/prisma/schema.prisma
# Let's also check the actual implementation of the premium tier logic
echo "Checking premium tier implementation..."
cat apps/web/utils/premium/index.ts
# Check if there are any other tier-to-variant mappings
echo "Checking for other tier mappings..."
rg "variantIdToTier|tierToVariantId"
Length of output: 3874
apps/web/app/(app)/premium/config.ts
Outdated
const variantIdToTier: Record<number, PremiumTier> = { | ||
[env.NEXT_PUBLIC_BASIC_MONTHLY_VARIANT_ID]: PremiumTier.BASIC_MONTHLY, | ||
[env.NEXT_PUBLIC_BASIC_ANNUALLY_VARIANT_ID]: PremiumTier.BASIC_ANNUALLY, | ||
[env.NEXT_PUBLIC_PRO_MONTHLY_VARIANT_ID]: PremiumTier.PRO_MONTHLY, | ||
[env.NEXT_PUBLIC_PRO_ANNUALLY_VARIANT_ID]: PremiumTier.PRO_ANNUALLY, | ||
[env.NEXT_PUBLIC_BUSINESS_MONTHLY_VARIANT_ID]: PremiumTier.BUSINESS_MONTHLY, | ||
[env.NEXT_PUBLIC_BUSINESS_ANNUALLY_VARIANT_ID]: PremiumTier.BUSINESS_ANNUALLY, | ||
[env.NEXT_PUBLIC_COPILOT_MONTHLY_VARIANT_ID]: PremiumTier.COPILOT_MONTHLY, | ||
}; | ||
|
||
const tierToVariantId: Record<PremiumTier, number> = { | ||
[PremiumTier.BASIC_MONTHLY]: env.NEXT_PUBLIC_BASIC_MONTHLY_VARIANT_ID, | ||
[PremiumTier.BASIC_ANNUALLY]: env.NEXT_PUBLIC_BASIC_ANNUALLY_VARIANT_ID, | ||
[PremiumTier.PRO_MONTHLY]: env.NEXT_PUBLIC_PRO_MONTHLY_VARIANT_ID, | ||
[PremiumTier.PRO_ANNUALLY]: env.NEXT_PUBLIC_PRO_ANNUALLY_VARIANT_ID, | ||
[PremiumTier.BUSINESS_MONTHLY]: env.NEXT_PUBLIC_BUSINESS_MONTHLY_VARIANT_ID, | ||
[PremiumTier.BUSINESS_ANNUALLY]: env.NEXT_PUBLIC_BUSINESS_ANNUALLY_VARIANT_ID, | ||
[PremiumTier.COPILOT_MONTHLY]: env.NEXT_PUBLIC_COPILOT_MONTHLY_VARIANT_ID, | ||
[PremiumTier.LIFETIME]: env.NEXT_PUBLIC_LIFETIME_VARIANT_ID, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Address inconsistencies in tier mappings
The mappings between variant IDs and tiers have several issues that need attention:
- The
LIFETIME
tier is missing invariantIdToTier
but present intierToVariantId
, creating an asymmetric mapping. - Environment variables lack type safety, which could lead to runtime errors if they're undefined.
Apply this diff to fix the issues:
const variantIdToTier: Record<number, PremiumTier> = {
[env.NEXT_PUBLIC_BASIC_MONTHLY_VARIANT_ID]: PremiumTier.BASIC_MONTHLY,
[env.NEXT_PUBLIC_BASIC_ANNUALLY_VARIANT_ID]: PremiumTier.BASIC_ANNUALLY,
[env.NEXT_PUBLIC_PRO_MONTHLY_VARIANT_ID]: PremiumTier.PRO_MONTHLY,
[env.NEXT_PUBLIC_PRO_ANNUALLY_VARIANT_ID]: PremiumTier.PRO_ANNUALLY,
[env.NEXT_PUBLIC_BUSINESS_MONTHLY_VARIANT_ID]: PremiumTier.BUSINESS_MONTHLY,
[env.NEXT_PUBLIC_BUSINESS_ANNUALLY_VARIANT_ID]: PremiumTier.BUSINESS_ANNUALLY,
[env.NEXT_PUBLIC_COPILOT_MONTHLY_VARIANT_ID]: PremiumTier.COPILOT_MONTHLY,
+ [env.NEXT_PUBLIC_LIFETIME_VARIANT_ID]: PremiumTier.LIFETIME,
};
+// Ensure all variant IDs are defined
+const requiredVariantIds = [
+ env.NEXT_PUBLIC_BASIC_MONTHLY_VARIANT_ID,
+ env.NEXT_PUBLIC_BASIC_ANNUALLY_VARIANT_ID,
+ env.NEXT_PUBLIC_PRO_MONTHLY_VARIANT_ID,
+ env.NEXT_PUBLIC_PRO_ANNUALLY_VARIANT_ID,
+ env.NEXT_PUBLIC_BUSINESS_MONTHLY_VARIANT_ID,
+ env.NEXT_PUBLIC_BUSINESS_ANNUALLY_VARIANT_ID,
+ env.NEXT_PUBLIC_COPILOT_MONTHLY_VARIANT_ID,
+ env.NEXT_PUBLIC_LIFETIME_VARIANT_ID,
+] as const;
+
+if (requiredVariantIds.some(id => id === undefined)) {
+ throw new Error('Missing required variant IDs in environment variables');
+}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const variantIdToTier: Record<number, PremiumTier> = { | |
[env.NEXT_PUBLIC_BASIC_MONTHLY_VARIANT_ID]: PremiumTier.BASIC_MONTHLY, | |
[env.NEXT_PUBLIC_BASIC_ANNUALLY_VARIANT_ID]: PremiumTier.BASIC_ANNUALLY, | |
[env.NEXT_PUBLIC_PRO_MONTHLY_VARIANT_ID]: PremiumTier.PRO_MONTHLY, | |
[env.NEXT_PUBLIC_PRO_ANNUALLY_VARIANT_ID]: PremiumTier.PRO_ANNUALLY, | |
[env.NEXT_PUBLIC_BUSINESS_MONTHLY_VARIANT_ID]: PremiumTier.BUSINESS_MONTHLY, | |
[env.NEXT_PUBLIC_BUSINESS_ANNUALLY_VARIANT_ID]: PremiumTier.BUSINESS_ANNUALLY, | |
[env.NEXT_PUBLIC_COPILOT_MONTHLY_VARIANT_ID]: PremiumTier.COPILOT_MONTHLY, | |
}; | |
const tierToVariantId: Record<PremiumTier, number> = { | |
[PremiumTier.BASIC_MONTHLY]: env.NEXT_PUBLIC_BASIC_MONTHLY_VARIANT_ID, | |
[PremiumTier.BASIC_ANNUALLY]: env.NEXT_PUBLIC_BASIC_ANNUALLY_VARIANT_ID, | |
[PremiumTier.PRO_MONTHLY]: env.NEXT_PUBLIC_PRO_MONTHLY_VARIANT_ID, | |
[PremiumTier.PRO_ANNUALLY]: env.NEXT_PUBLIC_PRO_ANNUALLY_VARIANT_ID, | |
[PremiumTier.BUSINESS_MONTHLY]: env.NEXT_PUBLIC_BUSINESS_MONTHLY_VARIANT_ID, | |
[PremiumTier.BUSINESS_ANNUALLY]: env.NEXT_PUBLIC_BUSINESS_ANNUALLY_VARIANT_ID, | |
[PremiumTier.COPILOT_MONTHLY]: env.NEXT_PUBLIC_COPILOT_MONTHLY_VARIANT_ID, | |
[PremiumTier.LIFETIME]: env.NEXT_PUBLIC_LIFETIME_VARIANT_ID, | |
}; | |
const variantIdToTier: Record<number, PremiumTier> = { | |
[env.NEXT_PUBLIC_BASIC_MONTHLY_VARIANT_ID]: PremiumTier.BASIC_MONTHLY, | |
[env.NEXT_PUBLIC_BASIC_ANNUALLY_VARIANT_ID]: PremiumTier.BASIC_ANNUALLY, | |
[env.NEXT_PUBLIC_PRO_MONTHLY_VARIANT_ID]: PremiumTier.PRO_MONTHLY, | |
[env.NEXT_PUBLIC_PRO_ANNUALLY_VARIANT_ID]: PremiumTier.PRO_ANNUALLY, | |
[env.NEXT_PUBLIC_BUSINESS_MONTHLY_VARIANT_ID]: PremiumTier.BUSINESS_MONTHLY, | |
[env.NEXT_PUBLIC_BUSINESS_ANNUALLY_VARIANT_ID]: PremiumTier.BUSINESS_ANNUALLY, | |
[env.NEXT_PUBLIC_COPILOT_MONTHLY_VARIANT_ID]: PremiumTier.COPILOT_MONTHLY, | |
[env.NEXT_PUBLIC_LIFETIME_VARIANT_ID]: PremiumTier.LIFETIME, | |
}; | |
// Ensure all variant IDs are defined | |
const requiredVariantIds = [ | |
env.NEXT_PUBLIC_BASIC_MONTHLY_VARIANT_ID, | |
env.NEXT_PUBLIC_BASIC_ANNUALLY_VARIANT_ID, | |
env.NEXT_PUBLIC_PRO_MONTHLY_VARIANT_ID, | |
env.NEXT_PUBLIC_PRO_ANNUALLY_VARIANT_ID, | |
env.NEXT_PUBLIC_BUSINESS_MONTHLY_VARIANT_ID, | |
env.NEXT_PUBLIC_BUSINESS_ANNUALLY_VARIANT_ID, | |
env.NEXT_PUBLIC_COPILOT_MONTHLY_VARIANT_ID, | |
env.NEXT_PUBLIC_LIFETIME_VARIANT_ID, | |
] as const; | |
if (requiredVariantIds.some(id => id === undefined)) { | |
throw new Error('Missing required variant IDs in environment variables'); | |
} | |
const tierToVariantId: Record<PremiumTier, number> = { | |
[PremiumTier.BASIC_MONTHLY]: env.NEXT_PUBLIC_BASIC_MONTHLY_VARIANT_ID, | |
[PremiumTier.BASIC_ANNUALLY]: env.NEXT_PUBLIC_BASIC_ANNUALLY_VARIANT_ID, | |
[PremiumTier.PRO_MONTHLY]: env.NEXT_PUBLIC_PRO_MONTHLY_VARIANT_ID, | |
[PremiumTier.PRO_ANNUALLY]: env.NEXT_PUBLIC_PRO_ANNUALLY_VARIANT_ID, | |
[PremiumTier.BUSINESS_MONTHLY]: env.NEXT_PUBLIC_BUSINESS_MONTHLY_VARIANT_ID, | |
[PremiumTier.BUSINESS_ANNUALLY]: env.NEXT_PUBLIC_BUSINESS_ANNUALLY_VARIANT_ID, | |
[PremiumTier.COPILOT_MONTHLY]: env.NEXT_PUBLIC_COPILOT_MONTHLY_VARIANT_ID, | |
[PremiumTier.LIFETIME]: env.NEXT_PUBLIC_LIFETIME_VARIANT_ID, | |
}; |
Also handling cancellation of a different plan better
Summary by CodeRabbit
New Features
Bug Fixes
Improvements