feat(trade): connect WeChat refund API and callback flow
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,44 +1,40 @@
|
||||
import { Controller, Headers, Post, Req, Res } from '@nestjs/common';
|
||||
import { Controller, Headers, Inject, Post, Req, Res } from '@nestjs/common';
|
||||
import type { Request, Response } from 'express';
|
||||
import { PrismaService } from '../common/prisma/prisma.module';
|
||||
import { TradeService } from '../modules/trade/trade.service';
|
||||
import { WECHAT_PROVIDER } from '../integrations/integrations.constants';
|
||||
import type { IWechatProvider } from '../integrations/wechat/wechat.interface';
|
||||
import { AlertService } from '../common/alert/alert.service';
|
||||
|
||||
type RawBodyRequest = Request & { body: Buffer };
|
||||
|
||||
@Controller('callbacks/wechat')
|
||||
export class WechatRefundCallbackController {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly tradeService: TradeService,
|
||||
@Inject(WECHAT_PROVIDER) private readonly wechat: IWechatProvider,
|
||||
private readonly alert: AlertService,
|
||||
) {}
|
||||
|
||||
@Post('refund')
|
||||
async refundNotify(
|
||||
@Req() req: Request,
|
||||
@Headers() _headers: Record<string, string | string[] | undefined>,
|
||||
@Req() req: RawBodyRequest,
|
||||
@Headers() headers: Record<string, string | string[] | undefined>,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
let outRefundNo: string | undefined;
|
||||
try {
|
||||
const body = typeof req.body === 'object' ? req.body : {};
|
||||
const outRefundNo = String((body as Record<string, unknown>).out_refund_no ?? '');
|
||||
const refundId = String((body as Record<string, unknown>).refund_id ?? outRefundNo);
|
||||
|
||||
const existing = await this.prisma.logThirdParty.findFirst({
|
||||
where: { provider: 'WECHAT_REFUND', externalNo: refundId, status: 'SUCCESS' },
|
||||
const rawBody =
|
||||
(req as Request & { rawBody?: Buffer }).rawBody?.toString('utf8') ??
|
||||
(typeof req.body === 'string' ? req.body : JSON.stringify(req.body ?? {}));
|
||||
const notify = await this.wechat.parseRefundNotification(headers, rawBody);
|
||||
outRefundNo = notify.outRefundNo;
|
||||
await this.tradeService.handleRefundSuccess({
|
||||
outRefundNo: notify.outRefundNo,
|
||||
refundId: notify.refundId,
|
||||
amountFen: notify.amountFen,
|
||||
remark: '微信退款回调确认',
|
||||
actorType: 'WECHAT_REFUND',
|
||||
});
|
||||
if (existing) {
|
||||
return res.status(200).json({ code: 'SUCCESS', message: '成功' });
|
||||
}
|
||||
|
||||
await this.prisma.logThirdParty.create({
|
||||
data: {
|
||||
provider: 'WECHAT_REFUND',
|
||||
scene: 'ORDER_REFUND_CALLBACK',
|
||||
refType: 'TICKET',
|
||||
refId: BigInt(0),
|
||||
externalNo: refundId,
|
||||
status: 'SUCCESS',
|
||||
},
|
||||
});
|
||||
|
||||
return res.status(200).json({ code: 'SUCCESS', message: '成功' });
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : '处理失败';
|
||||
@@ -46,8 +42,8 @@ export class WechatRefundCallbackController {
|
||||
level: 'P0',
|
||||
category: 'pay',
|
||||
title: '退款回调处理失败',
|
||||
detail: message,
|
||||
dedupeKey: `refund_callback_fail|${message.slice(0, 40)}`,
|
||||
detail: `${outRefundNo ? `退款单 ${outRefundNo}\n` : ''}${message}`,
|
||||
dedupeKey: `refund_callback_fail|${outRefundNo ?? message.slice(0, 40)}`,
|
||||
dedupeTtlSec: 120,
|
||||
});
|
||||
return res.status(500).json({ code: 'FAIL', message });
|
||||
|
||||
Reference in New Issue
Block a user