对接微信支付

This commit is contained in:
2026-07-01 22:41:49 +08:00
parent b78ef8798c
commit ff4622ff6c
14 changed files with 389 additions and 19 deletions
@@ -222,6 +222,87 @@ export class TradeService {
return this.getOrder(userId, orderId);
}
/** 微信支付回调:幂等更新订单为已支付并发券 */
async handlePaySuccess(params: {
orderNo: string;
transactionId: string;
amountFen: number;
}) {
const order = await this.prisma.order.findUnique({ where: { orderNo: params.orderNo } });
if (!order) {
throw new NotFoundException('订单不存在');
}
if (order.payStatus === 'PAID') {
return { orderId: order.id.toString(), alreadyPaid: true };
}
const expectedFen = Math.round(Number(order.payAmount) * 100);
if (params.amountFen > 0 && params.amountFen !== expectedFen) {
throw new BadRequestException('支付金额与订单不符');
}
const existingLog = await this.prisma.logThirdParty.findFirst({
where: {
provider: 'WECHAT_PAY',
externalNo: params.transactionId,
status: 'SUCCESS',
},
});
if (existingLog) {
return { orderId: order.id.toString(), alreadyPaid: true };
}
const now = new Date();
await this.prisma.$transaction(async (tx) => {
const current = await tx.order.findUnique({ where: { id: order.id } });
if (!current || current.payStatus === 'PAID') return;
await tx.order.update({
where: { id: order.id },
data: {
status: 'PENDING_SHIP',
payStatus: 'PAID',
paidAt: now,
payExternalNo: params.transactionId,
},
});
await tx.logThirdParty.create({
data: {
provider: 'WECHAT_PAY',
scene: 'ORDER_PAY',
refType: 'ORDER',
refId: order.id,
externalNo: params.transactionId,
amount: order.payAmount,
status: 'SUCCESS',
},
});
await tx.commonEvent.create({
data: buildOrderStatusEvent({
orderId: order.id,
fromStatus: 'PENDING_PAY',
toStatus: 'PENDING_SHIP',
operator: 'WECHAT_PAY',
}),
});
const delivery = await tx.orderDelivery.findUnique({ where: { orderId: order.id } });
if (!delivery) {
await tx.orderDelivery.create({
data: { orderId: order.id, provider: 'MANUAL' },
});
}
});
const refreshed = await this.prisma.order.findUnique({ where: { id: order.id } });
if (refreshed?.payStatus === 'PAID') {
await this.benefitService.grantOnOrderPaid(order.id);
await this.deliveryProvider.scheduleAutoAdvance(order.id);
}
return { orderId: order.id.toString(), alreadyPaid: false };
}
async listOrders(userId: bigint, tab = 'all', page = 1, pageSize = 20) {
const statuses = orderTabToStatuses(tab);
const where = {