<?php

class WxpayService
{
    protected $mchid;
    protected $appid;
    protected $apiKey;

    public function __construct ($mchid , $appid , $key)
    {
        $this->mchid  = $mchid;
        $this->appid  = $appid;
        $this->apiKey = $key;
    }

    /**
     * 发起订单
     *
     * @param float  $totalFee   收款总费用 单位元
     * @param string $outTradeNo 唯一的订单号
     * @param string $orderName  订单名称
     * @param string $notifyUrl  支付结果通知url 不要有问号
     * @param string $timestamp  订单发起时间
     *
     * @return array
     */
    public function createJsBizPackage ($totalFee , $outTradeNo , $orderName , $notifyUrl , $timestamp)
    {
        $config = [
            'mch_id' => $this->mchid ,
            'appid'  => $this->appid ,
            'key'    => $this->apiKey ,
        ];
        //$orderName = iconv('GBK','UTF-8',$orderName);
        $unified           = [
            'appid'            => $config[ 'appid' ] ,
            'attach'           => 'pay' ,
            //商家数据包，原样返回，如果填写中文，请注意转换为utf-8
            'body'             => $orderName ,
            'mch_id'           => $config[ 'mch_id' ] ,
            'nonce_str'        => self::createNonceStr() ,
            'notify_url'       => $notifyUrl ,
            'out_trade_no'     => $outTradeNo ,
            'spbill_create_ip' => '127.0.0.1' ,
            'total_fee'        => floatval($totalFee) * 100 ,
            //单位 转为分
            'trade_type'       => 'NATIVE' ,
        ];
        $unified[ 'sign' ] = self::getSign($unified , $config[ 'key' ]);
        $responseXml       = self::curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder' , self::arrayToXml($unified));
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $unifiedOrder = simplexml_load_string($responseXml , 'SimpleXMLElement' , LIBXML_NOCDATA);
        if ($unifiedOrder === false) {
            die('parse xml error');
        }
        if ($unifiedOrder->return_code != 'SUCCESS') {
            die($unifiedOrder->return_msg);
        }
        if ($unifiedOrder->result_code != 'SUCCESS') {
            die($unifiedOrder->err_code);
        }
        $codeUrl = (array)( $unifiedOrder->code_url );
        if ( !$codeUrl[ 0 ]) exit('get code_url error');
        $arr              = [
            "appId"     => $config[ 'appid' ] ,
            "timeStamp" => $timestamp ,
            "nonceStr"  => self::createNonceStr() ,
            "package"   => "prepay_id=" . $unifiedOrder->prepay_id ,
            "signType"  => 'MD5' ,
            "code_url"  => $codeUrl[ 0 ] ,
        ];
        $arr[ 'paySign' ] = self::getSign($arr , $config[ 'key' ]);
        return $arr;
    }

    public function orderquery ($outTradeNo)
    {
        $config = [
            'mch_id' => $this->mchid ,
            'appid'  => $this->appid ,
            'key'    => $this->apiKey ,
        ];
        //$orderName = iconv('GBK','UTF-8',$orderName);
        $unified           = [
            'appid'        => $config[ 'appid' ] ,
            'mch_id'       => $config[ 'mch_id' ] ,
            'out_trade_no' => $outTradeNo ,
            'nonce_str'    => self::createNonceStr() ,
        ];
        $unified[ 'sign' ] = self::getSign($unified , $config[ 'key' ]);
        $responseXml       = self::curlPost('https://api.mch.weixin.qq.com/pay/orderquery' , self::arrayToXml($unified));
        $queryResult       = simplexml_load_string($responseXml , 'SimpleXMLElement' , LIBXML_NOCDATA);
        if ($queryResult === false) {
            die('parse xml error');
        }
        if ($queryResult->return_code != 'SUCCESS') {
            die($queryResult->return_msg);
        }
        $trade_state    = $queryResult->trade_state;
        $data[ 'code' ] = $trade_state == 'SUCCESS' ? 1 : 0;
        $data[ 'data' ] = $trade_state;
        $data[ 'msg' ]  = $this->getTradeSTate($trade_state);
        $data[ 'time' ] = date('Y-m-d H:i:s');
        return $data;
    }

    public function getTradeSTate ($str)
    {
        switch ($str) {
            case 'SUCCESS';
                return '支付成功';
            case 'REFUND';
                return '转入退款';
            case 'NOTPAY';
                return '未支付';
            case 'CLOSED';
                return '已关闭';
            case 'REVOKED';
                return '已撤销（刷卡支付）';
            case 'USERPAYING';
                return '用户支付中';
            case 'PAYERROR';
                return '支付失败';
        }
    }

    public static function curlPost ($url = '' , $postData = '' , $options = [])
    {
        if (is_array($postData)) {
            $postData = http_build_query($postData);
        }
        $ch = curl_init();
        curl_setopt($ch , CURLOPT_URL , $url);
        curl_setopt($ch , CURLOPT_RETURNTRANSFER , 1);
        curl_setopt($ch , CURLOPT_POST , 1);
        curl_setopt($ch , CURLOPT_POSTFIELDS , $postData);
        curl_setopt($ch , CURLOPT_TIMEOUT , 30); //设置cURL允许执行的最长秒数
        if ( !empty($options)) {
            curl_setopt_array($ch , $options);
        }
        //https请求 不验证证书和host
        curl_setopt($ch , CURLOPT_SSL_VERIFYPEER , false);
        curl_setopt($ch , CURLOPT_SSL_VERIFYHOST , false);
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }

    public static function createNonceStr ($length = 16)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $str   = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars , mt_rand(0 , strlen($chars) - 1) , 1);
        }
        return $str;
    }

    public static 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;
    }

    /**
     * 获取签名
     */
    public static function getSign ($params , $key)
    {
        ksort($params , SORT_STRING);
        $unSignParaString = self::formatQueryParaMap($params , false);
        $signStr          = strtoupper(md5($unSignParaString . "&key=" . $key));
        return $signStr;
    }

    protected static function formatQueryParaMap ($paraMap , $urlEncode = false)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if (NULL != $v && "null" != $v) {
                if ($urlEncode) {
                    $v = urlencode($v);
                }
                $buff .= $k . "=" . $v . "&";
            }
        }
        $reqPar = '';
        if (strlen($buff) > 0) {
            $reqPar = substr($buff , 0 , strlen($buff) - 1);
        }
        return $reqPar;
    }
}