nestJs中接入微信支付
要在 Nest 中接入微信支付,我们需要完成以下几个步骤:
- 申请微信支付商户号和 API 密钥
- 安装
xml2js和request这两个依赖库 - 配置微信支付相关参数
- 实现统一下单接口和支付结果通知接口的控制器逻辑
下面我来具体介绍每一步。
1. 申请微信支付商户号和 API 密钥
在使用微信支付之前,我们需要先注册一个微信支付商户号,然后在商户平台上创建一个 API 密钥。具体步骤可以参考微信支付官方文档。
2. 安装依赖库
在使用 Nest 接入微信支付时,我们需要用到两个依赖库:xml2js 和 request。xml2js 是用于解析微信支付接口返回的 XML 格式数据,而 request 则是用于发送 HTTP 请求。可以使用 npm 进行安装:
npm install xml2js request --save
3. 配置微信支付相关参数
在开始使用微信支付之前,我们需要设置一些必要的支付参数,例如 appid、商户号、API 密钥等。可以将这些参数放在配置文件中,然后在代码中进行读取和使用。以下是一个示例配置文件 wechatpay.config.ts:
export const wechatpayConfig = {
appid: 'xxxxxxxx', // 公众号 AppID
mch_id: 'xxxxxxxx', // 商户号
notify_url: 'https://example.com/pay/notify', // 支付结果通知网址
key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // API 密钥
};
4. 实现统一下单接口和支付结果通知接口的控制器逻辑
在 Nest 中实现微信支付,需要编写两个控制器方法,一个用于发起统一下单请求,并返回预支付订单信息,另一个用于接收微信支付结果通知,并处理支付成功的业务逻辑。
首先是统一下单接口控制器代码:
import { Controller, Post, Body } from '@nestjs/common';
import * as request from 'request';
import { parseString } from 'xml2js';
import { wechatpayConfig } from './wechatpay.config';
@Controller('pay')
export class PayController {
@Post('unifiedorder')
async unifiedorder(@Body() body: any) {
const timestamp = Math.floor(Date.now() / 1000); // 当前时间戳
// 构造统一下单请求参数
const params = {
appid: wechatpayConfig.appid,
mch_id: wechatpayConfig.mch_id,
nonce_str: Math.random().toString(36).substr(2, 15),
body: body.productName, // 商品描述
out_trade_no: body.orderNo, // 商户订单号
total_fee: body.totalFee, // 总金额,单位:分
spbill_create_ip: body.ipAddress, // 终端 IP
notify_url: wechatpayConfig.notify_url,
trade_type: 'JSAPI',
openid: body.openid, // 用户 openid
sign_type: 'MD5',
};
params['sign'] = this.generateSign(params); // 生成签名
// 发送统一下单请求
const responseXml = await new Promise<string>((resolve, reject) => {
request.post({
url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
body: this.buildXmlRequest(params),
}, (error, response, body) => {
if (error) {
reject(error);
} else {
resolve(body);
}
});
});
// 解析响应结果
const result = await new Promise<any>((resolve, reject) => {
parseString(responseXml, { explicitArray: false }, (err, res) => {
if (err) {
reject(err);
} else {
const data = res.xml;
if (data.return_code === 'SUCCESS' && data.result_code === 'SUCCESS') {
resolve({
appId: wechatpayConfig.appid,
timeStamp: timestamp.toString(),
nonceStr: Math.random().toString(36).substr(2, 15),
package: `prepay_id=${data.prepay_id}`,
signType: 'MD5',
paySign: this.generateSign({
appId: wechatpayConfig.appid,
timeStamp: timestamp.toString(),
nonceStr: Math.random().toString(36).substr(2, 15),
package: `prepay_id=${data.prepay_id}`,
signType: 'MD5',
})
});
} else {
reject(data.err_code_des || '统一下单失败');
}
}
});
});
return result;
}
// 生成签名
private generateSign(params) {
const keys = Object.keys(params).sort();
const string = keys.map(k => `${k}=${params[k]}`).join('&') + `&key=${wechatpayConfig.key}`;
return require('crypto').createHash('md5')
.update(string, 'utf8')
.digest('hex').toUpperCase();
}
// 构造 XML 格式请求数据
private buildXmlRequest(params) {
const builder = new require('xml2js').Builder({ rootName: 'xml', headless: true });
return builder.buildObject(params);
}
}
在上述代码中,我们通过 @Post 装饰器定义了一个 unifiedorder() 方法,用于发起统一下单请求,并返回预支付订单信息。在该方法中,我们首先构造了统一下单请求参数,并发送 HTTP POST 请求到微信支付服务器,得到预支付订单的信息。然后,我们对响应结果进行解析,并通过加密算法生成签名串和支付签名,最后将数据返回给客户端。
接着是支付结果通知控制器代码:
import { Controller, Post, Body, Req } from '@nestjs/common';
import { parseString } from 'xml2js';
@Controller('pay')
export class PayController {
@Post('notify')
async notify(@Req() request, @Body() body: any) {
const notifyXml = request.rawBody; // 获取原始的请求体内容
const result = await new Promise<any>((resolve, reject) => {
parseString(notifyXml, { explicitArray: false }, (err, res) => {
if (err) {
reject(err);
} else {
const data = res.xml;
if (data.return_code === 'SUCCESS' && data.result_code === 'SUCCESS') {
// TODO: 处理支付成功的业务逻辑,例如更新订单状态等
resolve('<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>');
} else {
reject(data.err_code_des || '支付失败');
}
}
});
});
return result;
}
}
在上述代码中,我们通过 @Post 装饰器定义了一个 notify() 方法,用于接收微信支付结果通知,并处理支付成功的业务逻辑。在该方法中,我们首先获取原始的请求体内容,并解析成 JSON 格式数据。然后,判断支付是否成功,如果成功,则执行后续的业务逻辑,例如更新订单状态等。最后,我们需要返回一个 XML 格式的响应数据,告知微信支付服务器接收到通知。
以上是在 Nest 中接入微信支付的基本流程和关键代码实现,您可以根据自己的需求进行相应的修改和优化。