Stripe Integration

This commit is contained in:
2024-08-20 23:27:07 +02:00
parent 056db7b199
commit 48bff89526
28 changed files with 728 additions and 21 deletions

View File

@@ -0,0 +1,36 @@
import { Body, Controller, HttpException, HttpStatus, Post, Req, Res } from '@nestjs/common';
import { Request, Response } from 'express';
import { Checkout } from 'src/models/main.model.js';
import Stripe from 'stripe';
import { PaymentService } from './payment.service.js';
@Controller('payment')
export class PaymentController {
constructor(private readonly paymentService: PaymentService) {}
// @Post()
// async createSubscription(@Body() subscriptionData: any) {
// return this.paymentService.createSubscription(subscriptionData);
// }
@Post('create-checkout-session')
async calculateTax(@Body() checkout: Checkout) {
return this.paymentService.checkout(checkout);
}
@Post('webhook')
async handleWebhook(@Req() req: Request, @Res() res: Response): Promise<void> {
const signature = req.headers['stripe-signature'] as string;
try {
const event = await this.paymentService.constructEvent(req.body, signature);
if (event.type === 'checkout.session.completed') {
await this.paymentService.handleCheckoutSessionCompleted(event.data.object as Stripe.Checkout.Session);
}
res.status(200).send('Webhook received');
} catch (error) {
console.error(`Webhook Error: ${error.message}`);
throw new HttpException('Webhook Error', HttpStatus.BAD_REQUEST);
}
}
}

View File

@@ -0,0 +1,17 @@
import { Module } from '@nestjs/common';
import { DrizzleModule } from '../drizzle/drizzle.module.js';
import { FileService } from '../file/file.service.js';
import { GeoService } from '../geo/geo.service.js';
import { MailModule } from '../mail/mail.module.js';
import { MailService } from '../mail/mail.service.js';
import { UserModule } from '../user/user.module.js';
import { UserService } from '../user/user.service.js';
import { PaymentController } from './payment.controller.js';
import { PaymentService } from './payment.service.js';
@Module({
imports: [DrizzleModule, UserModule, MailModule],
providers: [PaymentService, UserService, MailService, FileService, GeoService],
controllers: [PaymentController],
})
export class PaymentModule {}

View File

@@ -0,0 +1,81 @@
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { NodePgDatabase } from 'drizzle-orm/node-postgres/driver.js';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import Stripe from 'stripe';
import { Logger } from 'winston';
import * as schema from '../drizzle/schema.js';
import { PG_CONNECTION } from '../drizzle/schema.js';
import { MailService } from '../mail/mail.service.js';
import { Checkout } from '../models/main.model.js';
import { UserService } from '../user/user.service.js';
export interface BillingAddress {
country: string;
state: string;
}
@Injectable()
export class PaymentService {
private stripe: Stripe;
constructor(
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
@Inject(PG_CONNECTION) private conn: NodePgDatabase<typeof schema>,
private readonly userService: UserService,
private readonly mailService: MailService,
) {
this.stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: '2024-06-20',
});
}
async checkout(checkout: Checkout) {
try {
const session = await this.stripe.checkout.sessions.create({
mode: 'subscription',
payment_method_types: ['card'],
line_items: [
{
price: checkout.priceId,
quantity: 1,
},
],
success_url: 'http://localhost:4200/success',
cancel_url: 'http://localhost:4200/pricing',
customer_email: checkout.email,
shipping_address_collection: {
allowed_countries: ['US'],
},
client_reference_id: '1234',
locale: 'en',
});
return session;
} catch (e) {
throw new BadRequestException(`error during checkout: ${e}`);
}
}
async constructEvent(body: string | Buffer, signature: string) {
return this.stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_WEBHOOK_SECRET!);
}
async handleCheckoutSessionCompleted(session: Stripe.Checkout.Session): Promise<void> {
this.logger.info(JSON.stringify(session));
//const jwtUser:JwtUser = {firstname:,lastname:}
const user = await this.userService.getUserByMail(session.customer_details.email);
user.stripeCustomerId = session.customer as string;
user.subscriptionId = session.subscription as string;
user.planActive = true;
//session.shipping_details ->
// "shipping_details": {
// "address": {
// "city": "Springfield",
// "country": "US",
// "line1": "West Maple Avenue South",
// "line2": null,
// "postal_code": "62704",
// "state": "IL"
// },
// "name": "Johnathan Miller"
// }
user.subscriptionPlan = session.amount_total === 4900 ? 'broker' : 'professional'; //session.metadata['subscriptionPlan'] as 'free' | 'professional' | 'broker';
this.userService.saveUser(user);
this.mailService.sendSubscriptionConfirmation(user);
}
}