微信小程式支付全問題解決
阿新 • • 發佈:2018-11-19
這幾天在做小程式的支付,沒有用官方的SDK,這裡就純用官方的文件搞一發。
* 注作者使用的PHP,不過支付流程都是這樣
開發前必讀
主要流程
- 小程式前端傳送求參請求
- 接受請求封裝 “統一下單” 獲取
package
- 小程式接受 “統一下單” 獲取的
package
值帶入wx.requestPayment
發起支付請求
準備工具
- 申請小程式微信支付
- 拿到小程式微信支付的商戶號及設定祕鑰
注意:小程式就只需要這兩步,如果是web的話還需要設定支付目錄授權域名,文件裡面也有寫的:https://pay.weixin.qq.com/wik...
統一下單
官方文件: https://pay.weixin.qq.com/wik...
/** * 統一訂單 */ public function unifiedorder(){ // 以下配置是必填項,如有其它需求請自行配置 $config = array( 'appid' => 'xxxxxxx',//這裡是小程式appid 'mch_id' => 'xxxxxxx',//商戶ID 'nonce_str' => $this->getNonceStr(),//隨機字串 'body' => '這裡是測試 - 測試',//請按照文件要求填寫合格名稱 'out_trade_no' => time().$this->getNonceStr(2),//流水單號 'total_fee' => '20',//金額,分為單位,這裡是0.2元 'spbill_create_ip' => '123.123.123.123',//當前IP 'notify_url' => 'http://xxxx.com',//請恕我愚昧,我沒搞懂他有什麼用 'trade_type' => 'JSAPI',//必須填寫JSAPI 'openid' => 'xxxxxxxx'//當前使用者的openid,在trade_type=JSAPI的時候,此項就變成必填項了 ); $config['sign'] = $this->getSignPay($config); $xmlData = $this->ToXml($config);//轉成xml資料 $postData = $this->http_post($xmlData); $arrayData = $this->FromXml($postData); if($arrayData['return_code'] == 'SUCCESS' || $arrayData['result_code'] == 'SUCCESS'){ return $arrayData['prepay_id'];//重點來了:走了這麼多路,就為了這個值。到這一步就證明成功一多半了。 }else{ return $arrayData;//返回錯誤 } } /** * 獲取簽名 */ public function getSignPay($config){ $key = 'xxxxxxx';//商戶祕鑰,就是自己生成的32位密碼 $strA = 'appid='.$config['appid'].'&body='.$config['body'].'&mch_id='.$config['mch_id'].'&nonce_str='.$config['nonce_str'].'&notify_url='.$config['notify_url'].'&spbill_create_ip'.$config['spbill_create_ip'].'&total_fee='.$config['total_fee'].'&trade_type='.$config['trade_type'];//ASCII 字典序 $strB = $strA.'&key='.$key; $sign = strtoupper(md5($strB));//大寫MD5 return $sign; } /** * 隨機字串 32位 */ public function getNonceStr($length = 32){ $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; $str =""; for ( $i = 0; $i < $length; $i++ ) { $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1); } return $str; } /** * array轉XML */ public function ToXml($data){ if(!is_array($data) || count($data) <= 0){ throw new WxPayException("陣列資料異常!"); } $xml = "<xml>"; foreach ($data as $key=>$val){ $xml.="<".$key.">".$val."</".$key.">"; } $xml.="</xml>"; return $xml; } /** * xml轉array */ public function FromXml($xml){ if(!$xml){ throw new WxPayException("xml資料異常!"); } libxml_disable_entity_loader(true); $this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $this->values; } /** * post 請求 */ public function http_post($url,$param,$post_file=false){ $oCurl = curl_init(); if(stripos($url,"https://")!==FALSE){ curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 } if (PHP_VERSION_ID >= 50500 && class_exists('\CURLFile')) { $is_curlFile = true; } else { $is_curlFile = false; if (defined('CURLOPT_SAFE_UPLOAD')) { curl_setopt($oCurl, CURLOPT_SAFE_UPLOAD, false); } } if (is_string($param)) { $strPOST = $param; }elseif($post_file) { if($is_curlFile) { foreach ($param as $key => $val) { if (substr($val, 0, 1) == '@') { } } } $strPOST = $param; } else { $aPOST = array(); foreach($param as $key=>$val){ $aPOST[] = $key."=".urlencode($val); } $strPOST = join("&", $aPOST); } curl_setopt($oCurl, CURLOPT_URL, $url); curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 ); curl_setopt($oCurl, CURLOPT_POST,true); curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST); $sContent = curl_exec($oCurl); $aStatus = curl_getinfo($oCurl); curl_close($oCurl); if(intval($aStatus["http_code"])==200){ return $sContent; }else{ return false; } }
好了現在已經獲取到了 prepay_id
的值,我們的統一下單就算完成了,其實我更樂意叫他資料封裝
小程式微信支付
官方文件: https://developers.weixin.qq....
先來一個插曲,首先我們小程式的前端需要去觸發pay,實現的功能肯定是要點選小程式的一個觸發,然後才能支付對吧,
pay:function(e){ //這裡面使用post去請求。然後通過我接下來要寫的API支付程式碼獲取小程式支付引數 success:function(res){ wx.requestPayment({ 'timeStamp':toString(res.timeStamp),//這裡轉字串,這裡被坑過,不轉的話可能會出現total_fee為空 'nonceStr':toString(res.nonceStr), 'package':toString(res.package), 'signType':'MD5', 'paySign':toString(res.paySign), success:function(res){ console.log(res);//這裡可以跳轉到帶參地址 }, fail:function(res){ console.info('支付失敗',res); }, complete:function(){ console.info('支付觸發回撥',res); } }) } }
api支付
也就是上面小程式程式碼的後端請求地址
/**
* api組裝資料
*/
public function payApiBlack(){
$appid = 'xxxxxx';//小程式appid,上面有重複,不過這樣比較直觀
$timeStamp = time();
$nonceStr = $this->getNonceStr();//這是呼叫統一下單裡面的方法,為了直觀,我把這些程式碼都寫在了一個類裡
$package = 'prepay_id='.$this->unifiedorder();
$signType = 'MD5';
$key = 'xxxxxx';//這裡是商戶祕鑰,32位,同上面也有
$strA = 'appId='.$appid.'&nonceStr='.$nonceStr.'package='.$package.'&= signType='.$signType.'&timeStamp='.$timeStamp.'&key='.$key;
$paySign = strtoupper(md5($strA));
$data = array(
'appid'=>$appid,
'timeStamp'=>$timeStamp,
'nonceStr'=>$nonceStr,
'package'=>$package,
'signType'=>$signType
);
return $data;//返回給小程式
}
以上就是全部程式碼,還有小程式的支付回撥沒有什麼資訊,所以,我的思路判斷success後進行跳轉帶參
//此程式碼為wx.requestPayment success,部分程式碼省略
//res 回撥引數包括使用者uid及其他重要傳遞
success:function(res){
wx.redirect({
url:'pages/pay/done?uid='+res.uid
})
}
當然那個統一下單的 notify_url
好像與回撥有關,至於怎麼用,試了幾次回撥的CURD都沒反應,所以有空再研究啦。
以上程式碼僅作為支付流程解釋,所以真正要用到專案上,還是去套官方的SDK吧,畢竟涉及到錢嘛
原文地址:https://segmentfault.com/a/1190000015867859