Інтеграція з BitPay
BitPay — один з найстаріших крипто-платіжних процесорів, запущений у 2011 році. Сьогодні підтримує BTC, ETH, USDC, USDT на кількох мережах (Ethereum, Polygon, Arbitrum, Base) з автоматичною конвертацією в фіат. Для бізнесу який хоче приймати крипту без власної інфраструктури та з готовою юридичною документацією — розумний вибір.
API працює через рахунки-фактури (invoices): ваш backend створює інвойс на BitPay, отримує URL для редиректу користувача, BitPay приймає оплату та сповіщає ваш webhook.
API аутентифікація
BitPay використовує нестандартну схему аутентифікації на основі ECDSA підписів. Клієнт генерує ECDSA keypair, публічний ключ реєструється як «токен» на BitPay. Кожен запит підписується приватним ключем.
const BitPaySDK = require('bitpay-sdk');
const fs = require('fs');
// Генерація ключів та отримання токена (один раз)
async function setupBitPay() {
const client = new BitPaySDK.Client(
null, // конфіг файл
BitPaySDK.Env.Prod, // або Env.Test для testnet
fs.readFileSync('./private.key', 'utf8') // ECDSA приватний ключ
);
// Токен з BitPay Dashboard → API Tokens
await client.authorizeClient('your-pairing-code');
return client;
}
Для більшості інтеграцій простіше використовувати офіційний BitPay SDK (Node.js, PHP, Python, Ruby, Java) — він інкапсулює підпис запитів.
Створення інвойса
const BitPaySDK = require('bitpay-sdk');
async function createInvoice(orderId, amount, currency = 'USD') {
const invoice = new BitPaySDK.Models.Invoice(amount, currency);
invoice.orderId = orderId;
invoice.notificationUrl = `https://yourapp.com/webhooks/bitpay`;
invoice.redirectUrl = `https://yourapp.com/orders/${orderId}/success`;
invoice.closeUrl = `https://yourapp.com/orders/${orderId}/cancel`;
// Метаданні для reconciliation
invoice.buyer = new BitPaySDK.Models.Buyer();
invoice.buyer.email = customerEmail;
// Опціонально: приймати тільки конкретні монети
// invoice.paymentCurrencies = ['BTC', 'USDC'];
const created = await client.createInvoice(invoice);
return {
invoiceId: created.id,
paymentUrl: created.url, // редирект користувача
expirationTime: created.expirationTime
};
}
Інвойс дійсний 15 хвилин (за умовчанням) — користувач повинен оплатити в цей період. Сума в USD фіксується за курсом BitPay на момент створення інвойса.
Обробка webhook
BitPay відправляє IPN (Instant Payment Notification) на notificationUrl. Критично: перевіряти статус інвойса через API, а не просто довіряти webhook даним.
const express = require('express');
const router = express.Router();
router.post('/webhooks/bitpay', async (req, res) => {
const { id: invoiceId, status } = req.body.data || {};
if (!invoiceId) {
return res.status(400).json({ error: 'Missing invoice ID' });
}
// ВАЖЛИВО: верифікуємо через API, не довіряємо телу webhook
const invoice = await client.getInvoice(invoiceId);
switch (invoice.status) {
case 'paid':
// Оплачено, але чекаємо підтверджень
await updateOrderStatus(invoice.orderId, 'paid_unconfirmed');
break;
case 'confirmed':
// Достатньо підтверджень (зазвичай 1 для більшості монет)
await updateOrderStatus(invoice.orderId, 'confirmed');
break;
case 'complete':
// Всі підтвердження отримані, засоби зараховані
await fulfillOrder(invoice.orderId);
break;
case 'expired':
await updateOrderStatus(invoice.orderId, 'expired');
break;
case 'invalid':
// Underpayment або інша помилка
await handleInvalidPayment(invoice.orderId, invoice);
break;
}
res.json({ success: true });
});
Цепочка статусів: new → paid → confirmed → complete. Для фулфілмента використовуйте confirmed або complete залежно від tolerance до ризику. complete — найбезпечніший, але більша задержка.
Повернення грошей (Refunds)
BitPay вимагає адресу для повернення — запросіть її у користувача при оплаті або при ініціації повернення.
async function createRefund(invoiceId, amount, currency) {
const refund = new BitPaySDK.Models.Refund();
refund.invoiceId = invoiceId;
refund.amount = amount;
refund.currency = currency; // валюта повернення
const created = await client.createRefund(refund);
// BitPay відправить email користувачу з запитом адреси
return created;
}
Типові проблеми при інтеграції
Webhook не приходить. BitPay вимагає HTTPS з валідним сертифікатом на notificationUrl. Localhost недоступний — для розробки використовуйте ngrok або BitPay Testnet з публічним URL.
Дублюючі webhook-и. BitPay може відправити кілька сповіщень про один статус (retry при таймауті). Використовуйте invoiceId як idempotency key: INSERT ... ON CONFLICT (invoice_id, status) DO NOTHING.
Partial payment. Якщо користувач оплатив менше — статус invalid. BitPay автоматично повертає underpayment при наявності email покупця.
Timezone в expirationTime. Поле повертається як Unix timestamp у мілісекундах. new Date(invoice.expirationTime) — не забудьте про мс, не секунди.
Тестування
BitPay надає Testnet окружение (BitPaySDK.Env.Test) з тестовим Bitcoin. Створюєте інвойс, платите з testnet гамана — весь flow без реальних грошей. Паринг-код для тестового окружения створюється окремо в dashboard.
Процес інтеграції
Реєстрація → створення API токена → інтеграція SDK в backend → webhook endpoint → тестування на Testnet → production паринг.
Терміни 2-3 дні: 1 день на SDK інтеграцію та створення інвойсів, 1 день на webhook + статус машину, 1 день на тестування та edge cases (істекший інвойс, partial payment, webhook retry).







