<?php
namespace Payment\AliPayment;

use Payment\AliPayment\lib\AliPaymentTradeContentBuilder;
use Payment\AliPayment\lib\AliPaymentTradeRequest;
use Payment\AliPayment\lib\AopClient;
use Payment\CLogFileHandler;
use Payment\Log;
use Payment\PaymentException;

class AliPaymentGenerator
{
    private $config;

    private $aop;

    private $_method = [
        'precreate' =>  'alipay.trade.precreate',
        'wap'       =>  'alipay.trade.wap.pay',
        'app'       =>  'alipay.trade.app.pay',
    ];

    private $_response = [
        'precreate' => 'alipay_trade_precreate_response',
        'wap'       => 'alipay_trade_wap_pay_response',
        'app'       => 'alipay_trade_app_pay_response',
    ];

    private $_execute = [
        'precreate'     => 'execute',
        'wap'           => 'pageExecute',
        'app'           => 'sdkExecute'
    ];

    public function __construct($config)
    {
        $this -> config = new AliPayConfiguration($config);
        $this -> aop    = new AopClient($this -> config);
    }

    /**
     * @param array $paras
     * @return mixed
     * @throws PaymentException
     */
    public function order(array $paras)
    {
        $handle = new CLogFileHandler($this -> config->getLogPath().'/');
        Log::Init($handle);

        // 创建请求builder,设置请求参数
        $builder = new AliPaymentTradeContentBuilder();
        $builder -> setOutTradeNo($paras['out_trade_no'] ?? '');
        $builder -> setTotalAmount(bcdiv($paras['total_amount'],100,2) ?? '');
        $builder -> setTimeExpress("5m");
        $builder -> setSubject($paras['title'] ?? '');
        $builder -> setBody($paras['detail'] ?? '');
        $builder -> setGoodsDetailList($paras['goods'] ?? []);

        $bizContent = $builder -> getBizContent();

        $request = new AliPaymentTradeRequest();
        $request->setBizContent ( $bizContent ,$this->_method[$paras['trade_type']]);
        $request->setNotifyUrl ( $this->config->getNotifyUrl() );

        $execute = $this -> _execute[$paras['trade_type']];

        $result = $this -> aop ->$execute($request);

        if($paras['trade_type'] !== 'precreate') return $result;

        $response_name = $this->_response[$paras['trade_type']];
        $response = (array)$result -> $response_name;

        if(empty($result) || $response['code'] !== '10000')
        {
            throw new PaymentException($response['sub_msg']);
        }

        if(isset($response['qr_code']) == false)
        {
            throw new PaymentException('未知错误,无法获取二维码地址');
        }

        return $response['qr_code'];
    }

    /**
     * @param array $input
     * @param callable $func
     * @return string
     */
    function notify(array $input, callable $func)
    {
        //写入文件日志
        $handle = new CLogFileHandler($this -> config->getLogPath().'/notify');
        Log::Init($handle);

        Log::INFO(json_encode($input));

        $result = $this -> aop->rsaCheckV1($input, $this->config->getAlipayPublicKey(), $this->config->getSignType());

        if(empty($result))
        {
            Log::ERROR('异步通知数据校验失败');
            return "fail";
        }

        $func($input);

        return "success";
    }


    /**
     * @param string $number
     * @return array
     * @throws PaymentException
     * @throws lib\AliPaymentException
     */
    function query(string $number)
    {
        $builder = new AliPaymentTradeContentBuilder();
        $builder -> setOutTradeNo($number);

        $biz_content = $builder->getBizContent();
        $request = new AliPaymentTradeRequest();
        $request -> setBizContent ( $biz_content ,'alipay.trade.query' );

        $result = $this -> aop -> execute($request);

        $response = (array) $result -> alipay_trade_query_response;

        if($response['code'] != '10000')
        {
            throw new PaymentException($response['sub_msg']);
        }

        return $response;
    }


    /**
     * @param array $param
     * @return \SimpleXMLElement
     * @throws PaymentException
     * @throws lib\AliPaymentException
     */
    public function refund(array $param)
    {
        $builder = new AliPaymentTradeContentBuilder();
        $builder -> setOutTradeNo($param['order_number']);
        $builder -> setRefundAmount(bcdiv($param['amount'],100,2));
        $builder -> setRefundReason($param['complain']);

        $bizContent = $builder->getBizContent();
        $request = new AliPaymentTradeRequest();
        $request->setBizContent ( $bizContent ,'alipay.trade.refund');

        $result = $this -> aop -> execute($request,null,$builder->getAppAuthToken());

        $response = $result -> alipay_trade_refund_response;

        if( $response -> code != '10000')
        {
            throw new PaymentException($response->sub_msg);
        }

        return $response;
    }
}