import { Inject, Injectable, Logger } from '@nestjs/common' import { PrismaService } from '../Prisma/prisma.service' import PayOS from '@payos/node' import type { CheckoutRequestType, CheckoutResponseDataType, WebhookType, WebhookDataType, CancelPaymentLinkRequestType, DataType } from '@payos/node/lib/type' import { ChatRoomType, OrderStatus, PaymentStatus, ScheduleStatus } from '@prisma/client' export type CreatePaymentBody = CheckoutRequestType export type CreatePaymentResponse = CheckoutResponseDataType @Injectable() export class PayosService { constructor( private readonly prisma: PrismaService, @Inject('PayOS') private readonly payos: PayOS, ) {} async ping() { return 'pong' } async webhook(data: WebhookType) { Logger.log(`Webhook received: ${JSON.stringify(data)}`) /* -------------------------------------------------------------------------- */ /* Test payment */ /* -------------------------------------------------------------------------- */ if (data.data.orderCode === 123) { return { message: 'Payment received', } } const paymentData = this.payos.verifyPaymentWebhookData(data) if (!paymentData) { Logger.error(`Invalid checksum: ${JSON.stringify(data)}`) throw new Error('Invalid checksum') } const paymentStatus = paymentData.code === '00' ? PaymentStatus.PAID : PaymentStatus.CANCELLED /* ---------------------------- begin transaction --------------------------- */ try { await this.prisma.$transaction(async (tx) => { // update payment status const payment = await tx.payment.update({ where: { paymentCode: paymentData.paymentLinkId }, data: { status: paymentStatus, }, }) const orderStatus = paymentStatus === PaymentStatus.PAID ? OrderStatus.PAID : OrderStatus.FAILED // update order status await tx.order.update({ where: { id: payment.orderId }, data: { status: orderStatus, }, }) const order = await tx.order.findUniqueOrThrow({ where: { id: payment.orderId }, }) const schedule = await tx.schedule.findUnique({ where: { id: order?.scheduleId }, }) // update schedule order id await tx.schedule.update({ where: { id: schedule?.id }, data: { customerId: order?.userId, orderId: order?.id, status: ScheduleStatus.IN_PROGRESS, }, }) // get mentor id from managed service const managedService = await tx.managedService.findUniqueOrThrow({ where: { id: schedule?.managedServiceId }, }) const mentorId = managedService.mentorId // get center id from order service const orderService = await tx.service.findUniqueOrThrow({ where: { id: order?.serviceId }, }) const centerId = orderService.centerId // create chatroom for support await tx.chatRoom.create({ data: { type: ChatRoomType.SUPPORT, customerId: order.userId, centerId: centerId, mentorId: mentorId, }, }) // update orderId for schedule dates await tx.scheduleDate.updateMany({ where: { scheduleId: schedule?.id }, data: { orderId: order.id, }, }) return { message: 'Payment received', } }) } catch (error) { Logger.error(`Transaction failed: ${error}`) throw error } } async createPaymentURL(body: CheckoutRequestType) { return await this.payos.createPaymentLink(body) } async createPayment(body: CreatePaymentBody): Promise { return await this.payos.createPaymentLink(body) } async getPaymentStatus(orderId: string | number) { return await this.payos.getPaymentLinkInformation(orderId) } async cancelPaymentURL(orderId: string | number, cancellationReason?: string) { return await this.payos.cancelPaymentLink(orderId, cancellationReason) } // biome-ignore lint/suspicious/noExplicitAny: async refundPayment(body: any) { return body } }