Appearance
快速开始
前置条件
- [ ] 商户号(
pay_mch_id) - [ ] RSA 私钥
- [ ] 平台公钥
环境
| 环境 | 地址 |
|---|---|
| 测试 | https://sandbox-api.mifpay.com |
| 生产 | https://api.mifpay.com |
注意
开发阶段使用测试环境,测试完成后再切换生产环境。
支付请求示例
1. 构建参数
javascript
// 支付请求参数
const payParams = {
pay_mch_id: '1234567890', // 商户号
out_trade_no: 'ORDER' + Date.now(), // 商户订单号(唯一)
total_amount: 100, // 金额:100 分 = 1 元
subject: '测试商品', // 订单标题
body: '这是一个测试订单', // 订单描述
channel: 'wechat', // 支付渠道:wechat 或 alipay
openid: 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o', // 微信 openid
notify_url: 'https://your-domain.com/api/notify', // 异步通知地址
timestamp: String(Math.floor(Date.now() / 1000)), // 时间戳
nonce_str: generateNonceStr(), // 随机字符串
sign_type: 'RSA' // 签名类型
};
// 生成随机字符串
function generateNonceStr() {
return Math.random().toString(36).substring(2, 15);
}2. 生成签名
javascript
const crypto = require('crypto');
const fs = require('fs');
// 读取私钥
const privateKey = fs.readFileSync('private_key.pem', 'utf8');
// 生成签名
function sign(params, privateKey) {
// 1. 参数排序
const sortedKeys = Object.keys(params)
.filter(key => key !== 'sign' && params[key] !== null && params[key] !== undefined)
.sort();
// 2. 拼接字符串
const signString = sortedKeys
.map(key => `${key}=${params[key]}`)
.join('&');
console.log('待签名字符串:', signString);
// 3. 生成签名
const sign = crypto
.createSign('RSA-SHA256')
.update(signString)
.sign(privateKey, 'base64');
return sign;
}
// 添加签名到参数
payParams.sign = sign(payParams, privateKey);3. 发送请求
javascript
const https = require('https');
async function createPayOrder(params) {
const options = {
hostname: 'sandbox-api.mifpay.com',
path: '/api/v1/pay/jsapi',
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
resolve(JSON.parse(data));
});
});
req.on('error', reject);
req.write(JSON.stringify(params));
req.end();
});
}
// 发起支付请求
const result = await createPayOrder(payParams);
console.log('请求结果:', result);4. 处理响应
javascript
if (result.code === '0000') {
console.log('支付订单创建成功');
console.log('平台交易号:', result.data.trade_no);
console.log('支付参数:', result.data.pay_params);
// 将支付参数返回给前端,由前端调起支付
return {
success: true,
payParams: result.data.pay_params
};
} else {
console.error('支付订单创建失败:', result.msg);
return {
success: false,
message: result.msg
};
}5. 前端调起支付
微信支付(H5)
javascript
// 前端接收到 payParams 后
function invokeWechatPay(payParams) {
if (typeof WeixinJSBridge === 'undefined') {
alert('请使用微信浏览器打开');
return;
}
WeixinJSBridge.invoke('getBrandWCPayRequest', {
appId: payParams.appId,
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.package,
signType: payParams.signType,
paySign: payParams.paySign
}, function(res) {
if (res.err_msg === 'get_brand_wcpay_request:ok') {
// 支付成功
alert('支付成功');
window.location.href = '/pay/success';
} else {
// 支付失败或取消
alert('支付失败');
}
});
}完整示例
javascript
const crypto = require('crypto');
const fs = require('fs');
const https = require('https');
class MifPayClient {
constructor(config) {
this.mchId = config.mchId;
this.privateKey = fs.readFileSync(config.privateKeyPath, 'utf8');
this.baseUrl = config.sandbox ?
'https://sandbox-api.mifpay.com' :
'https://api.mifpay.com';
}
sign(params) {
const sortedKeys = Object.keys(params)
.filter(key => key !== 'sign' && params[key] !== null && params[key] !== undefined)
.sort();
const signString = sortedKeys
.map(key => `${key}=${params[key]}`)
.join('&');
return crypto
.createSign('RSA-SHA256')
.update(signString)
.sign(this.privateKey, 'base64');
}
async createPayOrder(params) {
const requestParams = {
pay_mch_id: this.mchId,
out_trade_no: params.outTradeNo,
total_amount: params.totalAmount,
subject: params.subject,
channel: params.channel,
openid: params.openid,
notify_url: params.notifyUrl,
timestamp: String(Math.floor(Date.now() / 1000)),
nonce_str: crypto.randomBytes(16).toString('hex'),
sign_type: 'RSA'
};
requestParams.sign = this.sign(requestParams);
return this.request('/api/v1/pay/jsapi', requestParams);
}
async request(path, params) {
const url = new URL(path, this.baseUrl);
const options = {
hostname: url.hostname,
path: url.pathname,
method: 'POST',
headers: { 'Content-Type': 'application/json' }
};
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let data = '';
res.on('data', chunk => { data += chunk; });
res.on('end', () => resolve(JSON.parse(data)));
});
req.on('error', reject);
req.write(JSON.stringify(params));
req.end();
});
}
}
// 使用
const client = new MifPayClient({
mchId: '1234567890',
privateKeyPath: './private_key.pem',
sandbox: true
});
const result = await client.createPayOrder({
outTradeNo: 'ORDER' + Date.now(),
totalAmount: 100,
subject: '测试商品',
channel: 'wechat',
openid: 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o',
notifyUrl: 'https://your-domain.com/api/notify'
});异步通知
javascript
app.post('/api/notify', (req, res) => {
const notifyData = req.body;
// 1. 验证签名
if (!verifySign(notifyData, platformPublicKey)) {
res.send('failure');
return;
}
// 2. 处理业务
if (notifyData.status === 'SUCCESS') {
updateOrderStatus(notifyData.out_trade_no, 'PAID');
}
// 3. 返回成功
res.send('success');
});切换生产
javascript
const client = new MifPayClient({
mchId: 'YOUR_PROD_MCH_ID',
privateKeyPath: './prod_private_key.pem',
sandbox: false
});