import { Controller, Headers, Inject, Post, Req, Res } from '@nestjs/common'; import type { Request, Response } from 'express'; import { TradeService } from '../modules/trade/trade.service'; import { WECHAT_PROVIDER } from '../integrations/integrations.constants'; import type { IWechatProvider } from '../integrations/wechat/wechat.interface'; import { PayRedeemAnomalyService } from '../common/alert/pay-redeem-anomaly.service'; import { AlertService } from '../common/alert/alert.service'; type RawBodyRequest = Request & { body: Buffer }; @Controller('callbacks/wechat') export class WechatPayCallbackController { constructor( private readonly tradeService: TradeService, @Inject(WECHAT_PROVIDER) private readonly wechat: IWechatProvider, private readonly payRedeemAnomaly: PayRedeemAnomalyService, private readonly alert: AlertService, ) {} @Post('pay') async payNotify( @Req() req: RawBodyRequest, @Headers() headers: Record, @Res() res: Response, ) { let orderNo: string | undefined; try { 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.parsePayNotification(headers, rawBody); orderNo = notify.outTradeNo; await this.tradeService.handlePaySuccess({ orderNo: notify.outTradeNo, transactionId: notify.transactionId, amountFen: notify.amountFen, }); return res.status(200).json({ code: 'SUCCESS', message: '成功' }); } catch (err) { const message = err instanceof Error ? err.message : '处理失败'; this.payRedeemAnomaly.onPayFail(message, { orderNo }); this.alert.notify({ level: 'P0', category: 'pay', title: '支付回调处理失败', detail: `${orderNo ? `订单 ${orderNo}\n` : ''}${message}`, dedupeKey: `pay_callback_fail|${orderNo ?? message.slice(0, 40)}`, dedupeTtlSec: 120, }); return res.status(500).json({ code: 'FAIL', message }); } } }