• 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 appindexcontroller;
    
    use payWxpay;
    
    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 appindexcontroller;
    
    use ThinkController;
    /** * 支付回调 -- 接收并处理调整数据库 */ 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日

  • 相关阅读:
    Winform中如何禁用最大化或最小化按钮
    联想笔记本白屏解决办法
    SqlSever查询当前数据库某个表的名称、列名称、列说明
    离线安装Microsoft SQL Server 2016时Microsoft R Open和Microsoft R Server的问题
    SqlSever查询当前数据库的所有表名及其描述
    Windows10系统C盘的ESD文件夹是什么?可以删除吗?
    飞秋截图的快捷键是什么?
    打开Word时报错:Cannot find the Word document template:WordToRqm.dot
    .Net Core 2.1 上传文件后保存在根目录下的文件夹中,但是通过网页链接访问不了
    Winform弹出确认窗口
  • 原文地址:https://www.cnblogs.com/YFYQ/p/10955850.html
Copyright © 2020-2023  润新知