From b7c3ef19bab3b18716e01fd917b21fecb4e0cd04 Mon Sep 17 00:00:00 2001 From: Emily Date: Tue, 11 Jun 2024 23:01:54 +0200 Subject: [PATCH] fix payments --- dashboard/nuxt.config.ts | 2 + .../api/pay/[project_id]/create.post.ts | 2 +- dashboard/server/api/pay/webhook.post.ts | 198 ++++++++---------- dashboard/server/api/project/delete.delete.ts | 4 +- .../server/api/user/delete_account.delete.ts | 1 - dashboard/server/init.ts | 3 +- dashboard/server/services/StripeService.ts | 6 +- shared/data/PREMIUM.ts | 9 +- 8 files changed, 101 insertions(+), 124 deletions(-) diff --git a/dashboard/nuxt.config.ts b/dashboard/nuxt.config.ts index 5bec66e..3265af5 100644 --- a/dashboard/nuxt.config.ts +++ b/dashboard/nuxt.config.ts @@ -41,6 +41,8 @@ export default defineNuxtConfig({ GOOGLE_AUTH_CLIENT_SECRET: process.env.GOOGLE_AUTH_CLIENT_SECRET, STRIPE_SECRET: process.env.STRIPE_SECRET, STRIPE_WH_SECRET: process.env.STRIPE_WH_SECRET, + STRIPE_SECRET_TEST: process.env.STRIPE_SECRET_TEST, + STRIPE_WH_SECRET_TEST: process.env.STRIPE_WH_SECRET_TEST, public: { PAYPAL_CLIENT_ID: '' } diff --git a/dashboard/server/api/pay/[project_id]/create.post.ts b/dashboard/server/api/pay/[project_id]/create.post.ts index 8f31072..ff04f52 100644 --- a/dashboard/server/api/pay/[project_id]/create.post.ts +++ b/dashboard/server/api/pay/[project_id]/create.post.ts @@ -24,7 +24,7 @@ export default defineEventHandler(async event => { } const checkout = await StripeService.cretePayment( - PLAN.PRICE, + StripeService.testMode ? PLAN.PRICE_TEST : PLAN.PRICE, 'https://dashboard.litlyx.com/payment_ok', project_id, project.customer_id diff --git a/dashboard/server/api/pay/webhook.post.ts b/dashboard/server/api/pay/webhook.post.ts index b625ddd..3bbd049 100644 --- a/dashboard/server/api/pay/webhook.post.ts +++ b/dashboard/server/api/pay/webhook.post.ts @@ -2,150 +2,129 @@ import StripeService from '~/server/services/StripeService'; import type Event from 'stripe'; import { ProjectModel } from '@schema/ProjectSchema'; -import { PREMIUM_PLAN, getPlanFromPrice } from '@data/PREMIUM'; +import { PREMIUM_DATA, PREMIUM_PLAN, getPlanFromPrice } from '@data/PREMIUM'; import { ProjectCountModel } from '@schema/ProjectsCounts'; import { ProjectLimitModel } from '@schema/ProjectsLimits'; import { UserModel } from '@schema/UserSchema'; async function onPaymentSuccess(event: Event.InvoicePaidEvent) { - // if (event.data.object.status === 'paid') { + if (event.data.object.status === 'paid') { - // const data = event.data.object; + const customer_id = event.data.object.customer as string; + const subscription_id = event.data.object.subscription as string; - // const pid = data.subscription_details?.metadata?.pid; - // if (!pid) return { error: 'ProjectId not found' } + const project = await ProjectModel.findOne({ customer_id }); + if (!project) return { error: 'CUSTOMER NOT EXIST' } - // const project = await ProjectModel.findById(pid); - // if (!project) return { error: 'Project not found' } + const subscriptionInfo = await StripeService.getSubscription(subscription_id); - // const price = data.lines.data[0].plan?.id; - // if (!price) return { error: 'Price not found' } + if (subscriptionInfo.status === 'active') { - // const PLAN = getPlanFromPrice(price); - // if (!PLAN) return { error: 'Plan not found' } + const price = subscriptionInfo.items.data[0].price.id; + if (!price) return { error: 'Price not found' } - // await ProjectModel.updateOne({ _id: pid }, { - // premium: true, - // customer_id: data.customer, - // premium_type: PLAN.ID, - // premium_expire_at: data.lines.data[0].period.end * 1000 - // }); + const PLAN = getPlanFromPrice(price, StripeService.testMode || false); + if (!PLAN) return { error: 'Plan not found' } - // await ProjectCountModel.create({ - // project_id: project._id, - // events: 0, - // visits: 0, - // ai_messages: 0, - // limit: PLAN.COUNT_LIMIT, - // ai_limit: PLAN.AI_MESSAGE_LIMIT, - // billing_start_at: event.data.object.lines.data[0].period.start * 1000, - // billing_expire_at: event.data.object.lines.data[0].period.end * 1000, - // }); + await addSubscriptionToProject(project._id.toString(), PLAN, subscription_id, subscriptionInfo.current_period_start, subscriptionInfo.current_period_end) - // return { ok: true } - // } + return { ok: true }; + } else { + return { received: true, warn: 'subscription status not active' } + } + + } + + return { received: true, warn: 'payment status not paid' } +} + + +async function addSubscriptionToProject(project_id: string, plan: PREMIUM_DATA, subscription_id: string, current_period_start: number, current_period_end: number) { + + await ProjectModel.updateOne({ _id: project_id }, { + premium: plan.ID != 0, + premium_type: plan.ID, + subscription_id, + premium_expire_at: current_period_end + }); + + await ProjectLimitModel.updateOne({ project_id }, { + events: 0, + visits: 0, + ai_messages: 0, + limit: plan.COUNT_LIMIT, + ai_limit: plan.AI_MESSAGE_LIMIT, + billing_start_at: current_period_start * 1000, + billing_expire_at: current_period_end * 1000, + }, { upsert: true }) - return { received: true } } async function onSubscriptionCreated(event: Event.CustomerSubscriptionCreatedEvent) { const project = await ProjectModel.findOne({ customer_id: event.data.object.customer }); - if (!project) return { error: 'Project not found' } + if (!project) return { error: 'CUSTOMER NOT EXIST' } const price = event.data.object.items.data[0].price.id; if (!price) return { error: 'Price not found' } - const PLAN = getPlanFromPrice(price); + const PLAN = getPlanFromPrice(price, StripeService.testMode || false); if (!PLAN) return { error: 'Plan not found' } - if (project.subscription_id != event.data.object.id) { - await StripeService.deleteSubscription(project.subscription_id); + try { + await StripeService.deleteSubscription(project.subscription_id); + } catch (ex) { } } - project.premium = PLAN.ID != 0; - project.premium_type = PLAN.ID; - project.subscription_id = event.data.object.id; - project.premium_expire_at = new Date(event.data.object.current_period_end * 1000); - await Promise.all([ + if (event.data.object.status === 'active') { + await addSubscriptionToProject( + project._id.toString(), + PLAN, + event.data.object.id, + event.data.object.current_period_start, + event.data.object.current_period_end + ); + } - project.save(), - ProjectLimitModel.updateOne({ project_id: project._id }, { - events: 0, - visits: 0, - ai_messages: 0, - limit: PLAN.COUNT_LIMIT, - ai_limit: PLAN.AI_MESSAGE_LIMIT, - billing_start_at: event.data.object.current_period_start * 1000, - billing_expire_at: event.data.object.current_period_end * 1000, - }, { upsert: true }) - - ]); return { ok: true } } async function onSubscriptionDeleted(event: Event.CustomerSubscriptionDeletedEvent) { - const project = await ProjectModel.findOne({ - customer_id: event.data.object.customer, - subscription_id: event.data.object.id - }); + // const project = await ProjectModel.findOne({ + // customer_id: event.data.object.customer, + // subscription_id: event.data.object.id + // }); - if (!project) return { error: 'Project not found' } + // if (!project) return { error: 'PROJECT WITH SUBSCRIPTION NOT FOUND' } - const PLAN = PREMIUM_PLAN['FREE']; + // const targetCustomer = await StripeService.getCustomer(project.customer_id); - const targetCustomer = await StripeService.getCustomer(project.customer_id); + // let customer: Event.Customer; - let customer: Event.Customer; + // if (!targetCustomer.deleted) { + // customer = targetCustomer; + // } else { + // const user = await UserModel.findById(project._id, { email: 1 }); + // if (!user) return { error: 'User not found' } + // const newCustomer = await StripeService.createCustomer(user.email); + // customer = newCustomer; + // } - if (!targetCustomer.deleted) { - customer = targetCustomer; - } else { - const user = await UserModel.findById(project._id, { email: 1 }); - if (!user) return { error: 'User not found' } - const newCustomer = await StripeService.createCustomer(user.email); - customer = newCustomer; - } + // await StripeService.createFreeSubscription(customer.id); - const freeSubscription = await StripeService.createFreeSubscription(customer.id); - - - project.premium = false; - project.premium_type = PLAN.ID; - project.subscription_id = freeSubscription.id; - project.premium_expire_at = new Date(freeSubscription.current_period_end * 1000); - - - await Promise.all([ - - project.save(), - - ProjectLimitModel.updateOne({ project_id: project._id }, { - events: 0, - visits: 0, - ai_messages: 0, - limit: PLAN.COUNT_LIMIT, - ai_limit: PLAN.AI_MESSAGE_LIMIT, - billing_start_at: event.data.object.current_period_start * 1000, - billing_expire_at: event.data.object.current_period_end * 1000, - }, { upsert: true }) - - ]); - - return { ok: true } + return { received: true } } async function onSubscriptionUpdated(event: Event.CustomerSubscriptionUpdatedEvent) { const project = await ProjectModel.findOne({ customer_id: event.data.object.customer, - subscription_id: event.data.object.id }); if (!project) return { error: 'Project not found' } @@ -153,29 +132,18 @@ async function onSubscriptionUpdated(event: Event.CustomerSubscriptionUpdatedEve const price = event.data.object.items.data[0].price.id; if (!price) return { error: 'Price not found' } - const PLAN = getPlanFromPrice(price); + const PLAN = getPlanFromPrice(price, StripeService.testMode || false); if (!PLAN) return { error: 'Plan not found' } - project.premium = PLAN.ID != 0; - project.premium_type = PLAN.ID; - project.subscription_id = event.data.object.id; - project.premium_expire_at = new Date(event.data.object.current_period_end * 1000); - - await Promise.all([ - - project.save(), - - ProjectLimitModel.updateOne({ project_id: project._id }, { - events: 0, - visits: 0, - ai_messages: 0, - limit: PLAN.COUNT_LIMIT, - ai_limit: PLAN.AI_MESSAGE_LIMIT, - billing_start_at: event.data.object.current_period_start * 1000, - billing_expire_at: event.data.object.current_period_end * 1000, - }, { upsert: true }) - - ]); + if (event.data.object.status === 'active') { + await addSubscriptionToProject( + project._id.toString(), + PLAN, + event.data.object.id, + event.data.object.current_period_start, + event.data.object.current_period_end + ); + } return { ok: true } } diff --git a/dashboard/server/api/project/delete.delete.ts b/dashboard/server/api/project/delete.delete.ts index 4358fa9..75daf8b 100644 --- a/dashboard/server/api/project/delete.delete.ts +++ b/dashboard/server/api/project/delete.delete.ts @@ -23,7 +23,9 @@ export default defineEventHandler(async event => { if (project.premium === true) return setResponseStatus(event, 400, 'Cannot delete premium project'); - await StripeService.deleteCustomer(project.customer_id); + if (project.customer_id) { + await StripeService.deleteCustomer(project.customer_id); + } const projectDeletation = await ProjectModel.deleteOne({ _id: project_id }); const countDeletation = await ProjectCountModel.deleteMany({ project_id }); diff --git a/dashboard/server/api/user/delete_account.delete.ts b/dashboard/server/api/user/delete_account.delete.ts index 6371f8c..ead7655 100644 --- a/dashboard/server/api/user/delete_account.delete.ts +++ b/dashboard/server/api/user/delete_account.delete.ts @@ -23,7 +23,6 @@ export default defineEventHandler(async event => { await StripeService.deleteCustomer(project.customer_id); const projectDeletation = await ProjectModel.deleteOne({ _id: project_id }); const userSettingsDeletation = await UserSettingsModel.deleteOne({ project_id }); - const countDeletation = await ProjectCountModel.deleteMany({ project_id }); const limitdeletation = await ProjectLimitModel.deleteMany({ project_id }); const sessionsDeletation = await SessionModel.deleteMany({ project_id }); diff --git a/dashboard/server/init.ts b/dashboard/server/init.ts index 2223a68..926a676 100644 --- a/dashboard/server/init.ts +++ b/dashboard/server/init.ts @@ -17,8 +17,7 @@ export default async () => { config.EMAIL_PASS, ); - StripeService.init(config.STRIPE_SECRET, config.STRIPE_WH_SECRET); - + StripeService.init(config.STRIPE_SECRET, config.STRIPE_WH_SECRET, false); if (!connection || connection.connection.readyState == mongoose.ConnectionStates.disconnected) { console.log('[DATABASE] Connecting'); diff --git a/dashboard/server/services/StripeService.ts b/dashboard/server/services/StripeService.ts index 733c52a..ee2b630 100644 --- a/dashboard/server/services/StripeService.ts +++ b/dashboard/server/services/StripeService.ts @@ -5,11 +5,13 @@ class StripeService { private stripe?: Stripe; private privateKey?: string; private webhookSecret?: string; + public testMode?: boolean; - init(privateKey: string, webhookSecret: string) { + init(privateKey: string, webhookSecret: string, testMode: boolean = false) { this.privateKey = privateKey; this.webhookSecret = webhookSecret; this.stripe = new Stripe(this.privateKey); + this.testMode = testMode; } parseWebhook(body: any, sig: string) { @@ -87,7 +89,7 @@ class StripeService { const subscription = await this.stripe.subscriptions.create({ customer: customer_id, items: [ - { price: FREE_PLAN.PRICE, quantity: 1 } + { price: this.testMode ? FREE_PLAN.PRICE_TEST : FREE_PLAN.PRICE, quantity: 1 } ] }); diff --git a/shared/data/PREMIUM.ts b/shared/data/PREMIUM.ts index 84cf5f7..f902337 100644 --- a/shared/data/PREMIUM.ts +++ b/shared/data/PREMIUM.ts @@ -57,9 +57,14 @@ export function getPlanFromId(id: number) { } } -export function getPlanFromPrice(price: string) { +export function getPlanFromPrice(price: string, testMode: boolean) { for (const tag of PREMIUM_TAGS) { const plan = getPlanFromTag(tag); - if (plan.PRICE === price) return plan; + if (testMode) { + if (plan.PRICE_TEST === price) return plan; + } else { + if (plan.PRICE === price) return plan; + } + } } \ No newline at end of file