
本文详解如何使用 cryptapi 提供的公钥签名验证机制校验回调请求真实性,并安全获取、解析交易数据,避免伪造请求风险。涵盖签名验证、url 构造、日志查询及金额确认全流程。
本文详解如何使用 cryptapi 提供的公钥签名验证机制校验回调请求真实性,并安全获取、解析交易数据,避免伪造请求风险。涵盖签名验证、url 构造、日志查询及金额确认全流程。
在集成 CryptAPI 支付回调时,双重验证机制是保障系统安全的核心:既要验证 HTTP 请求头中的签名(X-CA-Signature)以确认来源可信,又要主动调用 CryptAPI 日志接口(/logs/)核验交易真实性与到账状态。仅依赖签名或仅依赖 GET 参数均存在安全隐患。
✅ 正确的签名验证要点
原始代码中存在多个关键错误,需立即修正:
-
$data构造错误:CryptAPI 文档明确要求签名原文为 原始请求体(POST)或完整请求 URI(GET),但必须保持原始编码格式,不可二次 URL 解码或拼接。尤其注意:- GET 请求:应使用
$_SERVER['REQUEST_URI'](含原始 query string),不要手动拼接https://...,因为协议/主机可能被反向代理修改; - POST 请求:
file_get_contents('php://input')正确,但需确保未被php://filter或其他中间件篡改。
- GET 请求:应使用
签名解码与算法匹配:
base64_decode($_SERVER['HTTP_X_CA_SIGNATURE'])正确,但必须确保OPENSSL_ALGO_SHA256与 CryptAPI 实际使用的签名算法一致(文档确认为 SHA256-RSA)。公钥格式:提供的 PEM 公钥格式正确,但建议使用
trim()去除首尾空白,避免解析失败。
修正后的签名验证段如下:
$pubkey = trim("-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3FT0Ym8b3myVxhQW7ESuuu6lo
dGAsUJs4fq+Ey//jm27jQ7HHHDmP1YJO7XE7Jf/0DTEJgcw4EZhJFVwsk6d3+4fy
Bsn0tKeyGMiaE6cVkX0cy6Y85o8zgc/CwZKc0uw6d5siAo++xl2zl+RGMXCELQVE
ox7pp208zTvown577wIDAQAB
-----END PUBLIC KEY-----");
$signature = base64_decode($_SERVER['HTTP_X_CA_SIGNATURE'] ?? '');
$algo = OPENSSL_ALGO_SHA256;
// 精确构造签名原文:GET 用 REQUEST_URI,POST 用原始输入流
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$data = $_SERVER['REQUEST_URI'];
} else {
$data = file_get_contents('php://input');
}
if ($signature === false || openssl_verify($data, $signature, $pubkey, $algo) !== 1) {
http_response_code(400);
echo "Invalid signature";
exit;
}
✅ 主动查询交易日志(推荐做法)
签名验证仅证明请求来自 CryptAPI,不保证交易已确认或金额达标。因此必须调用 /logs/ 接口二次核验:
- 使用
$_GET['logticker'](如trc20_usdt)动态构建 API 地址; - 将原始回调参数(如
order_id,amount,email)通过http_build_query()传入callback参数; -
关键修复:若参数含
@符号(如邮箱),CryptAPI 存储为%40,需提前str_replace('@', '%40', $raw_query),否则日志查询失败。
示例日志查询逻辑:
if (!isset($_GET['order_id'], $_GET['amount'], $_GET['logticker'])) {
die('Missing required parameters');
}
$order_id = (int)$_GET['order_id'];
$expected_amount = (float)$_GET['amount'];
$logticker = $_GET['logticker'];
// 构造原始 callback query string(保留原始编码)
$raw_query = http_build_query($_GET);
$encoded_query = str_replace('@', '%40', $raw_query); // 修复邮箱 @ 编码
$api_url = "https://api.cryptapi.io/{$logticker}/logs/?callback=" . urlencode($encoded_query);
$ch = curl_init($api_url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_USERAGENT => 'CryptAPI-Validator/1.0'
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code !== 200 || !$response) {
error_log("CryptAPI logs API failed: HTTP {$http_code}");
http_response_code(500);
echo "Service unavailable";
exit;
}
$result = json_decode($response, true);
if (!isset($result['status']) || $result['status'] !== 'success') {
error_log("CryptAPI logs invalid response: " . print_r($result, true));
echo "Invalid callback data";
exit;
}
$confirmed_amount = 0.0;
foreach ($result['callbacks'] as $cb) {
// 仅统计已确认(pending == 0)且成功转发(result == 'sent')的交易
if ((int)$cb['pending'] === 0 && $cb['result'] === 'sent') {
$confirmed_amount += (float)$cb['value_coin'];
}
}
if ($confirmed_amount >= $expected_amount) {
// ✅ 交易确认完成:执行发货、更新订单状态等业务逻辑
update_order_status($order_id, 'paid', $confirmed_amount);
echo "*ok*";
} else {
// ⚠️ 金额不足或未确认,可记录日志并等待后续回调
error_log("Insufficient confirmed amount: {$confirmed_amount} <h3>⚠️ 重要注意事项</h3>
-
幂等性设计:CryptAPI 可能重复发送回调,所有业务操作(如发货、扣库存)必须基于
order_id做唯一性检查,避免重复执行。 - HTTPS 强制:回调 URL 必须使用 HTTPS,否则 CryptAPI 可能拒绝发送或签名失效。
-
超时与重试:
curl请求需设置合理超时(建议 ≤10s),并记录失败日志以便人工核查。 -
敏感信息脱敏:日志中避免打印完整
$_GET或私钥,使用error_log()记录结构化错误即可。 -
测试先行:使用 CryptAPI 沙盒环境和测试币种(如
btc_testnet)充分验证全流程,再上线主网。
通过严格实施签名验证 + 主动日志查询双保险,可彻底杜绝伪造回调与未确认交易带来的资金风险。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











