微信支付原理javascript
目前只講解關於H5頁面支付相關php
支付流程前端
咱們的訂單系統-->收到了訂單-->會把訂單相關信息發送給微信訂單服務器java
微信訂單服務器收到咱們的請求--開始下訂單此過程叫作訂單預處理--預處理完畢會返回1個預處理ID表示微信這邊已經處理完畢算法
咱們能夠用這個預處理ID和公衆號應用密匙用微信支付js sdk啓動微信支付api
1:微擎的site基類封裝了pay方法,該方法用於向微擎支付中心傳遞參數 而且打開微擎的支付中心
安全
簡要介紹下該方法所執行的業務,由於目前只針對微信支付,卡卷部分不涉及服務器
支付中心須要接受如下參數微信
商品名稱 :titleapp
模塊訂單系統的商品訂單:ordersn
商品金額:fee
優惠:key-value形式
封裝其餘參數:模塊名字 公衆號ID
若是支付金額《=0 直接調用site對象的payResult模擬支付成功
該模擬是模擬前端支付成功回調 需用用from==return判斷
微擎本身維護了核心支付表,請查看core_paylog
該表使用tid(模塊的訂單主鍵)和uniacid(公衆號)來關聯具體模塊的訂單系統
查詢當前公衆號的這個訂單是否被支付過 支付過提示 禁止重複支付
該業務只用了Message提示錯誤,可是代碼還會向下執行 沒有使用exit終止 此處做爲BUG
查詢是否配置了支付參數 即公衆號的setting信息 沒有配置提示出錯
一樣沒有執行exit語句 此處爲BUG
最終導航到支付中心:common/paycenter
2:在支付中心咱們目前只關注微信支付
微信支付表單被提交到了app\source\mc\cash.ctrl.php
該PHP的做用:用於把支付信息存入微擎支付歷史記錄表
大概介紹下業務:這個表的做用很是大 能夠體驗到微擎的設計前端回調
封裝core_paylog的全部字段信息 插入記錄表 設置訂單狀態爲0 未支付
構建支付參數信息 該參數是真正準備傳遞給微信訂單服務器的
$ps['tid'] = $log['plid'];//獲取支付記錄表主鍵 傳遞給微信的訂單,對應統一支付 api的商戶訂單號 $ps['user'] = $_W['fans']['from_user'];//支付人 $ps['fee'] = $log['card_fee'];//支付金額,對應統一支付API的total_fee/100 $ps['title'] = $params['title'];//對應統一支付API的body即商品描述 封裝加密數據 請求:/payment/wechat/pay.php 3:pay.php的核心業務 首先驗證加密是否匹配 匹配繼續向下執行 一樣進行了1次重複的系統核心訂單表的支付結果檢測,重複了! 開始構建參數和微信訂單服務器通訊 4:通訊函數wechat_build($params, $wechat) $params->即第2步封裝的參數信息 $wechar-->支付配置信息 裏面配置支付相關信息好比 partnerId:商戶號 註冊時分配的財付通號 構建通訊數據 $package = array(); $package['appid'] = $wechat['appid']; $package['mch_id'] = $wechat['mchid'];//微信支付分配的商戶號 $package['nonce_str'] = random(8);//隨機字符串,不長於32位。推薦隨機數生成算法 $package['body'] = $params['title'];//商品或支付單簡要描述 $package['attach'] = $_W['uniacid'];//該字段主要用於商戶攜帶訂單的自定義數據 $package['out_trade_no'] = $params['tid'];//商戶訂單號 $package['total_fee'] = $params['fee'] * 100;//訂單總金額,單位爲分, $package['spbill_create_ip'] = CLIENT_IP;//終端IP $package['time_start'] = date('YmdHis', TIMESTAMP);//交易起始時間 $package['time_expire'] = date('YmdHis', TIMESTAMP + 600);//交易結束時間 $package['notify_url'] = $_W['siteroot'] . 'payment/wechat/notify.php';//通知回調url $package['trade_type'] = 'JSAPI'; $package['openid'] = $_W['fans']['from_user'];//trade_type=JSAPI,此參數必傳,用戶在商戶appid下的惟一標 按照密鑰算法加密請求數據 即吧全部的請求參數按照URL_QUERY格式封裝 好比a=1&b=2 而後再用結果 拼接字符串key=你的微信支付密鑰 結果用md5加密 結果做爲sing的value $package['sign'] = strtoupper(md5($string1)) 最終生成XML文件:$dat = array2xml($package); 和微信訂單系統交互
$response = ihttp_request('https://api.mch.weixin.qq.com/pay/unifiedorder', $dat); 交互成功會返回預處理ID即 $prepayid = $xml->prepay_id; 使用預處理ID準備啓用JS支付SDK 繼續填充其餘參數 如下參數用於啓動JSSDK $wOpt['appId'] = $wechat['appid']; $wOpt['timeStamp'] = TIMESTAMP; $wOpt['nonceStr'] = random(8); $wOpt['package'] = 'prepay_id='.$prepayid; $wOpt['signType'] = 'MD5'; ksort($wOpt, SORT_STRING); foreach($wOpt as $key => $v) { $string .= "{$key}={$v}&"; } $string .= "key={$wechat['signkey']}"; $wOpt['paySign'] = strtoupper(md5($string));
5:微擎預留了前端通知接口
<script type="text/javascript"> document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { WeixinJSBridge.invoke('getBrandWCPayRequest', { 'appId' : '<?php echo $wOpt['appId'];?>', 'timeStamp': '<?php echo $wOpt['timeStamp'];?>', 'nonceStr' : '<?php echo $wOpt['nonceStr'];?>', 'package' : '<?php echo $wOpt['package'];?>', 'signType' : '<?php echo $wOpt['signType'];?>', 'paySign' : '<?php echo $wOpt['paySign'];?>' }, function(res) { if(res.err_msg == 'get_brand_wcpay_request:ok') { location.search += '&done=1';//支付成功之後再次會訪問pay.php執行done=1代碼塊 } else { //alert('啓動微信支付失敗, 請檢查你的支付參數. 詳細錯誤爲: ' + res.err_msg); history.go(-1); } }); }, false); </script> 該JS是用戶在支付成功之後點擊手機界面右上角完成按鈕會觸發get_brand_wcpay_request:ok 看下該鉤子的寫法 $site = WeUtility::createModuleSite($log['module']); if(!is_error($site)) { $method = 'payResult'; if (method_exists($site, $method)) { $ret = array(); $ret['weid'] = $log['uniacid'];//該鉤子獲取的信息都是從支付記錄中查詢到了 不得不說微擎的思路很贊 $ret['uniacid'] = $log['uniacid']; $ret['result'] = $log['status'] == '1' ? 'success' : 'failed';//該字段會在微信支付成功後觸發回調URL更新 $ret['type'] = $log['type']; $ret['from'] = 'return';//用於識別是JS返回仍是微信URL通知,因此咱們能夠同該參數的from屬性識別是否支付成功,該方式不安全,若是用戶支付完畢不點擊完成直接關閉頁面 是接受不到該參數的 即from一直爲空 致使系統認定你覺得沒有支付 $ret['tid'] = $log['tid']; $ret['user'] = $log['openid']; $ret['fee'] = $log['fee']; $ret['tag'] = $tag; $ret['is_usecard'] = $log['is_usecard']; $ret['card_type'] = $log['card_type']; $ret['card_fee'] = $log['card_fee']; $ret['card_id'] = $log['card_id']; exit($site->$method($ret)); }