mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 07:48:37 +01:00
fix payments
This commit is contained in:
@@ -6,7 +6,8 @@ export type PricingCardProp = {
|
|||||||
features: string[],
|
features: string[],
|
||||||
desc: string,
|
desc: string,
|
||||||
active: boolean,
|
active: boolean,
|
||||||
planId: number
|
planId: number,
|
||||||
|
isDowngrade: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<{ data: PricingCardProp }>();
|
const props = defineProps<{ data: PricingCardProp }>();
|
||||||
@@ -43,10 +44,14 @@ async function onUpgradeClick() {
|
|||||||
<div v-if="data.active" class="text-[1rem] bg-[#1f1f22] rounded-md py-2 text-center">
|
<div v-if="data.active" class="text-[1rem] bg-[#1f1f22] rounded-md py-2 text-center">
|
||||||
Current active plan
|
Current active plan
|
||||||
</div>
|
</div>
|
||||||
<div @click="onUpgradeClick()" v-if="!data.active"
|
<div @click="onUpgradeClick()" v-if="!data.active && !data.isDowngrade"
|
||||||
class="cursor-pointer text-[1rem] font-semibold bg-[#3a3af5] rounded-md py-2 text-center">
|
class="cursor-pointer text-[1rem] font-semibold bg-[#3a3af5] rounded-md py-2 text-center">
|
||||||
Upgrade
|
Upgrade
|
||||||
</div>
|
</div>
|
||||||
|
<div @click="onUpgradeClick()" v-if="!data.active && data.isDowngrade"
|
||||||
|
class="cursor-pointer text-[1rem] font-semibold bg-[#1f1f22] text-red-400 rounded-md py-2 text-center">
|
||||||
|
Downgrade
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-gray-400 h-[1px] w-full my-4"></div>
|
<div class="bg-gray-400 h-[1px] w-full my-4"></div>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import type { PricingCardProp } from './PricingCard.vue';
|
|||||||
|
|
||||||
const activeProject = useActiveProject();
|
const activeProject = useActiveProject();
|
||||||
|
|
||||||
|
const props = defineProps<{ currentSub: number }>();
|
||||||
|
|
||||||
|
|
||||||
const starterTierCardData = ref<PricingCardProp>({
|
const starterTierCardData = ref<PricingCardProp>({
|
||||||
title: 'STARTER',
|
title: 'STARTER',
|
||||||
@@ -23,6 +25,7 @@ const starterTierCardData = ref<PricingCardProp>({
|
|||||||
dedicated server we suggest to upgrade the
|
dedicated server we suggest to upgrade the
|
||||||
plan to an higher one!`,
|
plan to an higher one!`,
|
||||||
active: activeProject.value?.premium === false,
|
active: activeProject.value?.premium === false,
|
||||||
|
isDowngrade: props.currentSub > 0,
|
||||||
planId: 0
|
planId: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -41,6 +44,7 @@ const accelerationTierCardData = ref<PricingCardProp>({
|
|||||||
],
|
],
|
||||||
desc: `Your project is entering a growth phase. We simplify data analysis for you. For more support, try our Expansion plan—it's worth it!`,
|
desc: `Your project is entering a growth phase. We simplify data analysis for you. For more support, try our Expansion plan—it's worth it!`,
|
||||||
active: activeProject.value?.premium_type === 1,
|
active: activeProject.value?.premium_type === 1,
|
||||||
|
isDowngrade: props.currentSub > 1,
|
||||||
planId: 1
|
planId: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -59,6 +63,7 @@ const expansionTierCardData = ref<PricingCardProp>({
|
|||||||
],
|
],
|
||||||
desc: `We will support you with everything we can offer and give you the full power of our service. If you need more space and are growing, contact us for a custom offer!`,
|
desc: `We will support you with everything we can offer and give you the full power of our service. If you need more space and are growing, contact us for a custom offer!`,
|
||||||
active: activeProject.value?.premium_type === 2,
|
active: activeProject.value?.premium_type === 2,
|
||||||
|
isDowngrade: props.currentSub > 2,
|
||||||
planId: 2
|
planId: 2
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ function getPremiumName(type: number) {
|
|||||||
<div class="w-full h-full p-8 overflow-y-auto pb-40 lg:pb-0 relative overflow-x-hidden">
|
<div class="w-full h-full p-8 overflow-y-auto pb-40 lg:pb-0 relative overflow-x-hidden">
|
||||||
|
|
||||||
<Transition name="pdrawer">
|
<Transition name="pdrawer">
|
||||||
<PricingDrawer @onCloseClick="showPricingDrawer = false"
|
<PricingDrawer @onCloseClick="showPricingDrawer = false" :currentSub="planData?.premium_type || 0"
|
||||||
class="bg-black fixed right-0 top-0 w-full xl:w-[60vw] xl:min-w-[65rem] h-full z-[20]"
|
class="bg-black fixed right-0 top-0 w-full xl:w-[60vw] xl:min-w-[65rem] h-full z-[20]"
|
||||||
v-if=showPricingDrawer>
|
v-if=showPricingDrawer>
|
||||||
</PricingDrawer>
|
</PricingDrawer>
|
||||||
@@ -131,7 +131,7 @@ function getPremiumName(type: number) {
|
|||||||
<div> {{ prettyExpireDate }}</div>
|
<div> {{ prettyExpireDate }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div @click="onPlanUpgradeClick()"
|
<div @click="onPlanUpgradeClick()"
|
||||||
class="cursor-pointer flex items-center gap-2 text-[.9rem] text-accent drop-shadow-[0_0_8px_#000000]">
|
class="cursor-pointer flex items-center gap-2 text-[.9rem] text-white font-semibold bg-accent px-4 py-1 rounded-lg drop-shadow-[0_0_8px_#000000]">
|
||||||
<div class="poppins"> Upgrade plan </div>
|
<div class="poppins"> Upgrade plan </div>
|
||||||
<i class="fas fa-arrow-up-right"></i>
|
<i class="fas fa-arrow-up-right"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -164,15 +164,6 @@ function getPremiumName(type: number) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="my-4 w-full bg-gray-400/30 h-[1px]">
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-end px-8 flex-col sm:flex-row">
|
|
||||||
<div @click="onPlanUpgradeClick()"
|
|
||||||
class="cursor-pointer flex items-center gap-2 text-[.9rem] text-accent drop-shadow-[0_0_8px_#000000]">
|
|
||||||
<div class="poppins"> Upgrade plan </div>
|
|
||||||
<i class="fas fa-arrow-up-right"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -2,42 +2,11 @@
|
|||||||
import StripeService from '~/server/services/StripeService';
|
import StripeService from '~/server/services/StripeService';
|
||||||
import type Event from 'stripe';
|
import type Event from 'stripe';
|
||||||
import { ProjectModel } from '@schema/ProjectSchema';
|
import { ProjectModel } from '@schema/ProjectSchema';
|
||||||
import { PREMIUM_DATA, PREMIUM_PLAN, getPlanFromPrice } from '@data/PREMIUM';
|
import { PREMIUM_DATA, PREMIUM_PLAN, getPlanFromId, getPlanFromPrice, getPlanFromTag } from '@data/PREMIUM';
|
||||||
import { ProjectCountModel } from '@schema/ProjectsCounts';
|
import { ProjectCountModel } from '@schema/ProjectsCounts';
|
||||||
import { ProjectLimitModel } from '@schema/ProjectsLimits';
|
import { ProjectLimitModel } from '@schema/ProjectsLimits';
|
||||||
import { UserModel } from '@schema/UserSchema';
|
import { UserModel } from '@schema/UserSchema';
|
||||||
|
|
||||||
async function onPaymentSuccess(event: Event.InvoicePaidEvent) {
|
|
||||||
|
|
||||||
if (event.data.object.status === 'paid') {
|
|
||||||
|
|
||||||
const customer_id = event.data.object.customer as string;
|
|
||||||
const subscription_id = event.data.object.subscription as string;
|
|
||||||
|
|
||||||
const project = await ProjectModel.findOne({ customer_id });
|
|
||||||
if (!project) return { error: 'CUSTOMER NOT EXIST' }
|
|
||||||
|
|
||||||
const subscriptionInfo = await StripeService.getSubscription(subscription_id);
|
|
||||||
|
|
||||||
if (subscriptionInfo.status === 'active') {
|
|
||||||
|
|
||||||
const price = subscriptionInfo.items.data[0].price.id;
|
|
||||||
if (!price) return { error: 'Price not found' }
|
|
||||||
|
|
||||||
const PLAN = getPlanFromPrice(price, StripeService.testMode || false);
|
|
||||||
if (!PLAN) return { error: 'Plan not found' }
|
|
||||||
|
|
||||||
await addSubscriptionToProject(project._id.toString(), PLAN, subscription_id, subscriptionInfo.current_period_start, subscriptionInfo.current_period_end)
|
|
||||||
|
|
||||||
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) {
|
async function addSubscriptionToProject(project_id: string, plan: PREMIUM_DATA, subscription_id: string, current_period_start: number, current_period_end: number) {
|
||||||
@@ -46,7 +15,7 @@ async function addSubscriptionToProject(project_id: string, plan: PREMIUM_DATA,
|
|||||||
premium: plan.ID != 0,
|
premium: plan.ID != 0,
|
||||||
premium_type: plan.ID,
|
premium_type: plan.ID,
|
||||||
subscription_id,
|
subscription_id,
|
||||||
premium_expire_at: current_period_end
|
premium_expire_at: current_period_end * 1000
|
||||||
});
|
});
|
||||||
|
|
||||||
await ProjectLimitModel.updateOne({ project_id }, {
|
await ProjectLimitModel.updateOne({ project_id }, {
|
||||||
@@ -61,33 +30,106 @@ async function addSubscriptionToProject(project_id: string, plan: PREMIUM_DATA,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSubscriptionCreated(event: Event.CustomerSubscriptionCreatedEvent) {
|
|
||||||
|
|
||||||
const project = await ProjectModel.findOne({ customer_id: event.data.object.customer });
|
|
||||||
|
async function onPaymentFailed(event: Event.InvoicePaymentFailedEvent) {
|
||||||
|
|
||||||
|
//TODO: Send emails
|
||||||
|
|
||||||
|
if (event.data.object.attempt_count > 1) {
|
||||||
|
const customer_id = event.data.object.customer as string;
|
||||||
|
const project = await ProjectModel.findOne({ customer_id });
|
||||||
if (!project) return { error: 'CUSTOMER NOT EXIST' }
|
if (!project) return { error: 'CUSTOMER NOT EXIST' }
|
||||||
|
|
||||||
const price = event.data.object.items.data[0].price.id;
|
const allSubscriptions = await StripeService.getAllSubscriptions(customer_id);
|
||||||
|
|
||||||
|
for (const subscription of allSubscriptions.data) {
|
||||||
|
await StripeService.deleteSubscription(subscription.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const freeSub = await StripeService.createFreeSubscription(customer_id);
|
||||||
|
|
||||||
|
await addSubscriptionToProject(
|
||||||
|
project._id.toString(),
|
||||||
|
getPlanFromTag('FREE'),
|
||||||
|
freeSub.id,
|
||||||
|
freeSub.current_period_start,
|
||||||
|
freeSub.current_period_end
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ok: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onPaymentSuccess(event: Event.InvoicePaidEvent) {
|
||||||
|
|
||||||
|
const customer_id = event.data.object.customer as string;
|
||||||
|
const project = await ProjectModel.findOne({ customer_id });
|
||||||
|
if (!project) return { error: 'CUSTOMER NOT EXIST' }
|
||||||
|
|
||||||
|
|
||||||
|
if (event.data.object.status === 'paid') {
|
||||||
|
|
||||||
|
const subscription_id = event.data.object.subscription as string;
|
||||||
|
|
||||||
|
const allSubscriptions = await StripeService.getAllSubscriptions(customer_id);
|
||||||
|
|
||||||
|
const currentSubscription = allSubscriptions.data.find(e => e.id === subscription_id);
|
||||||
|
if (!currentSubscription) return { error: 'SUBSCRIPTION NOT EXIST' }
|
||||||
|
|
||||||
|
if (currentSubscription.status !== 'active') return { error: 'SUBSCRIPTION NOT ACTIVE' }
|
||||||
|
|
||||||
|
for (const subscription of allSubscriptions.data) {
|
||||||
|
if (subscription.id === subscription_id) continue;
|
||||||
|
await StripeService.deleteSubscription(subscription.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const price = currentSubscription.items.data[0].price.id;
|
||||||
if (!price) return { error: 'Price not found' }
|
if (!price) return { error: 'Price not found' }
|
||||||
|
|
||||||
const PLAN = getPlanFromPrice(price, StripeService.testMode || false);
|
const PLAN = getPlanFromPrice(price, StripeService.testMode || false);
|
||||||
if (!PLAN) return { error: 'Plan not found' }
|
if (!PLAN) return { error: 'Plan not found' }
|
||||||
|
|
||||||
if (project.subscription_id != event.data.object.id) {
|
await addSubscriptionToProject(project._id.toString(), PLAN, subscription_id, currentSubscription.current_period_start, currentSubscription.current_period_end)
|
||||||
try {
|
|
||||||
await StripeService.deleteSubscription(project.subscription_id);
|
return { ok: true };
|
||||||
} catch (ex) { }
|
|
||||||
|
|
||||||
|
}
|
||||||
|
return { received: true, warn: 'payment status not paid' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (event.data.object.status === 'active') {
|
|
||||||
await addSubscriptionToProject(
|
async function onSubscriptionCreated(event: Event.CustomerSubscriptionCreatedEvent) {
|
||||||
project._id.toString(),
|
|
||||||
PLAN,
|
// const project = await ProjectModel.findOne({ customer_id: event.data.object.customer });
|
||||||
event.data.object.id,
|
// if (!project) return { error: 'CUSTOMER NOT EXIST' }
|
||||||
event.data.object.current_period_start,
|
|
||||||
event.data.object.current_period_end
|
// const price = event.data.object.items.data[0].price.id;
|
||||||
);
|
// if (!price) return { error: 'Price not found' }
|
||||||
}
|
|
||||||
|
// const PLAN = getPlanFromPrice(price, StripeService.testMode || false);
|
||||||
|
// if (!PLAN) return { error: 'Plan not found' }
|
||||||
|
|
||||||
|
// if (project.subscription_id != event.data.object.id) {
|
||||||
|
// try {
|
||||||
|
// await StripeService.deleteSubscription(project.subscription_id);
|
||||||
|
// } catch (ex) { }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -123,27 +165,27 @@ async function onSubscriptionDeleted(event: Event.CustomerSubscriptionDeletedEve
|
|||||||
|
|
||||||
async function onSubscriptionUpdated(event: Event.CustomerSubscriptionUpdatedEvent) {
|
async function onSubscriptionUpdated(event: Event.CustomerSubscriptionUpdatedEvent) {
|
||||||
|
|
||||||
const project = await ProjectModel.findOne({
|
// const project = await ProjectModel.findOne({
|
||||||
customer_id: event.data.object.customer,
|
// customer_id: event.data.object.customer,
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (!project) return { error: 'Project not found' }
|
// if (!project) return { error: 'Project not found' }
|
||||||
|
|
||||||
const price = event.data.object.items.data[0].price.id;
|
// const price = event.data.object.items.data[0].price.id;
|
||||||
if (!price) return { error: 'Price not found' }
|
// if (!price) return { error: 'Price not found' }
|
||||||
|
|
||||||
const PLAN = getPlanFromPrice(price, StripeService.testMode || false);
|
// const PLAN = getPlanFromPrice(price, StripeService.testMode || false);
|
||||||
if (!PLAN) return { error: 'Plan not found' }
|
// if (!PLAN) return { error: 'Plan not found' }
|
||||||
|
|
||||||
if (event.data.object.status === 'active') {
|
// if (event.data.object.status === 'active') {
|
||||||
await addSubscriptionToProject(
|
// await addSubscriptionToProject(
|
||||||
project._id.toString(),
|
// project._id.toString(),
|
||||||
PLAN,
|
// PLAN,
|
||||||
event.data.object.id,
|
// event.data.object.id,
|
||||||
event.data.object.current_period_start,
|
// event.data.object.current_period_start,
|
||||||
event.data.object.current_period_end
|
// event.data.object.current_period_end
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
return { ok: true }
|
return { ok: true }
|
||||||
}
|
}
|
||||||
@@ -159,6 +201,7 @@ export default defineEventHandler(async event => {
|
|||||||
const eventData = StripeService.parseWebhook(body, signature);
|
const eventData = StripeService.parseWebhook(body, signature);
|
||||||
if (!eventData) return;
|
if (!eventData) return;
|
||||||
if (eventData.type === 'invoice.paid') return await onPaymentSuccess(eventData);
|
if (eventData.type === 'invoice.paid') return await onPaymentSuccess(eventData);
|
||||||
|
if (eventData.type === 'invoice.payment_failed') return await onPaymentFailed(eventData);
|
||||||
if (eventData.type === 'customer.subscription.deleted') return await onSubscriptionDeleted(eventData);
|
if (eventData.type === 'customer.subscription.deleted') return await onSubscriptionDeleted(eventData);
|
||||||
if (eventData.type === 'customer.subscription.created') return await onSubscriptionCreated(eventData);
|
if (eventData.type === 'customer.subscription.created') return await onSubscriptionCreated(eventData);
|
||||||
if (eventData.type === 'customer.subscription.updated') return await onSubscriptionUpdated(eventData);
|
if (eventData.type === 'customer.subscription.updated') return await onSubscriptionUpdated(eventData);
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ export default async () => {
|
|||||||
config.EMAIL_PASS,
|
config.EMAIL_PASS,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
StripeService.init(config.STRIPE_SECRET, config.STRIPE_WH_SECRET, false);
|
StripeService.init(config.STRIPE_SECRET, config.STRIPE_WH_SECRET, false);
|
||||||
|
|
||||||
|
|
||||||
if (!connection || connection.connection.readyState == mongoose.ConnectionStates.disconnected) {
|
if (!connection || connection.connection.readyState == mongoose.ConnectionStates.disconnected) {
|
||||||
console.log('[DATABASE] Connecting');
|
console.log('[DATABASE] Connecting');
|
||||||
connection = await mongoose.connect(config.MONGO_CONNECTION_STRING);
|
connection = await mongoose.connect(config.MONGO_CONNECTION_STRING);
|
||||||
|
|||||||
@@ -54,6 +54,12 @@ class StripeService {
|
|||||||
return subscription;
|
return subscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getAllSubscriptions(customer_id: string) {
|
||||||
|
if (!this.stripe) throw Error('Stripe not initialized');
|
||||||
|
const subscriptions = await this.stripe.subscriptions.list({customer: customer_id});
|
||||||
|
return subscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
async getInvoices(customer_id: string) {
|
async getInvoices(customer_id: string) {
|
||||||
const invoices = await this.stripe?.invoices.list({ customer: customer_id });
|
const invoices = await this.stripe?.invoices.list({ customer: customer_id });
|
||||||
return invoices;
|
return invoices;
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
|
import { CustomPremiumPriceModel } from "../schema/CustomPremiumPriceSchema";
|
||||||
|
|
||||||
export type PREMIUM_TAG = typeof PREMIUM_TAGS[number];
|
export type PREMIUM_TAG = typeof PREMIUM_TAGS[number];
|
||||||
|
|
||||||
export const PREMIUM_TAGS = [
|
export const PREMIUM_TAGS = ['FREE', 'PLAN_1', 'PLAN_2', 'CUSTOM_1'] as const;
|
||||||
'FREE', 'PLAN_1', 'PLAN_2', 'CUSTOM_1'
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
|
|
||||||
export type PREMIUM_DATA = {
|
export type PREMIUM_DATA = {
|
||||||
@@ -34,7 +33,7 @@ export const PREMIUM_PLAN: Record<PREMIUM_TAG, PREMIUM_DATA> = {
|
|||||||
COUNT_LIMIT: 500_000,
|
COUNT_LIMIT: 500_000,
|
||||||
AI_MESSAGE_LIMIT: 5_000,
|
AI_MESSAGE_LIMIT: 5_000,
|
||||||
PRICE: 'price_1POKCKB2lPUiVs9Vol8XOmhW',
|
PRICE: 'price_1POKCKB2lPUiVs9Vol8XOmhW',
|
||||||
PRICE_TEST: ''
|
PRICE_TEST: 'price_1POK34B2lPUiVs9VIROb0IIV'
|
||||||
},
|
},
|
||||||
CUSTOM_1: {
|
CUSTOM_1: {
|
||||||
ID: 1001,
|
ID: 1001,
|
||||||
@@ -45,6 +44,18 @@ export const PREMIUM_PLAN: Record<PREMIUM_TAG, PREMIUM_DATA> = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CustomPremiumPriceModel.find({}).then(custom_prices => {
|
||||||
|
for (const custom_price of custom_prices) {
|
||||||
|
PREMIUM_PLAN[custom_price.tag] = {
|
||||||
|
ID: custom_price.price_id,
|
||||||
|
COUNT_LIMIT: custom_price.count_limit,
|
||||||
|
AI_MESSAGE_LIMIT: custom_price.ai_message_limit,
|
||||||
|
PRICE: custom_price.price,
|
||||||
|
PRICE_TEST: custom_price.price_test || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
export function getPlanFromTag(tag: PREMIUM_TAG) {
|
export function getPlanFromTag(tag: PREMIUM_TAG) {
|
||||||
return PREMIUM_PLAN[tag];
|
return PREMIUM_PLAN[tag];
|
||||||
|
|||||||
21
shared/schema/CustomPremiumPriceSchema.ts
Normal file
21
shared/schema/CustomPremiumPriceSchema.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { model, Schema } from 'mongoose';
|
||||||
|
|
||||||
|
export type TCustomPremiumPrice = {
|
||||||
|
tag: string,
|
||||||
|
price_id: number,
|
||||||
|
count_limit: number,
|
||||||
|
ai_message_limit: number,
|
||||||
|
price: string,
|
||||||
|
price_test?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomPremiumPriceSchema = new Schema<TCustomPremiumPrice>({
|
||||||
|
tag: { type: String, required: true },
|
||||||
|
price_id: { type: Number, required: true },
|
||||||
|
count_limit: { type: Number, required: true },
|
||||||
|
ai_message_limit: { type: Number, required: true },
|
||||||
|
price: { type: String, required: true },
|
||||||
|
price_test: { type: String },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const CustomPremiumPriceModel = model<TCustomPremiumPrice>('custom_premium_prices', CustomPremiumPriceSchema);
|
||||||
Reference in New Issue
Block a user