Неверная аутентификация PHP (общая)

#php #coinbase-api

Вопрос:

У меня проблема с авторизацией. Я пытаюсь написать свой собственный код для управления API. Я не могу понять, почему я получаю ошибку {" errors ": [{" id ":" authentication_error "," message ":" invalid timestamp "}]}

Вот полный текст моего запроса ( CB-TEXT-TO-SIGN это отладочная информация, CB-ACCESS-KEY и секретный ключ api верен. CB-ACCESS-TIMESTAMP выглядит правильно):

НЕОБРАБОТАННЫЙ HTTP-КОД

 GET /v2/accounts HTTP/1.1
Host: api.coinbase.com
Accept: */*
CB-ACCESS-KEY: O###########b
CB-ACCESS-SIGN: YCanHaHFXdFu6lidCroLIUgk3ayzvG/oCwR/68MfLYw=
CB-ACCESS-TIMESTAMP: 2021-08-12T15:04:29.175Z
CB-TEXT-TO-SIGN: 2021-08-12T15:04:29.175ZGET/v2/accounts
Content-Type: application/json
User-Agent: Mozilla/5.0

HTTP/1.1 401 Unauthorized
Date: Thu, 12 Aug 2021 15:04:11 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: private, no-cache, must-revalidate
Content-Disposition: attachment; filename=response.json
Content-Security-Policy: <DELETED>
Expires: Sat, 01 Jan 2000 00:00:00 GMT
Pragma: no-cache
Referrer-Policy: strict-origin-when-cross-origin
Set-Cookie: <DELETED>
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Trace-Id: 8618971493290804459
Vary: Origin,Accept-Encoding
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: DENY
X-Permitted-Cross-Domain-Policies: none
X-Powered-By: Proof-of-Work
X-Request-Id: 1a0f2ff3-6fa8-49b3-b9ca-ee5d80b54d27
X-Xss-Protection: 1; mode=block
CF-Cache-Status: DYNAMIC
Set-Cookie: <DELETED>
Set-Cookie: <DELETED>
Server: cloudflare
CF-RAY: 67da983b9a4ab514-VNO
TIMESTAMP: 2021-08-12T15:04:30.203Z

{"errors":[{"id":"authentication_error","message":"invalid timestamp"}]}
 

исходный код

 <?php
class Config{
    public static $config=[
        "apiKey"=>"O##############b",
        "apiSecret"=>"F##############################e",
        //"passphrase"=>'',
    ];

    public static $debug=1;
}



class Utils
{
    static $apiKey = "O##############b";
    static $apiSecret = "F##############################e";
    //static $passphrase = '';
    static $textToSign = '';

    const FUTURE_API_URL = 'https://api.coinbase.com';
    const SERVER_TIMESTAMP_URL = '/time';

    public function __construct($config)
    {

        self::setParams($config);
    }

    public  static  function request($requestPath, $params, $method)
    {

        if (strtoupper($method) == 'GET') {
            $requestPath .= $params ? '?'.http_build_query($params) : '';
            $params = [];


        }

        $url = self::FUTURE_API_URL.$requestPath;

        $ch= curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);

        $body = $params ? json_encode($params, JSON_UNESCAPED_SLASHES) : '';
        $timestamp = self::getTimestamp();

        $sign = self::signature($timestamp, $method, $requestPath, $body, self::$apiSecret);
        $headers = self::getHeader(self::$apiKey, $sign, $timestamp, /*self::$passphrase,*/ self::$textToSign);


        if($method == "POST") {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        }


//        curl_setopt($ch, CURLOPT_TIMEOUT_MS,60);

        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER , TRUE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLINFO_HEADER_OUT,true);




        curl_setopt($ch, CURLOPT_HEADER, true);
//        curl_setopt($ch, CURLOPT_NOBODY,true);


        $return = curl_exec($ch);

        $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $headerTotal = strlen($return);
        $bodySize = $headerTotal - $headerSize;

        $body = substr($return, $headerSize, $bodySize);


        if(!curl_errno($ch))
        {
            $info = curl_getinfo($ch,CURLINFO_HEADER_OUT);
            if (Config::$debug)
            {

            }

            switch (true)
            {

                case Config::$debug==1;
                    echo ($info);


                $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);

//                $header = substr($return, 0, $headerSize);

                    print_r(substr($return, 0, $headerSize-2));
                    print_r("TIMESTAMP: ".self::getTimestamp());
                    print_r("nn");

                    print_r($body);
                    print_r("nn");
                    break;


                case Config::$debug==2;
                    $ntime = self::getTimestamp();
                    print_r($ntime." $bodyn");
                    break;
            }
        }

//        $body = substr($sContent, $headerSize, $bodySize);


//        print_r("headerSize:".$headerSize."n");
//        print_r("headerTotal:".$headerTotal."n");
//        print_r("bodySize:".$bodySize."n");

        $body = json_decode($body,true);

        return $body;
    }

    public static function getHeader($apiKey, $sign, $timestamp, /*$passphrase,*/ $textToSign)
    {
        $headers = array();

        $headers[] = "CB-ACCESS-KEY: $apiKey";
        $headers[] = "CB-ACCESS-SIGN: $sign";
        $headers[] = "CB-ACCESS-TIMESTAMP: $timestamp";
/*        $headers[] = "CB-ACCESS-PASSPHRASE: $passphrase"; */
        $headers[] = "CB-TEXT-TO-SIGN: $textToSign";
        $headers[] = "Content-Type: application/json";
        $headers[] = "User-Agent: Mozilla/5.0";


        return $headers;
    }


    public static function getTimestamp()
    {
        ini_set("date.timezone","UTC");
        return date("Y-m-dTH:i:s"). substr((string)microtime(), 1, 4) . 'Z';
    }


    function dateToTimestamp($isoTime)
    {
        @list($usec, $sec) = explode(".", $isoTime);
        $date = strtotime($usec);
        $return_data = str_pad($date.$sec,13,"0",STR_PAD_RIGHT); 
        return substr($return_data, 0, -1);

    }

    public static function getServerTimestamp(){
        try{
            $response = file_get_contents(self::FUTURE_API_URL.self::SERVER_TIMESTAMP_URL);
            $response = json_decode($response,true);

            return $response['iso'];
        }catch (Exception $e){
            return '';
        }
    }

    public static function signature($timestamp, $method, $requestPath, $body, $secretKey)
    {
        $message = (string) $timestamp . strtoupper($method) . $requestPath . (string) $body;

        self::$textToSign = $message;

        return base64_encode(hash_hmac('sha256', $message, $secretKey, true));
    }

    public static function wsSignature($timestamp, $method, $requestPath, $body, $secretKey)
    {
        $message = (string) $timestamp . strtoupper($method) . $requestPath . (string) $body;

        $ntime = self::getTimestamp();
        print_r($ntime." TEXT-TO-SIGN:$messagen");



        return base64_encode(hash_hmac('sha256', $message, $secretKey, true));
    }


    public static function get_millisecond()
    {
        $time = microtime(true);
        $msec=round($time*1000);

        return $msec/1000;
    }


    public static function setParams($configs){
        self::$apiKey=$configs["apiKey"];
        self::$apiSecret=$configs["apiSecret"];
        //self::$passphrase=$configs["passphrase"];
    }
}

$obj = new utils(Config::$config);
echo $res = $obj -> request('/v2/accounts', [], 'GET');
 

Any ideas, please?