Документация API

REST API и вебхуки для интернет-магазинов: создавайте счета и получайте подписанные уведомления об оплате.

Быстрый старт

Выставьте счёт через API, отправьте покупателя на checkout_url, дождитесь вебхука invoice.paid — и отгрузите заказ. Деньги к этому моменту уже на вашем кошельке.

  1. Получите ключ API у поддержки (Telegram или support@ethpaid.com) — на время пилота ключи выдаём вручную.
  2. Укажите адрес для вебхуков (PUT /v1/webhook) и сохраните секрет подписи.
  3. Создавайте счёт на каждый заказ и перенаправляйте покупателя на checkout_url.
  4. Обрабатывайте вебхуки, проверяя подпись.

Базовый адрес: 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)