update transaction

This commit is contained in:
2024-11-21 18:30:32 +07:00
parent cf8051b8a4
commit d56e1f9348
5 changed files with 110 additions and 128 deletions

View File

@@ -0,0 +1,8 @@
import { Module } from '@nestjs/common'
import { FinanceSchema } from './finance.schema'
@Module({
providers: [FinanceSchema],
exports: [FinanceSchema],
})
export class FinanceModule {}

View File

@@ -0,0 +1,5 @@
import { Injectable } from '@nestjs/common'
import { Pothos, PothosRef } from '@smatch-corp/nestjs-pothos'
@Injectable()
export class FinanceSchema {}

View File

@@ -1,10 +1,5 @@
import { Inject, Injectable } from '@nestjs/common'
import {
Pothos,
PothosRef,
PothosSchema,
SchemaBuilderToken,
} from '@smatch-corp/nestjs-pothos'
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos'
import { Builder } from '../Graphql/graphql.builder'
import { PrismaService } from '../Prisma/prisma.service'
import { OrderStatus } from '@prisma/client'
@@ -81,8 +76,7 @@ export class OrderSchema extends PothosSchema {
this.builder.queryFields((t) => ({
orders: t.prismaField({
type: [this.order()],
description:
'Retrieve a list of orders with optional filtering, ordering, and pagination.',
description: 'Retrieve a list of orders with optional filtering, ordering, and pagination.',
args: this.builder.generator.findManyArgs('Order'),
resolve: async (query, _root, args, _ctx, _info) => {
return await this.prisma.order.findMany({
@@ -113,17 +107,7 @@ export class OrderSchema extends PothosSchema {
description: 'Create a new order.',
args: {
data: t.arg({
type: this.builder.generator.getCreateInput('Order', [
'id',
'user',
'paymentId',
'payment',
'refundTicket',
'status',
'total',
'createdAt',
'updatedAt',
]),
type: this.builder.generator.getCreateInput('Order', ['id', 'user', 'paymentId', 'payment', 'refundTicket', 'status', 'total', 'createdAt', 'updatedAt']),
required: true,
}),
},
@@ -150,10 +134,7 @@ export class OrderSchema extends PothosSchema {
const order = await this.prisma.order.findUnique({
where: { id: schedule.orderId },
})
if (
order?.status === OrderStatus.PAID ||
order?.status === OrderStatus.PENDING
) {
if (order?.status === OrderStatus.PAID || order?.status === OrderStatus.PENDING) {
throw new Error('Schedule already has an order')
}
}
@@ -202,17 +183,9 @@ export class OrderSchema extends PothosSchema {
description: service.name,
buyerName: ctx.http.me?.name ?? '',
buyerEmail: ctx.http.me?.email ?? '',
returnUrl: `${process.env.PAYOS_RETURN_URL}`.replace(
'<serviceId>',
service.id,
),
cancelUrl: `${process.env.PAYOS_RETURN_URL}`.replace(
'<serviceId>',
service.id,
),
expiredAt: DateTimeUtils.now()
.plus({ minutes: 15 })
.toUnixInteger(),
returnUrl: `${process.env.PAYOS_RETURN_URL}`.replace('<serviceId>', service.id),
cancelUrl: `${process.env.PAYOS_RETURN_URL}`.replace('<serviceId>', service.id),
expiredAt: DateTimeUtils.now().plus({ minutes: 15 }).toUnixInteger(),
})
// update order payment id
await this.prisma.order.update({
@@ -230,12 +203,12 @@ export class OrderSchema extends PothosSchema {
})
// update orderId for schedule dates
await this.prisma.scheduleDate.updateMany({
where: { scheduleId: args.data.schedule.connect?.id ?? '' },
data: {
orderId: order.id,
},
})
// await this.prisma.scheduleDate.updateMany({
// where: { scheduleId: args.data.schedule.connect?.id ?? '' },
// data: {
// orderId: order.id,
// },
// })
// refetch order
return await this.prisma.order.findUnique({
@@ -267,10 +240,7 @@ export class OrderSchema extends PothosSchema {
description: 'Update an existing order.',
args: {
data: t.arg({
type: this.builder.generator.getUpdateInput('Order', [
'status',
'total',
]),
type: this.builder.generator.getUpdateInput('Order', ['status', 'total']),
required: true,
}),
where: t.arg({

View File

@@ -2,20 +2,8 @@ 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'
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()
@@ -31,74 +19,87 @@ export class PayosService {
async webhook(data: WebhookType) {
Logger.log(`Webhook received: ${JSON.stringify(data)}`)
// check if orderCode = 123 mean it's a test payment and auto response success
/* -------------------------------------------------------------------------- */
/* Test payment */
/* -------------------------------------------------------------------------- */
if (data.data.orderCode === 123) {
return {
message: 'Payment received',
}
}
// verify checksum
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
// update payment status
const payment = await this.prisma.payment.update({
where: { paymentCode: paymentData.paymentLinkId },
data: {
status: paymentStatus,
},
})
const orderStatus =
paymentStatus === PaymentStatus.PAID
? OrderStatus.PAID
: OrderStatus.FAILED
// update order status
await this.prisma.order.update({
where: { id: payment.orderId },
data: {
status: orderStatus,
},
})
const order = await this.prisma.order.findUniqueOrThrow({
where: { id: payment.orderId },
})
const schedule = await this.prisma.schedule.findUnique({
where: { id: order?.scheduleId },
})
// update schedule order id
await this.prisma.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 this.prisma.managedService.findUniqueOrThrow({
where: { id: schedule?.managedServiceId },
})
const mentorId = managedService.mentorId
// get center id from order service
const orderService = await this.prisma.service.findUniqueOrThrow({
where: { id: order?.serviceId },
})
const centerId = orderService.centerId
// create chatroom for service meeting room
await this.prisma.chatRoom.create({
data: {
type: ChatRoomType.SUPPORT,
customerId: order.userId,
centerId: centerId,
mentorId: mentorId,
},
})
return {
message: 'Payment received',
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
}
}
@@ -114,10 +115,7 @@ export class PayosService {
return await this.payos.getPaymentLinkInformation(orderId)
}
async cancelPaymentURL(
orderId: string | number,
cancellationReason?: string,
) {
async cancelPaymentURL(orderId: string | number, cancellationReason?: string) {
return await this.payos.cancelPaymentLink(orderId, cancellationReason)
}

View File

@@ -384,15 +384,16 @@ export class UserSchema extends PothosSchema {
email: t.arg({ type: 'String', required: true }),
},
resolve: async (_parent, args, ctx) => {
// check context
if (ctx.isSubscription) {
throw new Error('Not allowed')
}
// check context is admin
if (ctx.http.me?.role !== 'ADMIN') {
throw new UnauthorizedException(`Only admin can invite moderator`)
}
return this.prisma.$transaction(async (tx) => {
// check context
if (ctx.isSubscription) {
throw new Error('Not allowed')
}
// check context is admin
if (ctx.http.me?.role !== 'ADMIN') {
throw new UnauthorizedException(`Only admin can invite moderator`)
}
let user
// perform update role
try {