You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP调用Coinbase高级交易API:JWT鉴权返回NULL问题排查

PHP调用Coinbase高级交易API返回NULL问题排查

我尝试用PHP调用Coinbase高级交易API,该API采用JWT鉴权,但官方未提供PHP示例。自行编写代码后,调用接口返回NULL,已多次核对API密钥信息确认无误。

原代码

use Firebase\JWT\JWT;

$keyName = 'organizations/b0f974e{redacted}860/apiKeys/d840f8{redacted}b6d';
$keySecret = "-----BEGIN EC PRIVATE KEY-----
MHcC{redacted}bHEzMVwDhg==
-----END EC PRIVATE KEY-----
";

$requestMethod = "GET";
$requestHost = "api.coinbase.com";
$requestPath = "/api/v3/brokerage/accounts";

function buildJwt($uri, $keyName, $keySecret) {
    $privateKeyResource = openssl_pkey_get_private($keySecret);
    if (!$privateKeyResource) {
        throw new Exception('Private key could not be parsed or is invalid.');
    }
    $payload = [
        'sub' => $keyName,
        'iss' => 'coinbase-cloud',
        'iat' => time(),
        'exp' => time() + 120,  // Token expiration time (example: 120 seconds from now)
        'uri' => $uri,
    ];
    $jwt = JWT::encode($payload, $privateKeyResource, 'ES256');
    return $jwt;
}

function main($requestMethod, $requestHost, $requestPath, $keyName, $keySecret) {
    $uri = "$requestMethod $requestHost$requestPath";
    $jwtToken = buildJwt($uri, $keyName, $keySecret);
    return $jwtToken;
}

$myjwt = main($requestMethod, $requestHost, $requestPath, $keyName, $keySecret);

function getAccountDetails($jwt, $requestMethod, $requestHost, $requestPath) {
    $curl = curl_init();
    curl_setopt_array($curl, [
        CURLOPT_URL => "https://$requestHost$requestPath",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => '',
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => $requestMethod,
        CURLOPT_HTTPHEADER => [
            "Authorization: Bearer $jwt",
            "Content-Type: application/json"
        ],
    ]);
    $response = curl_exec($curl);
    $err = curl_error($curl);
    curl_close($curl);
    if ($err) {
        echo "cURL Error #:" . $err;
    } else {
        return json_decode($response, true);
    }
}

$res = getAccountDetails($myjwt, $requestMethod, $requestHost, $requestPath);
var_dump($res);

问题排查与修复方案

1. JWT的URI格式错误

Coinbase要求JWT payload中的uri字段格式为请求方法 路径,不需要包含host。原代码中拼接了$requestHost,导致URI格式不符合要求,这是核心问题。

2. 缺少HTTP状态码与原始响应排查

原代码仅捕获cURL错误,但API返回非200状态码时,json_decode可能返回NULL,无法定位具体错误原因。

3. 私钥格式隐患

双引号包裹私钥可能导致转义问题,建议改用单引号。

修复后的代码

use Firebase\JWT\JWT;

$keyName = 'organizations/b0f974e{redacted}860/apiKeys/d840f8{redacted}b6d';
$keySecret = '-----BEGIN EC PRIVATE KEY-----
MHcC{redacted}bHEzMVwDhg==
-----END EC PRIVATE KEY-----';

$requestMethod = "GET";
$requestHost = "api.coinbase.com";
$requestPath = "/api/v3/brokerage/accounts";

function buildJwt($uri, $keyName, $keySecret) {
    $privateKeyResource = openssl_pkey_get_private($keySecret);
    if (!$privateKeyResource) {
        throw new Exception('私钥解析失败或无效');
    }
    $payload = [
        'sub' => $keyName,
        'iss' => 'coinbase-cloud',
        'iat' => time(),
        'exp' => time() + 120,  // Token有效期120秒
        'uri' => $uri,
    ];
    $jwt = JWT::encode($payload, $privateKeyResource, 'ES256');
    return $jwt;
}

function main($requestMethod, $requestPath, $keyName, $keySecret) {
    // 修正URI格式:仅包含请求方法和路径
    $uri = "$requestMethod $requestPath";
    $jwtToken = buildJwt($uri, $keyName, $keySecret);
    return $jwtToken;
}

$myjwt = main($requestMethod, $requestPath, $keyName, $keySecret);

function getAccountDetails($jwt, $requestMethod, $requestHost, $requestPath) {
    $url = "https://$requestHost$requestPath";
    $curl = curl_init();
    curl_setopt_array($curl, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => '',
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => $requestMethod,
        CURLOPT_HTTPHEADER => [
            "Authorization: Bearer $jwt",
            "Content-Type: application/json"
        ],
    ]);
    $response = curl_exec($curl);
    $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); // 获取HTTP状态码
    $err = curl_error($curl);
    curl_close($curl);

    if ($err) {
        echo "cURL错误: " . $err;
        return null;
    } else {
        // 打印状态码和原始响应,方便排查
        echo "HTTP状态码: $httpCode\n";
        echo "原始响应: $response\n";
        return json_decode($response, true);
    }
}

$res = getAccountDetails($myjwt, $requestMethod, $requestHost, $requestPath);
var_dump($res);

额外排查点

  • 确认服务器时间与标准时间同步,JWT的iat/exp字段依赖准确时间,偏差过大会导致Token验证失败。
  • 检查API密钥是否拥有accounts:read权限,权限不足也会返回错误。

内容的提问来源于stack exchange,提问作者Dan Holt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 02:23:11