diff --git a/env.example b/env.example index 901f1ea..6422e72 100644 --- a/env.example +++ b/env.example @@ -167,7 +167,6 @@ TURNSTILE_SECRET_KEY="" # https://mksaas.com/docs/jobs#setup # ----------------------------------------------------------------------------- INNGEST_SIGNING_KEY="" -INNGEST_EVENT_KEY="" # ----------------------------------------------------------------------------- # AI diff --git a/src/config/website.tsx b/src/config/website.tsx index 0a8c76c..4cdefa2 100644 --- a/src/config/website.tsx +++ b/src/config/website.tsx @@ -120,7 +120,7 @@ export const websiteConfig: WebsiteConfig = { credits: { enable: true, amount: 1000, - expireDays: 90, + expireDays: 30, }, }, lifetime: { @@ -139,7 +139,7 @@ export const websiteConfig: WebsiteConfig = { credits: { enable: true, amount: 2000, - expireDays: 120, + expireDays: 30, }, }, }, @@ -169,7 +169,7 @@ export const websiteConfig: WebsiteConfig = { id: 'standard', popular: true, credits: 200, - expireDays: 60, + expireDays: 30, price: { priceId: process.env.NEXT_PUBLIC_STRIPE_PRICE_CREDITS_STANDARD!, amount: 1490, @@ -181,7 +181,7 @@ export const websiteConfig: WebsiteConfig = { id: 'premium', popular: false, credits: 500, - expireDays: 90, + expireDays: 30, price: { priceId: process.env.NEXT_PUBLIC_STRIPE_PRICE_CREDITS_PREMIUM!, amount: 3990, @@ -193,7 +193,7 @@ export const websiteConfig: WebsiteConfig = { id: 'enterprise', popular: false, credits: 1000, - expireDays: 120, + expireDays: 30, price: { priceId: process.env.NEXT_PUBLIC_STRIPE_PRICE_CREDITS_ENTERPRISE!, amount: 6990, diff --git a/src/credits/credits.ts b/src/credits/credits.ts index 8f96a6b..bc02471 100644 --- a/src/credits/credits.ts +++ b/src/credits/credits.ts @@ -354,45 +354,6 @@ export async function processExpiredCredits(userId: string) { } } -/** - * Add subscription renewal credits - * @param userId - User ID - * @param priceId - Price ID - */ -export async function addSubscriptionRenewalCredits( - userId: string, - priceId: string -) { - const pricePlan = findPlanByPriceId(priceId); - if ( - !pricePlan || - pricePlan.isFree || - !pricePlan.credits || - !pricePlan.credits.enable - ) { - console.log( - `addSubscriptionRenewalCredits, no credits configured for plan ${priceId}` - ); - return; - } - - const credits = pricePlan.credits.amount; - const expireDays = pricePlan.credits.expireDays; - const now = new Date(); - - await addCredits({ - userId, - amount: credits, - type: CREDIT_TRANSACTION_TYPE.SUBSCRIPTION_RENEWAL, - description: `Subscription renewal credits: ${credits} for ${now.getFullYear()}-${now.getMonth() + 1}`, - expireDays, - }); - - console.log( - `addSubscriptionRenewalCredits, ${credits} credits for user ${userId}, priceId: ${priceId}` - ); -} - /** * Add register gift credits * @param userId - User ID @@ -492,6 +453,45 @@ export async function addMonthlyFreeCreditsIfNeed(userId: string) { } } +/** + * Add subscription renewal credits + * @param userId - User ID + * @param priceId - Price ID + */ +export async function addSubscriptionRenewalCredits( + userId: string, + priceId: string +) { + const pricePlan = findPlanByPriceId(priceId); + if ( + !pricePlan || + pricePlan.isFree || + !pricePlan.credits || + !pricePlan.credits.enable + ) { + console.log( + `addSubscriptionRenewalCredits, no credits configured for plan ${priceId}` + ); + return; + } + + const credits = pricePlan.credits.amount; + const expireDays = pricePlan.credits.expireDays; + const now = new Date(); + + await addCredits({ + userId, + amount: credits, + type: CREDIT_TRANSACTION_TYPE.SUBSCRIPTION_RENEWAL, + description: `Subscription renewal credits: ${credits} for ${now.getFullYear()}-${now.getMonth() + 1}`, + expireDays, + }); + + console.log( + `addSubscriptionRenewalCredits, ${credits} credits for user ${userId}, priceId: ${priceId}` + ); +} + /** * Add lifetime monthly credits * @param userId - User ID diff --git a/src/payment/provider/stripe.ts b/src/payment/provider/stripe.ts index cd39852..ce01252 100644 --- a/src/payment/provider/stripe.ts +++ b/src/payment/provider/stripe.ts @@ -1,10 +1,19 @@ import { randomUUID } from 'crypto'; -import { addCredits, addSubscriptionRenewalCredits } from '@/credits/credits'; +import { websiteConfig } from '@/config/website'; +import { + addCredits, + addLifetimeMonthlyCreditsIfNeed, + addSubscriptionRenewalCredits, +} from '@/credits/credits'; import { getCreditPackageById } from '@/credits/server'; import { CREDIT_TRANSACTION_TYPE } from '@/credits/types'; import { getDb } from '@/db'; import { payment, user } from '@/db/schema'; -import { findPlanByPlanId, findPriceInPlan } from '@/lib/price-plan'; +import { + findPlanByPlanId, + findPlanByPriceId, + findPriceInPlan, +} from '@/lib/price-plan'; import { sendNotification } from '@/notification/notification'; import { desc, eq } from 'drizzle-orm'; import { Stripe } from 'stripe'; @@ -580,6 +589,15 @@ export class StripeProvider implements PaymentProvider { `<< No payment record created for Stripe subscription ${stripeSubscription.id}` ); } + + // Conditionally handle credits after subscription creation + if (websiteConfig.credits?.enableCredits) { + // Add subscription renewal credits if plan config enables credits + const pricePlan = findPlanByPriceId(priceId); + if (pricePlan?.credits?.enable) { + await addSubscriptionRenewalCredits(userId, priceId); + } + } } /** @@ -771,6 +789,17 @@ export class StripeProvider implements PaymentProvider { `<< Created one-time payment record for user ${userId}, price: ${priceId}` ); + // Conditionally handle credits after one-time payment + if (websiteConfig.credits?.enableCredits) { + // If the plan is lifetime and credits are enabled, add lifetime monthly credits if needed + const lifetimePlan = Object.values( + websiteConfig.price?.plans || {} + ).find((plan) => plan.isLifetime && plan.credits?.enable); + if (lifetimePlan?.prices?.some((p) => p.priceId === priceId)) { + await addLifetimeMonthlyCreditsIfNeed(userId); + } + } + // Send notification const amount = session.amount_total ? session.amount_total / 100 : 0; await sendNotification(session.id, customerId, userId, amount);