<?php
/**
 *  ╔═══════════════════════════════════════════════════╗
 *  ║                                                   ║
 *  ║     ██╗  ██╗   █████╗    ██████╗                  ║
 *  ║     ██║  ██║  ██╔══██╗  ██╔═══██╗                 ║
 *  ║     ███████║  ███████║  ██║   ██║                 ║
 *  ║     ██╔══██║  ██╔══██║  ██║   ██║                 ║
 *  ║     ██║  ██║  ██║  ██║  ╚██████╔╝   SNS           ║
 *  ║                                                   ║    
 *  ║                                                   ║    
 *  ║     © 2023 HaoSNS™ All Rights Reserved            ║
 *  ║     官方网站: https://www.haosns.com                *
 *  ║     本代码由赣州乐易网络科技有限公司®提供             *
 *  ║                                                    *
 *  ║   未经授权禁止复制、传播或用于其他商业目的            *
 *  ║                                                   ║
 *  ╚═══════════════════════════════════════════════════╝
 */



namespace app\common\service\pay;


use app\common\enum\AfterSaleLogEnum;
use app\common\enum\OrderEnum;
use app\common\enum\PayEnum;
use app\common\enum\UserTerminalEnum;
use app\common\logic\PayNotifyLogic;
use app\common\model\Order;
use app\common\model\RechargeOrder;
use app\common\model\UserAuth;
use app\common\service\after_sale\AfterSaleService;
use app\common\service\WeChatConfigService;

use EasyWeChat\Pay\Application;



/**
 * 微信支付
 * Class WeChatPayService
 * @package app\common\server
 */
class WeChatPayService extends BasePayService
{
    /**
     * 授权信息
     * @var UserAuth|array|\think\Model
     */
    protected $auth;


    /**
     * 微信配置
     * @var
     */
    protected $config;


    /**
     * easyWeChat实例
     * @var
     */
    // protected $pay;

    protected $app;


    /**
     * 当前使用客户端
     * @var
     */
    protected $terminal;


    /**
     * 初始化微信配置
     * @param $terminal //用户终端
     * @param null $userId //用户id(获取授权openid)
     */
    public function __construct($terminal, $userId = null)
    {
        $this->terminal = $terminal;
        
        $this->config = WeChatConfigService::getWechatConfigByTerminal($terminal);
        // dd($this->config);
        // 小程序appid
        // $this->config['http'] = [
        //     'throw'  => true,
        //     'timeout' => 5.0,
        // ];
        // dd($this->config);
        $this->app = new Application($this->config);
        // dd($terminal);
        if ($userId !== null) {
            $this->auth = UserAuth::where(['user_id' => $userId, 'terminal' => $terminal])->findOrEmpty();
        }
    }



    /**
     * @notes 发起微信支付统一下单
     * @param $from
     * @param $order
     * @return array|false|string
     */
    public function pay($from, $order)
    {
        try {
            switch ($this->terminal) {
                case UserTerminalEnum::WECHAT_MMP:
                    $result = $this->jsapiPay($from, $order);
                    break;
                case UserTerminalEnum::WECHAT_OA:
                    $result = $this->jsapiPay($from, $order);
                    break;
                case UserTerminalEnum::IOS:
                case UserTerminalEnum::ANDROID:
                    $result = $this->appPay($from, $order);
                    break;
                case UserTerminalEnum::H5:
                    $result = $this->mwebPay($from, $order);
                    break;
                default:
                    throw new \Exception('支付方式错误');
            }
            return [
                'config' => $result,
                'pay_way' => PayEnum::WECHAT_PAY
            ];
        } catch (\Exception $e) {
            $this->setError($e->getMessage());
            return false;
        }
    }


    /**
     * @notes jsapi支付(小程序,公众号)
     * @param $from
     * @param $order
     * @return array|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function jsapiPay($from, $order)
    {
        // $check_source = [UserTerminalEnum::WECHAT_MMP, UserTerminalEnum::WECHAT_OA];
        // if ($this->auth->isEmpty() && in_array($this->terminal, $check_source)) {
        //     throw new \Exception('获取授权信息失败');
        // }
        // $result = $this->pay->order->unify($this->getAttributes($from, $order));
        // $this->checkResultFail($result);
        // return $this->pay->jssdk->bridgeConfig($result['prepay_id'], false);
        // dd($this->config);

        // dd($order->toArray());
        // dd($this->auth->toArray());
        // 获取小程序的appid
        $json = [
            "appid"=>$this->app->getConfig()->get('app_id'),
            "mchid" =>$this->app->getConfig()->get('mch_id'),
            "description" => "商品",
            "out_trade_no" => $order->sn,
            "notify_url" => $this->app->getConfig()->get('notify_url'),
            "amount" => [
                "total" => intval(floatval($order->order_amount)  * 100),
                "currency" => "CNY"
            ],
            'attach' => $from,//来源
            "payer" => [
                "openid" => $this->auth['openid'],
            ]
        ];
        // dd($json);
        $result = $this->app->getClient()->post("/v3/pay/transactions/jsapi", [
            'json'=>$json
         ]);
        //  dd($result->toArray(false));//查询官网返回数据支付原错误
        //  dd($result->toArray());

        if (!isset($result['prepay_id'])) {
            throw new \Exception('获取prepay_id失败');
        }

        // 生成小程序支付参数
        $utils = $this->app->getUtils();
        // dd($utils);
        $config = $utils->buildBridgeConfig($result['prepay_id'],'wx75a732ad8a5eaa17');

        return [
            'prepay_id' => $result['prepay_id'],
            'payment_params' => $config,
        ];
    }


    /**
     * @notes app支付(android,ios)
     * @param $from
     * @param $order
     * @return array
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function appPay($from, $order)
    {
        $result = $this->pay->order->unify($this->getAttributes($from, $order));
        $this->checkResultFail($result);
        return $this->pay->jssdk->appConfig($result['prepay_id']);
    }


    /**
     * @notes h5支付 (非微信环境下h5)
     * @param $from
     * @param $order
     * @return string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function mwebPay($from, $order)
    {
        // echo '微信h5支付';
        // dd($order->toArray());
        // dd($this->getAttributes($from, $order));
        // echo intval(floatval($order->order_amount)  * 100);die;
        $json = [
            "appid"=>$this->app->getConfig()->get('app_id'),
            "mchid" =>$this->app->getConfig()->get('mch_id'),
            "description" => "商品",
            "out_trade_no" => $order->sn,
            "notify_url" => $this->app->getConfig()->get('notify_url'),
            "amount" => [
                "total" => intval(floatval($order->order_amount)  * 100),
                "currency" => "CNY"
            ],
            'attach' => $from,//来源
            'scene_info' => [
                'payer_client_ip' => $_SERVER['REMOTE_ADDR'], // 用户真实IP
                'h5_info' => ['type' => 'Wap'] // 必须填写
            ],
        ];
        // dd($json);
        $result = $this->app->getClient()->post("/v3/pay/transactions/h5", [
            'json'=>$json
         ]);
        //  \dd($response->toArray(false));die;
        // $result = $this->pay->order->unify($this->getAttributes($from, $order));
        // dd($result);
        // $this->checkResultFail($result);
        $redirect_url = request()->domain() . '/mobile/bundle/pages/h5_pay_query/h5_pay_query?pay_way='.PayEnum::WECHAT_PAY;
        $redirect_url = urlencode($redirect_url);
        return $result['h5_url'] . '&redirect_url=' . $redirect_url;
    }



    /**
     * @notes 验证微信返回数据
     * @param $result
     * @throws \Exception
     */
    public function checkResultFail($result)
    {
        if ($result['return_code'] != 'SUCCESS' || $result['result_code'] != 'SUCCESS') {
            if (isset($result['return_code']) && $result['return_code'] == 'FAIL') {
                throw new \Exception($result['return_msg']);
            }
            if (isset($result['err_code_des'])) {
                throw new \Exception($result['err_code_des']);
            }
            throw new \Exception('未知原因');
        }
    }


    /**
     * @notes 支付请求参数
     * @param $from
     * @param $order
     * @return array
     */
    public function getAttributes($from, $order)
    {
        switch ($from) {
            case 'order':
                $attributes = [
                    'trade_type' => 'JSAPI',
                    'body' => '商品',
                    'total_fee' => $order['order_amount'] * 100, // 单位：分
                    'openid' => $this->auth['openid'],
                    'attach' => 'order',
                ];
                break;
            case 'recharge':
                $attributes = [
                    'trade_type' => 'JSAPI',
                    'body' => '充值',
                    'total_fee' => $order['order_amount'] * 100, // 单位：分
                    'openid' => $this->auth['openid'],
                    'attach' => 'recharge',
                ];
                break;
        }

        //app支付类型
        if ($this->terminal == UserTerminalEnum::ANDROID || $this->terminal == UserTerminalEnum::IOS) {
            $attributes['trade_type'] = 'APP';
        }

        //h5支付类型
        if ($this->terminal == UserTerminalEnum::H5) {
            $attributes['trade_type'] = 'MWEB';
        }

        //NATIVE模式设置
        if ($this->terminal == UserTerminalEnum::PC) {
            $attributes['trade_type'] = 'NATIVE';
            $attributes['product_id'] = $order['sn'];
        }

        //修改微信统一下单,订单编号 -> 支付回调时截取前面的单号 18个
        //修改原因:回调时使用了不同的回调地址,导致跨客户端支付时(例如小程序,公众号)可能出现201,商户订单号重复错误
        $suffix = mb_substr(time(), -4);
        $attributes['out_trade_no'] = $order['sn'] . $attributes['trade_type'] . $this->terminal . $suffix;

        return $attributes;
    }


    /**
     * @notes 支付回调
     * @return \Symfony\Component\HttpFoundation\Response
     * @throws \EasyWeChat\Kernel\Exceptions\Exception
     */
    public function notify()
    {
        $server = $this->app->getServer();
         // 处理支付结果事件
        $server->handlePaid(function ($message,\Closure $next) {
            // dd($message->toArray());
            if ($message->trade_state !== 'SUCCESS') {
                return $next('支付失败');
            }
            // 用户是否支付成功
            $extra['transaction_id'] = $message['transaction_id'];
            $attach = $message['attach'];
            $message['out_trade_no'] = mb_substr($message['out_trade_no'], 0, 18);
            switch ($attach) {
                case 'order':
                    // 自营商城
                    $order = Order::where(['sn' => $message['out_trade_no']])->findOrEmpty();
                    if ($order->isEmpty() || $order['pay_status'] >= PayEnum::ISPAID) {
                        return true;
                    }

                    //特殊情况：用户在前端支付成功的情况下，调用回调接口之前，订单被关闭
                    if ($order['order_status'] == OrderEnum::STATUS_CLOSE) {
                        //更新订单支付状态为已支付
                        Order::update(['pay_status' => PayEnum::ISPAID], ['id' => $order['id']]);

                        //发起售后
                        AfterSaleService::orderRefund([
                            'order_id' => $order['id'],
                            'scene' => AfterSaleLogEnum::ORDER_CLOSE
                        ]);

                        return true;
                    }

                    PayNotifyLogic::handle('order', $message['out_trade_no'], $extra);
                    break;
                case 'recharge':
                    $order = RechargeOrder::where(['sn' => $message['out_trade_no']])->findOrEmpty();
                    if ($order->isEmpty() || $order->pay_status == PayEnum::ISPAID) {
                        return true;
                    }
                    PayNotifyLogic::handle('recharge', $message['out_trade_no'], $extra);
                    break;
                case 'two_order':
                    // 二手商城支付回调
                    echo '二手商城支付回调';

            }
          
            return true; // 返回处理完成



            // $content='微信支付订单号:'.$message['transaction_id'].',商户订单号:'.$message['out_trade_no'].',支付者:'.$message['payer']['openid'];
            // dd($content);
            // // file_put_contents('./log.txt', $content.PHP_EOL, FILE_APPEND);
            // writeToFile('./hd_pay_res.txt', '回调的内容');
            // echo '支付回调';

            // return $next($message);
        });
        // 默认返回 ['code' => 'SUCCESS', 'message' => '成功']
        $response = $server->serve();
        return response($response->getBody(), 200, $response->getHeaders());

        // $app = new Application($this->config);
        // $response = $app->handlePaidNotify(function ($message, $fail) {

        //     if ($message['return_code'] !== 'SUCCESS') {
        //         return $fail('通信失败');
        //     }

        //     // 用户是否支付成功
        //     if ($message['result_code'] === 'SUCCESS') {
        //         $extra['transaction_id'] = $message['transaction_id'];
        //         $attach = $message['attach'];
        //         $message['out_trade_no'] = mb_substr($message['out_trade_no'], 0, 18);
        //         switch ($attach) {
        //             case 'order':
        //                 $order = Order::where(['sn' => $message['out_trade_no']])->findOrEmpty();
        //                 if ($order->isEmpty() || $order['pay_status'] >= PayEnum::ISPAID) {
        //                     return true;
        //                 }

        //                 //特殊情况：用户在前端支付成功的情况下，调用回调接口之前，订单被关闭
        //                 if ($order['order_status'] == OrderEnum::STATUS_CLOSE) {
        //                     //更新订单支付状态为已支付
        //                     Order::update(['pay_status' => PayEnum::ISPAID],['id'=>$order['id']]);

        //                     //发起售后
        //                     AfterSaleService::orderRefund([
        //                         'order_id' => $order['id'],
        //                         'scene' =>  AfterSaleLogEnum::ORDER_CLOSE
        //                     ]);

        //                     return true;
        //                 }

        //                 PayNotifyLogic::handle('order', $message['out_trade_no'], $extra);
        //                 break;
        //             case 'recharge':
        //                 $order = RechargeOrder::where(['sn' => $message['out_trade_no']])->findOrEmpty();
        //                 if($order->isEmpty() || $order->pay_status == PayEnum::ISPAID) {
        //                     return true;
        //                 }
        //                 PayNotifyLogic::handle('recharge', $message['out_trade_no'], $extra);
        //                 break;

        //         }
        //     } elseif ($message['result_code'] === 'FAIL') {
        //         // 用户支付失败

        //     }
        //     return true; // 返回处理完成

        // });
        // return $response->send();
    }


    /**
     * @notes 退款
     * @param $data //微信订单号、商户退款单号、订单金额、退款金额、其他参数
     * @return array|\EasyWeChat\Kernel\Support\Collection|false|object|\Psr\Http\Message\ResponseInterface|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public function refund($data)
    {
        if (!empty($data["transaction_id"])) {
            return $this->pay->refund->byTransactionId(
                $data['transaction_id'],
                $data['refund_sn'],
                $data['total_fee'],
                $data['refund_fee']
            );
        } else {
            return false;
        }
    }

}