Appearance
快速开始
前置条件
- [ ] 商户号(
merchant_no) - [ ] RSA 私钥
- [ ] 平台公钥
环境
| 环境 | 地址 |
|---|---|
| 测试 | https://sandbox-api.meepay.org |
| 生产 | https://api.meepay.org |
注意
开发阶段使用测试环境,测试完成后再切换生产环境。
支付请求示例
1. 构建认证和业务参数
javascript
// 认证参数(用于生成签名)
const authParams = {
service_id: 'SVC1234567890', // 服务商号
timestamp: String(Math.floor(Date.now() / 1000)), // 时间戳
nonce_str: generateNonceStr() // 随机字符串
};
// 业务参数
const payParams = {
merchant_no: '1234567890', // 商户号
out_trade_no: 'ORDER' + Date.now(), // 商户订单号(唯一)
total_amount: 100, // 金额:100 分 = 1 元
subject: '测试商品', // 订单标题
pay_method: 'wechat', // 支付方式:wechat 或 alipay
sub_openid: 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o', // 微信 sub_openid
notify_url: 'https://your-domain.meepay.org/api/notify' // 异步通知地址
};
// 生成随机字符串
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(authParams, privateKey) {
// 1. 参数排序
const sortedKeys = Object.keys(authParams)
.filter(key => key !== 'sign' && authParams[key] !== null && authParams[key] !== undefined)
.sort();
// 2. 拼接字符串
const signString = sortedKeys
.map(key => `${key}=${authParams[key]}`)
.join('&');
console.log('待签名字符串:', signString);
// 3. 生成签名
const sign = crypto
.createSign('RSA-SHA256')
.update(signString)
.sign(privateKey, 'base64');
return sign;
}
const signValue = sign(authParams, privateKey);
const authorization = `RSA service_id=${authParams.service_id},timestamp=${authParams.timestamp},nonce_str=${authParams.nonce_str},sign=${signValue}`;3. 发送请求
javascript
const https = require('https');
async function createPayOrder(authHeader, params) {
const options = {
hostname: 'sandbox-api.meepay.org',
path: '/v1/pay/jsapi',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': authHeader
}
};
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(authorization, payParams);
console.log('请求结果:', result);4. 处理响应
javascript
if (result.code === '00000') {
console.log('支付订单创建成功');
console.log('商户单号:', result.data.pay_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
wx.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.package,
signType: payParams.signType,
paySign: payParams.paySign,
success: function(res) {
// 支付成功
wx.showToast({ title: '支付成功' });
},
fail: function(err) {
// 支付失败
wx.showToast({ title: '支付失败', icon: 'error' });
}
});支付宝支付(H5)
javascript
// 支付宝 H5 支付需要跳转到支付宝支付页面
function invokeAlipay(payUrl) {
// 方式1: 直接跳转到支付宝支付页面
window.location.href = payUrl;
// 方式2: 在新窗口打开(适用于PC端)
window.open(payUrl, '_blank');
}
// 如果返回的是支付宝表单,需要提交表单
function submitAlipayForm(formHtml) {
const div = document.createElement('div');
div.innerHTML = formHtml;
document.body.appendChild(div);
const form = div.querySelector('form');
if (form) {
form.submit();
}
}支付宝小程序支付
javascript
my.tradePay({
tradeNO: transactionId, // 从后端获取的支付宝交易号
success: function(res) {
if (res.resultCode === '9000') {
// 支付成功
my.showToast({ content: '支付成功' });
} else {
// 支付失败
my.showToast({ content: '支付失败: ' + res.resultMessage });
}
},
fail: function(err) {
my.showToast({ content: '支付失败' });
}
});完整示例
javascript
const crypto = require('crypto');
const fs = require('fs');
const https = require('https');
class MifPayClient {
constructor(config) {
this.serviceId = config.serviceId;
this.privateKey = fs.readFileSync(config.privateKeyPath, 'utf8');
this.baseUrl = config.sandbox ?
'https://sandbox-api.meepay.org' :
'https://api.meepay.org';
}
buildAuthorization() {
const authParams = {
service_id: this.serviceId,
timestamp: String(Math.floor(Date.now() / 1000)),
nonce_str: crypto.randomBytes(16).toString('hex')
};
const sign = this.sign(authParams);
return `RSA service_id=${authParams.service_id},timestamp=${authParams.timestamp},nonce_str=${authParams.nonce_str},sign=${sign}`;
}
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 authorization = this.buildAuthorization();
const requestParams = {
merchant_no: params.merchantNo,
out_trade_no: params.outTradeNo,
total_amount: params.totalAmount,
subject: params.subject,
pay_method: params.payMethod,
sub_openid: params.subOpenid,
notify_url: params.notifyUrl
};
return this.request('/v1/pay/jsapi', requestParams, authorization);
}
async request(path, params, authorization) {
const url = new URL(path, this.baseUrl);
const options = {
hostname: url.hostname,
path: url.pathname,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': authorization
}
};
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({
serviceId: 'SVC1234567890',
privateKeyPath: './private_key.pem',
sandbox: true
});
const result = await client.createPayOrder({
merchantNo: '1234567890',
outTradeNo: 'ORDER' + Date.now(),
totalAmount: 100,
subject: '测试商品',
payMethod: 'wechat',
subOpenid: 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o',
notifyUrl: 'https://your-domain.meepay.org/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({
serviceId: 'YOUR_PROD_SERVICE_ID',
merchantNo: 'YOUR_PROD_MERCHANT_NO',
privateKeyPath: './prod_private_key.pem',
sandbox: false
});