feat(trade): connect WeChat refund API and callback flow
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,6 +7,10 @@ export type PayOrderResult =
|
||||
| { mode: 'jsapi'; prepay: WechatJsapiPrepayParams }
|
||||
| { mode: 'native'; codeUrl: string; externalNo: string };
|
||||
|
||||
export type RefundOrderResult =
|
||||
| { mode: 'mock'; outRefundNo: string }
|
||||
| { mode: 'wechat'; outRefundNo: string; refundId?: string; status: 'PROCESSING' | 'SUCCESS' };
|
||||
|
||||
export interface IPayProvider {
|
||||
payOrder(
|
||||
orderId: bigint,
|
||||
@@ -14,4 +18,5 @@ export interface IPayProvider {
|
||||
platform?: 'h5' | 'mini',
|
||||
payMethod?: PayMethod,
|
||||
): Promise<PayOrderResult>;
|
||||
refundOrder(orderId: bigint, outRefundNo: string, reason?: string): Promise<RefundOrderResult>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { loadAppConfig } from '@dukang/shared-types';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import type { IPayProvider, PayMethod, PayOrderResult } from './pay.interface';
|
||||
import type { IPayProvider, PayMethod, PayOrderResult, RefundOrderResult } from './pay.interface';
|
||||
|
||||
@Injectable()
|
||||
export class PayMockProvider implements IPayProvider {
|
||||
@@ -27,4 +27,15 @@ export class PayMockProvider implements IPayProvider {
|
||||
}
|
||||
return { mode: 'mock', externalNo: `MOCK-${Date.now()}` };
|
||||
}
|
||||
|
||||
async refundOrder(
|
||||
_orderId: bigint,
|
||||
outRefundNo: string,
|
||||
_reason?: string,
|
||||
): Promise<RefundOrderResult> {
|
||||
if (!loadAppConfig().mockPay) {
|
||||
throw new Error('Real WeChat refund requires PayWechatProvider');
|
||||
}
|
||||
return { mode: 'mock', outRefundNo };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { loadAppConfig } from '@dukang/shared-types';
|
||||
import type { IPayProvider, PayMethod, PayOrderResult } from './pay.interface';
|
||||
import type { IPayProvider, PayMethod, PayOrderResult, RefundOrderResult } from './pay.interface';
|
||||
import { PayMockProvider } from './pay.mock.provider';
|
||||
import { PayWechatProvider } from './pay.wechat.provider';
|
||||
|
||||
@@ -24,4 +24,12 @@ export class PayRouterProvider implements IPayProvider {
|
||||
): Promise<PayOrderResult> {
|
||||
return this.resolve().payOrder(orderId, openId, platform, payMethod);
|
||||
}
|
||||
|
||||
refundOrder(
|
||||
orderId: bigint,
|
||||
outRefundNo: string,
|
||||
reason?: string,
|
||||
): Promise<RefundOrderResult> {
|
||||
return this.resolve().refundOrder(orderId, outRefundNo, reason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,4 +72,40 @@ export class PayWechatProvider implements IPayProvider {
|
||||
});
|
||||
return { mode: 'jsapi', prepay };
|
||||
}
|
||||
|
||||
async refundOrder(
|
||||
orderId: bigint,
|
||||
outRefundNo: string,
|
||||
reason?: string,
|
||||
) {
|
||||
if (loadAppConfig().mockPay) {
|
||||
return { mode: 'mock' as const, outRefundNo };
|
||||
}
|
||||
if (!this.wechat.isPayEnabled()) {
|
||||
throw new Error('微信支付未配置:请关闭 MOCK_PAY 并配置 WX_MCH_ID 等商户参数');
|
||||
}
|
||||
|
||||
const order = await this.prisma.order.findUnique({ where: { id: orderId } });
|
||||
if (!order) throw new Error('订单不存在');
|
||||
|
||||
const amountFen = Math.round(Number(order.payAmount) * 100);
|
||||
const notifyUrl = process.env.WX_REFUND_NOTIFY_URL ?? '';
|
||||
|
||||
this.logger.log(`create refund order=${order.orderNo} outRefundNo=${outRefundNo}`);
|
||||
const result = await this.wechat.createDomesticRefund({
|
||||
orderNo: order.orderNo,
|
||||
transactionId: order.payExternalNo ?? undefined,
|
||||
outRefundNo,
|
||||
amountFen,
|
||||
totalFen: amountFen,
|
||||
reason,
|
||||
notifyUrl,
|
||||
});
|
||||
return {
|
||||
mode: 'wechat' as const,
|
||||
outRefundNo: result.outRefundNo,
|
||||
refundId: result.refundId,
|
||||
status: result.status === 'SUCCESS' ? 'SUCCESS' as const : 'PROCESSING' as const,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user