Refactor: extract link between session and purchase to stripe session control

main
MiguelMLorente 2025-11-24 22:13:21 +01:00
parent d4e8883304
commit a92855396b
5 changed files with 46 additions and 42 deletions

View File

@ -1,4 +1,4 @@
import { Body, Controller, Get, Post, Redirect } from '@nestjs/common';
import { Body, Controller, Get, Logger, Param, Post, Redirect } from '@nestjs/common';
import { PurchaseService } from '../service/purchase.service';
import { UserService } from '../service/user.service';
import { PurchaseItem } from '../dto/purchase-item';
@ -7,6 +7,8 @@ import { PurchaseStatus } from 'src/dto/purchase-status';
@Controller('/buy')
export class BuyController {
private readonly logger = new Logger(BuyController.name);
constructor(
private readonly purchaseService: PurchaseService,
private readonly userService: UserService,
@ -24,22 +26,21 @@ export class BuyController {
PurchaseItem.PAELLA,
quantity,
);
const session = await this.stripeService.createStripeSession(
const { sessionUrl } = await this.stripeService.createStripeSession(
user,
purchase,
);
this.purchaseService.attachStripeSessionToPurchase(session.id, purchase.id);
console.log(session);
return { url: session.url };
this.logger.debug(`Redirecting to ${sessionUrl}`);
return { url: sessionUrl };
}
@Post('/complete')
public async complete(@Body('userId') userId: number) {
const user = this.userService.getUserById(userId);
const session = await this.stripeService.getStripeSessionForCustomer(user);
const isPaid = session.payment_status == 'paid';
this.purchaseService.findAndTransitionPurchaseStatus(
session.id,
const { isPaid, purchaseId } =
await this.stripeService.getStripeSessionForCustomer(user);
this.purchaseService.transitionPurchaseStatus(
purchaseId,
isPaid ? PurchaseStatus.COMPLETED : PurchaseStatus.IN_PROGRESS,
);
return { url: '/signup' };

View File

@ -8,5 +8,4 @@ export class Purchase {
purchasedProduct: PurchaseItem;
purchasedUnits: number;
status: PurchaseStatus;
stripeSessionId?: string;
}

View File

@ -0,0 +1,5 @@
export class StripeSession {
sessionId: string;
userId: number;
purchaseId: number;
}

View File

@ -28,26 +28,12 @@ export class PurchaseService {
return purchase;
}
public attachStripeSessionToPurchase(
sessionId: string,
public transitionPurchaseStatus(
purchaseId: number,
): void {
const purchase = this.purchases.find(
(purchase) => purchase.id === purchaseId,
);
if (!purchase) {
throw new NotFoundException();
}
purchase.stripeSessionId = sessionId;
}
public findAndTransitionPurchaseStatus(
sessionId: string,
purchaseStatus: PurchaseStatus,
) {
const purchase = this.purchases.find(
(purchase) => purchase.stripeSessionId === sessionId,
(purchase) => purchase.id === purchaseId,
);
if (!purchase) {
throw new NotFoundException();

View File

@ -2,22 +2,20 @@ import { Injectable, NotFoundException } from '@nestjs/common';
import { Purchase } from '../dto/purchase';
import Stripe from 'stripe';
import { User } from '../dto/user';
import { NotFoundError } from 'rxjs';
import { StripeSession } from 'src/dto/stripe-session';
@Injectable()
export class StripeService {
private readonly stripe: Stripe;
private readonly stripeSessions: Map<number, string> = new Map();
private readonly stripeSessions: StripeSession[] = [];
constructor() {
const stripePrivateKey = process.env.STRIPE_PRIVATE_KEY!;
this.stripe = new Stripe(stripePrivateKey);
}
public async createStripeSession(
user: User,
purchase: Purchase,
): Promise<Stripe.Response<Stripe.Checkout.Session>> {
public async createStripeSession(user: User, purchase: Purchase) {
const redirectUrl = `http://localhost:5173/buy/return?purchaseId=${purchase.id}`
const session = await this.stripe.checkout.sessions.create({
line_items: [
{
@ -26,21 +24,36 @@ export class StripeService {
},
],
mode: 'payment',
success_url: 'http://localhost:5173/buy/return',
cancel_url: 'http://localhost:5173/buy/return',
success_url: redirectUrl,
cancel_url: redirectUrl,
payment_method_types: ['card', 'paypal'],
});
this.stripeSessions.set(user.id, session.id);
return session;
const stripeSession: StripeSession = {
sessionId: session.id,
userId: user.id,
purchaseId: purchase.id,
};
this.stripeSessions.push(stripeSession);
return {
sessionUrl: session.url,
};
}
public getStripeSessionForCustomer(
user: User,
): Promise<Stripe.Response<Stripe.Checkout.Session>> {
const sessionId = this.stripeSessions.get(user.id);
if (!sessionId) {
public async getStripeSessionForCustomer(user: User) {
const stripeSession = this.stripeSessions.find(
(session) => session.userId === user.id,
);
if (!stripeSession) {
throw new NotFoundException();
}
return this.stripe.checkout.sessions.retrieve(sessionId);
const session = await this.stripe.checkout.sessions.retrieve(
stripeSession.sessionId,
);
return {
isPaid: session.payment_status === 'paid',
purchaseId: stripeSession.purchaseId,
};
}
}