Документация API
REST API и вебхуки для интернет-магазинов: создавайте счета и получайте подписанные уведомления об оплате.
Быстрый старт
Выставьте счёт через API, отправьте покупателя на checkout_url, дождитесь вебхука invoice.paid — и отгрузите заказ. Деньги к этому моменту уже на вашем кошельке.
- Получите ключ API у поддержки (Telegram или support@ethpaid.com) — на время пилота ключи выдаём вручную.
- Укажите адрес для вебхуков (PUT /v1/webhook) и сохраните секрет подписи.
- Создавайте счёт на каждый заказ и перенаправляйте покупателя на checkout_url.
- Обрабатывайте вебхуки, проверяя подпись.
Базовый адрес: https://api.ethpaid.com
Ключи и режимы
Ключ передаётся в заголовке Authorization: Bearer. Ключ sk_test_… работает с тестовыми сетями (Sepolia, Base Sepolia, BSC Testnet, Tron Nile) и позволяет имитировать оплату; sk_live_… — основные сети. Счета и ссылки тестового и боевого режимов не пересекаются. Ключ показывается один раз — храните его на сервере, не в браузере.
Создать счёт
POST /v1/invoices
curl https://api.ethpaid.com/v1/invoices \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042" \
-d '{"amount": "25.00", "currency": "USD", "order_id": "1042",
"description": "Заказ №1042", "redirect_url": "https://shop.example.com/thanks"}'| Поле | Тип | Описание |
|---|---|---|
amount | строка | Сумма, например "25.50" |
currency | строка | USD (по умолчанию), USDT, USDC, EUR |
order_id | строка | Ваш номер заказа, до 200 символов |
description | строка | Описание для покупателя, до 500 символов |
redirect_url | строка | Куда вернуть покупателя после оплаты (https) |
metadata | объект | Любые ваши данные — вернутся в вебхуке |
networks | массив | Ограничить сети: ethereum, bsc, base, tron (в тесте — sepolia, bsc-testnet, base-sepolia, tron-nile) |
Поле currency — USD, USDT, USDC или EUR (EUR пересчитывается по курсу на момент создания, сумма округляется вверх). order_id уникален в магазине: повтор вернёт 409. Заголовок Idempotency-Key защищает от двойного создания при повторе запроса.
Объект счёта
{
"id": "3401423f-c7f5-43b0-a115-3c4b14ef6ba6",
"object": "invoice",
"number": 128,
"mode": "test",
"status": "new",
"amount": "25",
"currency": "USD",
"order_id": "1042",
"checkout_url": "https://pay.ethpaid.com/i/3401423f-c7f5-43b0-a115-3c4b14ef6ba6",
"expires_at": "2026-10-26T12:00:00+00:00",
"paid_at": null,
"payment_options": [
{ "network": "base-sepolia", "token": "USDT", "address": "0x7F80…31BD", "amount": "25", "network_fee": "0" },
{ "network": "tron-nile", "token": "USDT", "address": "TYj1…k8Ux", "amount": "26.47", "network_fee": "1.47" }
]
}payment_options — варианты оплаты: для каждой сети свой адрес и точная сумма. network_fee — сбор сети Tron, если его платит покупатель (уже входит в amount).
Статусы
new | ждёт оплаты |
pending | платёж замечен, ждём подтверждений сети |
paid | оплачен — можно отгружать |
partially_paid | оплачен частично; покупатель может доплатить, или примите как есть (POST …/accept) |
overpaid | оплачен с переплатой — можно отгружать, лишнее можно вернуть |
paid_late | оплачен после срока — решите: принять или вернуть |
expired | срок истёк без оплаты |
cancelled | отменён |
Вебхуки
POST на ваш адрес при каждой смене статуса: invoice.pending, invoice.paid, invoice.partially_paid, invoice.overpaid, invoice.paid_late, invoice.expired, invoice.cancelled (и test.ping по запросу). Ответьте 2xx — иначе повторим через 1 мин, 5 мин, 15 мин, 1 ч, 6 ч и 24 ч. Адрес — только https, без внутренних IP.
{
"id": "6c1f…",
"type": "invoice.paid",
"created": 1792952000,
"data": { "id": "3401423f-…", "status": "paid", "order_id": "1042", "metadata": { … }, … }
}Проверка подписи
Заголовок ETHPAID-Signature: t=<время>,v1=<подпись>. Подпись — HMAC-SHA256 секретом вебхука от строки «t.тело_запроса» (тело — ровно как пришло). Отклоняйте запросы старше 5 минут. Событие может прийти повторно — используйте id события для защиты от дублей.
PHP
<?php
$secret = getenv('ETHPAID_WEBHOOK_SECRET');
$body = file_get_contents('php://input'); // raw body, do not re-encode
parse_str(str_replace(',', '&', $_SERVER['HTTP_ETHPAID_SIGNATURE'] ?? ''), $sig);
$expected = hash_hmac('sha256', $sig['t'] . '.' . $body, $secret);
if (!hash_equals($expected, $sig['v1'] ?? '') || abs(time() - (int)$sig['t']) > 300) {
http_response_code(400); exit;
}
$event = json_decode($body, true);
if ($event['type'] === 'invoice.paid') {
// mark order $event['data']['order_id'] as paid (once per $event['id'])
}
http_response_code(200);Node.js
import crypto from "node:crypto";
// Express: app.post("/ethpaid", express.raw({ type: "application/json" }), handler)
function verify(rawBody, header, secret) {
const p = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
const mac = crypto.createHmac("sha256", secret).update(`${p.t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(p.t)) <= 300;
const a = Buffer.from(mac), b = Buffer.from(p.v1 ?? "");
return fresh && a.length === b.length && crypto.timingSafeEqual(a, b);
}Другие методы
GET /v1/invoices/{id} | получить счёт |
GET /v1/invoices?status=paid&limit=20 | список счетов (before=<id> — следующая страница) |
POST /v1/invoices/{id}/cancel | отменить неоплаченный |
POST /v1/invoices/{id}/accept | принять недоплату или опоздание как оплату |
POST /v1/invoices/{id}/refunds | начать возврат: ссылка, где покупатель укажет адрес |
POST /v1/links | постоянная ссылка на оплату + HTML-кнопка |
POST /v1/invoices/{id}/simulate | имитировать оплату (только тестовый ключ) |
GET /v1/invoices.csv?days=90 | выгрузка CSV |
PUT /v1/webhook | задать адрес вебхуков, получить новый секрет |
POST /v1/webhook/test | отправить test.ping |
GET /v1/networks | доступные сети и токены |
Ошибки
401 | нет ключа или он неверный |
404 | счёт не найден (или другого режима) |
409 | конфликт: order_id уже занят, счёт нельзя отменить и т. п. |
422 | неверные данные: сумма ниже минимума сети, нет кошелька и т. п. |
503 | выдача новых счетов временно остановлена — повторите позже (Retry-After) |