<?php
/**
 * 微信支付
 * Created by PhpStorm.
 * User: phperstar
 * Date: 2019/11/28
 * Time: 2:12 PM
 */
namespace Util\WeiXin;

use Mall\Framework\Core\ErrorCode;
Use Mall\Framework\Core\ResultWrapper;

class Pay
{
    /**
     * 小程序 appId
     * @var string $appid
     */
    private $appid;
    /**
     * 商户号
     * @var string $mch_id
     */
    private $mch_id;

    /**
     * 商户支付密钥
     */
    private $partnerKey;

    /**
     * 微信支付异步通知地址
     */
    private $notifyUrl = PAY_NOTIFY_URL.'/common/WeiXinPayNotify/notify';

    /**
     * 公共的接口请求地址
     * @var string
     */
    private $apiUrl = 'https://api.mch.weixin.qq.com/pay/';

    /**
     * 基础得请求地址
     */
     private $baseUrl = 'https://api.mch.weixin.qq.com/';

    /**
     * Pay constructor.
     * @param string $appid
     * @param $mch_id
     * @param $partnerKey
     */
    public function __construct($appid='', $mch_id, $partnerKey)
    {
        $this->appid = $appid;
        $this->mch_id = $mch_id;
        $this->partnerKey = $partnerKey;
    }

    /**
     * 统一下单接口
     */
    public function unifiedorder($orderNo, $total_fee, $ip, $source, $shopName, $openid = '', $attach = '')
    {
        $url = $this->apiUrl.'unifiedorder';

        switch ($source){
            case 'byteDanceH5':
                $trade_type = 'MWEB';
                break;
            case 'H5':
                $trade_type = 'MWEB';
                break;
            case 'APP':
                $trade_type = 'APP';
                break;
            default:
                $trade_type = 'JSAPI';
                break;
        }

        $params = [
            'appid'  => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID
            'mch_id' => $this->mch_id, // 商户号
            'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串
            'out_trade_no' => $orderNo, // 商户订单号
            'total_fee'  => yuanToFen($total_fee), // 订单总金额,单位分
            'spbill_create_ip' => $ip, // 终端ip
            'trade_type' => $trade_type, // 交易类型
            'notify_url' => $this->notifyUrl, // 通知地址
            'body'  => $shopName.'-'.'线上商城',
            'attach' => $attach, // 附加数据
        ];
        if($trade_type == 'JSAPI'){
            $params['openid'] = $openid;
        }

        $params['sign'] = self::getSign($params);
        $post_xml = arrayToWeiXinXml($params);
        $curl_content = request($url, $post_xml);

        $result = $this->commonResult($curl_content);
        if(!$result->isSuccess()){
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $response_content = $result->getData();

        //获取统一下单返回的id
        $prepay_id = $response_content['prepay_id'];

        // H5支付返回的支付跳转链接
        $mweb_url = isset($response_content['mweb_url']) ? $response_content['mweb_url'] : '';

        switch ($source){
            case 'byteDanceH5':
                $payParams = $mweb_url;  // 字节跳动小程序调用微信H5支付获取支付链接
                break;
            case 'H5':
                $payParams =  self::payment($prepay_id);
                break;
            case 'APP':
                $payParams =  self::payment($prepay_id);
                break;
            default:
                $payParams =  self::paymentToMinprogram($prepay_id);
                break;
        }

        return ResultWrapper::success($payParams);
    }


    /**
     * 小程序调起支付用到的参数
     * 官方接口地址: https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3
     */
    public function paymentToMinprogram($prepay_id)
    {
        unset($params);
        $params = [
            'appId' => $this->appid, // 小程序ID
            'timeStamp' => (string)time(), //时间戳
            'nonceStr' => md5(md5(time().'qianniao.vip')), //32位随机字符串
            'package'  => 'prepay_id='.$prepay_id, // 数据包
            'signType' => 'MD5', // 签名方式
        ];
        $params['paySign'] = self::getSign($params);
        return $params;
    }

    /**
     * 调起支付用到的参数
     * 官方接口地址: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12
     * @param $prepay_id
     */
    public function payment($prepay_id)
    {
        unset($parm);
        $parm = array(
            'appid' => $this->appid,
            'partnerid' => $this->mch_id,
            'timestamp' => (string)time(), //时间戳
            'noncestr' => md5(md5(time().'qianniao.vip')), //32位随机字符串
            'prepayid' => $prepay_id, //统一下单接口返回的prepay_id 参数值
            'package' => 'Sign=WXPay',
        );
        $parm['sign'] = self::getSign($parm);
        return $parm;
    }

    /**
     * 付款码支付
     * 官方文档地址: https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1
     */
    public function micropay($orderNo, $total_fee, $ip, $shopName, $auth_code)
    {
        $url = $this->apiUrl.'micropay';

        $params = [
            'appid'  => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID
            'mch_id' => $this->mch_id, // 商户号
            'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串
            'body'  => $shopName.'-'.'线上商城',
            'out_trade_no' => $orderNo, // 商户订单号
            'total_fee'  => yuanToFen($total_fee), // 订单总金额,单位分
            'spbill_create_ip' => $ip, // 终端ip
            'auth_code' => $auth_code,
        ];
        $params['sign'] = self::getSign($params);
        $post_xml = arrayToWeiXinXml($params);
        $curl_content = request($url, $post_xml);

        if($curl_content['httpcode'] != '200'){
            return ResultWrapper::fail($curl_content['errorMsg'], ErrorCode::$apiNotResult);
        }

        $response_content = (array)simplexml_load_string($curl_content['content'], 'SimpleXMLElement', LIBXML_NOCDATA);
            if($response_content['return_code'] != 'SUCCESS'){
            return ResultWrapper::fail($response_content['return_msg'], ErrorCode::$weixinPayError);
        }

        if($response_content['result_code'] != 'SUCCESS' && $response_content['err_code'] != 'USERPAYING' ){
            return ResultWrapper::fail($response_content['err_code_des'], ErrorCode::$weixinPayError);
        }

        // 20s内查询支付状态，如果有明确错误直接返回错误，其他情况继续查询。如果成功直接返回成功
        for($i=1; $i<=10; $i++)
        {
            $url = $this->apiUrl.'orderquery';
            $params = [
                'appid'  => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID
                'mch_id' => $this->mch_id, // 商户号
                'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串
                'out_trade_no' => $orderNo, // 商户订单号
            ];

            $params['sign'] = self::getSign($params);
            $post_xml = arrayToWeiXinXml($params);
            $curl_content = request($url, $post_xml);

            if($curl_content['httpcode'] != '200'){
                return ResultWrapper::fail($curl_content['errorMsg'], ErrorCode::$apiNotResult);
            }

            $response_content = (array)simplexml_load_string($curl_content['content'], 'SimpleXMLElement', LIBXML_NOCDATA);
            if($response_content['return_code'] != 'SUCCESS'){
                return ResultWrapper::fail($response_content['return_msg'], ErrorCode::$weixinPayError);
            }

            if($response_content['result_code'] != 'SUCCESS' && $response_content['err_code'] == 'ORDERNOTEXIST'){
                return ResultWrapper::fail($response_content['err_code_des'], ErrorCode::$weixinPayError);
            }

            if($response_content['return_code'] == 'SUCCESS' &&  $response_content['result_code'] == 'SUCCESS' && $response_content['trade_state'] == 'SUCCESS'){
                return ResultWrapper::success($response_content['trade_state']);
            }

            sleep(2);
        }

        // 20s内没有支付则撤销订单
        for($i=1; $i<=10; $i++){
            $url = $this->baseUrl.'secapi/pay/reverse';
            $params = [
                'appid'  => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID
                'mch_id' => $this->mch_id, // 商户号
                'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串
                'out_trade_no' => $orderNo, // 商户订单号
            ];

            $params['sign'] = self::getSign($params);
            $post_xml = arrayToWeiXinXml($params);
            $curl_content = request($url, $post_xml, 10, false, [], true);

            if($curl_content['httpcode'] != '200'){
                return ResultWrapper::fail($curl_content['errorMsg'], ErrorCode::$apiNotResult);
            }
            $response_content = (array)simplexml_load_string($curl_content['content'], 'SimpleXMLElement', LIBXML_NOCDATA);
            if($response_content['return_code'] != 'SUCCESS'){
                return ResultWrapper::fail($response_content['return_msg'], ErrorCode::$weixinPayError);
            }

            if($response_content['result_code'] != 'SUCCESS' && $response_content['recall'] == 'N'){
                return ResultWrapper::fail($response_content['err_code_des'], ErrorCode::$weixinPayError);
            }

            if($response_content['return_code'] == 'SUCCESS' &&  $response_content['result_code'] == 'SUCCESS' && $response_content['recall'] == 'N'){
                return ResultWrapper::success($response_content['result_code']);
            }
        }

    }


    /**
     * H5查询支付状态接口
     */
    public function orderquery($orderNo)
    {
        $url = $this->apiUrl.'orderquery';
        $params = [
            'appid'  => $this->appid, // 小程序ID/微信开放平台审核通过的应用APPID
            'mch_id' => $this->mch_id, // 商户号
            'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串
            'out_trade_no' => $orderNo, // 商户订单号
        ];

        $params['sign'] = self::getSign($params);
        $post_xml = arrayToWeiXinXml($params);
        $curl_content = request($url, $post_xml);

        $result = $this->commonResult($curl_content);
        if(!$result->isSuccess()){
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $response_content = $result->getData();

        $trade_state = $response_content['trade_state'];

        return ResultWrapper::success($trade_state);
    }

    /**
     * 申请退款API
     * 官方文档地址:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_4
     */
    public function transfers($outerTradeNo, $out_refund_no, $refundMoney, $orderMoney, $sslData)
    {
        $url = $this->baseUrl.'secapi/pay/refund';
        $params = [
            'appid'  => $this->appid,
            'mch_id' => $this->mch_id,
            'nonce_str' => md5(md5(time().'qianniao.vip')), // 32位随机字符串
            'transaction_id' => $outerTradeNo, // 微信交易号
            'out_refund_no'  => $out_refund_no, // 商户退款单号
            'refund_fee' => yuanToFen($refundMoney), // 退款金额
            'total_fee'  => yuanToFen($orderMoney),  // 订单总金额
        ];

        $params['sign'] = self::getSign($params);
        $post_xml = arrayToWeiXinXml($params);
        $curl_content = request($url, $post_xml, 10, false, [], true, $sslData);

        $result = $this->commonResult($curl_content);
        if(!$result->isSuccess()){
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $response_content = $result->getData();

        return ResultWrapper::success($response_content['refund_id']);
    }

    /**
     * 公共处理返回结果方法
     */
    public function commonResult($curl_content)
    {
        if($curl_content['httpcode'] == '200'){
            $response_content = (array)simplexml_load_string($curl_content['content'], 'SimpleXMLElement', LIBXML_NOCDATA);
            if($response_content['return_code'] == 'SUCCESS'){
                if($response_content['result_code'] == 'SUCCESS'){
                    return ResultWrapper::success($response_content);
                }else{
                    return ResultWrapper::fail($response_content['err_code_des'], ErrorCode::$weixinPayError);
                }
            }else{
                return ResultWrapper::fail($response_content['return_msg'], ErrorCode::$weixinPayError);
            }
        }else{
            return ResultWrapper::fail($curl_content['errorMsg'], ErrorCode::$apiNotResult);
        }
    }

    /**
     * 生成签名方法
     * 官方接口地址: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3
     * 官方签名测试地址: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=20_1
     * @param $parm
     * @return string
     */
    public function getSign($parm)
    {
        //非空参数值的参数按照参数名ASCII码从小到大排序（字典序）
        if(ksort($parm)){
            $stringA = '';
            //使用URL键值对的格式拼接成字符串stringA
            foreach($parm as $key => $value){
                if($value == 0 || !empty($value)){
                    $stringA .=$key.'='.$value.'&';
                }
            }
        }else{
            echo "对参数排序出错";
            exit();
        }

        //在stringA最后拼接上key=商户支付密钥
        $stringSignTemp = $stringA.'key='.$this->partnerKey;

        //对stringSignTemp进行MD5运算，再将得到的字符串所有字符转换为大写
        $signValue = strtoupper(md5($stringSignTemp));
        return $signValue;
    }

}