<?php
require_once 'WxPay.Config.php';
class MchPay
{
    protected $_payment_url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
    protected $_curl_timeout = 30;
    protected $_error = '';
    public function __construct()
    {
    }
    /**
     * API 参数
     * @var array
     * 'mch_appid'         # 公众号APPID
     * 'mchid'             # 商户号
     * 'device_info'       # 设备号
     * 'nonce_str'         # 随机字符串
     * 'partner_trade_no'  # 商户订单号
     * 'openid'            # 收款用户openid
     * 'check_name'        # 校验用户姓名选项 针对实名认证的用户
     * 're_user_name'      # 收款用户姓名
     * 'amount'            # 付款金额
     * 'desc'              # 企业付款描述信息
     * 'spbill_create_ip'  # Ip地址
     * 'sign'              # 签名
     */
    public function postXmlSSL($parameters)
    {
        $parameters['mch_appid'] = WxPayConfig::$appid;
        $parameters['mchid'] = WxPayConfig::$mchid;
        $xml = $this->createXml($parameters);
        $response = $this->postXmlSSLCurl(
            $xml,
            $this->_payment_url,
            $this->_curl_timeout
        );
        return $response;
    }
    /**
     * 生成请求xml数据
     * @return string
     */
    protected function createXml($parameters)
    {
        $parameters['nonce_str'] = $this->createNoncestr();
        $parameters['sign'] = $this->getSign($parameters);
        return $this->arrayToXml($parameters);
    }
    /**
     *  作用：产生随机字符串，不长于32位
     */
    protected function createNoncestr($length = 32)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }
    /**
     *  作用：生成签名
     */
    protected function getSign($Obj)
    {
        foreach ($Obj as $k => $v) {
            $Parameters[$k] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        //echo '【string1】'.$String.'</br>';
        //签名步骤二：在string后加入KEY
        $String = $String . '&key=' . WxPayConfig::$key;
        //echo "【string2】".$String."</br>";
        //签名步骤三：MD5加密
        $String = md5($String);
        //echo "【string3】 ".$String."</br>";
        //签名步骤四：所有字符转为大写
        $result_ = strtoupper($String);
        //echo "【result】 ".$result_."</br>";
        return $result_;
    }
    /**
     *  作用：格式化参数，签名过程需要使用
     */
    protected function formatBizQueryParaMap($paraMap, $urlencode)
    {
        $buff = '';
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if ($urlencode) {
                $v = urlencode($v);
            }
            //$buff .= strtolower($k) . "=" . $v . "&";
            $buff .= $k . '=' . $v . '&';
        }
        $reqPar;
        if (strlen($buff) > 0) {
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;
    }
    /**
     *  作用：array转xml
     */
    protected function arrayToXml($arr)
    {
        $xml = '<xml>';
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= '<' . $key . '>' . $val . '</' . $key . '>';
            } else {
                $xml .= '<' . $key . '><![CDATA[' . $val . ']]></' . $key . '>';
            }
        }
        $xml .= '</xml>';
        return $xml;
    }
    /**
     *     作用：使用证书，以post方式提交xml到对应的接口url
     */
    protected function postXmlSSLCurl($xml, $url, $second = 30)
    {
        $ch = curl_init();
        //超时时间
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, false);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        //设置证书
        //使用证书：cert 与 key 分别属于两个.pem文件
        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt(
            $ch,
            CURLOPT_SSLCERT,
            getcwd() . '/data/cert/apiclient_cert.pem'
        );
        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt(
            $ch,
            CURLOPT_SSLKEY,
            getcwd() . '/data/cert/apiclient_key.pem'
        );
        curl_setopt($ch, CURLOPT_CAINFO, getcwd() . '/data/cert/rootca.pem');
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            libxml_disable_entity_loader(true);
            $xml_array = (array) simplexml_load_string(
                $data,
                'SimpleXMLElement',
                LIBXML_NOCDATA
            );
            if ($xml_array['err_code']) {
                $this->_error = $xml_array['err_code_des'];
                return false;
            } else {
                return true;
            }
        } else {
            $error = curl_errno($ch);
            $this->_error = 'curl出错，错误码:' . $error;
            curl_close($ch);
            return false;
        }
    }
    public function getError()
    {
        return $this->_error;
    }
}
