1. 程式人生 > >android 微信支付 + node.js 服務端

android 微信支付 + node.js 服務端

實現微信支付有很多方式,至於開始菜鳥的我使用app支付,後期考慮安全性問題,只好把統一下單,得到prepay_id之後再次簽名sign全在服務端寫了,廢話不多說,進入操作。

做任何東西,我喜歡瞭解整個流程,然後一步一步做起來,開始看清微信支付業務流程圖

業務流程步驟寫的很清楚。下面一步一步操作:

服務端提供介面,服務端需要拿到訂單號、商品介紹、商品價格(單位是分)

服務端對微信服務端發出統一下單請求

https://api.mch.weixin.qq.com/pay/unifiedorder

xml引數(注意引數名)

<xml>
   <appid>wx2421b1c4370ec43b</appid>
   <attach>支付測試</attach>
   <body>APP支付測試</body>
   <mch_id>10000100</mch_id>
   <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
   <notify_url>http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php</notify_url>
   <out_trade_no>1415659990</out_trade_no>
   <spbill_create_ip>14.23.150.211</spbill_create_ip>
   <total_fee>1</total_fee>
   <trade_type>APP</trade_type>
   <sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>

請求返回結果是success說明簽名成功

快捷測試方法:

使用https://pay.weixin.qq.com/wiki/tools/signverify/

得到xml引數簽名之後請求看返回結果

推薦一個線上http介面測試工具  http://coolaf.com/

在裡面放入http地址https://api.mch.weixin.qq.com/pay/unifiedorder

post引數放簽名xml引數

返回結果就可以檢測簽名是否成功,成功之後差不多成功了一半

服務端程式碼:weixinpay.js

var request = require('request');
var xml2js = require('xml2js');

function paysign(appid,attach,body,mch_id,nonce_str,notify_url,out_trade_no,spbill_create_ip,total_fee,trade_type) {    //統一下單簽名
    var ret = {
        appid: appid,
        attach: attach,
        body: body,
        mch_id: mch_id,
        nonce_str: nonce_str,
        notify_url:notify_url,
        out_trade_no:out_trade_no,
        spbill_create_ip:spbill_create_ip,
        total_fee:total_fee,
        trade_type:trade_type
    };
    var string = raw(ret);
    var key = '微信商戶金鑰'; 
    string = string + '&key='+key;  //key為在微信商戶平臺(pay.weixin.qq.com)-->賬戶設定-->API安全-->金鑰設定
    var crypto = require('crypto');
    console.log("簽名");
    console.log(crypto.createHash('md5').update(string,'utf8').digest('hex').toUpperCase());
    return crypto.createHash('md5').update(string,'utf8').digest('hex').toUpperCase();
};

function raw(args) {
  var keys = Object.keys(args);
  keys = keys.sort()                              
  var newArgs = {};
  keys.forEach(function (key) {
    newArgs[key.toLowerCase()] = args[key];
  });

  var string = '';
  for (var k in newArgs) {
    string += '&' + k + '=' + newArgs[k];
  }
  string = string.substr(1);
  console.log(string);
  return string;
};
exports.pay = function (req, res)    //微信支付函式
{
    var url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    var appid = '應用微信中的id';
    var mch_id = '商戶號';
    var notify_url = 'www.spamao.com';   
    var out_trade_no = req.query.orderId;<span style="background-color: rgb(255, 255, 102);"><span style="color:#FFFF66;"><span style="background-color: rgb(255, 255, 255);"></span></span></span>//客戶端<span style="background-color: rgb(255, 255, 102);"><span style="color:#FFFF66;"><span style="background-color: rgb(255, 255, 255);"></span></span></span>訂單號
    var total_fee = req.query.orderRate;<span style="background-color: rgb(255, 255, 102);"><span style="color:#FFFF66;"><span style="background-color: rgb(255, 255, 255);"></span></span></span>//客戶端<span style="background-color: rgb(255, 255, 102);"><span style="color:#FFFF66;"><span style="background-color: rgb(255, 255, 255);"></span></span></span>商品價格
    var attach = 'spamao使用者版app';
    var body = req.query.content;  //客戶端商品描述
    var nonce_str = '隨機32位之內字串';
    var formData = "<xml>";
    formData += "<appid>"+appid+"</appid>"; //appid
    formData += "<attach>"+attach+"</attach>"; //附加資料
    formData += "<body>"+body+"</body>"; //商品或支付單簡要描述
    formData += "<mch_id>"+mch_id+"</mch_id>"; //商戶號
    formData += "<nonce_str>"+nonce_str+"</nonce_str>"; //隨機字串,不長於32位
    formData += "<notify_url>"+notify_url+"</notify_url>"; //支付成功後微信伺服器通過POST請求通知這個地址
    
    formData += "<out_trade_no>"+out_trade_no+"</out_trade_no>"; //訂單號
    formData += "<spbill_create_ip>112.124.60.251</spbill_create_ip>"; //服務端ip
    formData += "<total_fee>"+total_fee+"</total_fee>"; //金額
    formData += "<trade_type>APP</trade_type>"; //型別APP
    formData += "<sign>" + paysign(appid,attach,body,mch_id,nonce_str,notify_url,out_trade_no,'112.124.60.251',total_fee,'APP') + "</sign>";
    formData += "</xml>";
    request(
    {
        url : url,
        method : 'POST',
        body : formData
    }, function (err, response, body)
    {
        if (!err && response.statusCode == 200)
        {
            console.log(body);
               var parser = new xml2js.Parser({ trim:true, explicitArray:false, explicitRoot:false });//解析簽名結果xml轉json
                parser.parseString(body, function(err, result){
                        var timeStamp = Date.parse(new Date()) / 1000; 
                        var sign = paySignTwo(appid,nonce_str,'Sign=WXPay',mch_id,result['prepay_id'],timeStamp);//得到prepay再次簽名
                        res.send({result: {'appid':appid, 'mch_id': mch_id, 'prepay_id': result['prepay_id'], 'nonce_str': nonce_str, 'time_stamp':timeStamp, 'package_value':'Sign=WXPay', 'sign': sign}});//返回客戶端資料
                });

        }
    }
    );
}
function buildXML(json){
    var builder = new xml2js.Builder();
    return builder.buildObject(json);
};
function paySignTwo(appid,notifystr,packagevalue,mchid,prepayid,timestamp) {    //引數名不可改,必須嚴格一模一樣(在此我掉坑一次)
    var ret = {
        appid: appid,
        noncestr: notifystr,
        package: packagevalue,
        partnerid: mchid,
        prepayid: prepayid,
        timestamp:timestamp
    };
    var string = raw(ret);
    var key = '商戶金鑰';
    string = string + '&key='+key;  //key為在微信商戶平臺(pay.weixin.qq.com)-->賬戶設定-->API安全-->金鑰設定
    var crypto = require('crypto');
    console.log("簽名");
    console.log(crypto.createHash('md5').update(string,'utf8').digest('hex').toUpperCase());
    return crypto.createHash('md5').update(string,'utf8').digest('hex').toUpperCase();
};

再次簽名引數名:

剩下就是客戶端的事了,短短几行程式碼調起微信支付介面請求。

req = new PayReq();
							req.appId			= weixinOrder.getString("appid");
							req.partnerId		= weixinOrder.getString("mch_id");
							req.prepayId		= weixinOrder.getString("prepay_id");
							req.nonceStr		= weixinOrder.getString("nonce_str");
							req.timeStamp		= weixinOrder.getString("time_stamp");
							req.packageValue	= weixinOrder.getString("package_value");
							req.sign			= weixinOrder.getString("sign");
							Log.i("sign-----jieguo", weixinOrder.getString("sign"));
							Toast.makeText(WorkPay.this, "正常調起支付", Toast.LENGTH_SHORT).show();
							// 在支付之前,如果應用沒有註冊到微信,應該先呼叫IWXMsg.registerApp將應用註冊到微信
							api.registerApp(Constants.APP_ID);
							api.sendReq(req);

manifest需要配置:
 <activity android:name="app包名.WorkPay"   <!--支付類需加上以下intent-filter內容-->
             android:exported="true"
            android:launchMode="singleTop">
           <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:scheme="wxb6ff1aa7f0350ccf"/>
            </intent-filter> 
        </activity>
        <activity
            android:name="aizhinong.yys.sbm.wxapi.WXPayEntryActivity"
            android:exported="true"
            android:launchMode="singleTop">
             
        </activity>

提醒:所有引數必須跟微信給的引數名一致(引數個數不能少),(使用微信簽名工具是一定需要分辨測試包簽名還是正式釋出簽名)

如果一切順利,恭喜你接入支付成功。

發這個目的是為了node.js服務端的同胞們多點資源共享