feat(pay): 接入微信支付V3原路退款(按支付渠道分流 + 幂等 + 退款回调)
- 新增微信退款客户端 createRefund/queryRefundByOutRefundNo(/v3/refund/domestic/refunds) - 新增退款桥接模块与 payment_refunds 幂等退款记录表 - 订单退款按渠道分流:微信支付原路退回微信;余额支付退回钱包余额;微信退款失败(502)不回退余额,防重复退款 - 覆盖拒单/买家取消/仲裁全额与部分/超时自动退款;新增 /api/pay/wechat/refund-notify 回调 - 后台新增退款回调地址配置;文档补充退款验收说明 - 新增回归护栏 wechat-refund.spec.ts(77 套件/605 用例全绿)
这个提交包含在:
@@ -0,0 +1,42 @@
|
||||
// 已读取AGENTS.md
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
/**
|
||||
* 第六十一批回归闸门:微信原路退款接入(按支付渠道分流 + 幂等)
|
||||
*/
|
||||
describe('第六十一批微信原路退款闸门', () => {
|
||||
const rootDir = path.resolve(__dirname, '../../');
|
||||
const payV3 = fs.readFileSync(path.join(rootDir, 'server/src/core/pay/wechat-pay-v3.ts'), 'utf-8');
|
||||
const bridge = fs.readFileSync(path.join(rootDir, 'server/src/core/pay/refund-bridge.ts'), 'utf-8');
|
||||
const repoCode = fs.readFileSync(path.join(rootDir, 'server/src/db/repository.ts'), 'utf-8');
|
||||
const indexCode = fs.readFileSync(path.join(rootDir, 'server/src/index.ts'), 'utf-8');
|
||||
const mysqlCode = fs.readFileSync(path.join(rootDir, 'server/src/db/mysql.ts'), 'utf-8');
|
||||
|
||||
it('支付客户端具备退款与退款查单能力', () => {
|
||||
expect(payV3).toContain('export async function createRefund(');
|
||||
expect(payV3).toContain("'/v3/refund/domestic/refunds'");
|
||||
expect(payV3).toContain('export async function queryRefundByOutRefundNo(');
|
||||
});
|
||||
|
||||
it('退款桥接模块可注入执行器', () => {
|
||||
expect(bridge).toContain('export function setWeChatRefundFn(');
|
||||
expect(bridge).toContain('export async function tryRefundToWeChat(');
|
||||
});
|
||||
|
||||
it('仓储层按支付渠道分流退款并支持幂等记录', () => {
|
||||
expect(repoCode).toContain('private async refundOrderPayment(');
|
||||
expect(repoCode).toContain('private async orderPaidViaWechat(');
|
||||
expect(repoCode).toContain('async getRefundByOutRefundNo(');
|
||||
expect(repoCode).toContain('async upsertRefundRecord(');
|
||||
// 四个退款入口均使用统一分流
|
||||
const count = (repoCode.match(/this\.refundOrderPayment\(/g) || []).length;
|
||||
expect(count).toBeGreaterThanOrEqual(5);
|
||||
expect(mysqlCode).toContain('CREATE TABLE IF NOT EXISTS payment_refunds');
|
||||
});
|
||||
|
||||
it('后端注册退款执行器并提供退款回调', () => {
|
||||
expect(indexCode).toContain('setWeChatRefundFn(async');
|
||||
expect(indexCode).toContain("app.post('/api/pay/wechat/refund-notify'");
|
||||
});
|
||||
});
|
||||
在新工单中引用
屏蔽一个用户