美文网首页微信
微信扫码支付Wxpay.class.php

微信扫码支付Wxpay.class.php

作者: 三天大愚 | 来源:发表于2016-07-07 12:09 被阅读228次

    在经过微信扫码支付DEMO的蹂躏后,决定将繁复杂乱的DEMO文件重新整合封装,最后就有了这个清晰简洁的Wxpay.class.php文件了。

    终极版 Wxpay.class.php
    <?php
    /**
     * Wxpay 自定义微信扫码类
     * 主要是将支微信支付DEMO中的方法整合成了一个文件
     * @version 1.0 2016-07-06
     */
    class Wxpay {
    
        // 交易类型 公众号支付
        const TRADE_TYPE_JSAPI = 'JSAPI';
        // 交易类型 原生扫码支付
        const TRADE_TYPE_NATIVE = 'NATIVE';
        // 交易类型 app支付
        const TRADE_TYPE_APP = 'APP';
    
        // 统一下单接口
        const URL_UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    
        // 订单查询接口
        const URL_ORDER_QUERY = "https://api.mch.weixin.qq.com/pay/orderquery";
    
        // 关单接口
        const URL_CLOSE_ORDER = 'https://api.mch.weixin.qq.com/pay/closeorder';
    
        // 申请退款接口
        const URL_REFUND = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
    
        // 查询退款接口
        const URL_REFUND_QUERY = 'https://api.mch.weixin.qq.com/pay/refundquery';
    
        // 下载对账单接口
        const URL_DOWNLOAD_BILL = 'https://api.mch.weixin.qq.com/pay/downloadbill';
    
        // 交易保障接口
        const URL_REPORT = 'https://api.mch.weixin.qq.com/payitil/report';
    
        // 转换短链接接口
        const URL_SHORT_URL = 'https://api.mch.weixin.qq.com/tools/shorturl';
    
        const URL_MICRO_PAY = 'https://api.mch.weixin.qq.com/pay/micropay';
    
    
    
        // 微信支付配置数组
        // appid        公众账号appid
        // mch_id       商户号
        // apikey       加密key
        // appsecret    公众号appsecret
        // sslcertPath  证书路径(apiclient_cert.pem)
        // sslkeyPath   密钥路径(apiclient_key.pem)
        private $_config;
    
    
        /**
         * 构造方法 初始化微信支付配置
         * @access public
         * @param $config 微信支付配置数组
         */
        public function __construct($config){
    
            $this->_config = $config;
    
        }
    
        /**
         * unifiedOrder 统一下单
         * @access public
         * @param array 统一下单参数数组
         * @return array
         */
        public function unifiedOrder($para = array()) {
            // out_trade_no 商户系统订单 必填
            if ( ! isset($para['out_trade_no'])) return false;
            // body 商品描述/订单描述 必填
            if ( ! isset($para['body'])) return false;
            // total_fee 总金额 必填
            if ( ! isset($para['total_fee'])) return false;
            // spbill_create_ip 终端IP 必填
            if ( ! isset($para['spbill_create_ip'])) $para['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];
            // APPID 公众账号ID 必填
            $para['appid'] = $this->_config['APPID'];
            // MCHID 商户号 必填
            $para['mch_id'] = $this->_config['MCHID'];
            // device_info 设备号
            if ( ! isset($para['device_info'])) $para['device_info'] = 'WEB';
            // nonce_str 32位随机字符串
            $para['nonce_str'] = $this->getNonceStr();
            // detail 商品详情 可选
            if ( ! isset($para['detail'])) $para['detail'] = '';
            // attach 附加数据 可选
            if ( ! isset($para['attach'])) $para['attach'] = '';
            // fee_type 货币类型 可选
            if ( ! isset($para['fee_type'])) $para['fee_type'] = 'CNY';
            // time_start 交易起始时间 可选
            if ( ! isset($para['time_start'])) $para['time_start'] = '';
            // time_expire 交易结束时间 可选
            if ( ! isset($para['time_expire'])) $para['time_expire'] = '';
            // goods_tag 商品标记 可选
            if ( ! isset($para['goods_tag'])) $para['goods_tag'] = '';
            // notify_url 通知地址 必填
            if ( ! isset($para['notify_url'])) $para['notify_url'] = $this->_config['NOTIFY_URL'];
            // trade_type 交易类型
            if ( ! isset($para['trade_type'])) $para['trade_type'] = 'NATIVE';
            if ($para['trade_type'] == 'NATIVE') {
                // product_id 商品ID 自定义 trade_type=NATIVE时 必填
                if ( ! isset($para['product_id'])) $para['product_id'] = $this->_config['PRODUCT_ID'];
                if ( ! isset($para['openid'])) $para['openid'] = '';
            }
            if ($para['trade_type'] == 'JSAPI') {
                if ( ! isset($para['product_id'])) $para['product_id'] = '';
                // openid 用户标识 trade_type=JSAPI时 必填
                if ( ! isset($para['openid'])) return false;
            }
            
            return $this->getHttpResponsePOST(self::URL_UNIFIED_ORDER, $para);
        }
    
        /**
         * getCodeUrl 扫码支付 模式二 获取支付二维码
         * @access public
         * @param array $para 支付参数数组
         * @return string
         */
        public function getCodeUrl($para = array()){
            $r = $this->unifiedOrder($para);
            if ($r['return_code'] == 'SUCCESS') {
                if ($r['result_code'] == 'SUCCESS') {
                    return $r['code_url'];
                } else {
                    $this->logDebug('#获取支付CODE_URL错误# '.$r['err_code'].' : '.$r['err_code_des']);
                    return false;
                }
            } else {
                $this->logDebug('#获取支付CODE_URL错误# '.$r['return_msg']);
                return false;
            }
        }
    
        /**
         * orderQuery 查询订单
         * @access public
         * @param string $order_code
         * @param bool $mode
         * @return array
         */
        public function orderQuery($order_code, $mode = false){
            $para = array();
            $para['appid'] = $this->_config['APPID'];
            $para['mch_id'] = $this->_config['MCHID'];
            $para['nonce_str'] = $this->getNonceStr();
            if ($mode) {    // 使用微信订单号
                $para['transaction_id'] = $order_code;
            } else {    // 使用商户订单号
                $para['out_trade_no'] = $order_code;
            }
    
            return $this->getHttpResponsePOST(self::URL_ORDER_QUERY, $para);
        }
    
        /**
         * closeOrder 关闭订单
         * @access public
         * @param string $out_trade_no
         * @return array
         */
        public function closeOrder($out_trade_no){
            $para = array(
                'appid'        => $this->_config['APPID'],
                'mch_id'       => $this->_config['MCHID'],
                'nonce_str'    => $this->getNonceStr(),
                'out_trade_no' => $out_trade_no
            );
    
            return $this->getHttpResponsePOST(self::URL_CLOSE_ORDER, $para);
        }
    
        /**
         * refund 申请退款
         * @access public
         * @param array $para 参数数组
         * @param bool $mode
         * @return array
         */
        public function refund($para = array(), $mode = false){
            $para['appid'] = $this->_config['APPID'];
            $para['mch_id'] = $this->_config['MCHID'];
            $para['nonce_str'] = $this->getNonceStr();
            if ($mode) {    // 使用微信订单号退款
                if ( ! isset($para['transaction_id'])) return false;
            } else {    // 使用商户订单号退款
                if ( ! isset($para['out_trade_no'])) return false;
            }
            if ( ! isset($para['out_refund_no'])) return false;
            if ( ! isset($para['total_fee'])) return false;
            if ( ! isset($para['refund_fee'])) return false;
            if ( ! isset($para['op_user_id'])) $para['op_user_id'] = $this->_config['MCHID'];
    
            return $this->getHttpResponsePOST(self::URL_REFUND, $para, true);
        }
    
        /**
         * downloadBill 下载对账单
         * @access public
         * @param string $bill_date 下载对账单的日期,格式:20140603
         * @param string $bill_type 类型
         * @return array
         */
        public function downloadBill($bill_date, $bill_type = 'ALL'){
            $para = array();
            $para['appid'] = $this->_config['APPID'];
            $para['mch_id'] = $this->_config['MCHID'];
            $para['nonce_str'] = $this->getNonceStr();
            $para['bill_date'] = $bill_date;
            $para['bill_type'] = $bill_type;
    
            return $this->getHttpResponsePOST(self::URL_DOWNLOAD_BILL, $para);
        }
    
        /**
         * 获取js支付使用的第二个参数
         */
        public function get_package($prepay_id) {
            $data = array();
            $data['appId'] = $this->_config['APPID'];
            $data['nonceStr']  = $this->getNonceStr();
            $data['timeStamp'] = time();
            $data['package']   = 'prepay_id='.$prepay_id;
            $data['signType']  = 'MD5';
            $data['paySign']   = $this->sign($data);
            
            return $data;
        }
    
    
        /**
         * getWxpayBackData 获取微信返回数据
         * @access public
         * @param void
         * @return array | bool
         */
        public function getWxpayBackData() {
            $xml = file_get_contents('php://input');
            $data = $this->xml2array($xml);
    
            return $this->checkSign($data) ? $data : NULL;
        }
    
        /**
         * replyNotify 回复通知
         * @access public
         * @param string $return_code 返回状态码
         * @param string $return_msg 返回信息
         * @return void
         */
        final public function replyNotify($return_code = 'SUCCESS', $return_msg = ''){
            $data = array();
            $data['return_code'] = $return_code;
            if ($return_msg) $data['return_msg'] = $return_msg;
    
            echo $this->array2xml($data);
        }
    
        /**
         * sign 生成签名
         * @access private
         * @param array $data
         * @return string
         */
        private function sign($data = array()){
            // 签名步骤一 按字典序排序参数
            ksort($data);
    
            $buff = '';
    
            // 去除空值
            // 把数组所有元素 按照“参数=参数值”的模式用“&”字符拼接成字符串
            // 签名步骤二 格式化参数格式化成url参数
            foreach ($data as $k => $v) {
                if($k != 'sign' && $v != '' && ! is_array($v)){
                    $buff .= $k . '=' . $v . '&';
                }
            }
            $buff = trim($buff, '&');
    
            // 签名步骤三 在buff后加入KEY
            $string_sign_temp = $buff . '&key=' . $this->_config['KEY'];
    
            return strtoupper(md5($string_sign_temp));
        }
    
        /**
         * checkSign 验证数据签名
         * @access public
         * @param array $data 数据数组
         * @return bool 数据校验结果
         */
        public function checkSign($data) {
            if ( ! isset($data['sign']))  return false;
            $sign = $data['sign'];
            unset($data["sign"]);
    
            return $this->sign($data) == $sign;
        }
    
        /**
         * array2xml 将数组转换为XML
         * @access private
         * @param array $arr
         * @return xml
         */
        private function array2xml($arr = array()){
            $xml = '<xml>' . PHP_EOL;
            foreach ($arr as $k => $v) {
                if ($v > 0 && trim($v) != '' && ! is_array($v)) {
                    if (is_numeric($v)) {
                        $xml.='<'.$k.'>'.$v.'</'.$k.'>' . PHP_EOL;
                    }else{
                        $xml.='<'.$k.'><![CDATA['.$v.']]></'.$k.'>' . PHP_EOL;
                    }
                }
            }
            $xml .= '</xml>';
    
            return $xml;
        }
    
        /**
         * xml2array 将XML转换为数组
         * @access private
         * @param string $xml
         * @return array $arr
         */
        private function xml2array($xml) {
            try {
                return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
            } catch(Exception $e) {
    
            }
        }
    
        /**
         * getNonceStr 产生随机字符串,不长于32位
         * @access public
         * @param void
         * @return 产生的随机字符串
         */
        public function getNonceStr($len = 32){
    
            return substr(str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789"), 0, $len);
    
        }
    
    
        /**
         * getHttpResponsePOST 远程获取数据 POST模式
         * @access private
         * @param string $url
         * @param array $data
         * @param bool $is_need_cert
         * @return array
         */
        private function getHttpResponsePOST($url, $data = array(), $is_need_cert = false){
            $data['sign'] = $this->sign($data);
            $xml = $this->array2xml($data);
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_URL, $url);
            if ($is_need_cert == true) {
                //使用证书 cert 与 key 分别属于两个.pem文件
                curl_setopt($ch,CURLOPT_SSLCERTTYPE, 'PEM');
                curl_setopt($ch,CURLOPT_SSLCERT, $this->_config['SSLCERT_PATH']);
                curl_setopt($ch,CURLOPT_SSLKEYTYPE, 'PEM');
                curl_setopt($ch,CURLOPT_SSLKEY, $this->_config['SSLKEY_PATH']);
            }
            $content = curl_exec($ch);
            $array = $this->xml2array($content);
            return $array;
        }
    
        /**
         * logDebug
         * 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
         * 注意:服务器需要开通fopen配置
         * @access public
         * @param string $content 要写入日志里的文本内容 默认值:空值
         * @return void
         */
        public function logDebug($content = '') {
            $fp = fopen($this->_config['LOG_PATH'], 'a+');
            flock($fp, LOCK_EX);    // 文件锁定 避免其他人同时操作
            fwrite($fp, '# Wxpay Debug : ' . date('Y-m-d H:i:s') . ' # '. $content . "\n\n");
            flock($fp, LOCK_UN);
            fclose($fp);
        }
    
    
    }

    相关文章

      网友评论

        本文标题:微信扫码支付Wxpay.class.php

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