<?php
/**
 * 支付宝支付
 * Created by PhpStorm.
 * User: phperstar
 * Date: 2019/12/03
 * Time: 09:51 AM
 */
namespace Util\AliPay;

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

class Pay
{
    // 支付宝应用id
    private $appid;

    // 开发者私钥
    private $rsaPrivateKey;

    // 支付宝公钥
    private $alipayrsaPublicKey;

    // 支付宝私钥文件路径
    private $rsaPrivateKeyFilePath;

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

    /**
     * 公共的接口请求地址
     * @var string
     */
    private $gatewayUrl = 'https://openapi.alipay.com/gateway.do';


    public function __construct($appid, $rsaPrivateKey, $alipayrsaPublicKey){
        $this->appid = $appid;
        $this->rsaPrivateKey = $rsaPrivateKey;
        $this->alipayrsaPublicKey = $alipayrsaPublicKey;
    }

    /**
     * 外部商户APP唤起快捷SDK创建订单并支付
     * 官方文档地址: https://docs.open.alipay.com/api_1/alipay.trade.app.pay
     */
    public function appPay($orderData)
    {
        // 获取业务请求参数集合
        $return = self::getAppPayBizContent($orderData);
        if(!$return->isSuccess()){
            return ResultWrapper::fail($return->getData(), $return->getErrorCode());
        }
        $biz_content = $return->getData();

        // 公共请求参数
        $params = [
            'app_id' => $this->appid,
            'method' => 'alipay.trade.app.pay',
            'format' => 'json',
            'charset' => 'UTF-8',
            'sign_type' => 'RSA2',
            'timestamp' => date("Y-m-d H:i:s"),
            'version' => '1.0',
            'notify_url'=> $this->notifyUrl,
        ];

        $params['biz_content'] = $biz_content;

        ksort($params);

        // 生成签名
        $params['sign'] = self::sign($params);
        foreach ($params as &$value) {
            $value = $this->characet($value, $params['charset']);
        }
        $return = http_build_query($params);
        return ResultWrapper::success($return);
    }

    /**
     * 业务参数
     * 请求字符编码必须是UTF-8
     */
    private function getAppPayBizContent($orderData)
    {
        $bizcontent = [];
        $bizcontent['subject'] = $orderData['subject']; // 商品的标题/交易标题/订单标题/订单关键字
        $bizcontent['out_trade_no']  = $orderData['out_trade_no']; // 商户网站唯一订单号
        $bizcontent['timeout_express'] = isset($orderData['timeout_express'])?$orderData['timeout_express']:'30m'; // 最晚付款时间
        $bizcontent['total_amount']    = $orderData['total_amount']; // 订单总金额，单位为元，精确到小数点后两位
        $bizcontent['passback_params'] = urlencode($orderData['passback_params']); // 回调数据
        $bizcontent['product_code']    = 'QUICK_MSECURITY_PAY';

        foreach ($bizcontent as $key => $value){
            if(empty($value)){
                return ResultWrapper::fail('业务参数'.$key.'为空值', ErrorCode::$paramError);
            }
        }

        return  ResultWrapper::success(json_encode($bizcontent, JSON_UNESCAPED_UNICODE));
    }

    /**
     * 单笔转账接口
     * 官网文档地址: https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.uni.transfer
     */
    public function transfer($orderData)
    {
        // 获取业务请求参数集合
        $return = self::getTransferBizContent($orderData);
        if(!$return->isSuccess()){
            return ResultWrapper::fail($return->getData(), $return->getErrorCode());
        }
        $biz_content = $return->getData();

        // 公共请求参数
        $params = [
            'app_id' => $this->appid,
            'method' => 'alipay.fund.trans.uni.transfer',
            'format' => 'json',
            'charset' => 'UTF-8',
            'sign_type' => 'RSA2',
            'timestamp' => date("Y-m-d H:i:s"),
            'version' => '1.0',
            'notify_url'=> $this->notifyUrl,
        ];

        $params['biz_content'] = $biz_content;
        ksort($params);

        // 生成签名
        $params['sign'] = self::sign($params);
        foreach ($params as &$value) {
            $value = $this->characet($value, $params['charset']);
        }
        $return = http_build_query($params);
        return ResultWrapper::success($return);
    }

    /**
     * 业务参数
     * 请求字符编码必须是UTF-8
     */
    private function getTransferBizContent($orderData)
    {
        $bizcontent = [];
        $bizcontent['out_biz_no']  = $orderData['out_biz_no']; // 商户网站唯一订单号
        $bizcontent['trans_amount']    = $orderData['trans_amount']; // 订单总金额，单位为元，精确到小数点后两位
        $bizcontent['product_code']    = 'TRANS_ACCOUNT_NO_PWD';
        $bizcontent['payee_info'] = [
            'identity' => $orderData['userCenterId'], // 参与方的唯一标识
            'identity_type' => 'ALIPAY_LOGON_ID',  // 支付宝登陆账号
            'name' => $orderData['customerName'], // 支付宝对应的正式名称
        ];

        foreach ($bizcontent as $key => $value){
            if(empty($value)){
                return ResultWrapper::fail('业务参数'.$key.'为空值', ErrorCode::$paramError);
            }
        }

        return  ResultWrapper::success(json_encode($bizcontent, JSON_UNESCAPED_UNICODE));
    }

    /**
     * 生成签名
     */
    private function sign($params)
    {
        if($this->checkEmpty($this->rsaPrivateKeyFilePath)){
            $priKey=$this->rsaPrivateKey;
            $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
                wordwrap($priKey, 64, "\n", true) .
                "\n-----END RSA PRIVATE KEY-----";
        }else {
            $priKey = file_get_contents($this->rsaPrivateKeyFilePath);
            $res = openssl_get_privatekey($priKey);
        }

        ($res) or die('您使用的私钥格式错误，请检查RSA私钥配置');

        $data = self::getSignContent($params);
        if ("RSA2" == $params['sign_type']) {
            openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
        } else {
            openssl_sign($data, $sign, $res);
        }

        if(!empty($this->rsaPrivateKeyFilePath)){
            openssl_free_key($res);
        }
        $sign = base64_encode($sign);
        return $sign;
    }

    /**
     * 获取要签名的参数内容
     * @param $params
     * @return string
     */
    public function getSignContent($params) {
        ksort($params);

        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {

                // 转换成目标字符集
                $v = $this->characet($v, "UTF-8");

                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }

        unset ($k, $v);
        return $stringToBeSigned;
    }

    /**
     * 转换字符集编码
     * @param $data
     * @param $targetCharset
     * @return string
     */
    function characet($data, $targetCharset)
    {
        if (!empty($data)) {
            $fileType = "UTF-8";
            if (strcasecmp($fileType, $targetCharset) != 0) {
                $data = mb_convert_encoding($data, $targetCharset, $fileType);
                //				$data = iconv($fileType, $targetCharset.'//IGNORE', $data);
            }
        }

        return $data;
    }

    /**
     * 校验$value是否非空
     *  if not set ,return true;
     *    if is null , return true;
     **/
    protected function checkEmpty($value)
    {
        if (!isset($value))
            return true;
        if ($value === null)
            return true;
        if (trim($value) === "")
            return true;

        return false;
    }
}