Skip to content

快速开始

前置条件

  • [ ] 商户号(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
});

更多

Copyright © 2024 米付科技