vendor/wxpay/pay.php
<?php /* +-----------------------------------+ | 微信支付類 | +-----------------------------------+ */ require_once 'config.php'; class pay{ public $params = array(); private $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; private $prepay_id; //統一下單號 /** * * 獲取jsapi支付的參數 * @param array $UnifiedOrderResult 統一支付接口返回的數據 * @throws WxPayException * @return json數據,可直接填入js函數做爲參數 */ public function GetJsApiParameters() { $this->doneOrder(); $timeStamp = time(); $this->params = array( 'appId' => Config::APPID, 'timeStamp'=> "$timeStamp", 'nonceStr' => $this->getNonceStr(), 'package' => 'prepay_id=' . $this->prepay_id, 'signType' => 'MD5' ); $this->params['paySign'] = $this->MakeSign(); } /** * 統一下單 * @return array */ public function doneOrder(){ $this->params['mch_id'] = Config::MCHID; $this->params['appid'] = Config::APPID; $this->params['sign'] = $this->MakeSign($this->params); $response = $this->FromXml($this->postXmlCurl($this->ToXml(), $this->url, false)); $this->prepay_id = $response['prepay_id']; } /** * 驗證 * @return array 返回數組格式的notify數據 */ public function notify(){ //獲取xml $xml = file_get_contents('php://input'); //轉成php數組 $this->params = $this->FromXml($xml); //保存原sign $data_sign = $this->params['sign']; //sign不參與簽名 unset($this->params['sign']); $sign = $this->makeSign(); //判斷簽名是否正確 判斷支付狀態 if ($sign===$data_sign && $this->params['return_code']=='SUCCESS' && $this->params['result_code']=='SUCCESS') { $result = $this->params; $str = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'; }else{ $result = false; $str = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[簽名失敗]]></return_msg></xml>'; } echo $str; return $result; } /** * 生成簽名 * @return 簽名,本函數不覆蓋sign成員變量,如要設置簽名須要調用SetSign方法賦值 */ public function MakeSign() { //簽名步驟一:按字典序排序參數 ksort($this->params); $string = $this->ToUrlParams(); //簽名步驟二:在string後加入KEY $string = $string . '&key=' . Config::KEY; //簽名步驟三:MD5加密 $string = md5($string); //簽名步驟四:全部字符轉爲大寫 $result = strtoupper($string); return $result; } /** * 格式化參數格式化成url參數 */ public function ToUrlParams() { $buff = ''; foreach($this->params as $k => $v) { if($k!='sign' && $v!='' && !is_array($v)){ $buff .= $k . '=' . $v . '&'; } } $buff = trim($buff, '&'); return $buff; } /** * 以post方式提交xml到對應的接口url * @param string $xml 須要post的xml數據 * @param string $url url * @param bool $useCert 是否須要證書,默認不須要 * @param int $second url執行超時時間,默認30s * @throws WxPayException */ private static function postXmlCurl($xml, $url, $useCert = false, $second = 30) { $ch = curl_init(); //設置超時 curl_setopt($ch, CURLOPT_TIMEOUT, $second); //若是有配置代理這裏就設置代理 if(Config::CURL_PROXY_HOST != "0.0.0.0" && Config::CURL_PROXY_PORT != 0){ curl_setopt($ch,CURLOPT_PROXY, Config::CURL_PROXY_HOST); curl_setopt($ch,CURLOPT_PROXYPORT, Config::CURL_PROXY_PORT); } curl_setopt($ch,CURLOPT_URL, $url); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);//嚴格校驗 //設置header curl_setopt($ch, CURLOPT_HEADER, FALSE); //要求結果爲字符串且輸出到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); if($useCert == true){ //設置證書 //使用證書:cert 與 key 分別屬於兩個.pem文件 curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLCERT, Config::SSLCERT_PATH); curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLKEY, Config::SSLKEY_PATH); } //post提交方式 curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); //運行curl $data = curl_exec($ch); //返回結果 if($data){ curl_close($ch); return $data; } else { $error = curl_errno($ch); curl_close($ch); throw new Exception("curl出錯,錯誤碼:$error"); } } /** * 輸出xml字符 * @throws WxPayException **/ public function ToXml() { if(!is_array($this->params) || count($this->params) <= 0) { throw new Exception('數組數據異常!'); } $xml = '<xml>'; foreach($this->params as $key=>$val) { if(is_numeric($val)){ $xml .= '<'.$key.'>'.$val.'</'.$key.'>'; }else{ $xml .= '<'.$key.'><![CDATA['.$val.']]></'.$key.'>'; } } $xml .= '</xml>'; return $xml; } /** * 將xml轉爲array * @param string $xml * @throws WxPayException */ public function FromXml($xml) { if(!$xml){ throw new Exception('xml數據異常!'); } //將XML轉爲array //禁止引用外部xml實體 libxml_disable_entity_loader(true); $this->params = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $this->params; } /** * * 產生隨機字符串,不長於32位 * @param int $length * @return 產生的隨機字符串 */ 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; } } ?>
vendor/wxpay/config.php
<?php /** * 配置帳號信息 */ class Config { //=======【基本信息設置】===================================== // /** * TODO: 修改這裏配置爲您本身申請的商戶信息 * 微信公衆號信息配置 * * APPID:綁定支付的APPID(必須配置,開戶郵件中可查看) * * MCHID:商戶號(必須配置,開戶郵件中可查看) * * KEY:商戶支付密鑰,參考開戶郵件設置(必須配置,登陸商戶平臺自行設置) * 設置地址:https://pay.weixin.qq.com/index.php/account/api_cert * * APPSECRET:公衆賬號secert(僅JSAPI支付的時候須要配置, 登陸公衆平臺,進入開發者中心可設置), * 獲取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN * @var string */ const APPID = 'wx000000000000'; const MCHID = 'xxxxxxx'; const KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx'; const APPSECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; //=======【證書路徑設置】===================================== /** * TODO:設置商戶證書路徑 * 證書路徑,注意應該填寫絕對路徑(僅退款、撤銷訂單時須要,可登陸商戶平臺下載, * API證書下載地址:https://pay.weixin.qq.com/index.php/account/api_cert,下載以前須要安裝商戶操做證書) * @var path */ const SSLCERT_PATH = 'cert/apiclient_cert.pem'; const SSLKEY_PATH = 'cert/apiclient_key.pem'; //=======【curl代理設置】=================================== /** * TODO:這裏設置代理機器,只有須要代理的時候才設置,不須要代理,請設置爲0.0.0.0和0 * 本例程經過curl使用HTTP POST方法,此處可修改代理服務器, * 默認CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0,此時不開啓代理(若有須要才設置) * @var unknown_type */ const CURL_PROXY_HOST = "0.0.0.0";//"10.152.18.220"; const CURL_PROXY_PORT = 0;//8080; //=======【上報信息配置】=================================== /** * TODO:接口調用上報等級,默認緊錯誤上報(注意:上報超時間爲【1s】,上報不管成敗【永不拋出異常】, * 不會影響接口調用流程),開啓上報以後,方便微信監控請求調用的質量,建議至少 * 開啓錯誤上報。 * 上報等級,0.關閉上報; 1.僅錯誤出錯上報; 2.全量上報 * @var int */ const REPORT_LEVENL = 0; }
vendor/cert/apiclient_cert.pem vendor/cert/apiclient_key.pem 控制器Controller
Vendor('wechat.pay'); class Wechat extends Base { public function wxPay(){ $payobj = new \pay(); $openid = input('post.openid/s'); $needMoney = input('post.money/s'); if (!empty($openid)) { //統一下單 $orderid = $this->build_order_no(); $notify_url = 'http://xxxxxx/Wechat/notify'; $payobj->params = [ 'openid' => $openid, 'body' => 'body', 'out_trade_no' => $orderid, 'total_fee' => $needMoney * 100, 'nonce_str' => $this->getNonceStr(), 'spbill_create_ip' => getIp(), 'notify_url' => $notify_url, 'trade_type' => 'JSAPI' ]; $payobj->GetJsApiParameters(); $data = [ 'order_num' => $orderid, 'user_open_id' => $openid, 'pay_money' => $needMoney, ]; $oid = Db::name('order_pay')->insert($data); if($oid){ return ['status'=>'0','info'=>'ok','data'=>json_encode($payobj->params)]; } return ['status'=>'-1','info'=>'下單失敗,請重試']; } } //微信支付回調 public function notify(){ $payobj = new \pay(); $result = $payobj->notify(); if($result){ //成功後回調 $where['order_num'] = $result['out_trade_no']; //根據需求處理邏輯 } } /** * 生成訂單號碼 * @return string */ public function build_order_no() { return date('Ymd') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 6), 1))), 0, 8) . mt_rand(100,999); } /** * 產生隨機字符串,不長於32位 * @param int $length * @return 產生的隨機字符串 */ 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; } }
頁面上View 引用jquery; 引用layui.js 引用jweixin-1.1.0.js
<script type="text/javascript"> $.ajax({ url:"http://xxxxxx/Wechat/wxPay", type:'post', data:{'money':money,'openid':openid}, async : false, dataType: "JSON", success:function(data){ if(data.status === '0'){ var params = $.parseJSON(data.data); // alert(data.url); WeixinJSBridge.invoke('getBrandWCPayRequest', params, function(res){ if(res.err_msg == 'get_brand_wcpay_request:cancel'){ layer.open({ content: '您消了這次支付' ,skin: 'msg' ,time: 5 //2秒後自動關閉 }); }else if(res.err_msg == 'get_brand_wcpay_request:fail'){ layer.open({ content: '支付失敗,請從新支付' ,skin: 'msg' ,time: 5 //2秒後自動關閉 }); }else if(res.err_msg == 'get_brand_wcpay_request:ok'){ layer.open({ content: '支付成功' ,skin: 'msg' ,time: 5 //2秒後自動關閉 }); setTimeout(function(){ window.location.href = 'index.html'; },2000); }else{ layer.open({ content: "未知錯誤"+res.error_msg ,skin: 'msg' ,time: 5 //2秒後自動關閉 }); } }); }else{ layer.open({ content: data.info ,skin: 'msg' ,time: 5 //2秒後自動關閉 }); return false; } } }); </script>