使用Coinbase API通过PHP发送Satoshis至邮箱时签名无效问题求助
解决Coinbase API调用时的"invalid signature"认证错误
嘿,我看你在调用Coinbase API发送比特币到邮箱时遇到了签名验证失败的问题,这是Coinbase API认证中非常常见的坑,我来帮你拆解问题并给出修复方案:
核心问题分析
你收到的authentication_error: invalid signature错误,大概率是以下几个原因导致的:
1. 签名未进行Base64编码
Coinbase API要求签名必须是Base64编码后的HMAC-SHA256结果,但你的代码里直接使用了hash_hmac返回的原始二进制数据(第三个参数true),没有做Base64编码,这是最可能的元凶。
2. 请求体与签名计算的内容不一致
签名计算时使用的$body必须和实际发送给API的请求体完全一致——包括参数顺序、编码格式、甚至空格,任何细微差异都会导致签名不匹配。
3. 本地时间与Coinbase服务器时间不同步
Coinbase API要求请求的时间戳与服务器时间差不能超过30秒,如果你的服务器本地时间偏差过大,也会触发认证错误。
修复后的完整代码
我把你的代码做了针对性修改,解决了上述问题:
<?php // 先获取Coinbase官方服务器时间,避免时间差问题 $time_response = file_get_contents('https://api.coinbase.com/v2/time'); $time_data = json_decode($time_response, true); $timestamp = $time_data['data']['epoch']; // 使用Coinbase提供的精确时间戳 $method = 'POST'; $request_path = '/v2/accounts/34en86m3-b0qa-5022-a45c-b110z38631k6/transactions'; // 保持请求体的格式与签名计算完全一致 $body = 'type=send&to=gabriele.zangari@hotmail.it&amount=0.00002504¤cy=BTC'; $account_id = '34en86m3-b0qa-5022-a45c-b110z38631k6'; $apiSecret = 'VmQruPgmAYsW6Pq1vsC5bnzObd5LpTIn'; $accesskey = '1XJZLVA1F4zjQ9cO'; // 正确计算签名:HMAC-SHA256后进行Base64编码 $hash_input = $timestamp . $method . $request_path . $body; $signature = base64_encode(hash_hmac('sha256', $hash_input, $apiSecret, true)); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://api.coinbase.com' . $request_path); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); // 添加必要的请求头,包括Content-Type确保API正确解析请求体 $headers = array( 'Cb-Access-Key: ' . $accesskey, 'Cb-Access-Sign: ' . $signature, 'Cb-Access-Timestamp: ' . $timestamp, 'Cb-version: 2017-08-07', 'Content-Type: application/x-www-form-urlencoded' ); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); // 明确设置请求体,避免curl自动修改格式 curl_setopt($ch, CURLOPT_POSTFIELDS, $body); $result = curl_exec($ch); if (curl_errno($ch)) { echo 'Error:' . curl_error($ch); } else { echo $result; } curl_close ($ch); ?>
额外优化建议:改用JSON格式请求体
URL编码的请求体对参数顺序非常敏感,稍有不慎就会导致签名不匹配。改用JSON格式的请求体可以避免这个问题,代码调整如下:
// 换成JSON格式的请求体 $body = json_encode(array( 'type' => 'send', 'to' => 'gabriele.zangari@hotmail.it', 'amount' => '0.00002504', 'currency' => 'BTC' )); // 签名计算时使用这个JSON字符串 $hash_input = $timestamp . $method . $request_path . $body; // 修改请求头的Content-Type为application/json $headers = array( 'Cb-Access-Key: ' . $accesskey, 'Cb-Access-Sign: ' . $signature, 'Cb-Access-Timestamp: ' . $timestamp, 'Cb-version: 2017-08-07', 'Content-Type: application/json' ); // 请求体设置为JSON字符串 curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
最后提醒
- 确保你的API密钥拥有发送交易的权限,并且账户有足够的余额和手续费
- 测试阶段建议使用Coinbase的沙箱环境(
https://api.sandbox.coinbase.com),避免真实资金损失 - 永远不要在代码中硬编码真实的API密钥,建议使用环境变量存储
内容的提问来源于stack exchange,提问作者juansanchez1993
相关产品推荐
相关产品推荐

