PS:微信支付基础条件
1.一个认证的服务号
2.在服务号后台申请微信支付,成功后获得一个商户号
3.在商户号里面选择native支付,这个就是扫码支付,按照提示开通这项。
4.按照微信支付文档的说法,扫码支付分两种模式
模式一:生成的二维码没有失效性,但是操作比较复杂,中间步骤比较多,容易出现很多问题。
模式二:生成的二维码有两小时时间限制,但是我感觉也十分够用了,这个步骤比较简单,很容易实现。
按照时序图,大概也就分两步
代码结构:
配置文件:
微信支付类:
<?php /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ namespace tools; use thinkConfig; /** * Description of WxPay * * @author admin */ class WxPay { /** * 获取签名 * @param type $arr * @return type */ public function getSign($arr) { //去除数组的空值 array_filter($arr); if(isset($arr['sign'])){ unset($arr['sign']); } //排序 ksort($arr); //组装字符 $str = $this->arrToUrl($arr) . '&key=' . Config::get('wx_pay')['key']; //使用md5 加密 转换成大写 return strtoupper(md5($str)); } /** * 校验签名 * @param type $arr * @return boolean */ public function checkSign($arr){ //生成新签名 $sign = $this->getSign($arr); //和数组中原始签名比较 if($sign == $arr['sign']){ return true; }else{ return false; } } /** * 获取带签名的数组 * @param array $arr * @return type */ public function setSign($arr) { $arr['sign'] = $this->getSign($arr); return $arr; } /** * 数组转URL字符串 不带key * @param type $arr * @return type */ public function arrToUrl($arr) { return urldecode(http_build_query($arr)); } /** * 记录到文件 * @param type $file * @param type $data */ public function logs($file,$data) { $data = is_array($data) ? print_r($data,true) : $data; file_put_contents('./public/paylogs/' .$file, $data); } /** * 接收POST推送 * @return type */ public function getPost() { return file_get_contents('php://input'); } /** * Xml 文件转数组 * @param type $xml * @return string */ public function XmlToArr($xml) { if($xml == '') return ''; libxml_disable_entity_loader(true); $arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $arr; } /** * 数组转XML * @param type $arr * @return string */ public function ArrToXml($arr) { if(!is_array($arr) || count($arr) == 0) return ''; $xml = "<xml>"; foreach ($arr as $key=>$val) { if (is_numeric($val)){ $xml.="<".$key.">".$val."</".$key.">"; }else{ $xml.="<".$key."><![CDATA[".$val."]]></".$key.">"; } } $xml.="</xml>"; return $xml; } /** * 发送POST请求 * @param type $url * @param type $postfields * @return type */ public function postStr($url,$postfields) { $ch = curl_init(); $params[CURLOPT_URL] = $url; //请求url地址 $params[CURLOPT_HEADER] = false; //是否返回响应头信息 $params[CURLOPT_RETURNTRANSFER] = true; //是否将结果返回 $params[CURLOPT_FOLLOWLOCATION] = true; //是否重定向 $params[CURLOPT_POST] = true; $params[CURLOPT_SSL_VERIFYPEER] = false;//禁用证书校验 $params[CURLOPT_SSL_VERIFYHOST] = false; $params[CURLOPT_POSTFIELDS] = $postfields; curl_setopt_array($ch, $params); //传入curl参数 $content = curl_exec($ch); //执行 curl_close($ch); //关闭连接 return $content; } /** * 统一下单 * @param type $params * @return boolean */ public function unifiedorder($params) { //获取到带签名的数组 $params = $this->setSign($params); //数组转xml $xml = $this->ArrToXml($params); //发送数据到统一下单API地址 $data = $this->postStr(Config::get('wx_pay')['uourl'], $xml); $arr = $this->XmlToArr($data); if($arr['result_code'] == 'SUCCESS' && $arr['return_code'] == 'SUCCESS'){ return $arr; }else{ $this->logs('error.txt', $data); return false; } } }
index控制器演示代码
<?php namespace appindexcontroller; use appcommoncontrollerHomeBase; use toolsWxPay; use thinkConfig; use phpqrcodeApiQrcode; use thinkRequest; use thinkCache; use toolsRetJosn; class Index extends HomeBase { /** * 首页 * @param WxPay $wxpay * @return mixed */ public function index(WxPay $wxpay) { //$wxpay->logs('log.txt',['213123','123123123']); echo 'index'; } /** * 扫码支付-模式二 * @param WxPay $wxpay */ public function index2(Request $request, WxPay $wxpay) { if ($request->isGet()) { return $this->fetch(); } } /** * 统一下单,生成二维码 */ public function getQrUrl(Request $request) { $pid = $request->get('id'); //调用统一下单API $params = [ 'appid' => Config::get('wx_pay')['appid'], 'mch_id' => Config::get('wx_pay')['mchid'], 'nonce_str' => md5(time()), 'body' => '订单号:'.$pid, 'out_trade_no' => $pid, 'total_fee' => 2, 'spbill_create_ip' => $_SERVER['SERVER_ADDR'], 'notify_url' => Config::get('wx_pay')['notify'], 'trade_type' => 'NATIVE', 'product_id' => $pid ]; $wxpay = new WxPay(); $arr = $wxpay->unifiedorder($params); if (!empty($arr['code_url'])) { Cache::set('send' . $params['out_trade_no'], $params['total_fee'], 3600); ApiQrcode::png($arr['code_url']); } else { return '下单失败!'; } } /** * 接收腾讯推送支付通知 * @param WxPay $wxpay */ public function backOrder(WxPay $wxpay) { try { // 获取腾讯传回来的通知数据 $xml = $wxpay->getPost(); // 将XML格式的数据转换为数组 $arr = $wxpay->XmlToArr($xml); $wxpay->logs('logs.txt', '1'); // 验证签名 if ($wxpay->checkSign($arr)) { Cache::set('back'.$arr['out_trade_no'],$arr['total_fee'],3600); } $wxpay->logs('logs.txt', '2'); $params = [ 'return_code' => 'SUCCESS', 'return_msg' => 'OK' ]; echo $wxpay->ArrToXml($params); } catch (Exception $e) { $wxpay->logs('logs.txt', $e->getMessage()); exit(); } } /** * 查询支付状态 * @param Request $request * @return type */ public function checkSuccess(Request $request){ $pid = $request->get('id'); if (Cache::get('send'.$pid) == Cache::get('back'.$pid)) { return RetJosn::successJson('支付成功'); } else { return RetJosn::errorJson(Cache::get('send'.$pid).'|'.Cache::get('back'.$pid)); } } }
二维码页面代码:
<!DOCTYPE html> <!-- To change this license header, choose License Headers in Project Properties. To change this template file, choose Tools | Templates and open the template in the editor. --> <html> <head> <title>TODO supply a title</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <div class="btn-box"> <button onclick="getQrcode()">刷新二维码</button> </div> <div class="qrcode-box"> <img id="qrimg" src=""> </div> <div class="status-box"> <h2 id="status"></h2> </div> </body> <script src="__STATIC__/default/js/jquery-1.9.1.min.js"></script> <script> (function () { getQrcode();//二维码生成 })(); /*验证码生成*/ function getQrcode() { var id = getId(10); var url = "{:url('Index/getQrUrl')}" + "?id=" + id; $("#qrimg").attr("src", url); sessionStorage.setItem('id',id); checkSuccess();//轮询支付状态 } /*生成唯一Id*/ function getId(length){ var tmp = Date.parse( new Date() ).toString(); tmp = tmp.substr(0,length); return tmp; } /*轮询支付状态*/ function checkSuccess(){ var interval = window.setInterval(function(){ var id = sessionStorage.getItem('id'); $.ajax({ url:"{:url('Index/checkSuccess')}?id=" + id, dataType:'json', success:function(res) { if (res.code == 200) { $('#status').html('订单号:'+id+','+res.msg);
clearInterval(interval); } } }) },2000) } </script> </html>
效果演示:
生成二维码,并开始启动轮询
手机上支付订单:
轮询到成功支付的状态: