zoukankan      html  css  js  c++  java
  • TP5 -- 微信支付整合(APP,JSAPI)

    原本项目上有一个提现功能,因原客户商户号和流水原因一直没做。

    最近因为刚刚符合条件的原因,今天才开始做。

    顺便浏览下博客,发现原来写的微信支付有些那个啥了。

    因为最近项目前后端分离居多,而且项目也多JSAPI、APP、NATIVE等多种一起居多

    所以就将原来的微信支付 重新整合了下

    与原来的相比,整合过后的更适用于前后端分离来做

    如果没有前后端分离,还可以借鉴原来的公众号支付:tp5 -- 微信公众号支付

    不过最好还是自己调整下,毕竟那个代码比较久了

    好了,话不多说,还是直接上代码吧

    因为此支付是自己封装,同样还是在 extend/  文件下 的Wxpay.php

    代码如下:

    <?php 
    namespace pay;
    
    class Wxpay{
        private $config =[
            "appid"  => "",  // 开放平台或商户平台APPID
            "mch_id" => "",  // 商户平台 商户号
            "key"    => "",  // 商户平台 秘钥KEY
            "TOKEN"  => "",  // 此参数非必传 有的前端在jsapi 支付时会要求返回signature 参数  此参数即为此准备
        ];
        public function index($param,$openid="")
        {
            $order = [
                'out_trade_no'  => $param['out_trade_no'],// 订单号
                'total_fee'     => intval($param['total_fee']*100),// 订单金额  以(分)为单位
                'body'          => $param['body'],// 商品描述
                'notify_url'    => $param['notify_url'], //回调地址
                'spbill_create_ip' => $param['spbill_create_ip'], //对应IP
                'trade_type'    => $param['trade_type']  //对应支付类型
            ];
    
            #当支付类型为JAPI时  openid  必传
            if($param['trade_type'] == "JSAPI")
            {
                $order['openid'] = $openid;
            }
    
            #统一下单 获取prepay_id
            $unified_order=$this->unifiedOrder($order);
    
            #获取当前时间戳
            $time = time();
    
            #JSAPI
            if($param['trade_type'] == 'JSAPI')
            {
                #组合jssdk需要用到的数据
                $data = [
                    '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);
                // #有的可能会有需求signature 此参数的在此加密一下即可
                // $token = $this->config['token'];
                // $tmpArr = array($token, strval($time),$unified_order['nonce_str']);
                // sort($tmpArr, SORT_STRING);
                // $tmpStr = implode( $tmpArr );
                // $tmpStr = sha1($tmpStr);
                // $data['signature'] = $tmpStr;
            }
            #APP
            elseif($param['trade_type'] == 'APP')
            {
                $data  = [
                    'appid'     => $this->config['appid'],
                    'partnerid' => $this->config['mch_id'],
                    'prepayid'  => $unified_order['prepay_id'],
                    'package'   => 'Sign=WXPay',
                    'noncestr'  => $unified_order['nonce_str'],// 随机字符串
                    'timestamp' => strval($time), //时间戳
                ];
                //生成签名
                $data['sign'] =  $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)
        {
            $config =[
                'appid'     => $this->config['appid'], //appid
                'mch_id'    => $this->config['mch_id'], //商户号ID
                'nonce_str' => $this->getNonceStr()
            ];
    
            # 合并配置数据和订单数据
            $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);
    
            #显示错误信息
            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>';
            }
            return $result;
        }
    }
    ?>

    下面就是调用方法超级简单:

    <?php
    namespace appindexcontroller;
    header("Content-Type: text/html;charset=utf-8");
    
    use thinkController;
    use payWxpay;
    
    class Buy extends Controller
    {
        #新的微信支付类调用
        public function newpay()
        {
            $pay_sn = date('YmdHis').rand(1000,9999);
            $total_fee = '0.01';
            $body   = "商品描述";
            $spbill_create_ip = '192.168.0.1';
            $notify_url = "你的回调地址";  #根据不同类型回调地址不同
            $trade_type = 'APP';
            #JSAPI--JSAPI支付(或小程序支付)、
            #NATIVE--Native支付、
            #APP--app支付,
            #MWEB--H5支付,
            #不同trade_type决定了调起支付的方式,请根据支付产品正确上传
           
    
            #新的需要参数为六个
            # out_trade_no 商户订单号
            # total_fee    订单总额
            # body         商品描述
            # spbill_create_ip   终端IP
            # notify_url   回调通知地址
            # trade_type   交易类型
            
            $wxpay  = new Wxpay();
            $date = [
                'out_trade_no'  => $pay_sn,
                'total_fee'     => $total_fee,
                'body'          => $body,
                'spbill_create_ip' => getIp(),
                'notify_url' => $notify_url,
                'trade_type' => $trade_type
            ];
            #根据 trade_type  类型不同,是否传递openid
        
            #APP   类型支付调用
            $res = $wxpay->index($date);
    
            #JSAPI 类型支付调用
            $res = $wxpay->index($date,$openid);
    
            #获得后将对应内容返回前端即可
            return $res;
        }
    
    }
    ?>

    最后就是回调咯:

    <?php
    namespace appindexcontroller;
    header("Content-Type: text/html;charset=utf-8");
    
    use thinkController;
    use payWxpay;
    
    class Pays extends Controller
    {
        public function notify()
        {
            $wxpay  = new Wxpay();
            $result = $wxpay->notify();
    
            #根据拿到的数据 来进行自己的数据逻辑
            if($result)
            {
                $out_trade_no = $result['out_trade_no'];
                echo "success";exit;
            }
            echo  "error";
        }
    }
    ?>

    以上就是本次整合的微信支付咯 

    感谢各位大大的观看。

    2020年4月17日

  • 相关阅读:
    GDI+常用操作、入门解析
    实用SQL语句大全
    SQL语句全解析
    ASP.NET MVC 浅析
    C#版文件分割器
    转一个orbitpanel GIS
    怎么给listbox 的item 添加动画1? GIS
    怎么给listbox 的item 添加动画? GIS
    关于查找可视树的一点问题 GIS
    判断gridview 滑动到最右端 GIS
  • 原文地址:https://www.cnblogs.com/YFYQ/p/12719393.html
Copyright © 2011-2022 走看看