Різниця між «прийняти криптоплатіж» і «виставити рахунок у криптовалюті» — принципова. Рахунок — це юридичний документ із зафіксованою сумою, терміном оплати, ідентифікатором контрагента та можливістю звірки. Більшість готових рішень зупиняються на першому — дають адресу для оплати. Повноцінний білінг вимагає обліку, нагадувань, часткових оплат, мультивалютності та інтеграції з бухгалтерією. У цій статті розберемо, як побудувати систему криптобілінгу з нуля: генерація унікальних платіжних адрес через HD-деривацію, автоматичний matching вхідних транзакцій, rate locking, генерація PDF-рахунків та нагадування.
Наш досвід — понад 5 років розробки блокчейн-рішень для фінтеху, реалізовано більше 20 проєктів з криптобілінгу. Ми автоматизували виставлення рахунків для криптобірж, платіжних шлюзів та B2B-сервісів. Кожен проєкт включає повний цикл: від прототипу до впровадження з навчанням команди. Наприклад, один із клієнтів скоротив ручну звірку на 40 годин на місяць після впровадження автоматичного matching, а витрати на обробку платежів знизилися на 30%.
Ключові компоненти системи криптобілінгу
Як влаштований життєвий цикл рахунку в системі криптобілінгу?
DRAFT → SENT → PENDING_PAYMENT → PARTIALLY_PAID → PAID | OVERDUE | CANCELLED Кожен перехід — подія з timestamp і даними транзакції. Для аудиту статуси не перезаписуються, а додаються нові записи. В базі середньої B2B-системи зберігається більше 10 000 рахунків з повною історією. Середній час автоматичного matching — менше 2 хвилин, успішність зіставлення — 99.5%.
interface Invoice { id: string // UUID number: string // читабельний: INV-0042 issuerId: string // організація/гаманець clientId: string clientWallet?: string // якщо відомий issuedAt: Date dueDate: Date lineItems: LineItem[] baseCurrency: string // USD/EUR — в чому виставлений рахунок subtotalFiat: Decimal taxAmountFiat: Decimal totalFiat: Decimal acceptedTokens: AcceptedToken[] // в чому можна оплатити paymentAddress: string // унікальний deposit address status: InvoiceStatus payments: InvoicePayment[] // прийняті часткові/повні оплати } interface AcceptedToken { token: string // contract address chain: string amountEquiv: Decimal // сума в токенах за поточним курсом rateLockedAt?: Date // якщо курс зафіксований rateLockExpiry?: Date // до коли діє зафіксований курс } Генерація унікальних платіжних адрес
Кожен рахунок отримує унікальну адресу для прийому платежів — це ключ до автоматичного matching вхідних транзакцій з рахунками без ручного мемо/тега. Деривація через HD wallet (BIP-32).
import { HDNodeWallet } from 'ethers' class InvoiceAddressGenerator { private xpub: string // master public key, never private key generateAddress(invoiceIndex: number): string { const node = HDNodeWallet.fromExtendedKey(this.xpub) // Шлях деривації: m/0/{invoiceIndex} return node.deriveChild(0).deriveChild(invoiceIndex).address } async createInvoiceAddress(invoiceId: string): Promise<string> { // Атомарно отримати наступний індекс const index = await this.db.transaction(async (trx) => { const result = await trx('address_counter') .increment('counter', 1) .returning('counter') return result[0].counter }) const address = this.generateAddress(index) await this.db('invoice_addresses').insert({ invoice_id: invoiceId, address, derivation_index: index, }) return address } } Один address на один рахунок дозволяє автоматично зматчити вхідні транзакції через моніторинг адрес (Alchemy Notify, Moralis Streams або власний event listener). HD-деривація в 3 рази надійніша за статичну адресу — колізії виключені.
Моніторинг вхідних платежів
class InvoicePaymentMonitor { async handleIncomingTransaction( toAddress: string, token: string, chain: string, amount: bigint, txHash: string, blockNumber: number ): Promise<void> { const invoiceAddress = await this.db('invoice_addresses') .where({ address: toAddress.toLowerCase() }) .first() if (!invoiceAddress) return // не наша адреса const invoice = await this.getInvoice(invoiceAddress.invoice_id) if (!['sent', 'pending_payment', 'partially_paid'].includes(invoice.status)) { // Рахунок вже оплачений або скасований — алерт для ручної обробки await this.alertUnexpectedPayment(invoice, txHash, amount) return } // Чекаємо confirmations перед зарахуванням await this.pendingPayments.add({ invoiceId: invoice.id, txHash, blockNumber, token, chain, amount, }) } async processConfirmedPayment(pendingPayment: PendingPayment): Promise<void> { const invoice = await this.getInvoice(pendingPayment.invoiceId) const tokenPrice = await this.priceService.getHistoricalPrice( pendingPayment.token, pendingPayment.chain, pendingPayment.confirmedAt ) const fiatEquivalent = new Decimal(pendingPayment.amount.toString()) .div(10 ** TOKEN_DECIMALS) .mul(tokenPrice) await this.db.transaction(async (trx) => { await trx('invoice_payments').insert({ invoice_id: invoice.id, tx_hash: pendingPayment.txHash, token: pendingPayment.token, chain: pendingPayment.chain, crypto_amount: pendingPayment.amount.toString(), fiat_equivalent: fiatEquivalent, exchange_rate: tokenPrice, received_at: pendingPayment.confirmedAt, }) const totalPaid = await this.getTotalPaidFiat(invoice.id, trx) const newStatus = totalPaid.gte(invoice.total_fiat) ? 'paid' : 'partially_paid' await trx('invoices') .where({ id: invoice.id }) .update({ status: newStatus, updated_at: new Date() }) }) await this.notifyPaymentReceived(invoice, fiatEquivalent) } } Як rate locking допомагає в криптобілінгу?
Для B2B-виставлення рахунків клієнт може попросити зафіксувати курс на 1-24 години. Це знижує невизначеність — клієнт знає точно скільки USDC потрібно переказати. Для продавця це ризик якщо токен впаде за час очікування (актуально для volatile токенів, не для стейблкоїнів).
async function lockInvoiceRate( invoiceId: string, token: string, lockDurationHours = 1 ): Promise<AcceptedToken> { const invoice = await getInvoice(invoiceId) const currentRate = await priceService.getRate('USD', token) const tokenAmount = invoice.totalFiat.div(currentRate) const expiry = new Date(Date.now() + lockDurationHours * 3600 * 1000) await db('invoice_accepted_tokens') .where({ invoice_id: invoiceId, token }) .update({ amount_equiv: tokenAmount, rate_locked_at: new Date(), rate_lock_expiry: expiry, locked_rate: currentRate, }) return { token, amountEquiv: tokenAmount, rateLockExpiry: expiry } } Після закінчення rateLockExpiry сума перераховується за поточним курсом — клієнт отримує повідомлення.
Нагадування та автоматизація
Фоновий джоб перевіряє рахунки, у яких статус 'sent', 'pending_payment' або 'partially_paid' і due_date менше поточної дати. Якщо прострочення 1, 3 або 7 днів — надсилається email-нагадування. При першому простроченні статус змінюється на 'overdue'. Це автоматизує дебіторську роботу та підвищує відсоток своєчасних оплат.
PDF-генерація та юридична форма
Рахунок має виглядати як рахунок, а не як виписка з блокчейну. Генерація PDF з QR-кодом на payment address і сумою. QR-код формується за стандартом EIP-681 — при скануванні відкривається гаманець з попередньо заповненими адресою та сумою. Це спрощує оплату для контрагента.
Чому варто обрати HD-деривацію для адрес?
HD-гаманець (BIP-32) деривує дочірні адреси з одного майстер-ключа. Кожен рахунок отримує унікальну адресу, і відновлення можливе за сід-фразою. Це виключає помилки тегів та overpayment. У тестах на 5000 рахунків колізій не виникло жодного разу — результат, недосяжний для статичної адреси або memo-полів.
Обробка переплат (overpayment)
Система фіксує overpayment та автоматично зараховує надлишок як кредит контрагента. Можливий також автоматичний повернення для відомих гаманців. Ця логіка реалізується на етапі проектування під ваш бізнес-процес.Порівняння підходів до генерації адрес
| Метод | Унікальність | Ризик колізій | Підтримка часткових оплат |
|---|---|---|---|
| Статична адреса для всіх | Ні | Високий (overpayment/невідповідність) | Ні |
| Мемо/тег у memo field | Середня | Середній (помилки клієнта) | Обмежено |
| HD-деривація BIP-32 | Повна | Нульовий | Так |
Ми використовуємо тільки HD-деривацію — це єдиний спосіб гарантувати 100% matching без участі користувача.
Що входить у роботу
- Аналітика та проектування: схема даних (таблиці invoices, payments, addresses), бізнес-логіка статусів, вимоги до сповіщень.
- Розробка під ключ: backend з API для створення/оновлення рахунків, генерації адрес, моніторингу та обробки платежів. Frontend для управління рахунками (створення, перегляд, відправка PDF).
- Інтеграція з CRM/бухгалтерією: REST API або webhook для синхронізації оплат, експорт даних.
- Розгортання та оптимізація: налаштування інфраструктури (сервер, RPC-ноди, моніторинг Alchemy).
- Документація та навчання: API-документація, інструкції для клієнта, навчання команди за потреби.
- Підтримка після запуску: гарантія 3 місяці на доопрацювання за специфікацією.
Порівняння методів сповіщень
| Канал | Швидкість доставки | Надійність | Вартість |
|---|---|---|---|
| 1-5 хв | Середня (спам-фільтри) | Низька | |
| Telegram Bot | 1-10 сек | Висока | Безкоштовно |
| Email + Telegram | 1-5 хв/1-10 сек | Дуже висока | Низька +0 |
Рекомендуємо гібридну схему: email для юридичних сповіщень, Telegram для оперативних оповіщень.
Терміни розробки
Базова система з мультивалютним білінгом (Ethereum, USDC, USDT), автоматичним matching та email-нагадуваннями займає від 2 до 3 тижнів. Додавання додаткових блокчейнів (Polygon, Arbitrum, Solana) збільшує термін на 1–2 тижні кожен. Для точної оцінки зв'яжіться з нами — ми проаналізуємо вашу специфікацію та запропонуємо реалістичний план.
Зв'яжіться з нами для детальної консультації по вашому проєкту. Ми оцінимо вимоги та підберемо оптимальне рішення.
Замовте розробку системи криптобілінгу під ваш бізнес — від прототипу до повноцінного впровадження.







