美文网首页开源项目
微信支付PHP类封装

微信支付PHP类封装

作者: echo1028 | 来源:发表于2018-06-20 20:09 被阅读119次

    一:类说明

    Tool.php: 工具类,封装了xml转数组 、数组转xml、curl post请求 、curl get请求、获取用户openid等方法;
    
    WxPayApi.php: 微信支付API列表类,例如统一下单接口等;
    
    WxPayConfig.php: 微信配置类;
    
    WxPayException: 异常处理类;
    
    WxPayDataBase: 数据基类;
    
    WxPayNotify:支付异步通知应答类;
    

    二:代码如下

    1.工具类:Tool.php

      <?php 
      namespace wexin\pay;
      class Tool
      {
            /**
         * 生成随机字符串,不长于32位
         *
         * @param int $length
         * @return string
         */
        public static 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;
        }/*}}}*/
    
        /**
         * array 转为 xml
         *
         * @param array $data | object
         * @return string
         * @throws WxPayException
         */
        public static function arrayToXml($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;
        }/*}}}*/
    
        /**
         * xml 转为 array
         *
         * @param string $xml
         * @return mixed
         * @throws WxPayException
         */
        public static function xmlToArray($xml = '')
        {/*{{{*/
            if (empty($xml))
                throw new WxPayException('xml数据异常!');
            // 将XML转为array
            // 禁止引用外部xml实体
            libxml_disable_entity_loader(true);
            $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
            return $data;
        }/*}}}*/
    
        /**
         * 格式化参数为URL参数
         *
         * @param array $data
         * @return string
         */
        public static function toUrlParams($data = [])
        {/*{{{*/
            $buff = '';
            foreach ($data as $key => $val) {
                if ($key != 'sign' && $val != '' && !is_array($val)) {
                    $buff .= $key . '=' . $val . '&';
                }
            }
            $buff = trim($buff, '&');
            return $buff;
        }/*}}}*/
    
        /**
         * 生成签名
         *
         * @param array $data
         * @return string
         */
        public static function makeSign($data = [])
        {/*{{{*/
            // 签名步骤一:按字典排序参数
            ksort($data);
            $string = self::toUrlParams($data);
            // 签名步骤二:在string后加入key
            $string = $string . '&key=' . WxPayConfig::KEY;
            // 签名步骤三:MD5加密
            $string = md5($string);
            // 签名步骤四:所有字符转为大写
            $result = strtoupper($string);
            return $result;
        }/*}}}*/
    
        /**
         * 获取客户端IP地址
         *
         * @param int $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
         * @param bool $adv 是否进行高级模式获取(有可能被伪装)
         * @return mixed
         */
        public static function getClientIP($type = 0, $adv = false)
        {/*{{{*/
            $type      = $type ? 1 : 0;
            static $ip = null;
            if (null !== $ip) {
                return $ip[$type];
            }
    
            if ($adv) {
                if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                    $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
                    $pos = array_search('unknown', $arr);
                    if (false !== $pos) {
                        unset($arr[$pos]);
                    }
                    $ip = trim(current($arr));
                } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
                    $ip = $_SERVER['HTTP_CLIENT_IP'];
                } elseif (isset($_SERVER['REMOTE_ADDR'])) {
                    $ip = $_SERVER['REMOTE_ADDR'];
                }
            } elseif (isset($_SERVER['REMOTE_ADDR'])) {
                $ip = $_SERVER['REMOTE_ADDR'];
            }
            // IP地址合法验证
            $long = sprintf("%u", ip2long($ip));
            $ip   = $long ? [$ip, $long] : ['0.0.0.0', 0];
            return $ip[$type];
        }/*}}}*/
    
        /**
         * 获取毫秒级时间戳
         *
         * @return array|string
         */
        public static function getMillisecond()
        {/*{{{*/
            $time  = explode (" ", microtime ());
            $time  = $time[1] . ($time[0] * 1000);
            $time2 = explode( ".", $time );
            $time  = $time2[0];
            return $time;
        }/*}}}*/
        
        /**
         * post方式提交数据到对应的接口
         *
         * @param string $data    需要post的xml数据
         * @param $url string    url
         * @param bool $useCert 是否需要证书,默认不需要
         * @param int $timeout   请求执行超时时间,默认30s
         * @return mixed
         * @throws WxPayException
         */
        public static function postCurl($data, $url, $useCert = false, $timeout = 30)
        {/*{{{*/
            $ch = curl_init();
            // 设置超时
            curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    
            // 如果有配置代理这里就设置代理
            if (WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"
                && WxPayConfig::CURL_PROXY_PORT != 0) {
                curl_setopt($ch, CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);
                curl_setopt($ch, CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);
            }
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // 严格校验
            // 设置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, WxPayConfig::SSLCERT_PATH);
                curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
                curl_setopt($ch, CURLOPT_SSLKEY, WxPayConfig::SSLKEY_PATH);
            }
            // post方式提交数据
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
            // 运行curl
            $result = curl_exec($ch);
            // 返回结果
            if ($result) {
                curl_close($ch);
                return $result;
            } else {
                $error = curl_errno($ch);
                curl_close($ch);
                throw new WxPayException('curl出错,错误码: ' . $error);
            }
        }/*}}}*/
    
        /**
         * curl GET提交
         *
         * @param $url
         * @param int $timeout
         * @return mixed
         */
        public static function getCurl($url, $timeout = 5)
        {/*{{{*/
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); // 设置超时
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_HEADER, 0);
            $result = curl_exec($ch);
            curl_close($ch);
            return $result;
     
        }/*}}}*/
    
        /**
         * 获取用户openid
         */
        public static function getOpenid()
        {
            // 通过code获取openid
            if (!isset($_GET['code'])) {
                // 触发微信返回code码
                $url     = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . $_SERVER['QUERY_STRING'];
                $baseUrl = urlencode($url);
                $redirectUrl = self::createOauthUrlForCode($baseUrl);
                header("Location: $redirectUrl");
                exit();
            } else {
                // 获取code,通过code获取openid
                $code = $_GET['code'];
                // 构造获取openid的链接
                $url  = self::createOauthUrlForOpenid($code);
                // 发送get请求
                $data = self::getCurl($url);
                $data = json_decode($data, true);
                return $data['openid'];
    
            }
        }
    
        /**
         * 构造获取code的url连接
         *
         * @param string $redirectUrl 微信服务器回跳的url,需要url编码
         * @return string
         */
        public static function createOauthUrlForCode($redirectUrl)
        {
            $params['appid']         = WxPayConfig::APPID;
            $params['redirect_uri']  = $redirectUrl;
            $params['response_type'] = 'code';
            $params['scope']         = 'snsapi_base';
            $params['state']         = 'STATE#wechat_redirect';
            $queryString = self::toUrlParams($params);
            return 'https://open.weixin.qq.com/connect/oauth2/authorize?' . $queryString;
        }
    
        /**
         * 构造通过code获取openid的url链接
         *
         * @param $code
         * @return string
         */
        public static function createOauthUrlForOpenid($code)
        {
            $params['appid']      = WxPayConfig::APPID;
            $params['secret']     = WxPayConfig::APPSECRET;
            $params['code']       = $code;
            $params['grant_type'] = 'authorization_code';
            $queryString = self::toUrlParams($params);
            return 'https://api.weixin.qq.com/sns/oauth2/access_token?' . $queryString;
        }
      }
    

    2.微信支付API列表类:WxPayApi.php

    <?php
    namespace wexin\pay;
    class WxPayApi
    {
         /**
         * 统一下单请求地址
         */
        const UNIFORM_ORDER_URL = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
    
        /**
         * 统一下单接口
         * 
         * @param $inputObj
         * @param int $timeOut
         * @return mixed
         * @throws WxPayException
         */
        public static function unifiedOrder($inputObj, $timeOut = 6)
        {
            // 检测必填参数
            if (!isset($inputObj->appid)) {
                throw new WxPayException('缺少统一下单接口必填参数公众号ID:appid!');
            } elseif (!isset($inputObj->mch_id)) {
                throw new WxPayException('缺少统一下单接口必填参数商户号:mch_id!');
            } elseif (!isset($inputObj->sub_mch_id)) {
                throw new WxPayException('缺少统一下单接口必填参数子商户号:sub_mch_id!');
            } elseif (!isset($inputObj->sign)) {
                throw new WxPayException('缺少统一下单接口必填参数签名:sign!');
            } elseif (!isset($inputObj->body)) {
                throw new WxPayException('缺少统一下单接口必填参数商品描述:body!');
            } elseif (!isset($inputObj->out_trade_no)) {
                throw new WxPayException('缺少统一下单接口必填参数商户订单号:out_trade_no!');
            } elseif (!isset($inputObj->total_fee)) {
                throw new WxPayException('缺少统一下单接口必填参数总金额:total_fee!');
            } elseif (!isset($inputObj->notify_url)) {
                throw new WxPayException('缺少统一下单接口必填参数通知地址:notify_url!');
            } elseif (!isset($inputObj->trade_type)) {
                throw new WxPayException('缺少统一下单接口必填参数交易类型:trade_type!');
            } elseif (!isset($inputObj->nonce_str)) {
                throw new WxPayException('缺少统一下单接口必填参数随机字符串:nonce_str!');
            } elseif (!isset($inputObj->spbill_create_ip)) {
                throw new WxPayException('缺少统一下单接口必填参数终端IP:spbill_create_ip!');
            }
            // 关联参数
            if ($inputObj->trade_type == 'JSAPI' &&  !isset($inputObj->openid)) {
                throw new WxPayException("统一下单接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
            }
            if($inputObj->trade_type == 'NATIVE' && !isset($inputObj->product_id)) {
                throw new WxPayException("统一下单接口中,缺少必填参数product_id!trade_type为NATIVE时,product_id为必填参数!");
            }
            
            // 格式化xml数据
            $xml = Tool::arrayToXml($inputObj->getValues());
            // 请求开始时间
    //        $startTimeStamp = Tool::getMillisecond();
            // 发送请求
            $response = Tool::postCurl($xml, self::UNIFORM_ORDER_URL, false, $timeOut);
            $result   = Tool::xmlToArray($response);
            return $result;
        } 
    }
    

    3.配置类:WxPayConfig.php

    <?php
    namespace weixin\pay;
    class WxPayConfig
    {
          /**
         * 微信公众号信息配置
         *
         * APPID: 绑定支付的APPID,必须配置
         * MCHID: 商户号 必须配置
         * KEY: 商户支付密钥,必须配置
         * APPSECRET: 公众号secert (仅JSAPI支付的时候需要配置, 登录公众平台,进入开发者中心可设置) 
         * @var string 
         */
        const APPID = '';
        const MCHID = '';
        const KEY   = '';
        const APPSECRET = '';
        //=======【证书路径设置】==================================
        /**
         * 证书路径,绝对路径(仅退款、撤销订单时需要)
         */
        const SSLCERT_PATH = '../cert/apiclient_cert.pem';
        const SSLKEY_PATH  = '../cert/apiclient_key.pem';
    
        //=======【curl代理设置】===================================
        const CURL_PROXY_HOST = "0.0.0.0";
        const CURL_PROXY_PORT = 0;
    
        //=======【上报信息配置】===================================
        const REPORT_LEVENL = 1;
    
        //=======【支付异步通知URL】===================================
        const NOTIFY_URL = '';
    }
    

    4.异常类:WxPayException.php

    <?php
    namespace weixin\pay;
    class WxPayException extends \Exception
    {
         /**
         * @return string
         */
        public function errorMessage()
        {
            return $this->getMessage();
        }
    }
    

    5.数据基类:WxPayDataBase.php

    <?php
    namespace weixin\pay;
    class WxPayDataBase
    {
         /**
         * @var array
         */
        protected $values = [];
    
        /**
         * @param $name
         * @param $value
         */
        public function __set($name, $value)
        {
            $this->values[$name] = $value;
        }
    
        /**
         * __get 
         * 
         * @param mixed $name 
         * @access public
         * @return void
         */
        public function __get($name)
        {
            return $this->values[$name];
        }
    
        /**
         * __isset 
         * 
         * @param mixed $name 
         * @access public
         * @return void
         */
        public function __isset($name)
        {
            return array_key_exists($name, $this->values); 
        }
    
        /**
         * setSign 设置签名
         * 
         * @return void
         */
        public function setSign()
        {
            $sign = Tool::makeSign($this->values);
            $this->values['sign'] = $sign; 
        }
    
        /**
         * getValues 获取设置的值
         * 
         * @return array
         */
        public function getValues()
        {
            return $this->values;
        }
    }
    

    6.支付异步通知应答类: WxPayNotify.php

    <?php
    namespace weixin\pay;
    /**
     * 微信支付异步通知应答
     *
     * Class WxPayNotify
     * @package weixin\pay
     */
    class WxPayNotify extends WxPayDataBase
    {
    
        /**
         * @param $xml
         */
        public function replayNotify($xml)
        {
            echo $xml;
        }
    }
    

    使用如下:

    // 统一下单
    <?php
     $WxPayData               = new WxPayDataBase();                          
     $WxPayData->appid        = WxPayConfig::APPID;                           
     $WxPayData->mch_id       = WxPayConfig::MCHID;                           
     $WxPayData->sub_mch_id   = '1508144001';                                 
     $WxPayData->body         = '商品';                                   
     $WxPayData->out_trade_no = Tool::getNonceStr();                          
     // 金额:分                                                              
     $WxPayData->total_fee    = 1;                                            
     $WxPayData->notify_url   = 'https://www.qq.com/Wx/WxPay/notify';  
     $WxPayData->trade_type   = 'JSAPI';                                      
     $WxPayData->openid       = '你的openid';               
     // 随机字符串                                                            
     $WxPayData->nonce_str        = Tool::getNonceStr();                      
     // 终端ip                                                                
     $WxPayData->spbill_create_ip = Tool::getClientIP();                      
     $WxPayData->setSign();                                                   
     try {                                                                    
         $result = WxPayApi::unifiedOrder($WxPayData);                                                                       
     } catch (WxPayException $e) {                                            
         die($e->getMessage());                                               
     }        
    
    // 支付异步通知处理函数
    function notify()
    {
            $xml   = file_get_contents('php://input');   
            $array = Tool::xmlToArray($xml);
            // 记录日志
            file_put_contents('/Data/logs/app/store/notify.log', json_encode($array) . PHP_EOL, FILE_APPEND);
            // 商户应答
            $notify = new WxPayNotify();
            if ($array['result_code'] == 'SUCCESS' && $array['return_code'] == 'SUCCESS') {
                $result_sign = $array['sign'];
                unset($array['sign']);
                $sign = Tool::makeSign($array); 
                // 验证签名 
                if ($sign == $result_sign) {
                    $notify->return_code = 'SUCCESS';
                    $notify->return_msg  = 'OK';
                    $xml = Tool::arrayToXml($notify->getValues());
                    $notify->replayNotify();
                } else {
                    $notify->return_code = 'FAIL';
                    $notify->return_msg  = '签名错误';
                    $xml = Tool::arrayToXml($notify->getValues());
                    $notify->replayNotify();
                }
                
            } else {
                    $notify->return_code = 'FAIL';
                    $notify->return_msg  = '支付失败';
                    $xml = Tool::arrayToXml($notify->getValues());
                    $notify->replayNotify();
            }
    }                                                                
    

    jsapi发起支付

    微信内H5调起支付

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <title>微信支付样例-支付</title>
        <script>
        function jsApiCall()
        {
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest',
                {
                    "appId": "",
                    "timeStamp": "",
                    "nonceStr": "",
                    "package": "",
                    "signType": "",
                    "paySign": "",
                },
                function(res) {
                    WeixinJSBridge.log(res.err_msg);
                    alert(res.err_code+res.err_desc+res.err_msg);
                }
            );
        }   
        function callpay()
        {
        
            if (typeof WeixinJSBridge == "undefined"){
                if ( document.addEventListener ) {
                    document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
                } else if (document.attachEvent) {
                    document.attachEvent('WeixinJSBridgeReady', jsApiCall); 
                    document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
                }
            } else {
                jsApiCall();
            }
        }
        </script>
    </head>
    <body>
        <br/>
        <font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px">1分</span>钱</b></font><br/><br/>
        <div align="center">
            <button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer;  color:white;  font-size:16px;" type="button" onclick="callpay()" >立即支付</button>
        </div>
    </body>
    </html>
    

    相关文章

      网友评论

        本文标题:微信支付PHP类封装

        本文链接:https://www.haomeiwen.com/subject/mlaxyftx.html