微信小程序支付功能打通
This commit is contained in:
@@ -5,5 +5,5 @@ export type PayOrderResult =
|
||||
| { mode: 'jsapi'; prepay: WechatJsapiPrepayParams };
|
||||
|
||||
export interface IPayProvider {
|
||||
payOrder(orderId: bigint, openId?: string): Promise<PayOrderResult>;
|
||||
payOrder(orderId: bigint, openId?: string, platform?: 'h5' | 'mini'): Promise<PayOrderResult>;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { IPayProvider, PayOrderResult } from './pay.interface';
|
||||
|
||||
@Injectable()
|
||||
export class PayMockProvider implements IPayProvider {
|
||||
async payOrder(_orderId: bigint, _openId?: string): Promise<PayOrderResult> {
|
||||
async payOrder(_orderId: bigint, _openId?: string, _platform?: 'h5' | 'mini'): Promise<PayOrderResult> {
|
||||
if (!loadAppConfig().mockPay) {
|
||||
throw new Error('Real WeChat pay requires PayWechatProvider');
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export class PayRouterProvider implements IPayProvider {
|
||||
return loadAppConfig().mockPay ? this.mock : this.wechat;
|
||||
}
|
||||
|
||||
payOrder(orderId: bigint, openId?: string): Promise<PayOrderResult> {
|
||||
return this.resolve().payOrder(orderId, openId);
|
||||
payOrder(orderId: bigint, openId?: string, platform?: 'h5' | 'mini'): Promise<PayOrderResult> {
|
||||
return this.resolve().payOrder(orderId, openId, platform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export class PayWechatProvider implements IPayProvider {
|
||||
@Inject(WECHAT_PROVIDER) private readonly wechat: IWechatProvider,
|
||||
) {}
|
||||
|
||||
async payOrder(orderId: bigint, openId?: string): Promise<PayOrderResult> {
|
||||
async payOrder(orderId: bigint, openId?: string, platform: 'h5' | 'mini' = 'h5'): Promise<PayOrderResult> {
|
||||
if (loadAppConfig().mockPay) {
|
||||
return { mode: 'mock', externalNo: `MOCK-${Date.now()}` };
|
||||
}
|
||||
@@ -29,13 +29,14 @@ export class PayWechatProvider implements IPayProvider {
|
||||
if (!order) throw new Error('订单不存在');
|
||||
|
||||
const amountFen = Math.round(Number(order.payAmount) * 100);
|
||||
this.logger.log(`create JSAPI prepay order=${order.orderNo} mchid=${this.wechat.getMchId()}`);
|
||||
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: process.env.WX_PAY_NOTIFY_URL ?? '',
|
||||
platform,
|
||||
});
|
||||
return { mode: 'jsapi', prepay };
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ export class WechatApiProvider implements IWechatProvider {
|
||||
const cfg = loadAppConfig();
|
||||
return (
|
||||
!cfg.mockPay &&
|
||||
!!this.appId &&
|
||||
!!(this.miniAppId || this.appId) &&
|
||||
!!this.mchId &&
|
||||
!!this.mchSerialNo &&
|
||||
!!this.mchPrivateKey &&
|
||||
@@ -288,6 +288,7 @@ export class WechatApiProvider implements IWechatProvider {
|
||||
amountFen: number;
|
||||
openId: string;
|
||||
notifyUrl: string;
|
||||
platform?: 'h5' | 'mini';
|
||||
}) {
|
||||
if (!this.isPayEnabled()) {
|
||||
throw new InternalServerErrorException(
|
||||
@@ -298,8 +299,17 @@ export class WechatApiProvider implements IWechatProvider {
|
||||
if (!notifyUrl) {
|
||||
throw new InternalServerErrorException('请配置 WX_PAY_NOTIFY_URL');
|
||||
}
|
||||
const platform = params.platform ?? 'h5';
|
||||
const payAppId = platform === 'mini' ? this.miniAppId || this.appId : this.appId || this.miniAppId;
|
||||
if (!payAppId) {
|
||||
throw new InternalServerErrorException(
|
||||
platform === 'mini'
|
||||
? '小程序支付未配置:请设置 WX_MINI_APP_ID'
|
||||
: '微信支付未配置:请设置 WX_APP_ID',
|
||||
);
|
||||
}
|
||||
const body = {
|
||||
appid: this.miniAppId || this.appId,
|
||||
appid: payAppId,
|
||||
mchid: this.mchId,
|
||||
description: params.description,
|
||||
out_trade_no: params.orderNo,
|
||||
@@ -329,13 +339,13 @@ export class WechatApiProvider implements IWechatProvider {
|
||||
const timeStamp = String(Math.floor(Date.now() / 1000));
|
||||
const nonceStr = randomUUID().replace(/-/g, '');
|
||||
const packageStr = `prepay_id=${res.prepay_id}`;
|
||||
const message = `${this.appId}\n${timeStamp}\n${nonceStr}\n${packageStr}\n`;
|
||||
const message = `${payAppId}\n${timeStamp}\n${nonceStr}\n${packageStr}\n`;
|
||||
const sign = createSign('RSA-SHA256');
|
||||
sign.update(message);
|
||||
sign.end();
|
||||
const paySign = sign.sign(this.mchPrivateKey, 'base64');
|
||||
return {
|
||||
appId: this.appId,
|
||||
appId: payAppId,
|
||||
timeStamp,
|
||||
nonceStr,
|
||||
package: packageStr,
|
||||
|
||||
@@ -73,6 +73,7 @@ export interface IWechatProvider {
|
||||
amountFen: number;
|
||||
openId: string;
|
||||
notifyUrl: string;
|
||||
platform?: 'h5' | 'mini';
|
||||
}): Promise<WechatJsapiPrepayParams>;
|
||||
|
||||
/** 解析并验签支付回调通知 */
|
||||
|
||||
@@ -75,6 +75,7 @@ export class WechatRouterProvider implements IWechatProvider {
|
||||
amountFen: number;
|
||||
openId: string;
|
||||
notifyUrl: string;
|
||||
platform?: 'h5' | 'mini';
|
||||
}) {
|
||||
return this.resolve().createJsapiPrepay(params);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export class TradeController {
|
||||
@Post(':id/pay')
|
||||
@UseGuards(PhoneVerifiedGuard)
|
||||
pay(@CurrentUser() user: AuthUser, @Param('id') id: string) {
|
||||
return this.tradeService.payOrder(user.actorId, BigInt(id));
|
||||
return this.tradeService.payOrder(user.actorId, BigInt(id), user.clientApp);
|
||||
}
|
||||
|
||||
@Put(':id/address')
|
||||
|
||||
@@ -199,7 +199,7 @@ export class TradeService {
|
||||
return serializeBigInt(mapOrderCompat(order));
|
||||
}
|
||||
|
||||
async payOrder(userId: bigint, orderId: bigint) {
|
||||
async payOrder(userId: bigint, orderId: bigint, clientApp?: ClientApp) {
|
||||
const order = await this.prisma.order.findFirst({
|
||||
where: { id: orderId, userId },
|
||||
});
|
||||
@@ -214,7 +214,8 @@ export class TradeService {
|
||||
if (!appConfig.mockPay && !openId) {
|
||||
throw new BadRequestException(WECHAT_AUTH_REQUIRED);
|
||||
}
|
||||
const payResult = await this.payProvider.payOrder(orderId, openId);
|
||||
const payPlatform = clientApp === ClientApp.USER_MINI ? 'mini' : 'h5';
|
||||
const payResult = await this.payProvider.payOrder(orderId, openId, payPlatform);
|
||||
|
||||
if (payResult.mode === 'jsapi') {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user