<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/1/2
 * Time: 13:48
 */

namespace app\modules\third\payment;

use app\modules\sys\log\ImLog;
use Yii;
use app\modules\common\Helper;
use yii\db\Exception;
class Wxpay
{
    private $APPID = 'wxaacd29b1b82053ad';
    //private $MCHID = '1556784471'; //old
    private $MCHID = '1563941181';//new
    private $KEY = 'B8910EA69419B782B4683CBDC55E871B';
    private $APPSECRET = 'd3652b3704c3ec9d18d54b540b1a69c5';

    private $notify_url = 'http://test-shop-api.zgytkj.com/v1/receive/wxpaycallback';

    private $body = '技术服务费';

    public function __construct($version)
    {
        $params = '';
        //$params = Yii::$app->params['wxpay']['repucar'];
        if (YII_ENV_PROD) {
            $this->notify_url = 'http://shop-api.jymcj.com/v1/receive/wxpaycallback';
        }
    }
    /**
     *
     * 统一下单
     *
     */
    public function unifiedOrder($data, $timeOut = 6)
    {
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $nonce_str = Helper::genSalt(16);
        $request_data = [
            'appid' => $this->APPID,
            'mch_id' => $this->MCHID,
            'notify_url' => $this->notify_url,
            'body' => $this->body,
            'nonce_str' => $nonce_str,
            'spbill_create_ip' => Helper::getUserIp(),
            'trade_type' => 'APP',
            'out_trade_no' => $data['order_nid'],
            'total_fee' => $data['money'] * 100,
        ];
        $request_data['sign'] = $this->makeSign($request_data);
        ImLog::instance('wx_app_pay.log')->Log($request_data);
        $xml = $this->toXml($request_data);
        $response = self::postXmlCurl($xml, $url, false, $timeOut);

        if ($response) {
            $result = $this->response($response);
            if (is_array($result)) {
                if ($result['return_code'] == 'SUCCESS') {
                    if ($result['result_code'] == 'SUCCESS') {
                        $return['code'] = 1;
                        $pay_data = [
                            'appid' => $this->APPID,
                            'partnerid' => $this->MCHID,
                            'prepayid' => $result['prepay_id'],
                            'noncestr' => $nonce_str,
                            'timestamp' => time(),
                            'package' => 'Sign=WXPay'
                        ];
                        $pay_data['sign'] = $this->makeSign($pay_data);
                        $return['data'] = $pay_data;
                        $return['prepay_id'] = $result['prepay_id'];
                        return $return;
                    } else {
                        $msg = '未知错误';
                        if (isset($result['err_code_des'])) {
                            $msg = $result['err_code_des'];
                        }
                        return ['code' => 0, 'msg' => $msg];
                    }
                } else {
                    return ['code' => 0, 'msg' => $result['return_msg']];
                }
            } else {
                return ['code' => 0, 'msg' => '返回验签失败'];
            }
        } else {
            return ['code' => 0, 'msg' => '请求失败'];
        }

    }
    /**
     * 退费
     */
    public function refund($data, $timeOut = 6)
    {
        $url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
        $request_data = [
            'appid' => $this->APPID,
            'mch_id' => $this->MCHID,
            'nonce_str' => Helper::genSalt(16),
            'transaction_id' => $data['transaction_id'],
            'out_trade_no' => $data['out_trade_no'],
            'out_refund_no' => $data['order_nid'],
            'total_fee' => $data['money'] * 100,
            'refund_fee' => $data['money'] * 100,
        ];
        $request_data['sign'] = $this->makeSign($request_data);
        $xml = $this->toXml($request_data);
        $response = self::postXmlCurl($xml, $url, true, $timeOut);
        $result = $this->response($response);
        return $result;
    }

    /**
     * 用户提现操作
     * @param $nid  string        订单id
     * @param $amount int   金额 单位（分）
     * @param $openid  string 用户openid
     * @return mixed
     * @throws \yii\base\Exception
     */
    public function tiXian($nid, $amount, $openid)
    {
        if (!YII_ENV_PROD) {
            return '测试环境不提现';
        }
        if (!$nid or !$amount or !$openid) {
            return '微信请求参数错误';
        }
        $url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
        $request_data = [
            'mch_appid' => $this->APPID,
            'mchid' => $this->MCHID,
            'nonce_str' => \Yii::$app->security->generateRandomString(32),
            'partner_trade_no' => $nid,
            'openid' => $openid,
            'check_name' => 'NO_CHECK',
            'amount' => $amount,
            'desc' => '用户资金提现',
            'spbill_create_ip' => Helper::getUserIp(),
        ];
        $request_data['sign'] = $this->makeSign($request_data);
        $xml = $this->toXml($request_data);
        $response = self::postXmlCurl($xml, $url, true, 6);
        $result = $this->FromXml($response);
        if (isset($result['result_code']) and $result['result_code'] == 'SUCCESS') {
            return $result;
        } else {
            Yii::warning('提现失败:' . $response);
            return $result['return_msg'] . ':' . $result['err_code_des'];
        }
    }
    
    /**
     * 输出xml字符
     *
     **/
    public function toXml($data)
    {

        $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
     */
    public function FromXml($xml)
    {

        //禁止引用外部xml实体
        $disableLibxmlEntityLoader = libxml_disable_entity_loader(true); //改为这句
        $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        libxml_disable_entity_loader($disableLibxmlEntityLoader); //添加这句

        return $data;
    }


    public function toUrlParams($data)
    {
        $buff = "";
        foreach ($data as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
            if (is_array($v)) {
                foreach ($v as $t) {
                    $buff .= $k . "=" . $t . "&";
                }
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     * 生成签名
     *
     */
    public function makeSign($data)
    {
        //签名步骤一：按字典序排序参数
        ksort($data);
        $string = $this->toUrlParams($data);

        //签名步骤二：在string后加入KEY
        $string = $string . "&key=" . $this->KEY;

        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    private static function postXmlCurl($xml, $url, $useCert = false, $second = 30)
    {
        $SSLCERT_PATH = Yii::$app->basePath . '/cert/apiclient_cert.pem';
        $SSLKEY_PATH = Yii::$app->basePath . '/cert/apiclient_key.pem';
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_URL, $url);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, false);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        if ($useCert == true) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验

            //设置证书
            //使用证书：cert 与 key 分别属于两个.pem文件

            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');

            curl_setopt($ch, CURLOPT_SSLCERT, $SSLCERT_PATH);

            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');

            curl_setopt($ch, CURLOPT_SSLKEY, $SSLKEY_PATH);

        } else {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        $error = curl_errno($ch);
        if ($error) {
            Yii::warning($error);
        }
        curl_close($ch);
        return $data;
    }

    /**
     *
     * 检测签名
     */
    public function CheckSign($data)
    {
        $sign = $this->makeSign($data);
        if ($data['sign'] == $sign) {
            return true;
        } else {
            return false;
        }
    }

    

    /**
     * 将xml转为array
     *
     */
    public function response($xml)
    {
        $data = $this->FromXml($xml);
        if ($data['return_code'] != 'SUCCESS') {
            ImLog::instance('wx_pay_calbak.log')->Log('签名错误：' . $xml);
            return $data;
        }
        $result = $this->CheckSign($data);
        if ($result) {
            return $data;
        } else {
            return false;
        }
    }

    public function status($data)
    {
        //需要签名参数
        $order_data = [
            'appid' => $this->APPID,
            'mch_id' => $this->MCHID,
            'out_trade_no' => $data['nid'],
            'nonce_str' => Yii::$app->getSecurity()->generateRandomString(32)
        ];

        $order_data['sign'] = $this->makeSign($order_data);
        $xml_data = $this->toXml($order_data);
        //检测订单状态接口连接
        $check_url = 'https://api.mch.weixin.qq.com/pay/orderquery';
        $result_data = $this->postXmlCurl($xml_data, $check_url, true);
        $result_data = $this->FromXml($result_data);
        return $result_data;
    }
}