<?php

namespace common\helpers\UPOP;

use common\helpers\LoggerTool;

class PaySign {
    private $frontPayUrl;
    private $backPayUrl;
    private $agencyId;
    private $terminalId;
    private $childMerchantId;
    private $notifyUrl;
    private $returnUrl;

    public function __construct()
    {
        $this->frontPayUrl = \Yii::$app->params['UPOP']['frontPayUrl'];
        $this->backPayUrl = \Yii::$app->params['UPOP']['backPayUrl'];
        $this->agencyId = \Yii::$app->params['UPOP']['agencyId'];
        $this->terminalId = \Yii::$app->params['UPOP']['terminalId'];
        $this->childMerchantId = \Yii::$app->params['UPOP']['childMerchantId'];
        $this->notifyUrl = \Yii::$app->params['UPOP']['notifyUrl'];
        $this->returnUrl = \Yii::$app->params['UPOP']['returnUrl'];
    }

    public function sendEncodeData($payload, $url)
    {
        $json_content = json_encode($payload);

        // 随机生成aes密钥
        $aes_key = $this->randomKeys();
        // 商户RSA私钥
        $private_rsa_key = file_get_contents($this->getPath() . $this->agencyId . '/' . $this->agencyId.  '.pem');
        // 平台RSA公钥
        $public_rsa_key = file_get_contents($this->getPath() . $this->agencyId . '/' . 'GHT_ROOT.pem');
        // 用随机生成的aes密钥加密请求报文
        $data['encryptData'] = $this->aesEncode($json_content, $aes_key);
        // 用我司平台rsa公钥加密上面随机生成的aes密钥
        $data['encryptKey'] = $this->rsaEncode($aes_key, $public_rsa_key);
        // 用商户ras私钥签名
        $data['signData'] = $this->rsaSign($json_content, $private_rsa_key);
        // 商户号
        $data['agencyId'] = $this->agencyId;
        $data_content = json_encode($data);
        // 发送请求
        $request_result = $this->httpPost($data_content, $url);
        // 解密返回报文
        $result_json = json_decode($request_result,true);
        // 用商户私钥解密aes密钥（是由平台随机生成的）
        $result_aes_key = $this->rsaDecode($result_json['encryptKey'], $private_rsa_key);
        // 用aes密钥解密报文
        $decode_content = $this->aesDecode($result_json['encryptData'], $result_aes_key);
        // 用平台公钥验签
        if ($this->verifySign($decode_content, $result_json['signData'], $public_rsa_key)) {
            return json_decode($decode_content, true);
        } else {
            // 验签失败
            return false;
        }
    }

    public function decodeData($encryptKey, $signData, $encryptData)
    {
        // 商户RSA私钥
        $private_rsa_key = file_get_contents($this->getPath() . $this->agencyId . '/' . $this->agencyId.  '.pem');
        // 平台RSA公钥
        $public_rsa_key = file_get_contents($this->getPath() . $this->agencyId . '/' . 'GHT_ROOT.pem');
        // encryptKey RSA解密
        $rootAesKey = $this->rsaDecode($encryptKey, $private_rsa_key);
        // encryptData AES解密
        $payload = $this->aesDecode($encryptData, $rootAesKey);
        // 用平台公钥对signData验签
        if ($this->verifySign($payload, $signData, $public_rsa_key)) {
            return json_decode($payload, true);
        } else {
            // 验签失败
            return false;
        }
    }

    private function aesEncode($data, $aes_key): string
    {
        $encrypt_data = openssl_encrypt($this->pad($data), "aes-128-ecb", $aes_key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
        return base64_encode($encrypt_data);
    }

    public function aesDecode($data, $aes_key)
    {
        $data = base64_decode($data);
        return openssl_decrypt($data, "aes-128-ecb", $aes_key, OPENSSL_RAW_DATA);
    }

    private function rsaEncode($data, $public_rsa_key)
    {
        $ret = false;
//        if (!self::_checkPadding(OPENSSL_PKCS1_PADDING, 'en')){
//            return 'padding error';
//        }
        $key = openssl_get_publickey($public_rsa_key);
        if (openssl_public_encrypt($data,$result,$key,OPENSSL_PKCS1_PADDING)) {
            $ret = base64_encode($result);
        }
        return $ret;
    }

    private function rsaDecode($data, $private_rsa_key)
    {
        $ret = false;
        $data = base64_decode($data);
        if ($data !== false){
            if (openssl_private_decrypt($data, $result, $private_rsa_key, OPENSSL_PKCS1_PADDING)){
                $ret = $result;
            }
        }
        return $ret;
    }

    private function rsaSign($data, $private_rsa_key)
    {
        $res = openssl_get_privatekey($private_rsa_key);
        openssl_sign($data,$sign, $res);
        openssl_free_key($res);

        return base64_encode($sign);
    }

    private function verifySign($data, $signData, $public_rsa_key): bool
    {
        $signData = base64_decode($signData);
        $res = openssl_get_publickey($public_rsa_key);
        $result = openssl_verify($data, $signData, $res);
        openssl_free_key($res);
        if ($result === 1) {
            return true;
        } else {
            return false;
        }
    }

    private function httpPost($data, $url): string
    {
        try {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
            curl_setopt($ch,CURLOPT_TIMEOUT,600);
            curl_setopt($ch,CURLOPT_URL, $url);
            curl_setopt($ch,CURLOPT_POST,true);
            curl_setopt($ch,CURLOPT_POSTFIELDS, $data);
            curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
            if (strpos($url, 'https') !== false) {
                curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
                curl_setopt($ch, CURLOPT_SSLVERSION, 1);
            }
            $ret_data = trim(curl_exec($ch));
            curl_close($ch);

            return $ret_data;
        } catch (\Exception $e) {
            LoggerTool::info($e->getMessage());
            return '';
        }
    }

    /*
     * 获取授权文件位置.
     */
    private function getPath()
    {
        return \Yii::getAlias('@common/runtime/upop/');
//        return dirname(__FILE__) . '/';
    }

    private function randomKeys(): string
    {
        $key = '';
        $pattern = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMNOPQRSTUVWXYZ';
        for ($i = 0; $i < 16; $i++) {
            $key .= $pattern[mt_rand(0, 35)];
        }
        return $key;
    }

    private function pad($data, $blocksize = 16): string
    {
        $pad = 16 - (strlen($data) % 16);
        return $data . str_repeat(chr($pad), $pad);
    }
}