Files
epess-web-backend/src/Payos/payos.service.ts

165 lines
4.9 KiB
TypeScript

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,
MessageType,
OrderStatus,
PaymentStatus,
ScheduleDateStatus,
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
const chatRoom = await tx.chatRoom.create({
data: {
type: ChatRoomType.SUPPORT,
customerId: order.userId,
centerId: centerId,
mentorId: mentorId,
},
})
// update order chatRoomId
await tx.order.update({
where: { id: order.id },
data: {
chatRoomId: chatRoom.id,
},
})
// update orderId for schedule dates
await tx.scheduleDate.updateMany({
where: { scheduleId: schedule?.id },
data: {
orderId: order.id,
},
})
// send first message from mentor to customer
await tx.message.create({
data: {
content:
'Xin chào, mình là hướng dẫn viên của bạn, hãy bắt đầu học ngay nhé!',
type: MessageType.TEXT,
chatRoomId: chatRoom.id,
senderId: mentorId,
},
})
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<CreatePaymentResponse> {
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: <explanation>
async refundPayment(body: any) {
return body
}
}