mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 07:48:37 +01:00
fix dashboard + payments
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import type { SettingsTemplateEntry } from './Template.vue';
|
import type { SettingsTemplateEntry } from './Template.vue';
|
||||||
import { getPlanFromId, PREMIUM_PLAN, type PREMIUM_TAG } from '@data/PREMIUM';
|
import { getPlanFromId, PREMIUM_PLAN, type PLAN_TAG } from '@data/PREMIUM';
|
||||||
|
|
||||||
const { projectId, isGuest } = useProject();
|
const { projectId, isGuest } = useProject();
|
||||||
|
|
||||||
@@ -53,10 +53,10 @@ function openInvoice(link: string) {
|
|||||||
window.open(link, '_blank');
|
window.open(link, '_blank');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPremiumName(type: number) {
|
function getTagName(type: number) {
|
||||||
|
|
||||||
return Object.keys(PREMIUM_PLAN).map(e => ({
|
return Object.keys(PREMIUM_PLAN).map(e => ({
|
||||||
...PREMIUM_PLAN[e as PREMIUM_TAG], name: e
|
...PREMIUM_PLAN[e as PLAN_TAG], name: e
|
||||||
})).find(e => e.ID == type)?.name;
|
})).find(e => e.ID == type)?.name;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ const { showDrawer } = useDrawer();
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex lato text-[.7rem] bg-transparent border-[#262626] border-[1px] px-[.6rem] rounded-sm">
|
class="flex lato text-[.7rem] bg-transparent border-[#262626] border-[1px] px-[.6rem] rounded-sm">
|
||||||
{{ planData.premium ? getPremiumName(planData.premium_type) : 'FREE' }}
|
{{ planData.premium ? getTagName(planData.premium_type) : 'FREE' }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -187,9 +187,6 @@ const { showDrawer } = useDrawer();
|
|||||||
</div>
|
</div>
|
||||||
<div class="poppins"> {{ daysLeft }} days left </div>
|
<div class="poppins"> {{ daysLeft }} days left </div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-center">
|
|
||||||
Subscription: {{ planData.subscription_status }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="my-4 w-full bg-gray-400/30 h-[1px]">
|
<div class="my-4 w-full bg-gray-400/30 h-[1px]">
|
||||||
|
|||||||
@@ -45,15 +45,12 @@ export default defineNuxtConfig({
|
|||||||
AI_PROJECT: process.env.AI_PROJECT,
|
AI_PROJECT: process.env.AI_PROJECT,
|
||||||
AI_KEY: process.env.AI_KEY,
|
AI_KEY: process.env.AI_KEY,
|
||||||
EMAIL_SECRET: process.env.EMAIL_SECRET,
|
EMAIL_SECRET: process.env.EMAIL_SECRET,
|
||||||
|
PAYMENT_SECRET: process.env.PAYMENT_SECRET,
|
||||||
AUTH_JWT_SECRET: process.env.AUTH_JWT_SECRET,
|
AUTH_JWT_SECRET: process.env.AUTH_JWT_SECRET,
|
||||||
GOOGLE_AUTH_CLIENT_ID: process.env.GOOGLE_AUTH_CLIENT_ID,
|
GOOGLE_AUTH_CLIENT_ID: process.env.GOOGLE_AUTH_CLIENT_ID,
|
||||||
GOOGLE_AUTH_CLIENT_SECRET: process.env.GOOGLE_AUTH_CLIENT_SECRET,
|
GOOGLE_AUTH_CLIENT_SECRET: process.env.GOOGLE_AUTH_CLIENT_SECRET,
|
||||||
GITHUB_AUTH_CLIENT_ID: process.env.GITHUB_AUTH_CLIENT_ID,
|
GITHUB_AUTH_CLIENT_ID: process.env.GITHUB_AUTH_CLIENT_ID,
|
||||||
GITHUB_AUTH_CLIENT_SECRET: process.env.GITHUB_AUTH_CLIENT_SECRET,
|
GITHUB_AUTH_CLIENT_SECRET: process.env.GITHUB_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,
|
|
||||||
NOAUTH_USER_EMAIL: process.env.NOAUTH_USER_EMAIL,
|
NOAUTH_USER_EMAIL: process.env.NOAUTH_USER_EMAIL,
|
||||||
NOAUTH_USER_PASS: process.env.NOAUTH_USER_PASS,
|
NOAUTH_USER_PASS: process.env.NOAUTH_USER_PASS,
|
||||||
MODE: process.env.MODE || 'NONE',
|
MODE: process.env.MODE || 'NONE',
|
||||||
|
|||||||
8
dashboard/pnpm-lock.yaml
generated
8
dashboard/pnpm-lock.yaml
generated
@@ -5255,8 +5255,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: ^3.2.0
|
vue: ^3.2.0
|
||||||
|
|
||||||
vue3-google-signin@2.0.1:
|
vue3-google-signin@2.1.1:
|
||||||
resolution: {integrity: sha512-vZTlVrG56JERtqQ+6YI8e92wqfhAMDyNONCsLgKKXxzCNCWEfSkZcAvz7COm9V4bvzmGsebZ8KC3ljol2qsIcg==}
|
resolution: {integrity: sha512-RwlwyeCv8+PZjK35C/UyJN4/9MH+Fsz4bJDO7IjtmRuOaTMrvmMmI6SP5qkJgD7w9MIKOCj5rYVFMAL7+NCM0A==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: ^3
|
vue: ^3
|
||||||
|
|
||||||
@@ -9868,7 +9868,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@nuxt/kit': 3.11.2(rollup@4.18.0)
|
'@nuxt/kit': 3.11.2(rollup@4.18.0)
|
||||||
unimport: 3.7.2(rollup@4.18.0)
|
unimport: 3.7.2(rollup@4.18.0)
|
||||||
vue3-google-signin: 2.0.1(vue@3.4.27(typescript@5.4.2))
|
vue3-google-signin: 2.1.1(vue@3.4.27(typescript@5.4.2))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- rollup
|
- rollup
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -11525,7 +11525,7 @@ snapshots:
|
|||||||
vue-observe-visibility: 2.0.0-alpha.1(vue@3.4.27(typescript@5.4.2))
|
vue-observe-visibility: 2.0.0-alpha.1(vue@3.4.27(typescript@5.4.2))
|
||||||
vue-resize: 2.0.0-alpha.1(vue@3.4.27(typescript@5.4.2))
|
vue-resize: 2.0.0-alpha.1(vue@3.4.27(typescript@5.4.2))
|
||||||
|
|
||||||
vue3-google-signin@2.0.1(vue@3.4.27(typescript@5.4.2)):
|
vue3-google-signin@2.1.1(vue@3.4.27(typescript@5.4.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
vue: 3.4.27(typescript@5.4.2)
|
vue: 3.4.27(typescript@5.4.2)
|
||||||
|
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
|
|
||||||
import { UserLimitModel } from "@schema/UserLimitSchema";
|
|
||||||
import { AIPlugin } from "../Plugin";
|
|
||||||
import { MAX_LOG_LIMIT_PERCENT } from "@data/broker/Limits";
|
|
||||||
import { ProjectModel } from "@schema/project/ProjectSchema";
|
|
||||||
import StripeService from "~/server/services/StripeService";
|
|
||||||
import { InvoiceData } from "~/server/api/pay/invoices";
|
|
||||||
|
|
||||||
export class AiBilling extends AIPlugin<[
|
|
||||||
'getBillingInfo',
|
|
||||||
'getLimits',
|
|
||||||
'getInvoices'
|
|
||||||
]> {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super({
|
|
||||||
|
|
||||||
'getInvoices': {
|
|
||||||
handler: async (data: { user_id: string }) => {
|
|
||||||
|
|
||||||
const project = await ProjectModel.findOne({ user_id: data.user_id });
|
|
||||||
if (!project) return { error: 'Project not found' };
|
|
||||||
const invoices = await StripeService.getInvoices(data.user_id);
|
|
||||||
if (!invoices) return [];
|
|
||||||
|
|
||||||
return invoices?.data.map(e => {
|
|
||||||
const result: InvoiceData = {
|
|
||||||
link: e.invoice_pdf || '',
|
|
||||||
id: e.number || '',
|
|
||||||
date: e.created * 1000,
|
|
||||||
status: e.status || 'NO_STATUS',
|
|
||||||
cost: e.amount_due
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
tool: {
|
|
||||||
type: 'function',
|
|
||||||
function: {
|
|
||||||
name: 'getInvoices',
|
|
||||||
description: 'Gets the invoices of the user project',
|
|
||||||
parameters: {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
'getBillingInfo': {
|
|
||||||
handler: async (data: { user_id: string }) => {
|
|
||||||
|
|
||||||
return { error: 'NOT IMPLEMENTED YET' }
|
|
||||||
// if (project.subscription_id === 'onetime') {
|
|
||||||
|
|
||||||
// const projectLimits = await ProjectLimitModel.findOne({ project_id: data.project_id });
|
|
||||||
// if (!projectLimits) return { error: 'Limits not found' }
|
|
||||||
|
|
||||||
// const result = {
|
|
||||||
// premium: project.premium,
|
|
||||||
// premium_type: project.premium_type,
|
|
||||||
// billing_start_at: projectLimits.billing_start_at,
|
|
||||||
// billing_expire_at: projectLimits.billing_expire_at,
|
|
||||||
// limit: projectLimits.limit,
|
|
||||||
// count: projectLimits.events + projectLimits.visits,
|
|
||||||
// subscription_status: StripeService.isDisabled() ? 'Disabled mode' : ('One time payment')
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return result;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const subscription = await StripeService.getSubscription(project.subscription_id);
|
|
||||||
|
|
||||||
// const projectLimits = await ProjectLimitModel.findOne({ project_id: data.project_id });
|
|
||||||
// if (!projectLimits) return { error: 'Limits not found' }
|
|
||||||
|
|
||||||
|
|
||||||
// const result = {
|
|
||||||
// premium: project.premium,
|
|
||||||
// premium_type: project.premium_type,
|
|
||||||
// billing_start_at: projectLimits.billing_start_at,
|
|
||||||
// billing_expire_at: projectLimits.billing_expire_at,
|
|
||||||
// limit: projectLimits.limit,
|
|
||||||
// count: projectLimits.events + projectLimits.visits,
|
|
||||||
// subscription_status: StripeService.isDisabled() ? 'Disabled mode' : (subscription?.status ?? '?')
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return result;
|
|
||||||
},
|
|
||||||
tool: {
|
|
||||||
type: 'function',
|
|
||||||
function: {
|
|
||||||
name: 'getBillingInfo',
|
|
||||||
description: 'Gets the informations about the billing of the user project, limits, count, subscription_status, is premium, premium type, billing start at, billing expire at',
|
|
||||||
parameters: {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
'getLimits': {
|
|
||||||
handler: async (data: { project_id: string }) => {
|
|
||||||
return { error: 'NOT IMPLEMENTED YET' }
|
|
||||||
// const projectLimits = await ProjectLimitModel.findOne({ project_id: data.project_id });
|
|
||||||
// if (!projectLimits) return { error: 'Project limits not found' };
|
|
||||||
// const TOTAL_COUNT = projectLimits.events + projectLimits.visits;
|
|
||||||
// const COUNT_LIMIT = projectLimits.limit;
|
|
||||||
// return {
|
|
||||||
// total: TOTAL_COUNT,
|
|
||||||
// limit: COUNT_LIMIT,
|
|
||||||
// limited: TOTAL_COUNT > COUNT_LIMIT * MAX_LOG_LIMIT_PERCENT,
|
|
||||||
// percent: Math.round(100 / COUNT_LIMIT * TOTAL_COUNT)
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
tool: {
|
|
||||||
type: 'function',
|
|
||||||
function: {
|
|
||||||
name: 'getLimits',
|
|
||||||
description: 'Gets the informations about the limits of the user project',
|
|
||||||
parameters: {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AiBillingInstance = new AiBilling();
|
|
||||||
@@ -2,7 +2,6 @@ import { ProjectModel } from "@schema/project/ProjectSchema";
|
|||||||
import { ProjectCountModel } from "@schema/project/ProjectsCounts";
|
import { ProjectCountModel } from "@schema/project/ProjectsCounts";
|
||||||
import { UserLimitModel } from "@schema/UserLimitSchema";
|
import { UserLimitModel } from "@schema/UserLimitSchema";
|
||||||
import { UserModel } from "@schema/UserSchema";
|
import { UserModel } from "@schema/UserSchema";
|
||||||
import StripeService from '~/server/services/StripeService';
|
|
||||||
import { PremiumModel } from "~/shared/schema/PremiumSchema";
|
import { PremiumModel } from "~/shared/schema/PremiumSchema";
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
@@ -20,14 +19,18 @@ export default defineEventHandler(async event => {
|
|||||||
|
|
||||||
const premium = await PremiumModel.findOne({ user_id: userData.id });
|
const premium = await PremiumModel.findOne({ user_id: userData.id });
|
||||||
|
|
||||||
const subscription =
|
// const subscription =
|
||||||
premium?.subscription_id ?
|
// premium?.subscription_id ?
|
||||||
await StripeService.getSubscription(premium.subscription_id) : 'NONE';
|
// await StripeService.getSubscription(premium.subscription_id) : 'NONE';
|
||||||
|
|
||||||
const customer =
|
// const customer =
|
||||||
premium?.customer_id ?
|
// premium?.customer_id ?
|
||||||
await StripeService.getCustomer(premium.customer_id) : 'NONE';
|
// await StripeService.getCustomer(premium.customer_id) : 'NONE';
|
||||||
|
|
||||||
return { project, limits, counts, user, subscription, customer }
|
return {
|
||||||
|
project, limits, counts, user,
|
||||||
|
subscription: '',
|
||||||
|
customer: ''
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -4,6 +4,7 @@ import { UserModel } from '@schema/UserSchema';
|
|||||||
import { PasswordModel } from '@schema/PasswordSchema';
|
import { PasswordModel } from '@schema/PasswordSchema';
|
||||||
import { EmailService } from '@services/EmailService';
|
import { EmailService } from '@services/EmailService';
|
||||||
import { EmailServiceHelper } from '~/server/services/EmailServiceHelper';
|
import { EmailServiceHelper } from '~/server/services/EmailServiceHelper';
|
||||||
|
import { PaymentServiceHelper } from '~/server/services/PaymentServiceHelper';
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
|
|
||||||
@@ -13,15 +14,22 @@ export default defineEventHandler(async event => {
|
|||||||
if (!data) return setResponseStatus(event, 400, 'Error decoding register_code');
|
if (!data) return setResponseStatus(event, 400, 'Error decoding register_code');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await PasswordModel.create({ email: data.email, password: data.password })
|
await PasswordModel.updateOne({ email: data.email }, { password: data.password }, { upsert: true });
|
||||||
await UserModel.create({ email: data.email, given_name: '', name: 'EmailLogin', locale: '', picture: '', created_at: Date.now() });
|
|
||||||
|
const user = await UserModel.create({ email: data.email, given_name: '', name: 'EmailLogin', locale: '', picture: '', created_at: Date.now() });
|
||||||
|
|
||||||
|
const [ok, error] = await PaymentServiceHelper.create_customer(user.id);
|
||||||
|
if (!ok) throw error;
|
||||||
|
|
||||||
setImmediate(() => {
|
setImmediate(() => {
|
||||||
const emailData = EmailService.getEmailServerInfo('welcome', { target: data.email });
|
const emailData = EmailService.getEmailServerInfo('welcome', { target: data.email });
|
||||||
EmailServiceHelper.sendEmail(emailData);
|
EmailServiceHelper.sendEmail(emailData);
|
||||||
});
|
});
|
||||||
|
|
||||||
const jwt = createUserJwt({ email: data.email, name: 'EmailLogin' });
|
const jwt = createUserJwt({ email: data.email, name: 'EmailLogin' });
|
||||||
return sendRedirect(event, `https://dashboard.litlyx.com/jwt_login?jwt_login=${jwt}`);
|
return sendRedirect(event, `https://dashboard.litlyx.com/jwt_login?jwt_login=${jwt}`);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
console.error(ex);
|
||||||
return setResponseStatus(event, 400, 'Error creating user');
|
return setResponseStatus(event, 400, 'Error creating user');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
import { getPlanFromId } from "@data/PREMIUM";
|
import { getPlanFromId } from "@data/PREMIUM";
|
||||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
||||||
import StripeService from '~/server/services/StripeService';
|
// import StripeService from '~/server/services/StripeService';
|
||||||
|
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
|
|
||||||
const data = await getRequestDataOld(event, { requireSchema: false, allowGuests: false, allowLitlyx: false });
|
// const data = await getRequestDataOld(event, { requireSchema: false, allowGuests: false, allowLitlyx: false });
|
||||||
if (!data) return;
|
// if (!data) return;
|
||||||
|
|
||||||
const { project, pid } = data;
|
// const { project, pid } = data;
|
||||||
|
|
||||||
const body = await readBody(event);
|
// const body = await readBody(event);
|
||||||
|
|
||||||
const { planId } = body;
|
// const { planId } = body;
|
||||||
|
|
||||||
const PLAN = getPlanFromId(planId);
|
// const PLAN = getPlanFromId(planId);
|
||||||
|
|
||||||
if (!PLAN) {
|
// if (!PLAN) {
|
||||||
console.error('PLAN', planId, 'NOT EXIST');
|
// console.error('PLAN', planId, 'NOT EXIST');
|
||||||
return setResponseStatus(event, 400, 'Plan not exist');
|
// return setResponseStatus(event, 400, 'Plan not exist');
|
||||||
}
|
// }
|
||||||
|
|
||||||
const intent = await StripeService.createOnetimePayment(
|
// const intent = await StripeService.createOnetimePayment(
|
||||||
StripeService.testMode ? PLAN.PRICE_TEST : PLAN.PRICE,
|
// StripeService.testMode ? PLAN.PRICE_TEST : PLAN.PRICE,
|
||||||
'https://dashboard.litlyx.com/payment_ok',
|
// 'https://dashboard.litlyx.com/payment_ok',
|
||||||
pid,
|
// pid,
|
||||||
project.customer_id
|
// project.customer_id
|
||||||
)
|
// )
|
||||||
|
|
||||||
if (!intent) {
|
// if (!intent) {
|
||||||
console.error('Cannot create Intent', { plan: PLAN });
|
// console.error('Cannot create Intent', { plan: PLAN });
|
||||||
return setResponseStatus(event, 400, 'Cannot create intent');
|
// return setResponseStatus(event, 400, 'Cannot create intent');
|
||||||
}
|
// }
|
||||||
|
|
||||||
return intent.url;
|
// return intent.url;
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { getPlanFromId } from "@data/PREMIUM";
|
import { getPlanFromId } from "@data/PREMIUM";
|
||||||
import StripeService from '~/server/services/StripeService';
|
// import StripeService from '~/server/services/StripeService';
|
||||||
|
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
@@ -9,29 +9,30 @@ export default defineEventHandler(async event => {
|
|||||||
|
|
||||||
const { project, pid } = data;
|
const { project, pid } = data;
|
||||||
|
|
||||||
const body = await readBody(event);
|
// const body = await readBody(event);
|
||||||
|
|
||||||
const { planId } = body;
|
// const { planId } = body;
|
||||||
|
|
||||||
const PLAN = getPlanFromId(planId);
|
// const PLAN = getPlanFromId(planId);
|
||||||
|
|
||||||
if (!PLAN) {
|
// if (!PLAN) {
|
||||||
console.error('PLAN', planId, 'NOT EXIST');
|
// console.error('PLAN', planId, 'NOT EXIST');
|
||||||
return setResponseStatus(event, 400, 'Plan not exist');
|
// return setResponseStatus(event, 400, 'Plan not exist');
|
||||||
}
|
// }
|
||||||
|
|
||||||
const checkout = await StripeService.createPayment(
|
// const checkout = await StripeService.createPayment(
|
||||||
StripeService.testMode ? PLAN.PRICE_TEST : PLAN.PRICE,
|
// StripeService.testMode ? PLAN.PRICE_TEST : PLAN.PRICE,
|
||||||
'https://dashboard.litlyx.com/payment_ok',
|
// 'https://dashboard.litlyx.com/payment_ok',
|
||||||
pid,
|
// pid,
|
||||||
project.customer_id
|
// project.customer_id
|
||||||
);
|
// );
|
||||||
|
|
||||||
if (!checkout) {
|
// if (!checkout) {
|
||||||
console.error('Cannot create payment', { plan: PLAN });
|
// console.error('Cannot create payment', { plan: PLAN });
|
||||||
return setResponseStatus(event, 400, 'Cannot create payment');
|
// return setResponseStatus(event, 400, 'Cannot create payment');
|
||||||
}
|
// }
|
||||||
|
|
||||||
return checkout.url;
|
// return checkout.url;
|
||||||
|
return '';
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import StripeService from '~/server/services/StripeService';
|
import { PaymentServiceHelper } from '~/server/services/PaymentServiceHelper';
|
||||||
import { PremiumModel } from '~/shared/schema/PremiumSchema';
|
import { PremiumModel } from '~/shared/schema/PremiumSchema';
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
@@ -10,9 +10,9 @@ export default defineEventHandler(async event => {
|
|||||||
const premium = await PremiumModel.findOne({ user_id: data.user.id })
|
const premium = await PremiumModel.findOne({ user_id: data.user.id })
|
||||||
if (!premium) return;
|
if (!premium) return;
|
||||||
|
|
||||||
const customer = await StripeService.getCustomer(premium.customer_id);
|
const [ok, customerInfoOrError] = await PaymentServiceHelper.customer_info(data.user.id);
|
||||||
if (customer?.deleted) return;
|
if (!ok) throw customerInfoOrError;
|
||||||
|
|
||||||
return customer?.address;
|
return customerInfoOrError;
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
|
||||||
import { Redis } from "~/server/services/CacheService";
|
import { Redis } from "~/server/services/CacheService";
|
||||||
import StripeService from '~/server/services/StripeService';
|
import { PaymentServiceHelper } from "~/server/services/PaymentServiceHelper";
|
||||||
import { PremiumModel } from "~/shared/schema/PremiumSchema";
|
import { PremiumModel } from "~/shared/schema/PremiumSchema";
|
||||||
|
|
||||||
|
|
||||||
export type InvoiceData = {
|
export type InvoiceData = {
|
||||||
date: number,
|
date: number,
|
||||||
cost: number,
|
cost: number,
|
||||||
@@ -22,10 +20,13 @@ export default defineEventHandler(async event => {
|
|||||||
const premium = await PremiumModel.findOne({ user_id: data.user.id });
|
const premium = await PremiumModel.findOne({ user_id: data.user.id });
|
||||||
if (!premium) return [];
|
if (!premium) return [];
|
||||||
|
|
||||||
const invoices = await StripeService.getInvoices(premium.customer_id);
|
const [ok, invoicesOrError] = await PaymentServiceHelper.invoices_list(data.user.id);
|
||||||
if (!invoices) return [];
|
if (!ok) {
|
||||||
|
console.error(invoicesOrError);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return invoices?.data.map(e => {
|
return invoicesOrError.invoices.map(e => {
|
||||||
const result: InvoiceData = {
|
const result: InvoiceData = {
|
||||||
link: e.invoice_pdf || '',
|
link: e.invoice_pdf || '',
|
||||||
id: e.number || '',
|
id: e.number || '',
|
||||||
@@ -36,7 +37,6 @@ export default defineEventHandler(async event => {
|
|||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { getPlanFromId, PREMIUM_PLAN } from "@data/PREMIUM";
|
import { getPlanFromId, PREMIUM_PLAN } from "@data/PREMIUM";
|
||||||
import { canTryAppsumoCode, checkAppsumoCode, useAppsumoCode, useTryAppsumoCode } from "~/server/services/AppsumoService";
|
import { canTryAppsumoCode, checkAppsumoCode, useAppsumoCode, useTryAppsumoCode } from "~/server/services/AppsumoService";
|
||||||
import StripeService from '~/server/services/StripeService';
|
|
||||||
|
|
||||||
function getPlanToActivate(current_plan_id: number) {
|
function getPlanToActivate(current_plan_id: number) {
|
||||||
if (current_plan_id === PREMIUM_PLAN.FREE.ID) {
|
if (current_plan_id === PREMIUM_PLAN.FREE.ID) {
|
||||||
@@ -38,13 +38,13 @@ export default defineEventHandler(async event => {
|
|||||||
const valid = await checkAppsumoCode(code);
|
const valid = await checkAppsumoCode(code);
|
||||||
if (!valid) return setResponseStatus(event, 400, 'Code not valid');
|
if (!valid) return setResponseStatus(event, 400, 'Code not valid');
|
||||||
|
|
||||||
const currentPlan = getPlanFromId(project.premium_type);
|
// const currentPlan = getPlanFromId(project.premium_type);
|
||||||
if (!currentPlan) return setResponseStatus(event, 400, 'Current plan not found');
|
// if (!currentPlan) return setResponseStatus(event, 400, 'Current plan not found');
|
||||||
const planToActivate = getPlanToActivate(currentPlan.ID);
|
// const planToActivate = getPlanToActivate(currentPlan.ID);
|
||||||
if (!planToActivate) return setResponseStatus(event, 400, 'Cannot use code on current plan');
|
// if (!planToActivate) return setResponseStatus(event, 400, 'Cannot use code on current plan');
|
||||||
|
|
||||||
await StripeService.createSubscription(project.customer_id, planToActivate.ID);
|
// await StripeService.createSubscription(project.customer_id, planToActivate.ID);
|
||||||
|
|
||||||
await useAppsumoCode(pid, code);
|
// await useAppsumoCode(pid, code);
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1,19 +1,17 @@
|
|||||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
import { PaymentServiceHelper } from '~/server/services/PaymentServiceHelper';
|
||||||
import StripeService from '~/server/services/StripeService';
|
import { PremiumModel } from '~/shared/schema/PremiumSchema';
|
||||||
|
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
|
|
||||||
const data = await getRequestData(event, []);
|
const data = await getRequestData(event, []);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
const { project } = data;
|
const premium = await PremiumModel.findOne({ user_id: data.user.id })
|
||||||
|
if (!premium) return;
|
||||||
if (!project.customer_id) return setResponseStatus(event, 400, 'Project has no customer_id');
|
|
||||||
|
|
||||||
const body = await readBody(event);
|
const body = await readBody(event);
|
||||||
const res = await StripeService.setCustomerInfo(project.customer_id, body);
|
|
||||||
|
|
||||||
return { ok: true, data: res }
|
return await PaymentServiceHelper.update_customer_info(data.user.id, body);
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ProjectModel, TProject } from "@schema/project/ProjectSchema";
|
import { ProjectModel, TProject } from "@schema/project/ProjectSchema";
|
||||||
import { ProjectCountModel } from "@schema/project/ProjectsCounts";
|
import { ProjectCountModel } from "@schema/project/ProjectsCounts";
|
||||||
import StripeService from '~/server/services/StripeService';
|
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
|
|
||||||
@@ -19,58 +18,12 @@ export default defineEventHandler(async event => {
|
|||||||
const existingUserProjects = await ProjectModel.countDocuments({ owner: userData.id });
|
const existingUserProjects = await ProjectModel.countDocuments({ owner: userData.id });
|
||||||
if (existingUserProjects >= maxProjects) return setResponseStatus(event, 400, 'Already have max number of projects');
|
if (existingUserProjects >= maxProjects) return setResponseStatus(event, 400, 'Already have max number of projects');
|
||||||
|
|
||||||
if (StripeService.isDisabled()) {
|
const project = await ProjectModel.create({ owner: userData.id, name: newProjectName });
|
||||||
|
|
||||||
const project = await ProjectModel.create({
|
await ProjectCountModel.create({ project_id: project._id, events: 0, visits: 0, sessions: 0 });
|
||||||
owner: userData.id,
|
|
||||||
name: newProjectName,
|
|
||||||
premium: false,
|
|
||||||
premium_type: 0,
|
|
||||||
customer_id: 'DISABLED_MODE',
|
|
||||||
subscription_id: "DISABLED_MODE",
|
|
||||||
premium_expire_at: new Date(3000, 1, 1)
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
await ProjectCountModel.create({
|
|
||||||
project_id: project._id,
|
|
||||||
events: 0,
|
|
||||||
visits: 0,
|
|
||||||
sessions: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
return project.toJSON() as TProject;
|
return project.toJSON() as TProject;
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
const customer = await StripeService.createCustomer(userData.user.email);
|
|
||||||
if (!customer) return setResponseStatus(event, 400, 'Error creating customer');
|
|
||||||
|
|
||||||
const subscription = await StripeService.createFreeSubscription(customer.id);
|
|
||||||
if (!subscription) return setResponseStatus(event, 400, 'Error creating subscription');
|
|
||||||
|
|
||||||
const project = await ProjectModel.create({
|
|
||||||
owner: userData.id,
|
|
||||||
name: newProjectName,
|
|
||||||
premium: false,
|
|
||||||
premium_type: 0,
|
|
||||||
customer_id: customer.id,
|
|
||||||
subscription_id: subscription.id,
|
|
||||||
premium_expire_at: subscription.current_period_end * 1000
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
await ProjectCountModel.create({
|
|
||||||
project_id: project._id,
|
|
||||||
events: 0,
|
|
||||||
visits: 0,
|
|
||||||
sessions: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
return project.toJSON() as TProject;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { UserLimitModel } from "@schema/UserLimitSchema";
|
import { UserLimitModel } from "@schema/UserLimitSchema";
|
||||||
import StripeService from '~/server/services/StripeService';
|
|
||||||
import { PremiumModel } from "~/shared/schema/PremiumSchema";
|
import { PremiumModel } from "~/shared/schema/PremiumSchema";
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
@@ -22,14 +21,12 @@ export default defineEventHandler(async event => {
|
|||||||
billing_expire_at: userLimits.billing_expire_at,
|
billing_expire_at: userLimits.billing_expire_at,
|
||||||
limit: userLimits.limit,
|
limit: userLimits.limit,
|
||||||
count: userLimits.events + userLimits.visits,
|
count: userLimits.events + userLimits.visits,
|
||||||
subscription_status: StripeService.isDisabled() ? 'Disabled mode' : ('One time payment')
|
subscription_status: 'One time'
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const subscription = await StripeService.getSubscription(premium.subscription_id);
|
|
||||||
|
|
||||||
const userLimits = await UserLimitModel.findOne({ user_id: data.user.id });
|
const userLimits = await UserLimitModel.findOne({ user_id: data.user.id });
|
||||||
if (!userLimits) return setResponseStatus(event, 400, 'User limits not found');
|
if (!userLimits) return setResponseStatus(event, 400, 'User limits not found');
|
||||||
|
|
||||||
@@ -41,7 +38,7 @@ export default defineEventHandler(async event => {
|
|||||||
billing_expire_at: userLimits.billing_expire_at,
|
billing_expire_at: userLimits.billing_expire_at,
|
||||||
limit: userLimits.limit,
|
limit: userLimits.limit,
|
||||||
count: userLimits.events + userLimits.visits,
|
count: userLimits.events + userLimits.visits,
|
||||||
subscription_status: StripeService.isDisabled() ? 'Disabled mode' : (subscription?.status ?? '?')
|
subscription_status: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { UserSettingsModel } from "@schema/UserSettings";
|
|||||||
import { AiChatModel } from "@schema/ai/AiChatSchema";
|
import { AiChatModel } from "@schema/ai/AiChatSchema";
|
||||||
import { LimitNotifyModel } from "@schema/broker/LimitNotifySchema";
|
import { LimitNotifyModel } from "@schema/broker/LimitNotifySchema";
|
||||||
import { SessionModel } from "@schema/metrics/SessionSchema";
|
import { SessionModel } from "@schema/metrics/SessionSchema";
|
||||||
import StripeService from "~/server/services/StripeService";
|
|
||||||
import { UserModel } from "@schema/UserSchema";
|
import { UserModel } from "@schema/UserSchema";
|
||||||
import { AddressBlacklistModel } from "~/shared/schema/shields/AddressBlacklistSchema";
|
import { AddressBlacklistModel } from "~/shared/schema/shields/AddressBlacklistSchema";
|
||||||
import { DomainWhitelistModel } from "~/shared/schema/shields/DomainWhitelistSchema";
|
import { DomainWhitelistModel } from "~/shared/schema/shields/DomainWhitelistSchema";
|
||||||
@@ -36,7 +35,7 @@ export default defineEventHandler(async event => {
|
|||||||
const limitdeletation = await UserLimitModel.deleteMany({ user_id: userData.id });
|
const limitdeletation = await UserLimitModel.deleteMany({ user_id: userData.id });
|
||||||
const notifiesDeletation = await LimitNotifyModel.deleteMany({ user_id: userData.id });
|
const notifiesDeletation = await LimitNotifyModel.deleteMany({ user_id: userData.id });
|
||||||
|
|
||||||
await StripeService.deleteCustomer(premium.customer_id);
|
// await StripeService.deleteCustomer(premium.customer_id);
|
||||||
|
|
||||||
for (const project of projects) {
|
for (const project of projects) {
|
||||||
const project_id = project._id;
|
const project_id = project._id;
|
||||||
@@ -52,11 +51,10 @@ export default defineEventHandler(async event => {
|
|||||||
const countryBlacklistDeletation = await CountryBlacklistModel.deleteMany({ project_id });
|
const countryBlacklistDeletation = await CountryBlacklistModel.deleteMany({ project_id });
|
||||||
const domainWhitelistDeletation = await DomainWhitelistModel.deleteMany({ project_id });
|
const domainWhitelistDeletation = await DomainWhitelistModel.deleteMany({ project_id });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const userDeletation = await UserModel.deleteOne({ _id: userData.id });
|
const userDeletation = await UserModel.deleteOne({ _id: userData.id });
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import mongoose from "mongoose";
|
import mongoose from "mongoose";
|
||||||
import { Redis } from "~/server/services/CacheService";
|
import { Redis } from "~/server/services/CacheService";
|
||||||
import StripeService from '~/server/services/StripeService';
|
|
||||||
import { logger } from "./Logger";
|
import { logger } from "./Logger";
|
||||||
|
|
||||||
|
|
||||||
@@ -13,16 +12,6 @@ export default async () => {
|
|||||||
|
|
||||||
logger.info('[SERVER] Initializing');
|
logger.info('[SERVER] Initializing');
|
||||||
|
|
||||||
if (config.STRIPE_SECRET) {
|
|
||||||
const TEST_MODE = config.MODE === 'TEST';
|
|
||||||
StripeService.init(config.STRIPE_SECRET, config.STRIPE_WH_SECRET, TEST_MODE);
|
|
||||||
logger.info('[STRIPE] Initialized');
|
|
||||||
} else {
|
|
||||||
StripeService.disable();
|
|
||||||
logger.warn('[STRIPE] No stripe key - Disabled mode');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!connection || connection.connection.readyState == mongoose.ConnectionStates.disconnected) {
|
if (!connection || connection.connection.readyState == mongoose.ConnectionStates.disconnected) {
|
||||||
logger.info('[DATABASE] Connecting');
|
logger.info('[DATABASE] Connecting');
|
||||||
connection = await mongoose.connect(config.MONGO_CONNECTION_STRING);
|
connection = await mongoose.connect(config.MONGO_CONNECTION_STRING);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { AiChatModel } from '@schema/ai/AiChatSchema';
|
|||||||
import { AiEventsInstance } from '../ai/functions/AI_Events';
|
import { AiEventsInstance } from '../ai/functions/AI_Events';
|
||||||
import { AiVisitsInstance } from '../ai/functions/AI_Visits';
|
import { AiVisitsInstance } from '../ai/functions/AI_Visits';
|
||||||
import { AiSessionsInstance } from '../ai/functions/AI_Sessions';
|
import { AiSessionsInstance } from '../ai/functions/AI_Sessions';
|
||||||
import { AiBillingInstance } from '../ai/functions/AI_Billing';
|
|
||||||
import { AiSnapshotInstance } from '../ai/functions/AI_Snapshots';
|
import { AiSnapshotInstance } from '../ai/functions/AI_Snapshots';
|
||||||
import { AiComposableChartInstance } from '../ai/functions/AI_ComposableChart';
|
import { AiComposableChartInstance } from '../ai/functions/AI_ComposableChart';
|
||||||
|
|
||||||
@@ -20,7 +19,6 @@ const tools: OpenAI.Chat.Completions.ChatCompletionTool[] = [
|
|||||||
...AiVisitsInstance.getTools(),
|
...AiVisitsInstance.getTools(),
|
||||||
...AiEventsInstance.getTools(),
|
...AiEventsInstance.getTools(),
|
||||||
...AiSessionsInstance.getTools(),
|
...AiSessionsInstance.getTools(),
|
||||||
...AiBillingInstance.getTools(),
|
|
||||||
...AiSnapshotInstance.getTools(),
|
...AiSnapshotInstance.getTools(),
|
||||||
...AiComposableChartInstance.getTools(),
|
...AiComposableChartInstance.getTools(),
|
||||||
]
|
]
|
||||||
@@ -30,7 +28,6 @@ const functions: any = {
|
|||||||
...AiVisitsInstance.getHandlers(),
|
...AiVisitsInstance.getHandlers(),
|
||||||
...AiEventsInstance.getHandlers(),
|
...AiEventsInstance.getHandlers(),
|
||||||
...AiSessionsInstance.getHandlers(),
|
...AiSessionsInstance.getHandlers(),
|
||||||
...AiBillingInstance.getHandlers(),
|
|
||||||
...AiSnapshotInstance.getHandlers(),
|
...AiSnapshotInstance.getHandlers(),
|
||||||
...AiComposableChartInstance.getHandlers()
|
...AiComposableChartInstance.getHandlers()
|
||||||
}
|
}
|
||||||
|
|||||||
50
dashboard/server/services/PaymentServiceHelper.ts
Normal file
50
dashboard/server/services/PaymentServiceHelper.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
const { PAYMENT_SECRET } = useRuntimeConfig();
|
||||||
|
|
||||||
|
type ErrorResponse = [false, Error];
|
||||||
|
type OkResponse<T> = [true, T];
|
||||||
|
|
||||||
|
type PaymentServiceResponse<T> = Promise<OkResponse<T> | ErrorResponse>
|
||||||
|
|
||||||
|
export class PaymentServiceHelper {
|
||||||
|
|
||||||
|
static BASE_URL = 'https://test-payments.litlyx.com/payment';
|
||||||
|
|
||||||
|
private static async send(endpoint: string, body: Record<string, any>): PaymentServiceResponse<any> {
|
||||||
|
try {
|
||||||
|
const res = await $fetch(`${this.BASE_URL}${endpoint}`, {
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'x-litlyx-token': PAYMENT_SECRET
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return [true, res];
|
||||||
|
} catch (ex: any) {
|
||||||
|
console.error(ex);
|
||||||
|
return [false, ex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async create_customer(user_id: string): PaymentServiceResponse<{ ok: true }> {
|
||||||
|
return await this.send('/create_customer', { user_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async create_payment(user_id: string, plan_id: number): PaymentServiceResponse<{ url: string }> {
|
||||||
|
return await this.send('/create_payment', { user_id, plan_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async invoices_list(user_id: string): PaymentServiceResponse<{ invoices: any[] }> {
|
||||||
|
return await this.send('/invoices_list', { user_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async customer_info(user_id: string): PaymentServiceResponse<any> {
|
||||||
|
return await this.send('/customer_info', { user_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update_customer_info(user_id: string, address: { line1: string, line2: string, city: string, country: string, postal_code: string, state: string }): PaymentServiceResponse<{ ok: true }> {
|
||||||
|
return await this.send('/update_customer_info', { user_id, address });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
import { getPlanFromId, getPlanFromTag, PREMIUM_TAG } from '@data/PREMIUM';
|
|
||||||
import Stripe from 'stripe';
|
|
||||||
|
|
||||||
class StripeService {
|
|
||||||
private stripe?: Stripe;
|
|
||||||
private privateKey?: string;
|
|
||||||
private webhookSecret?: string;
|
|
||||||
public testMode?: boolean;
|
|
||||||
private disabledMode: boolean = false;
|
|
||||||
|
|
||||||
init(privateKey: string, webhookSecret: string, testMode: boolean = false) {
|
|
||||||
this.privateKey = privateKey;
|
|
||||||
this.webhookSecret = webhookSecret;
|
|
||||||
this.stripe = new Stripe(this.privateKey);
|
|
||||||
this.testMode = testMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
disable() { this.disabledMode = true; }
|
|
||||||
enable() { this.disabledMode = false; }
|
|
||||||
isDisabled() { return this.disabledMode; }
|
|
||||||
|
|
||||||
parseWebhook(body: any, sig: string) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
if (!this.webhookSecret) {
|
|
||||||
console.error('Stripe not initialized')
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return this.stripe.webhooks.constructEvent(body, sig, this.webhookSecret);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async createOnetimePayment(price: string, success_url: string, pid: string, customer?: string) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
|
|
||||||
const checkout = await this.stripe.checkout.sessions.create({
|
|
||||||
allow_promotion_codes: true,
|
|
||||||
payment_method_types: ['card'],
|
|
||||||
invoice_creation: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
line_items: [
|
|
||||||
{ price, quantity: 1 }
|
|
||||||
],
|
|
||||||
payment_intent_data: {
|
|
||||||
metadata: {
|
|
||||||
pid, price
|
|
||||||
}
|
|
||||||
},
|
|
||||||
customer,
|
|
||||||
success_url,
|
|
||||||
mode: 'payment'
|
|
||||||
});
|
|
||||||
|
|
||||||
return checkout;
|
|
||||||
}
|
|
||||||
|
|
||||||
async createPayment(price: string, success_url: string, pid: string, customer?: string) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
|
|
||||||
const checkout = await this.stripe.checkout.sessions.create({
|
|
||||||
allow_promotion_codes: true,
|
|
||||||
payment_method_types: ['card'],
|
|
||||||
line_items: [
|
|
||||||
{ price, quantity: 1 }
|
|
||||||
],
|
|
||||||
subscription_data: {
|
|
||||||
metadata: { pid },
|
|
||||||
},
|
|
||||||
customer,
|
|
||||||
success_url,
|
|
||||||
mode: 'subscription'
|
|
||||||
});
|
|
||||||
|
|
||||||
return checkout;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getPriceData(priceId: string) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
const priceData = await this.stripe.prices.retrieve(priceId);
|
|
||||||
return priceData;
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteSubscription(subscriptionId: string) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
const subscription = await this.stripe.subscriptions.cancel(subscriptionId);
|
|
||||||
return subscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getSubscription(subscriptionId: string) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
const subscription = await this.stripe.subscriptions.retrieve(subscriptionId);
|
|
||||||
return subscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAllSubscriptions(customer_id: string) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
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) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
const invoices = await this.stripe?.invoices.list({ customer: customer_id });
|
|
||||||
return invoices;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCustomer(customer_id: string) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
const customer = await this.stripe.customers.retrieve(customer_id, { expand: [] })
|
|
||||||
return customer;
|
|
||||||
}
|
|
||||||
|
|
||||||
async createCustomer(email: string) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
const customer = await this.stripe.customers.create({ email });
|
|
||||||
return customer;
|
|
||||||
}
|
|
||||||
|
|
||||||
async setCustomerInfo(customer_id: string, address: { line1: string, line2: string, city: string, country: string, postal_code: string, state: string }) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
const customer = await this.stripe.customers.update(customer_id, {
|
|
||||||
address: {
|
|
||||||
line1: address.line1,
|
|
||||||
line2: address.line2,
|
|
||||||
city: address.city,
|
|
||||||
country: address.country,
|
|
||||||
postal_code: address.postal_code,
|
|
||||||
state: address.state
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return customer.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteCustomer(customer_id: string) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
const { deleted } = await this.stripe.customers.del(customer_id);
|
|
||||||
return deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
async createStripeCode(plan: PREMIUM_TAG) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
|
|
||||||
const INCUBATION_COUPON = 'sDD7Weh3';
|
|
||||||
|
|
||||||
if (plan === 'INCUBATION') {
|
|
||||||
await this.stripe.promotionCodes.create({
|
|
||||||
coupon: INCUBATION_COUPON,
|
|
||||||
active: true,
|
|
||||||
code: 'TESTCACCA1',
|
|
||||||
max_redemptions: 1,
|
|
||||||
})
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async createSubscription(customer_id: string, planId: number) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
|
|
||||||
const PLAN = getPlanFromId(planId);
|
|
||||||
if (!PLAN) throw Error('Plan not found');
|
|
||||||
|
|
||||||
const subscription = await this.stripe.subscriptions.create({
|
|
||||||
customer: customer_id,
|
|
||||||
items: [
|
|
||||||
{ price: this.testMode ? PLAN.PRICE_TEST : PLAN.PRICE, quantity: 1 }
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
return subscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
async createOneTimeSubscriptionDummy(customer_id: string, planId: number) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
|
|
||||||
const PLAN = getPlanFromId(planId);
|
|
||||||
if (!PLAN) throw Error('Plan not found');
|
|
||||||
|
|
||||||
const subscription = await this.stripe.subscriptions.create({
|
|
||||||
customer: customer_id,
|
|
||||||
items: [
|
|
||||||
{ price: this.testMode ? PLAN.PRICE_TEST : PLAN.PRICE, quantity: 1 }
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
return subscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
async createFreeSubscription(customer_id: string) {
|
|
||||||
if (this.disabledMode) return;
|
|
||||||
if (!this.stripe) throw Error('Stripe not initialized');
|
|
||||||
|
|
||||||
const FREE_PLAN = getPlanFromTag('FREE');
|
|
||||||
|
|
||||||
const subscription = await this.stripe.subscriptions.create({
|
|
||||||
customer: customer_id,
|
|
||||||
items: [
|
|
||||||
{ price: this.testMode ? FREE_PLAN.PRICE_TEST : FREE_PLAN.PRICE, quantity: 1 }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
return subscription;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const instance = new StripeService();
|
|
||||||
export default instance;
|
|
||||||
@@ -80,7 +80,10 @@ export async function onPaymentSuccess(event: Event.InvoicePaidEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await addSubscriptionToUser(premiumData.user_id.toString(), plan, subscription_id, event.data.object.period_start, event.data.object.period_end);
|
await addSubscriptionToUser(premiumData.user_id.toString(), plan, subscription_id,
|
||||||
|
event.data.object.lines.data[0].period.start,
|
||||||
|
event.data.object.lines.data[0].period.end
|
||||||
|
);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (plan.ID == 0) return;
|
if (plan.ID == 0) return;
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ console.log('Stripe started in', STRIPE_TESTMODE ? 'TESTMODE' : 'LIVEMODE');
|
|||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
console.log(req.path);
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
|
||||||
app.use('/webhook', webhookRouter);
|
app.use('/webhook', webhookRouter);
|
||||||
app.use('/payment', paymentRouter);
|
app.use('/payment', paymentRouter);
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,41 @@ import StripeService from '../services/StripeService';
|
|||||||
import { sendJson } from '../Utils';
|
import { sendJson } from '../Utils';
|
||||||
import { PremiumModel } from '../shared/schema/PremiumSchema';
|
import { PremiumModel } from '../shared/schema/PremiumSchema';
|
||||||
import { Types } from 'mongoose';
|
import { Types } from 'mongoose';
|
||||||
|
import { UserModel } from '../shared/schema/UserSchema';
|
||||||
|
|
||||||
export const paymentRouter = Router();
|
export const paymentRouter = Router();
|
||||||
|
|
||||||
|
export const ZBodyCreateCustomer = z.object({
|
||||||
|
user_id: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
paymentRouter.post('/create_customer', json(), async (req, res) => {
|
||||||
|
try {
|
||||||
|
const createCustomerData = ZBodyCreateCustomer.parse(req.body);
|
||||||
|
const user = await UserModel.findOne({ _id: createCustomerData.user_id });
|
||||||
|
if (!user) return sendJson(res, 400, { error: 'user not found' });
|
||||||
|
|
||||||
|
const customer = await StripeService.createCustomer(user.email);
|
||||||
|
const freesub = await StripeService.createFreeSubscription(customer.id);
|
||||||
|
|
||||||
|
await PremiumModel.create({
|
||||||
|
user_id: user.id,
|
||||||
|
customer_id: customer.id,
|
||||||
|
premium_type: 0,
|
||||||
|
subscription_id: freesub.id
|
||||||
|
})
|
||||||
|
|
||||||
|
return sendJson(res, 200, { ok: true });
|
||||||
|
} catch (ex) {
|
||||||
|
res.status(500).json({ error: ex.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
export const ZBodyCreatePayment = z.object({
|
export const ZBodyCreatePayment = z.object({
|
||||||
user_id: z.string(),
|
user_id: z.string(),
|
||||||
plan_id: z.number()
|
plan_id: z.number()
|
||||||
})
|
});
|
||||||
|
|
||||||
paymentRouter.post('/create', json(), async (req, res) => {
|
paymentRouter.post('/create', json(), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
@@ -42,3 +69,81 @@ paymentRouter.post('/create', json(), async (req, res) => {
|
|||||||
res.status(500).json({ error: ex.message });
|
res.status(500).json({ error: ex.message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const ZBodyInvoicesList = z.object({
|
||||||
|
user_id: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
paymentRouter.post('/invoices_list', json(), async (req, res) => {
|
||||||
|
try {
|
||||||
|
const invoicesListData = ZBodyInvoicesList.parse(req.body);
|
||||||
|
|
||||||
|
const premiumData = await PremiumModel.findOne({ user_id: invoicesListData.user_id });
|
||||||
|
if (!premiumData) return sendJson(res, 400, { error: 'user not found' });
|
||||||
|
if (!premiumData.customer_id) return sendJson(res, 400, { error: 'user have no customer_id' });
|
||||||
|
|
||||||
|
const invoices = await StripeService.getInvoices(premiumData.customer_id);
|
||||||
|
return sendJson(res, 200, { invoices: invoices.data });
|
||||||
|
|
||||||
|
} catch (ex) {
|
||||||
|
res.status(500).json({ error: ex.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const ZBodyCustomerInfo = z.object({
|
||||||
|
user_id: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
paymentRouter.post('/customer_info', json(), async (req, res) => {
|
||||||
|
try {
|
||||||
|
const customerInfoData = ZBodyCustomerInfo.parse(req.body);
|
||||||
|
|
||||||
|
const premiumData = await PremiumModel.findOne({ user_id: customerInfoData.user_id });
|
||||||
|
if (!premiumData) return sendJson(res, 400, { error: 'user not found' });
|
||||||
|
if (!premiumData.customer_id) return sendJson(res, 400, { error: 'user have no customer_id' });
|
||||||
|
|
||||||
|
const customer = await StripeService.getCustomer(premiumData.customer_id);
|
||||||
|
if (!customer) return sendJson(res, 200, {});
|
||||||
|
if (customer.deleted === true) return sendJson(res, 200, {});
|
||||||
|
return sendJson(res, 200, customer.address);
|
||||||
|
|
||||||
|
} catch (ex) {
|
||||||
|
console.error(ex);
|
||||||
|
res.status(500).json({ error: ex.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const ZBodyUpdateCustomerInfo = z.object({
|
||||||
|
user_id: z.string(),
|
||||||
|
address: z.object({
|
||||||
|
line1: z.string(),
|
||||||
|
line2: z.string(),
|
||||||
|
city: z.string(),
|
||||||
|
country: z.string(),
|
||||||
|
postal_code: z.string(),
|
||||||
|
state: z.string()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
paymentRouter.post('/update_customer_info', json(), async (req, res) => {
|
||||||
|
try {
|
||||||
|
const updateCustomerInfoData = ZBodyUpdateCustomerInfo.parse(req.body);
|
||||||
|
|
||||||
|
const premiumData = await PremiumModel.findOne({ user_id: updateCustomerInfoData.user_id });
|
||||||
|
if (!premiumData) return sendJson(res, 400, { error: 'user not found' });
|
||||||
|
if (!premiumData.customer_id) return sendJson(res, 400, { error: 'user have no customer_id' });
|
||||||
|
await StripeService.setCustomerInfo(
|
||||||
|
premiumData.customer_id,
|
||||||
|
updateCustomerInfoData.address as any
|
||||||
|
);
|
||||||
|
return sendJson(res, 200, { ok: true });
|
||||||
|
|
||||||
|
} catch (ex) {
|
||||||
|
res.status(500).json({ error: ex.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user