使用场景与示例
CoinVertus API 实践:收款、在自有服务中集成钱包、网络分析以及 PHP 客户端。
目录
API 地址: https://api.coinvertus.net/v1
使用场景
三个典型场景:自行收款、在自有服务中集成钱包,以及网络分析。示例展示调用顺序;完整字段见上文各节。
作为 SCI:收款
每个订单通过 getnewaddress 获取独立地址,label 为订单号。收款以 tx.new 回调送达,确认以 tx.confirmed 送达;在 confirmed 时完成订单。没有回调时用 txinfo 核对。带账单和状态的现成支付页面请使用 SCI 接口。
# Order created: a dedicated address (right: addresses)
POST /v1/getnewaddress/DOGE {"network": "dogecoin-mainnet", "label": "order-1042"}
-> {"coin": "DOGE", "network": "dogecoin-mainnet", "address": "D8vFz4p1L37jdg47HXKtSHA5uYLYxbGgPD", ...}
# The buyer pays: webhook tx.new, then tx.confirmed
{"event": "tx.confirmed", "transaction": {"txid": "c0c0…", "direction": "in", "status": "confirmed",
"transfers": [{"address": "D8vFz4p1L37jdg47HXKtSHA5uYLYxbGgPD", "amount": "250"}]}}
# No webhook received: reconcile (right: read)
POST /v1/txinfo/DOGE {"network": "dogecoin-mainnet", "txid": "c0c0…"}作为钱包:余额与发送
余额用 getbalance,历史用 transactions。转账:用 getfee 估算,用 preparetx 生成计划,用 posttx 和 confirm 确认,然后通过 task 或 task.done 回调跟踪任务。只有任务为 confirmed 时资金才算发出。
# Balances and history (right: read)
POST /v1/getbalance/DOGE {"network": "dogecoin-mainnet"}
POST /v1/transactions/DOGE {"network": "dogecoin-mainnet", "page": 1}
# Transfer (right: send)
POST /v1/getfee/DOGE {"network": "dogecoin-mainnet", "to": [{"address": "D8vFz4p1L37jdg47HXKtSHA5uYLYxbGgPD", "amount": "100"}]}
POST /v1/preparetx/DOGE {"network": "dogecoin-mainnet", "to": [{"address": "D8vFz4p1L37jdg47HXKtSHA5uYLYxbGgPD", "amount": "100"}], "fee": "normal"}
-> {"prepare_id": "prep_…", "prepare_confirm": "…", "prepare_time": 1789647000, "fingerprint": "…", ...}
POST /v1/posttx {"prepare_id": "prep_…", "confirm": "hex(HMAC-SHA256(secret, key_id.fingerprint.prepare_confirm.prepare_time))"}
-> {"task_id": "task_…", "task_info": "queue", ...}
POST /v1/task/task_… {}
-> {"task_info": "confirmed", "txid": "b7b7…", ...} (or the task.done webhook)作为分析:网络、区块、地址
网络与索引状态用 chains、lastblock 和 info;数据用 blocks、block、tx 和 address,每页 100 条;汇率用 rates。这些调用对访客开放时无需密钥;使用密钥时适用您套餐的限额。
# Network and index state (key optional when open to guests)
GET /v1/chains
GET /v1/chains/dash/lastblock
GET /v1/chains/dash/info
# Blocks, transactions, addresses: pages of 100
GET /v1/chains/dash/blocks?page=1
GET /v1/chains/dash/block/2371204
GET /v1/chains/dash/tx/e7e7…
GET /v1/chains/dash/address/XcF5mKwWsiv3k394GBQNpYAuk3CVJ48Xnp?direction=in&page=2
# Rates
POST /v1/rates {"coins": ["DASH", "LTC"]}PHP 示例
签名调用、发送计划及其确认:
<?php
const CVC_API = 'https://api.coinvertus.net/v1';
function cvcCall(string $method, array $params = []): array
{
$keyId = getenv('CVC_API_KEY_ID');
$secret = getenv('CVC_API_SECRET');
$path = '/v1/' . $method;
$body = $params === [] ? '' : json_encode($params, JSON_UNESCAPED_SLASHES);
$t = time();
$v1 = hash_hmac('sha256', $t . '.POST.' . $path . '.' . $body, $secret);
$curl = curl_init(CVC_API . '/' . $method);
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-CVC-Key: ' . $keyId,
'X-CVC-Signature: t=' . $t . ',v1=' . $v1,
],
]);
$response = json_decode((string) curl_exec($curl), true);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
if ($status !== 200) {
throw new RuntimeException($status . ' ' . ($response['error']['code'] ?? '') . ' ' . ($response['error']['message'] ?? ''));
}
return $response;
}
$balances = cvcCall('getbalance/DOGE');
$plan = cvcCall('preparetx/DOGE', [
'to' => [['address' => 'D8vFz4p1L37jdg47HXKtSHA5uYLYxbGgPD', 'amount' => '100']],
]);
// Проверьте план: получатели, network_fee, service_fee_usd — и подтвердите его.
$confirm = hash_hmac('sha256', getenv('CVC_API_KEY_ID') . '.' . $plan['fingerprint'] . '.'
. $plan['prepare_confirm'] . '.' . $plan['prepare_time'], getenv('CVC_API_SECRET'));
$task = cvcCall('posttx', ['prepare_id' => $plan['prepare_id'], 'confirm' => $confirm]);
// Итог — вебхук task.done или опрос task/{TASK_ID}.
$task = cvcCall('task/' . $task['task_id']);接收 Webhook:
<?php
$secret = getenv('CVC_API_WEBHOOK_SECRET');
$body = file_get_contents('php://input');
$t = null;
$signatures = [];
foreach (explode(',', $_SERVER['HTTP_X_CVC_SIGNATURE'] ?? '') as $pair) {
[$name, $value] = array_pad(explode('=', trim($pair), 2), 2, '');
if ($name === 't') {
$t = $value;
} elseif ($name === 'v1') {
$signatures[] = strtolower($value);
}
}
$expected = hash_hmac('sha256', $t . '.' . $body, $secret);
$valid = $t !== null && ctype_digit($t) && abs(time() - (int) $t) <= 300
&& array_filter($signatures, fn (string $v1): bool => hash_equals($expected, $v1)) !== [];
if (!$valid) {
http_response_code(400);
exit;
}
$hook = json_decode($body, true);
if (!$events->remember($hook['event_id'])) {
http_response_code(200);
exit;
}
switch ($hook['event']) {
case 'tx.new':
case 'tx.confirmed':
case 'tx.dropped':
$ledger->apply($hook['transaction']);
break;
case 'task.done':
$payouts->finish($hook['task']['task_id'], $hook['task']['task_info'], $hook['task']['txid']);
break;
case 'paybalance.low':
$alerts->notify($hook['reason'], $hook['paybalance']['usd']);
break;
}
http_response_code(200);解密 getnewaddress 返回的 privkey:
<?php
$raw = base64_decode($response['privkey']);
$key = hash_hmac('sha256', 'privkey', getenv('CVC_API_SECRET'), true);
$privkey = openssl_decrypt(substr($raw, 12, -16), 'aes-256-gcm', $key, OPENSSL_RAW_DATA,
substr($raw, 0, 12), substr($raw, -16));示例已简化:遇到 429 和 503 时请间隔后重试,并按错误代码处理错误。