tp5 -- 微信公众号支付
近来期间比较忙, 忙完之后发现最近有挺多的东西没有整理,于是乎。就将以前用到的一些小东西整理了一下。
如果您需要前后端分离的JASPI或者APP支付 请查看:TP5 -- 微信支付整合(APP,JSAPI)
如果对您有帮助,则是我最大的幸运。
本篇主要是说了一下整合TP5的微信公众号支付。
不过由于最近TP6已经出了,小伙伴们要记得向最新的进发哦。
好了,废话不多说了。开始。
首先呢,需要引入我们封装好的类库:
同样在 extend/ 下
因为会将支付类库放在一起,于是就在extend 文件夹下创建了一个pay文件夹用来存储所有类文件。
以下内容为wxpay类的内容:
namespace pay; class Wxpay { private $config =[ "appid" => "********", // 公众号ID "mch_id" => "********", // 商户号 "notify_url" => "********", // 回调地址 "key" => "********", // 微信支付 商户秘钥KEY ]; public function index($param,$openid) { $order=array( 'body' => $param['body'],// 商品描述 'total_fee' => intval($param['total']*100),// 订单金额 以(分)为单位 // 'total_fee' => 1, 'out_trade_no' => $param['order_sn'],// 订单号 'trade_type' => 'JSAPI',// JSAPI公众号支付 'openid' => $openid// 获取到的openid ); $userip = $param['userip']; // 统一下单 获取prepay_id $unified_order=$this->unifiedOrder($order,$userip); // 获取当前时间戳 $time=time(); // 组合jssdk需要用到的数据 $data=array( 'appId' =>$this->config['appid'], //appid 'timeStamp' =>strval($time), //时间戳 'nonceStr' =>$unified_order['nonce_str'],// 随机字符串 'package' =>'prepay_id='.$unified_order['prepay_id'],// 预支付交易会话标识 'signType' =>'MD5' //加密方式 ); // 生成签名 $data['paySign']=$this->makeSign($data); return $data; } /** * 统一下单 * @param array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型:JSAPI,NATIVE,APP) */ public function unifiedOrder($order,$userip) { $config=array( 'appid' => $this->config['appid'], //appid 'mch_id' => $this->config['mch_id'], //商户号ID 'nonce_str' => $this->getNonceStr(), 'spbill_create_ip' => $userip, //你的IP 'notify_url' => $this->config['notify_url'], //'notify_url' =>$url ); // 合并配置数据和订单数据 $data=array_merge($order,$config); // 生成签名 $sign=$this->makeSign($data); $data['sign']=$sign; //转换成xml $xml=$this->toXml($data); $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';//接收xml数据的文件 $header[] = "Content-type: text/xml";//定义content-type为xml,注意是数组 $ch = curl_init ($url); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 兼容本地没有指定curl.cainfo路径的错误 curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); $response = curl_exec($ch); if(curl_errno($ch)){ // 显示报错信息;终止继续执行 die(curl_error($ch)); } curl_close($ch); //转换成数组 $result=$this->toArray($response); //dump($result); // 显示错误信息 if ($result['return_code']=='FAIL') { die($result['return_msg']); } $result['sign']=$sign; $result['nonce_str']=$this->getNonceStr(); return $result; } /** * 生成签名 * @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值 */ public function makeSign($data) { // 去空 $data=array_filter($data); //签名步骤一:按字典序排序参数 ksort($data); //将数组转成url形式 $string_a=http_build_query($data); $string_a=urldecode($string_a); //签名步骤二:在string后加入KEY $string_sign_temp=$string_a."&key=".$this->config['key']; //签名步骤三:MD5加密 $sign = md5($string_sign_temp); // 签名步骤四:所有字符转为大写 $result=strtoupper($sign); return $result; } /** * 将xml转为array * @param string $xml xml字符串 * @return array 转换得到的数组 */ public function toArray($xml){ //禁止引用外部xml实体 libxml_disable_entity_loader(true); $result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $result; } /** * * 产生随机字符串,不长于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; } /** * 输出xml字符 * @throws WxPayException */ public function toXml($data){ if(!is_array($data) || count($data) <= 0){ throw new WxPayException("数组数据异常!"); } $xml = "<xml>"; foreach ($data as $key=>$val){ if (is_numeric($val)){ $xml.="<".$key.">".$val."</".$key.">"; }else{ $xml.="<".$key."><![CDATA[".$val."]]></".$key.">"; } } $xml.="</xml>"; return $xml; } /** * 验证 * @return array 返回数组格式的notify数据 */ public function notify(){ // 获取xml $xml=file_get_contents('php://input', 'r'); // 转成php数组 $data=$this->toArray($xml); // 保存原sign $data_sign=$data['sign']; // sign不参与签名 unset($data['sign']); $sign=$this->makeSign($data); // 判断签名是否正确 判断支付状态 if ($sign===$data_sign && $data['return_code']=='SUCCESS' && $data['result_code']=='SUCCESS') { $result=$data; }else{ $result=false; } // 返回状态给微信服务器 if ($result) { $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'; }else{ $str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>'; } // file_put_contents("./Public/aaa.text",$result); // echo $str; return $result; } public function https_request($url, $data = null) { $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); if (!empty($data)){ curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $data); } curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($curl); // var_dump($output); curl_close($curl); return $output; } }
然后就是调用方法了:
namespace app\index\controller; use pay\Wxpay; class Index extends Base { /** *调用支付接口 * */ public function getorder() { $param = input('get.'); $pays = model('pay')->where('id',$id)->find(); $wxpay = [ 'order_sn' => $pays['pay_sn'], //支付订单号 'total' => $pays['money'], //支付总额 'body' => $pays['body'], //支付说明 'userip' => $pays['userip'], //用户IP ]; $openid = $this->_user['openid']; $pay = new Wxpay(); $data = $pay->index($wxpay,$openid); $logUrl = "********"; //支付完成跳转地址 $this->assign([ 'data'=>json_encode($data), 'logUrl'=>$logUrl, ]); return $this->fetch(); } }
其次是对应的调用空白页面:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>订单提交成功</title> <style> .info{ text-align: center; margin-top: 40px; } </style> </head> <body> <div class="info">支付进行中,请稍候...</div> <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> <script type="text/javascript"> //调用微信JS api 支付 function onBridgeReady(){ var data = {$data}; WeixinJSBridge.invoke( 'getBrandWCPayRequest', data, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) { // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 window.location.href="{$logUrl}"; }else{ alert('支付失败'); // alert(res.err_code+res.err_desc+res.err_msg); // 显示错误信息 } } ); } if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } }else{ onBridgeReady(); } </script> </body> </html>
最后就是回调了(拿到回调在此控制器汇中进行数据库操作):
namespace app\index\controller; use Think\Controller;
/** * 支付回调 -- 接收并处理调整数据库 */ class ClassName extends AnotherClass { private $config =[ "key" => "*******", //微信支付 商户秘钥KEY ]; /** * notify_url接收页面 */ public function wxpaynotify() { $result=$this->notify(); if ($result) {//以下为处理数据库操作等 操作完成后输出success echo "SUCCESS"; } } /** * 一下验证类 活获取 token 等可写入至 common 公告类中 * 验证 * @return array 返回数组格式的notify数据 */ public function notify(){ // 获取xml $xml=file_get_contents('php://input', 'r'); // 转成php数组 $data=$this->toArray($xml); // 保存原sign $data_sign=$data['sign']; // sign不参与签名 unset($data['sign']); $sign=$this->makeSign($data); // 判断签名是否正确 判断支付状态 if ($sign===$data_sign && $data['return_code']=='SUCCESS' && $data['result_code']=='SUCCESS') { $result=$data; }else{ $result=false; } // 返回状态给微信服务器 if ($result) { $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'; }else{ $str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>'; } // file_put_contents("./Public/aaa.text",$result); // echo $str; return $result; } /** * 将xml转为array * @param string $xml xml字符串 * @return array 转换得到的数组 */ public function toArray($xml){ //禁止引用外部xml实体 libxml_disable_entity_loader(true); $result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $result; } /** * 生成签名 * @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值 */ public function makeSign($data) { // 去空 $data=array_filter($data); //签名步骤一:按字典序排序参数 ksort($data); //将数组转成url形式 $string_a=http_build_query($data); $string_a=urldecode($string_a); //签名步骤二:在string后加入KEY $string_sign_temp=$string_a."&key=".$this->config['key']; //签名步骤三:MD5加密 $sign = md5($string_sign_temp); // 签名步骤四:所有字符转为大写 $result=strtoupper($sign); return $result; } }
以上就是微信公众号支付的全部内容。
如有疑问,请评论或留言。
感谢您的查看。
2019年05月31日