微信支付PHP类封装

一:类说明

Tool.php: 工具类,封装了xml转数组 、数组转xml、curl post请求 、curl get请求、获取用户openid等方法;

WxPayApi.php: 微信支付API列表类,例如统一下单接口等;

WxPayConfig.php: 微信配置类;

WxPayException: 异常处理类;

WxPayDataBase: 数据基类;

WxPayNotify:支付异步通知应答类;

二:代码如下

1.工具类:Tool.php

  ';
        foreach ($data as $key => $val) {
            if (is_numeric($val)) {
                $xml .= '<' . $key . '>' . $val . '';
            } else {
                $xml .= '<' . $key . '>';
            }
        }
        $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

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

4.异常类:WxPayException.php

getMessage();
    }
}

5.数据基类:WxPayDataBase.php

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

使用如下:

// 统一下单
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调起支付




    微信支付样例-支付
    


    
该笔订单支付金额为1分

你可能感兴趣的:(微信支付PHP类封装)