103 lines
3.4 KiB
TypeScript
103 lines
3.4 KiB
TypeScript
import { Inject, Injectable, Logger } from '@nestjs/common';
|
|
import { loadAppConfig } from '@dukang/shared-types';
|
|
import { PrismaService } from '../../common/prisma/prisma.module';
|
|
import { WECHAT_PROVIDER } from '../integrations.constants';
|
|
import type { IWechatProvider } from '../wechat/wechat.interface';
|
|
import type { IPayProvider, PayMethod, PayOrderResult } from './pay.interface';
|
|
|
|
@Injectable()
|
|
export class PayWechatProvider implements IPayProvider {
|
|
private readonly logger = new Logger(PayWechatProvider.name);
|
|
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
@Inject(WECHAT_PROVIDER) private readonly wechat: IWechatProvider,
|
|
) {}
|
|
|
|
async payOrder(
|
|
orderId: bigint,
|
|
openId?: string,
|
|
platform: 'h5' | 'mini' = 'h5',
|
|
payMethod: PayMethod = 'JSAPI',
|
|
): Promise<PayOrderResult> {
|
|
if (loadAppConfig().mockPay) {
|
|
return { mode: 'mock', externalNo: `MOCK-${Date.now()}` };
|
|
}
|
|
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_PAY_NOTIFY_URL ?? '';
|
|
|
|
if (payMethod === 'NATIVE') {
|
|
this.logger.log(`create NATIVE prepay order=${order.orderNo} mchid=${this.wechat.getMchId()}`);
|
|
const { codeUrl } = await this.wechat.createNativePrepay({
|
|
orderNo: order.orderNo,
|
|
description: `杜康好客订单 ${order.orderNo}`,
|
|
amountFen,
|
|
notifyUrl,
|
|
});
|
|
return {
|
|
mode: 'native',
|
|
codeUrl,
|
|
externalNo: `NATIVE-${order.orderNo}`,
|
|
};
|
|
}
|
|
|
|
if (!openId) {
|
|
throw new Error('微信支付需要用户 openId,请先完成微信授权登录');
|
|
}
|
|
|
|
this.logger.log(`create JSAPI prepay order=${order.orderNo} mchid=${this.wechat.getMchId()} platform=${platform}`);
|
|
const prepay = await this.wechat.createJsapiPrepay({
|
|
orderNo: order.orderNo,
|
|
description: `杜康好客订单 ${order.orderNo}`,
|
|
amountFen,
|
|
openId,
|
|
notifyUrl,
|
|
platform,
|
|
});
|
|
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,
|
|
};
|
|
}
|
|
}
|