Прием Bitcoin: генерация HD-адресов, мониторинг и edge-кейсы

Первая и самая критическая ошибка при интеграции Bitcoin-платежей – использование единого адреса для всех клиентов. Два пользователя могут прислать одинаковую сумму в одной транзакции, получить несколько UTXO, частично покрывающих сумму, или столкнуться с батчингом со стороны биржи. **Правильный под

Направления блокчейн-разработки

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1441
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    998
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1267
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    713
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1003

Первая и самая критическая ошибка при интеграции Bitcoin-платежей – использование единого адреса для всех клиентов. Два пользователя могут прислать одинаковую сумму в одной транзакции, получить несколько UTXO, частично покрывающих сумму, или столкнуться с батчингом со стороны биржи. Правильный подход – генерация уникального адреса на каждый платёж. Из нашей практики: при интеграции для одного маркетплейса мы решили проблему дублирования адресов, что сократило время обработки платежей на 30% и исключило путаницу с зачислениями.

Биткойн: электронная пиринговая система — Сатоши Накамото

Как работает деривация адресов?

Стандарт BIP-32/BIP-44 позволяет из одного master seed генерировать бесконечное дерево адресов детерминированно. Для приёма платежей используется xpub (extended public key) – публичная часть, которую сервер хранит открыто и из которой генерирует адреса. Приватный ключ хранится отдельно (cold storage, hardware wallet) и нужен только для вывода средств.

Master Seed → xpub (m/44'/0'/0') ↓ index=0: 1A1zP1... (payment #1) index=1: 1B2zP2... (payment #2) index=N: ... (payment #N) 

Путь деривации по BIP-44 для Bitcoin mainnet: m/44'/0'/account'/change/index. Для приёма – change=0, index инкрементируем. Для Native SegWit используется стандарт BIP-84 с путём m/84'/0'/0'.

Как обеспечить уникальность адреса для каждого платежа?

Используйте расширенный публичный ключ (xpub) и индекс, соответствующий ID заказа в вашей базе. Это гарантирует, что клиент не сможет повторно использовать старый адрес, а вы избежите пересечения платежей. Никогда не генерируйте адреса случайным образом – только детерминированно.

Типы адресов: сравнение

Тип Формат SegWit Экономия комиссии Рекомендация
P2PKH (Legacy) 1... Нет 0% Не использовать (высокие комиссии)
P2SH-P2WPKH (Wrapped SegWit) 3... Да ~20% Для совместимости со старыми кошельками
P2WPKH (Native SegWit) bc1q... Да ~37% Основной выбор
P2TR (Taproot) bc1p... Да ~38% + Schnorr Для новых проектов с мультиподписью

Native SegWit (bc1q) даёт экономию до 40% на комиссиях по сравнению с Legacy – это делает его почти в 2 раза выгоднее для клиента. Taproot с Schnorr-подписями позволяет объединять транзакции, что дополнительно уменьшает размер блока на 20% относительно Native SegWit.

Реализация: Node.js + bitcoinjs-lib

import * as bitcoin from 'bitcoinjs-lib' import { BIP32Factory } from 'bip32' import * as ecc from 'tiny-secp256k1' bitcoin.initEccLib(ecc) const bip32 = BIP32Factory(ecc) const NETWORK = bitcoin.networks.bitcoin // или networks.testnet // Один раз: генерация xpub из seed (выполняется в cold storage) // const seed = bip39.mnemonicToSeedSync(mnemonic) // const root = bip32.fromSeed(seed, NETWORK) // const account = root.derivePath("m/84'/0'/0'") // BIP-84 для Native SegWit // const xpub = account.neutered().toBase58() // console.log(xpub) // хранить в .env как BITCOIN_XPUB // На сервере: генерация адреса по индексу function getPaymentAddress(xpub: string, index: number): string { const node = bip32.fromBase58(xpub, NETWORK) const child = node.derive(0).derive(index) // external chain, index N const { address } = bitcoin.payments.p2wpkh({ pubkey: Buffer.from(child.publicKey), network: NETWORK, }) if (!address) throw new Error('Failed to derive address') return address } 

База данных: схема платежей

CREATE TABLE bitcoin_payments ( id BIGSERIAL PRIMARY KEY, order_id UUID NOT NULL REFERENCES orders(id), address VARCHAR(62) NOT NULL UNIQUE, hd_index INTEGER NOT NULL UNIQUE, amount_sat BIGINT NOT NULL, -- сумма в сатоши status VARCHAR(20) DEFAULT 'pending', -- pending/underpaid/confirmed/expired created_at TIMESTAMPTZ DEFAULT NOW(), expires_at TIMESTAMPTZ NOT NULL, confirmed_at TIMESTAMPTZ, tx_hash VARCHAR(64) ); CREATE INDEX ON bitcoin_payments(address); CREATE INDEX ON bitcoin_payments(status) WHERE status = 'pending'; 

Мониторинг транзакций

Для production используйте собственную Bitcoin-ноду с electrs. Публичные API (Blockstream) подходят только для прототипов. Пример WebSocket-мониторинга:

import ElectrumClient from 'electrum-client' const client = new ElectrumClient(50002, 'your-electrs-host', 'tls') await client.connect('payment-monitor', '1.4') async function watchAddress(address: string, onPayment: (tx: any) => void) { const scriptHash = addressToScriptHash(address) // sha256 reversedLE await client.subscribe.on('blockchain.scripthash.subscribe', async (updates) => { const [scripthash, status] = updates if (scripthash === scriptHash && status !== null) { const history = await client.blockchainScripthash_getHistory(scriptHash) onPayment(history) } }) await client.blockchainScripthash_subscribe(scriptHash) } 

Количество подтверждений

Сумма Рекомендуемые подтверждения
< $100 1
$100 – $1 000 3
$1 000 – $10 000 6
> $10 000 6+ или по бизнес-логике

0-conf допустим только для физических точек с небольшими чеком и при RBF=false. В e-commerce – ждите хотя бы одно подтверждение.

Как правильно обрабатывать edge-кейсы?

Overpayment – зачислите полную стоимость, а разницу сохраните на балансе пользователя. Возврат возможен только если клиент предоставит адрес для возврата.

Underpayment – заморозьте платёж и попросите доплатить на тот же адрес в течение времени жизни заказа. Не засчитывайте частичную сумму как полную.

Expiry – адрес «просрочен», но транзакция всё же пришла. Храните такие адреса активными ещё 24 часа для зачисления, но не показывайте для новых платежей.

Не доверяйте неподтверждённым транзакциям с флагом BIP125-opt-in-RBF=true. Дождитесь хотя бы одного блока.

Встроенная логика должна различать статусы: pending, underpaid, confirmed, expired. Для underpaid автоматически продлите время ожидания на 15 минут. Для expired – спишите заказ, но сохраните возможность зачисления при поздней транзакции (вручную).

Вывод средств

Для вывода UTXO используйте PSBT с правильным coin selection. Рекомендуем делать sweep раз в сутки скриптом, а не триггерить автоматически на каждый платёж – это сокращает количество транзакций и комиссии.

Что входит в работу

  • Проектирование архитектуры HD-кошелька
  • Реализация серверной части на Node.js с bitcoinjs-lib
  • Интеграция electrs для мониторинга транзакций
  • Схема БД и логика обработки платежей (overpayment, underpayment, expiry)
  • Документация API и инструкция по эксплуатации
  • Обучение администраторов работе с панелью
  • Гарантия 30 дней на корректную работу платёжного шлюза

Наши инженеры имеют сертификаты по блокчейн-технологиям и опыт внедрения платежей для 20+ проектов. Оценим ваш проект бесплатно. Получите консультацию по вашему проекту — напишите нам. Закажите внедрение платежного шлюза под ключ.