Стартовый шаблон Next.js
Готовое приложение Next.js с авторизацией, страницей тарифов и предварительно настроенными платежами ChainPay. Клонируйте и запустите за 5 минут.
Быстрый старт
Принимайте криптоплатежи в три шага — без знаний блокчейна.
Создайте платёжный заказ
const response = await fetch("https://chainpay.pro/api/v1/orders", {
method: "POST",
headers: {
"Authorization": "Bearer sk_live_YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
amount: 29.99,
currency: "USDT",
chain: "trc20",
externalId: "order_abc123", // your internal order ID
metadata: { userId: "user_456" } // arbitrary JSON, returned in webhook
})
});
const order = await response.json();
// order.checkoutUrl → redirect user here
// order.payAddress → or show the address directlyПеренаправьте пользователя на страницу оплаты
// Server-side redirect
res.redirect(order.checkoutUrl);
// Or client-side
window.location.href = order.checkoutUrl;Получите вебхук, когда платёж подтверждён
{
"event": "payment.completed",
"orderId": "ord_a1b2c3d4e5f6",
"externalId": "order_abc123",
"amount": "29.99",
"netAmount": "29.75", // after 0.8% fee
"currency": "USDT",
"chain": "trc20",
"txHash": "e3b0c44298fc1c149afb...",
"completedAt": "2026-03-22T08:15:00Z",
"metadata": { "userId": "user_456" }
}Проверьте заголовок X-ChainPay-Signature перед обработкой. Код проверки см. в разделе .
Аутентификация
Все запросы к API должны содержать ваш API-ключ в виде Bearer-токена.
Authorization: Bearer sk_live_YOUR_API_KEYcurl -X POST https://chainpay.pro/api/v1/orders \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 10, "currency": "USDT", "chain": "trc20"}'| Префикс ключа | Окружение | Блокчейн |
|---|---|---|
| sk_live_… | Продакшен | Реальные транзакции |
| sk_test_… | Песочница | Реальные средства не перемещаются |
Создание заказа
https://chainpay.pro/api/v1/ordersПараметры запроса
| Параметр | Тип | Обязательно | Описание |
|---|---|---|---|
| amount | number | обязательно | Сумма платежа в USD (например, 29.99). Эквивалентная сумма в крипто рассчитывается по текущему рыночному курсу. |
| currency | string | обязательно | Криптовалюта для получения. Одно из: "USDT", "ETH", "BTC", "SOL". |
| chain | string | обязательно | Блокчейн-сеть. Одно из: "trc20", "erc20", "btc", "sol". Должна быть совместима с выбранной валютой. |
| externalId | string | необязательно | Ваш внутренний идентификатор заказа. Возвращается без изменений в вебхуках. Должен быть уникальным для каждого мерчанта. |
| metadata | object | необязательно | Произвольный JSON-объект (макс. 2 КБ). Возвращается во всех вебхуках для этого заказа — удобно для передачи ID пользователей, ссылок на корзину и т. п. |
Поддерживаемые сочетания валюта + сеть
| currency | chain | Сеть | Среднее время подтверждения |
|---|---|---|---|
| USDT | trc20 | TRON (TRC-20) | ~1 мин |
| USDT | erc20 | Ethereum (ERC-20) | ~2 мин |
| ETH | erc20 | Ethereum | ~2 мин |
| BTC | btc | Bitcoin | ~30 мин |
| SOL | sol | Solana | <5 сек |
Ответ
{
"orderId": "ord_a1b2c3d4e5f6g7h8",
"payAddress": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"cryptoAmount": "29.99",
"currency": "USDT",
"chain": "trc20",
"checkoutUrl": "https://chainpay.pro/pay/ord_a1b2c3d4",
"expiresAt": "2026-03-22T09:00:00Z",
"createdAt": "2026-03-22T08:30:00Z"
}Процесс оплаты
Как работает размещённая платёжная страница.
- 1Перенаправление — Ваш сервер перенаправляет пользователя на checkoutUrl, который возвращается при создании заказа.
- 2Платёжная страница — Размещённая страница показывает QR-код и адрес кошелька, заранее заполненный точной суммой в крипто. Пользователь сканирует QR-код или копирует адрес в приложение своего кошелька.
- 3Подтверждение — ChainPay отслеживает блокчейн. Как только достигнуто нужное число подтверждений, статус заказа меняется на confirming, затем на completed.
- 4Возврат на сайт — Платёжная страница показывает экран успеха. Если в панели настроен successUrl, пользователь автоматически перенаправляется туда.
- 5Вебхук — Ваш сервер получает вебхук payment.completed. Выполняйте заказ только после проверки подписи вебхука.
expired и отправляется вебхук payment.expired. Создайте новый заказ для повторной попытки.Вебхуки
ChainPay отправляет подписанные HTTP POST-запросы на ваш URL вебхука при изменении статуса заказа.
Настройка
Перейдите в Панель → Приложения → Новое приложение
Укажите URL вебхука (например, https://yourapp.com/api/webhooks/chainpay)
Скопируйте сгенерированный секрет вебхука — используйте его для проверки подписей
События вебхуков
Поддерживаемые на данный момент события:
| Событие | Когда срабатывает |
|---|---|
| payment.completed | Заказ оплачен и подтверждён в сети |
| payment.expired | Срок заказа истёк без оплаты |
Формат тела запроса
{
"event": "payment.completed",
"orderId": "ord_xxxxxxxx",
"amount": "9.99",
"currency": "USDT",
"chain": "trc20",
"txHash": "abc123...",
"netAmount": "9.92",
"completedAt": "2026-01-01T00:00:00.000Z",
"metadata": {
"userId": "your-user-id",
"plan": "monthly"
}
}Проверка подписи
Каждый запрос вебхука содержит заголовок X-ChainPay-Signature. Проверяйте его с помощью HMAC-SHA256, используя секрет вебхука вашего приложения:
import crypto from 'crypto'
function verifyWebhook(rawBody: string, signature: string, secret: string): boolean {
const computed = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(computed),
Buffer.from(signature)
)
}
// In your webhook handler:
export async function POST(req: Request) {
const rawBody = await req.text()
const signature = req.headers.get('x-chainpay-signature') ?? ''
if (!verifyWebhook(rawBody, signature, process.env.CHAINPAY_WEBHOOK_SECRET!)) {
return new Response('Unauthorized', { status: 401 })
}
const event = JSON.parse(rawBody)
if (event.event === 'payment.completed') {
const { userId, plan } = event.metadata
// activate user's plan
}
return new Response('OK')
}import hmac, hashlib
def verify_webhook(raw_body: bytes, signature: str, secret: str) -> bool:
computed = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(computed, signature)Протестируйте свой вебхук
Перейдите в Панель → Приложения → выберите своё приложение
Нажмите «Отправить тестовое событие»
Убедитесь, что ваш сервер получил запрос (ожидается HTTP 200)
Политика повторов
Неудачные доставки повторяются с экспоненциальной задержкой: 1 мин → 5 мин → 30 мин → 2 часа → 24 часа (всего 5 попыток).
После 5 неудачных попыток вебхук помечается как failed. Неудачные доставки смотрите в Панель → Оповещения.
Лучшие практики
- Всегда проверяйте подпись перед обработкой
- Быстро возвращайте HTTP 200 — тяжёлую обработку выполняйте асинхронно
- Используйте
orderIdдля дедупликации событий (вебхуки могут доставляться более одного раза) - Сохраняйте сырое тело запроса до парсинга, чтобы обеспечить точную проверку подписи
Запрос заказа
https://chainpay.pro/api/v1/orders/:id{
"id": "ord_a1b2c3d4e5f6g7h8",
"amount": "29.99",
"cryptoAmount":"29.99",
"netAmount": "29.75",
"currency": "USDT",
"chain": "trc20",
"status": "completed",
"payAddress": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"txHash": "e3b0c44298fc1c149afbf4c8996fb92...",
"externalId": "order_abc123",
"metadata": { "userId": "user_456" },
"createdAt": "2026-03-22T08:30:00Z",
"expiresAt": "2026-03-22T09:00:00Z",
"completedAt": "2026-03-22T08:45:00Z"
}Значения статуса заказа
| Статус | Описание |
|---|---|
| pending | Заказ создан, ожидается отправка платежа пользователем. |
| confirming | Транзакция обнаружена в сети, ожидается достаточное число подтверждений блоков. |
| completed | Платёж полностью подтверждён. Можно безопасно выполнять заказ. |
| expired | Прошло 30 минут без обнаруженного платежа. |
| failed | Произошла непредвиденная ошибка. Обратитесь в поддержку, указав orderId. |
API курсов
Получайте текущие курсы обмена к USD. API-ключ не требуется.
https://chainpay.pro/api/v1/rates{
"USDT": 1.0,
"ETH": 3450.00,
"BTC": 87500.00,
"SOL": 145.00,
"updatedAt": "2026-03-22T08:00:00Z"
}Курсы обновляются каждые 60 секунд на основе агрегированных рыночных данных. Параметр amount, передаваемый в эндпоинт создания заказа, всегда интерпретируется в USD; API конвертирует его в крипто по курсу, актуальному на момент создания заказа.
Режим песочницы
Протестируйте всю интеграцию без перемещения реальных средств.
Зарегистрируйтесь с ?mode=test, чтобы получить API-ключ sk_test_. Все стандартные эндпоинты работают в режиме песочницы одинаково — опрос блокчейна пропускается, а подтверждение вы запускаете вручную.
Шаг 1 — Получите API-ключ песочницы
curl -X POST "https://chainpay.pro/api/auth/register?mode=test" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "yourpassword"}'
# Response includes:
# { "merchant": { "apiKey": "sk_test_xxxxxxxxxxxxxxxx" } }Шаг 2 — Создайте заказ с тестовым ключом
curl -X POST https://chainpay.pro/api/v1/orders \
-H "Authorization: Bearer sk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 10, "currency": "USDT", "chain": "trc20", "externalId": "test_order_1"}'Шаг 3 — Симулируйте платёж
curl -X POST https://chainpay.pro/api/v1/sandbox/simulate-payment \
-H "Authorization: Bearer sk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"orderId": "ord_abc123"}'
# Response:
{
"success": true,
"orderId": "ord_abc123",
"txHash": "sandbox_ord_abc123_1711000000000",
"netAmount": "9.92000000"
}
# The order status is now "completed" and your webhook is fired.sandbox_. Эндпоинт симуляции платежа доступен только с ключами sk_test_. Попытка использовать его с боевым ключом возвращает 403.Коды ошибок
Все ответы об ошибках используют единую JSON-структуру:
{
"error": "INVALID_CHAIN",
"message": "chain 'foo' is not supported for currency USDT",
"status": 400
}| HTTP-статус | Распространённые коды ошибок | Причина и решение |
|---|---|---|
| 400 | INVALID_AMOUNT, INVALID_CURRENCY, INVALID_CHAIN, MISSING_FIELD | Тело запроса повреждено или отсутствует обязательное поле. Проверьте таблицу параметров запроса выше. |
| 401 | UNAUTHORIZED, INVALID_API_KEY | Отсутствует или недействителен заголовок Authorization. Убедитесь, что вы отправляете Bearer <key>. |
| 403 | FORBIDDEN, SANDBOX_ONLY | API-ключ не имеет прав на это действие (например, использование simulate-payment с боевым ключом). |
| 404 | ORDER_NOT_FOUND | Заказа с указанным ID не существует или он не принадлежит вашему аккаунту мерчанта. |
| 429 | RATE_LIMITED | Слишком много запросов. Лимит по умолчанию — 60 запросов в минуту на API-ключ. Снизьте частоту и повторите после значения заголовка Retry-After. |
| 500 | INTERNAL_ERROR | Непредвиденная ошибка на стороне сервера. Повторите с экспоненциальной задержкой. Если ошибка повторяется, обратитесь в поддержку, указав ID запроса. |
Выплаты
Как и когда ChainPay отправляет собранные средства на ваш кошелёк.
Выплата отправляет все завершённые, ещё не выплаченные суммы на адреса кошельков, которые вы настраиваете в панели в разделе Настройки → Кошельки для выплат. Вы можете настроить отдельные адреса выплат для каждой валюты.
netAmount в вебхуках уже отражает сумму после удержания комиссии. Выплата переводит сумму всех значений netAmount за день.Готовы к интеграции?
Создайте аккаунт и получите API-ключ за считанные секунды.