Files
litlyx/payments/src/services/StripeService.ts
2025-11-28 16:49:20 +01:00

243 lines
8.6 KiB
TypeScript

import Stripe from "stripe";
import { getPlanFromTag } from "../shared/data/PLANS";
import { PremiumModel } from "../shared/schema/PremiumSchema";
class StripeService {
private stripe?: Stripe;
private privateKey?: string;
private webhookSecret?: string;
public testMode?: boolean;
init(privateKey: string, webhookSecret: string) {
this.privateKey = privateKey;
this.webhookSecret = webhookSecret;
this.stripe = new Stripe(this.privateKey);
this.testMode = this.privateKey.startsWith('sk_test');
}
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, user_id: 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: { user_id },
},
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 getCustomerPaymentMethods(customer_id: string) {
if (!this.stripe) throw Error('Stripe not initialized');
const paymentMethods = await this.stripe.customers.listPaymentMethods(customer_id);
return paymentMethods;
}
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 createSubscription(customer_id: string, planTag: string) {
if (!this.stripe) throw Error('Stripe not initialized');
const PLAN_DATA = getPlanFromTag(planTag as any);
if (!PLAN_DATA) throw Error('Plan not found');
const subscription = await this.stripe.subscriptions.create({
customer: customer_id,
items: [
{ price: this.testMode ? PLAN_DATA.PRICE_TEST : PLAN_DATA.PRICE, quantity: 1 }
],
});
return subscription;
}
async getPreviewUpgrade(customer_id: string, subscription_id: string, planTag: string) {
if (!this.stripe) throw Error('Stripe not initialized');
const PLAN_DATA = getPlanFromTag(planTag as any);
if (!PLAN_DATA) throw Error('Plan not found');
const premiumData = await PremiumModel.findOne({ customer_id });
if (!premiumData) throw Error('Plan not found');
const currentSubscription = await this.stripe.subscriptions.retrieve(premiumData?.subscription_id)
if (!premiumData) throw Error('Subscription not found');
const preview = await this.stripe.invoices.createPreview({
customer: customer_id,
subscription: subscription_id,
subscription_details: {
items: [
{ id: currentSubscription.items.data[0].id, price: this.testMode ? PLAN_DATA.PRICE_TEST : PLAN_DATA.PRICE, quantity: 1 }
],
proration_date: Math.floor(Date.now() / 1000),
proration_behavior: 'always_invoice'
}
});
return preview;
}
async createInvoice(customer_id: string) {
if (!this.stripe) throw Error('Stripe not initialized');
const premiumData = await PremiumModel.findOne({ customer_id });
if (!premiumData) throw Error('Plan not found');
const currentSubscription = await this.stripe.subscriptions.retrieve(premiumData?.subscription_id)
if (!premiumData) throw Error('Subscription not found');
const invoice = await this.stripe.invoices.create({
customer: customer_id,
subscription: currentSubscription.id,
});
if (!invoice || !invoice.id) throw Error('Cannot create invoice');
const finalized = this.stripe.invoices.finalizeInvoice(invoice.id);
return finalized;
}
async updateSubscriptionWithPrice(customer_id: string, subscription_id: string, plan_tag: string) {
if (!this.stripe) throw Error('Stripe not initialized');
const PLAN_DATA = getPlanFromTag(plan_tag as any);
if (!PLAN_DATA) throw Error('Plan not found');
const premiumData = await PremiumModel.findOne({ customer_id });
if (!premiumData) throw Error('Plan not found');
const currentSubscription = await this.stripe.subscriptions.retrieve(premiumData?.subscription_id)
if (!premiumData) throw Error('Subscription not found');
this.stripe.subscriptions.update(subscription_id, {
items: [
{ id: currentSubscription.items.data[0].id, price: this.testMode ? PLAN_DATA.PRICE_TEST : PLAN_DATA.PRICE, quantity: 1 }
],
proration_date: Math.floor(Date.now() / 1000),
proration_behavior: 'always_invoice'
})
}
async cancelPlan(customer_id: string) {
if (!this.stripe) throw Error('Stripe not initialized');
const paymentMethods = await this.stripe.customers.listPaymentMethods(customer_id);
for (const paymentMethod of paymentMethods.data) {
await this.stripe.paymentMethods.detach(paymentMethod.id);
}
return { ok: true, count: paymentMethods.data.length }
}
}
const instance = new StripeService();
export default instance;