mirror of
https://github.com/Litlyx/litlyx
synced 2026-02-04 22:52:19 +01:00
add payment service
This commit is contained in:
6
payments/src/Utils.ts
Normal file
6
payments/src/Utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { Response } from "express";
|
||||
|
||||
|
||||
export function sendJson(res: Response, status: number, data: Record<string, any>): void {
|
||||
res.status(status).json(data);
|
||||
}
|
||||
0
payments/src/index.ts
Normal file
0
payments/src/index.ts
Normal file
43
payments/src/routers/PaymentRouter.ts
Normal file
43
payments/src/routers/PaymentRouter.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { json, Router } from 'express';
|
||||
import z from 'zod';
|
||||
import { getPlanFromId } from '../shared/data/PLANS';
|
||||
import StripeService from '../services/StripeService';
|
||||
import { sendJson } from '../Utils';
|
||||
import { ProjectModel } from '../shared/schema/project/ProjectSchema';
|
||||
|
||||
export const paymentRouter = Router();
|
||||
|
||||
|
||||
export const ZBodyCreatePayment = z.object({
|
||||
pid: z.string(),
|
||||
plan_id: z.number()
|
||||
})
|
||||
|
||||
paymentRouter.post('/create', json(), async (req, res) => {
|
||||
try {
|
||||
const createPaymentData = ZBodyCreatePayment.parse(req.body);
|
||||
|
||||
const plan = getPlanFromId(createPaymentData.plan_id);
|
||||
if (!plan) return sendJson(res, 400, { error: 'plan not found' });
|
||||
|
||||
const project = await ProjectModel.findById(createPaymentData.pid);
|
||||
if (!project) return sendJson(res, 400, { error: 'project not found' });
|
||||
if (!project.customer_id) return sendJson(res, 400, { error: 'project have no customer_id' });
|
||||
|
||||
const price = StripeService.testMode ? plan.PRICE_TEST : plan.PRICE;
|
||||
|
||||
const checkout = await StripeService.createPayment(
|
||||
price,
|
||||
'https://dashboard.litlyx.com/payment_ok',
|
||||
createPaymentData.pid,
|
||||
project.customer_id
|
||||
);
|
||||
|
||||
if (!checkout) return sendJson(res, 400, { error: 'cannot create payment' });
|
||||
|
||||
return sendJson(res, 200, { url: checkout.url });
|
||||
|
||||
} catch (ex) {
|
||||
res.status(500).json({ error: ex.message });
|
||||
}
|
||||
});
|
||||
19
payments/src/routers/WebhookRouter.ts
Normal file
19
payments/src/routers/WebhookRouter.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
import { json, Router } from 'express';
|
||||
|
||||
export const webhookRouter = Router();
|
||||
|
||||
|
||||
webhookRouter.get('/', json(), async (req, res) => {
|
||||
try {
|
||||
|
||||
const signature = req.header('stripe-signature');
|
||||
if (!signature) {
|
||||
console.error('No signature on the webhook')
|
||||
}
|
||||
|
||||
|
||||
} catch (ex) {
|
||||
res.status(500).json({ error: ex.message });
|
||||
}
|
||||
});
|
||||
209
payments/src/services/StripeService.ts
Normal file
209
payments/src/services/StripeService.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
|
||||
import Stripe from "stripe";
|
||||
|
||||
|
||||
|
||||
class StripeService {
|
||||
private stripe?: Stripe;
|
||||
private privateKey?: string;
|
||||
private webhookSecret?: string;
|
||||
public testMode?: boolean;
|
||||
|
||||
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) {
|
||||
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.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.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.stripe) throw Error('Stripe not initialized');
|
||||
const priceData = await this.stripe.prices.retrieve(priceId);
|
||||
return priceData;
|
||||
}
|
||||
|
||||
async deleteSubscription(subscriptionId: string) {
|
||||
if (!this.stripe) throw Error('Stripe not initialized');
|
||||
const subscription = await this.stripe.subscriptions.cancel(subscriptionId);
|
||||
return subscription;
|
||||
}
|
||||
|
||||
async getSubscription(subscriptionId: string) {
|
||||
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.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.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.stripe) throw Error('Stripe not initialized');
|
||||
const customer = await this.stripe.customers.retrieve(customer_id, { expand: [] })
|
||||
return customer;
|
||||
}
|
||||
|
||||
async createCustomer(email: string) {
|
||||
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.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.stripe) throw Error('Stripe not initialized');
|
||||
const { deleted } = await this.stripe.customers.del(customer_id);
|
||||
return deleted;
|
||||
}
|
||||
|
||||
// async createStripeCode(plan: PREMIUM_TAG) {
|
||||
// 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;
|
||||
Reference in New Issue
Block a user