Розробка платіжногоWidget для криптовалют
Типова вимога: "ми хочемо приймати крипту на сайті, як PayPal але для USDT". На практиці це означає вирішити кілька нетривіальних завдань одночасно: генерувати унікальні адреси для кожного платежу, детектувати вхідні транзакції, обробляти різні мережі та токени, коректно працювати з підтвердженнями та reorg'ами. Готові рішення, такі як Coinbase Commerce або NOWPayments, беруть 0,5–1% комісії та мають обмежену кастомізацію. Власний widget виправданий при обсязі від кількох тисяч доларів на місяць або коли потрібна глибока інтеграція з бізнес-логікою.
Архітектура: що всередині widget'а
Widget — це тільки UI-частина. Реальна робота відбувається на бекенді:
Frontend Widget
│ створити замовлення / показати адресу та QR
▼
Backend API
│ генерація адреси → запис у БД → polling/webhook
▼
Blockchain Monitoring Service
│ стежить за транзакціями на адресах
▼
Payment Processor
│ підтвердження → callback до додатка
Генерація адрес: HD Wallet
Для кожного платежу потрібна унікальна адреса — інакше неможливо зіставити вхідний платіж з конкретним замовленням. Стандартний підхід — BIP-44 HD Wallet:
import { ethers } from 'ethers';
const masterWallet = ethers.HDNodeWallet.fromMnemonic(
ethers.Mnemonic.fromPhrase(process.env.PAYMENT_MNEMONIC!)
);
function derivePaymentAddress(orderId: number): string {
// path: m/44'/60'/0'/0/{orderId}
const child = masterWallet.derivePath(`m/44'/60'/0'/0/${orderId}`);
return child.address;
}
Мнемоніка зберігається в HSM або Vault, приватні ключі ніколи не матеріалізуються на сервері у простому тексті. Для мультивалютності: різні coin types за BIP-44 (60 для Ethereum/EVM, 0 для Bitcoin, 195 для Tron).
Для EVM-мереж з однаковими адресами (Ethereum, BNB Chain, Polygon, Arbitrum), одна адреса працює на всіх мережах — але ви повинні моніторити кожну мережу окремо.
Моніторинг транзакцій
Два підходи: polling RPC та webhook-підписки.
Polling — простіше, але створює навантаження:
async function pollAddress(address: string, network: string) {
const provider = getProvider(network);
// Для ERC-20 токенів слухаємо Transfer events
const usdtContract = new ethers.Contract(USDT_ADDRESS, ERC20_ABI, provider);
const filter = usdtContract.filters.Transfer(null, address);
const latestBlock = await provider.getBlockNumber();
// Перевіряємо останні N блоків
const events = await usdtContract.queryFilter(filter, latestBlock - 10, latestBlock);
for (const event of events) {
await processIncomingPayment({
txHash: event.transactionHash,
amount: event.args.value,
token: 'USDT',
network,
});
}
}
Webhooks — через Alchemy, QuickNode або Moralis. Підпишіться на подій адреси, отримуйте push-сповіщення при кожній транзакції. Надійніше за polling, але залежить від постачальника:
// Alchemy Notify webhook
const webhook = await alchemy.notify.createWebhook(
'https://your-api.com/webhook/payment',
WebhookType.ADDRESS_ACTIVITY,
{ addresses: [paymentAddress] }
);
Підтвердження та захист від подвійної трати
Різні активи вимагають різного числа підтвердження:
| Актив/мережа | Рекомендовані підтвердження | Час |
|---|---|---|
| ETH / ERC-20 (Ethereum) | 12–20 блоків | ~3–4 хв |
| BNB Chain | 15–20 блоків | ~1 хв |
| Polygon | 100–150 блоків | ~4–6 хв |
| TRON TRC-20 | 20 блоків | ~1 хв |
| Bitcoin | 3–6 блоків | ~30–60 хв |
Polygon вимагає більше підтвердження через вищу ймовірність reorg. Не вважайте платіж остаточним до досягнення потрібного порогу.
Для stablecoins: додатково перевірте, що контракт токена офіційний. Користувач може надіслати підроблений токен з ім'ям "USDT". Whitelist адрес контрактів обов'язковий.
Компоненти UI/UX widget'а
Мінімальний набір для конверсії:
┌─────────────────────────────────────┐
│ Оплатити: 47.50 USDT │
│ │
│ Мережа: [Ethereum ▼] [BNB Chain ▼]│
│ │
│ [QR-код] 0x7f3a...b2c4 │
│ [Копіювати] │
│ │
│ ⏱ Очікування оплати: 14:32 │
│ ● Очікуємо транзакцію... │
└─────────────────────────────────────┘
Критично: таймер сеансу (зазвичай 15–30 хвилин), після якого адреса звільняється та курс перераховується. Статус оновлюється через WebSocket або SSE — polling кожні 5 секунд дратує та створює навантаження.
Конвертація фіатної суми в крипто: використовуйте Chainlink Price Feeds або CoinGecko API з кешуванням. Додайте буфер 1–2% до курсу з урахуванням волатильності під час очікування.
Обробка недоплати та переплати
Реальні користувачі часто платять неточну суму:
- Недоплата (надіслали менше): або заблокуйте замовлення до доплати, або прийміть з позначкою "часткова оплата" — залежить від бізнес-логіки
- Переплата (надіслали більше): зараховуйте як кредит користувача, або повертайте різницю автоматично
- Комісія мережі: для нативних монет (ETH, BNB) користувач повинен мати їх у гаманці окремо — UX-момент, який важливо пояснити
Безпека
- Мнемоніка у Vault (HashiCorp Vault або AWS Secrets Manager), ніколи в
.env - Rate limiting на endpoint генерації адрес
- Ідемпотентність: той же
orderIdзавжди повертає ту ж адресу - Audit log усіх транзакцій з підтвердженнями
- Періодична звірка: сума підтверджених платежів у БД повинна збігатися з on-chain балансами







