Разработка системы мультичейн-оплаты
Принимать крипто-платежи "в одной сети" — давно решённая задача. Но как только клиент хочет принимать платежи в ETH, USDC, BNB, SOL, USDT на Tron и ещё Bitcoin — это уже архитектурная задача. Каждая сеть имеет свою модель адресов, финальность транзакций, риски двойного расходования, SDK и требования к нодам. Склеить всё это в единую надёжную систему — нетривиально.
Ключевые архитектурные решения
Прежде чем писать код, нужно принять три решения, которые определяют всю архитектуру.
Custodial vs Non-custodial
Custodial — система сама хранит средства до вывода мерчантом. Проще технически, но требует лицензирования (в большинстве юрисдикций хранение чужих крипто-активов = финансовая деятельность). Требуется HSM или MPC для ключей, регулярные аудиты.
Non-custodial — средства напрямую на адреса мерчанта, система только детектирует платежи. Технически сложнее (нет единого hot wallet), но регуляторно чище. Большинство B2B решений используют этот подход.
HD Wallet vs Smart Contract адреса
HD Wallet (BIP32/BIP44) — генерируем уникальный deposit адрес для каждого платежа из одного seed. m/44'/60'/0'/0/invoice_id — каждый invoice получает свой адрес. Работает для всех EVM сетей и Bitcoin. Мониторинг: подписываемся на события всех сгенерированных адресов.
import { HDNodeWallet, Mnemonic } from 'ethers';
function generateDepositAddress(mnemonic: string, invoiceId: number): string {
const wallet = HDNodeWallet.fromPhrase(
mnemonic,
`m/44'/60'/0'/0/${invoiceId}`
);
return wallet.address; // одинаковый адрес для всех EVM сетей
}
Важно: для Bitcoin и Solana нужны разные derivation paths (BIP44 coin types: 0 для BTC, 501 для SOL, 60 для ETH).
Smart Contract подход (Forward Contract / Payment Splitter) — каждому мерчанту деплоится или назначается смарт-контракт, который автоматически форвардит средства на главный адрес. Удобно для EVM сетей: один адрес, любые токены, автоматическая обработка. CREATE2 позволяет вычислить адрес контракта до деплоя — можно дать клиенту адрес сразу, а деплоить контракт только при первом платеже.
// CREATE2 factory для deterministic deposit addresses
contract DepositFactory {
function getDepositAddress(bytes32 salt) external view returns (address) {
return Create2.computeAddress(salt, keccak256(type(ForwardDeposit).creationCode));
}
function deployDeposit(bytes32 salt, address recipient) external returns (address) {
return address(new ForwardDeposit{salt: salt}(recipient));
}
}
Confirmation requirements
Разные сети требуют разного количества подтверждений для безопасной финальности:
| Сеть | Рекомендуемые подтверждения | Время |
|---|---|---|
| Bitcoin | 3-6 | 30-60 мин |
| Ethereum | 12-20 | 3-4 мин |
| BNB Chain | 15-20 | 45-60 сек |
| Polygon | 256 | ~8 мин |
| Solana | 32 (finalized) | ~15 сек |
| Tron | 20 | ~1 мин |
| Arbitrum | 1 (L2) | <1 сек |
Polygon PoS имеет глубокие реорги — 256 подтверждений для safe finality не преувеличение. Arbitrum наследует финальность от Ethereum после settlement.
Архитектура системы
[Payment Gateway API]
↓
[Invoice Service] ← хранит invoice state, triggers, webhooks
↓
[Address Generator] ← HD wallet или CREATE2 factory
↓
[Chain Monitors] ← один процесс на сеть
├─ EthereumMonitor (WebSocket eth_subscribe)
├─ BscMonitor (WebSocket)
├─ SolanaMonitor (WebSocket account subscribe)
├─ TronMonitor (Event API polling)
└─ BitcoinMonitor (ZMQ или Electrum)
↓
[Confirmation Tracker] ← ждёт N подтверждений
↓
[Webhook Dispatcher] ← уведомляет мерчанта
Chain Monitors — наиболее критичный компонент. Каждый монитор должен:
- Переживать обрывы соединения с нодой (auto-reconnect + catch-up)
- Обрабатывать реорганизации (invalidate pending confirmations)
- Детектировать как нативные монеты так и ERC-20/BEP-20/SPL токены
- Работать независимо — падение одного монитора не должно валить остальные
EVM мониторинг
import { createPublicClient, webSocket, parseAbiItem } from 'viem';
const client = createPublicClient({
chain: mainnet,
transport: webSocket('wss://eth-mainnet.g.alchemy.com/v2/...'),
});
// Мониторинг ERC-20 Transfer на наши адреса
const unwatch = client.watchEvent({
event: parseAbiItem('event Transfer(address indexed from, address indexed to, uint256 value)'),
args: { to: monitoredAddresses },
onLogs: async (logs) => {
for (const log of logs) {
await processIncomingTransfer({
chain: 'ethereum',
token: log.address,
from: log.args.from,
to: log.args.to,
amount: log.args.value,
txHash: log.transactionHash,
blockNumber: log.blockNumber,
});
}
},
});
Solana мониторинг
Solana имеет иную модель: токены хранятся не на адресе пользователя напрямую, а в Associated Token Accounts (ATA). Для приёма USDC нужно знать ATA адрес пользователя для этого токена:
import { Connection, PublicKey } from '@solana/web3.js';
import { getAssociatedTokenAddress, TOKEN_PROGRAM_ID } from '@solana/spl-token';
const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
async function getUsdcDepositAddress(userPublicKey: PublicKey): Promise<string> {
const ata = await getAssociatedTokenAddress(USDC_MINT, userPublicKey);
return ata.toBase58();
}
// Мониторинг через WebSocket
const connection = new Connection('wss://api.mainnet-beta.solana.com');
connection.onAccountChange(ataAddress, (accountInfo) => {
// обработка изменения баланса
});
Bitcoin мониторинг
Для Bitcoin без кастомной ноды можно использовать Electrum Protocol или сторонние сервисы (BlockCypher API, Tatum). Для серьёзной production системы рекомендую собственный bitcoind + Electrs (Electrum Rust server):
from electrum_client import ElectrumClient
client = ElectrumClient('127.0.0.1', 50001)
# Подписка на историю адреса
async def monitor_bitcoin_address(address: str):
script_hash = address_to_scripthash(address) # sha256(scriptPubKey), reversed
await client.subscribe_scripthash(script_hash, callback=on_history_change)
Обработка токенов и конвертация
Whitelist токенов. Не принимайте произвольные токены — только pre-approved список. Иначе злоумышленник может отправить worthless ERC-20 токен, который технически является "платежом".
Slippage при конвертации в stablecoins. Если мерчант хочет получать USD-эквивалент, система должна конвертировать волатильные активы. Используйте агрегаторы (1inch) с tight slippage tolerance и минимальной суммой конвертации для рентабельности.
Underpayment handling. Клиент заплатил 99.5 USDC вместо 100. Нужна политика: допустимая погрешность (обычно 0.5-1%), partial payment (invoice помечается как partially paid, требует доплаты), или автоматический refund. Всё это должно быть в бизнес-логике, не в смарт-контракте.
Webhook reliability
Уведомления мерчанта о платеже — критически важный момент. Webhook может упасть, зависнуть, вернуть 5xx. Паттерн надёжной доставки:
class WebhookDispatcher:
MAX_ATTEMPTS = 5
RETRY_DELAYS = [30, 120, 600, 3600, 86400] # сек: 30с, 2м, 10м, 1ч, 24ч
async def dispatch_with_retry(self, webhook_url: str, payload: dict, attempt: int = 0):
try:
async with aiohttp.ClientSession() as session:
resp = await session.post(
webhook_url,
json=payload,
headers={'X-Signature': self.sign_payload(payload)},
timeout=aiohttp.ClientTimeout(total=30)
)
if resp.status == 200:
await self.mark_delivered(payload['event_id'])
return
except Exception as e:
log.error(f"Webhook failed attempt {attempt}: {e}")
if attempt < self.MAX_ATTEMPTS:
await asyncio.sleep(self.RETRY_DELAYS[attempt])
await self.dispatch_with_retry(webhook_url, payload, attempt + 1)
HMAC-подпись payload (X-Signature header) позволяет мерчанту верифицировать подлинность уведомления.
Инфраструктура и безопасность
Ключи HD wallet — master seed хранится в KMS (AWS KMS или HashiCorp Vault). Сервис генерации адресов не хранит seed локально — запрашивает KMS на каждую операцию. Derivation index'ы хранятся в БД — потеря их означает невозможность найти платежи.
Rate limiting и abuse. Invoice generation должна быть rate-limited на уровне API key. Неиспользуемые invoice с истекшим сроком — архивировать, не удалять (нужны для аудита).
Reconciliation. Ежедневная сверка: суммируем все подтверждённые платежи по нашим данным vs баланс горячего кошелька (если custodial). Расхождения — алерт немедленно.
Стек и сроки
Технологический стек:
| Слой | Выбор |
|---|---|
| API | FastAPI (Python) или Fastify (Node.js) |
| Chain мониторинг | Python asyncio / Node.js workers, по одному process на chain |
| БД | PostgreSQL (invoices, transactions) + Redis (pending confirmations, cache) |
| Очереди | Redis Streams или RabbitMQ для webhook dispatch |
| Деплой | Kubernetes (High Availability критична) |
Этапы разработки:
- Базовый EVM мониторинг + invoice flow: 3-4 недели
- Добавление Bitcoin и Solana: +2-3 недели
- Конвертация в stablecoins: +1-2 недели
- Webhook system + dashboard для мерчантов: +2-3 недели
- Load testing, security review, production deployment: +2 недели
Итого для полнофункциональной системы с 5-6 поддерживаемыми сетями: 10-14 недель.







