<?php
declare (strict_types=1);

namespace app\common\library\payment\gateway\driver;

use app\common\library\payment\gateway\Driver;
use cores\exception\BaseException;
use app\common\library\Log;
use app\common\enum\Client as ClientEnum;
/**
 * 全民付移动支付驱动
 * Class Wechat
 * @package app\common\library\payment\gateway\driver
 */
class Huifu extends Driver
{
    private $domain = "https://api-mop.chinaums.com";
    private $result;
    /**
     * 设置支付宝配置信息(全局只需设置一次)
     * @param array $options 支付宝配置信息
     * @param string $client 下单客户端
     * @return Driver|null
     */
    public function setOptions(array $options, string $client): ?Driver
    {
        $this->client = $client ?: null;
        if ($options['mchType'] === 'provider') {
            $config = [
                'appid' => $options['provider']['appid'],
                'appkey' => $options['provider']['appkey'],
                'msgSrcId' => $options['provider']['msgSrcId'],
                'msgSrc' => $options['provider']['msgSrc'],
                'md5Secret' => $options['provider']['md5Secret'],
                'mid' => $options['provider']['mid'],
                'tid' => $options['provider']['tid'],
            ];
        } else {
            $config = [
                'appid' => $options['normal']['appid'],
                'appkey' => $options['normal']['appkey'],
                'msgSrcId' => $options['normal']['msgSrcId'],
                'msgSrc' => $options['normal']['msgSrc'],
                'md5Secret' => $options['normal']['md5Secret'],
                'mid' => $options['normal']['mid'],
                'tid' => $options['normal']['tid'],
            ];
        }
        $this->config = $config;
        return $this;
    }
   
    /* 统一下单API
     * @param string $outTradeNo 交易订单号
     * @param string $totalFee 实际付款金额
     * @param array $extra 附加的数据 (需要携带openid)
     * @return bool
     * @throws BaseException
     */
    public function addReceiver(string $type, string $account, string $name, string $relation_type = "HEADQUARTER", string $custom_relation = ""): bool
    {
        
        return true;
    }
     /**
     * 统一下单API
     * @param string $outTradeNo 交易订单号
     * @param string $totalFee 实际付款金额
     * @param array $extra 附加的数据 (需要携带openid)
     * @return bool
     * @throws BaseException
     */
    public function profitsharing(string $transaction_id, string $out_order_no, array $receivers): bool
    {
        return true;
    }
    public function profitsharingQuery(string $out_order_no, string $transaction_id): ?array{
        return [];
    }
    /**
     * 统一下单API
     * @param string $outTradeNo 交易订单号
     * @param string $totalFee 实际付款金额
     * @param array $extra 附加的数据 (需要携带openid)
     * @return bool
     * @throws BaseException
     */
    public function unify(string $outTradeNo, string $totalFee, array $extra = []): bool
    {
        try {
            $result = null;
            // 发起API调用 微信小程序端
            if ($this->client === ClientEnum::MP_WEIXIN) {

                //$outTradeNo = date("YmdHis").mt_rand(1000,9999);
                $order_url = $this->domain . "/v1/netpay/wx/mini-pre-order";
                //$order_url = $this->domain . "/v1/netpay/wx/unified-order";
                $params = [
                    "requestTimestamp" => date("Y-m-d H:i:s"),
                    "merOrderId" => $this->config['msgSrcId'].$outTradeNo,
                    "mid" => $this->config['mid'],
                    "tid" => $this->config['tid'],
                    "subAppId" => $this->config['appid'],
                    "subOpenId" => $extra['openid'],
                    "tradeType" => "MINI",
                    "notifyUrl" => $this->notifyUrl(),
                    "orderDesc" => "商品支付",
                    "totalAmount" => bcmul($totalFee, "100"),
                    "divisionFlag" => false,//分账标记
                    //"platformAmount" => false,//平台商户分 账金额
                    // "subOrders" => [
                    //     [
                    //         "mid" => "111",
                    //         "totalAmount" => 1,
                    //     ]
                        
                    // ],
                ];
                $authorization = $this->getAuthorization($params);
                $headers = ["Authorization:".$authorization];
                
                $data = httpRequest($order_url, "POST", json_encode($params), $headers, false);
                if ($data['errCode'] != "SUCCESS") {
                    Log::append('银联-unify', ['client' => $this->client, 'result' => $data]);
                    return false;
                }
                $this->result = $data;
                return true;
            }
        } catch (\Throwable $e) {
            $this->throwError('银联API下单失败:' . $e->getMessage(), true, 'unify');
        }
        return false;
    }
    /**
     * 交易查询 (主动查询订单支付状态)
     * @param string $outTradeNo 交易订单号
     * @return array|null
     * @throws BaseException
     */
    public function tradeQuery(string $outTradeNo): ?array
    {
        try {
             $tradeQuery_url = $this->domain . "/v1/netpay/wx/mini-pre-query";
            $params = [
                "requestTimestamp" => date("Y-m-d H:i:s"),
                "merOrderId" => $outTradeNo,//date("YmdHis").mt_rand(1000,9999),
                "mid" => $this->config['mid'],
                "tid" => $this->config['tid'],
            ];
            $authorization = $this->getAuthorization($params);
            $headers = ["Authorization:".$authorization];
            
            $data = httpRequest($tradeQuery_url, "POST", json_encode($params), $headers, false);
            // echo "<pre>";
            // print_r($data);
            // exit();
            if ($data['errCode'] != "SUCCESS") {
                Log::append('huifu-tradeQuery', ['client' => $this->client, 'result' => $data]);
                return false;
            }
            return [
                // 支付状态: true成功 false失败
                'paySuccess' => isset($data['status']) && $data['status'] === 'TRADE_SUCCESS',
                // 第三方交易流水号
                'tradeNo' => $data['targetOrderId'] ?? ""
            ];
            return $data;
        } catch (\Throwable $e) {
            $this->throwError('银联API交易查询失败:' . $e->getMessage(), true, 'tradeQuery');
        }
        return false;
       
    }

    /**
     * 输出错误信息
     * @param string $errMessage 错误信息
     * @param bool $isLog 是否记录日志
     * @param string $action 当前的操作
     * @throws BaseException
     */
    private function throwError(string $errMessage, bool $isLog = false, string $action = '')
    {
        $this->error = $errMessage;
        $isLog && Log::append("银联-{$action}", ['errMessage' => $errMessage]);
        throwError($errMessage);
    }
    
    

    /**
     * 支付成功后的异步通知
     * @return bool
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     */
    public function notify(): bool
    {
       // 接收表单数据
        $this->notifyParams = request()->filter([])->post();

        $notifyParams = $this->notifyParams;
        $sign = $notifyParams['sign'];
        unset($notifyParams['sign']);
        ksort($notifyParams);
        $str = http_build_query($notifyParams);
        $verifyNotify = false;
        if ($sign != md5($str)) {
            $verifyNotify = true;
        }
        
        // 判断交易单状态必须是支付成功
        $this->notifyResult = $verifyNotify && $this->notifyParams['status'] === 'TRADE_SUCCESS';
        // 记录日志
        Log::append('银联-notify', [
            'params' => $this->notifyParams,
            'verifyNotify' => $verifyNotify,
            'response' => $this->getNotifyResponse(),
            'result' => $this->notifyResult,
            'message' => '银联异步回调验证' . ($this->notifyResult ? '成功' : '失败')
        ]);
        return $this->notifyResult;
    }

    /**
     * 微信支付退款API
     * @param string $outTradeNo 第三方交易单号
     * @param string $refundAmount 退款金额
     * @param array $extra 附加数据 (需要携带订单付款总金额)
     * @return bool
     * @throws BaseException
     */
    public function refund(string $outTradeNo, string $refundAmount, array $extra = []): bool
    {

        try {
            $refund_url = $this->domain . "/v1/netpay/refund";
            $params = [
                "requestTimestamp" => date("Y-m-d H:i:s"),
                "merOrderId" => $outTradeNo,
                "mid" => $this->config['mid'],
                "tid" => $this->config['tid'],
                "targetOrderId" => $extra['trade_no'],
                "refundAmount" => bcmul($refundAmount, "100"),
                "refundOrderId" => $this->config['msgSrcId'].date("YmdHis").mt_rand(1000, 9999),
                "refundDesc" => "订单退款",
            ];
            $authorization = $this->getAuthorization($params);
            $headers = ["Authorization:".$authorization];
            
            $data = httpRequest($refund_url, "POST", json_encode($params), $headers, false);
            //var_dump($data);

            if ($data['errCode'] != "SUCCESS") {
                $this->throwError($data['errMsg']);
            }
            // 请求成功
            return true;
         } catch (\Throwable $e) {
            $this->throwError('银联API退款请求:' . $e->getMessage(), true, 'refund');
        }
        return false;
    }

    /**
     * 商家转账到零钱API
     * @param string $outTradeNo 交易订单号
     * @param string $totalFee 实际付款金额
     * @param array $extra 附加的数据 (需要携带openid、desc)
     * @return bool
     * @throws BaseException
     */
    public function transfers(string $outTradeNo, string $totalFee, array $extra = []): bool
    {
        if (!$this->getApp()->transfers($outTradeNo, $totalFee, $extra)) {
            $this->setError($this->getApp()->getError());
            return false;
        }
        return true;
    }

    /**
     * 获取异步回调的请求参数
     * @return array
     */
    public function getNotifyParams(): array
    {
        return [
            // 第三方交易流水号
            'tradeNo' => $this->notifyParams['targetOrderId']
        ];
    }

    /**
     * 返回异步通知结果的输出内容
     * @return string
     */
    public function getNotifyResponse(): string
    {
        return $this->notifyResult ? 'SUCCESS' : 'FAILED';
    }

    /**
     * 返回统一下单API的结果 (用于前端)
     * @return array
     * @throws BaseException
     */
    public function getUnifyResult(): array
    {
        if (empty($this->result)) {
            $this->throwError('当前没有unify结果', true, 'getUnifyResult');
        }
        $this->result['out_trade_no'] =  $this->result['merOrderId'];//str_replace($this->config['msgSrcId'], "", $this->result['merOrderId']);
        return $this->result;
    }
    /**
     * 生成授权码
     * [getAuthorization description]
     * @param  [type] $data [description]
     * @return [type]       [description]
     */
    private function getAuthorization($data){
        $appid = $this->config['appid'];
        $appkey = $this->config['appkey'];

        $timestamp = date("YmdHis",time());
        $nonce = md5(uniqid((string)microtime(true),true));
        $body = json_encode($data);
        //echo $body;
        $str = bin2hex(hash('sha256', $body, true));
        $signature = base64_encode(hash_hmac('sha256', "$appid$timestamp$nonce$str", $appkey, true));
        $authorization = 'OPEN-BODY-SIG AppId="'.$appid.'", Timestamp="'.$timestamp.'", Nonce="'.$nonce.'", Signature="'.$signature.'"';

        return $authorization;
    }
    /**
     * 异步回调地址
     * @return string
     */
    private function notifyUrl(): string
    {
        // 例如:https://www.xxxx.com/alipayNotice.php
        return base_url() . 'huifuNotice.php';
    }
}