Проектирование архитектуры криптоплатежного шлюза

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Проектирование архитектуры криптоплатежного шлюза
Сложная
~5 рабочих дней
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1238
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1167
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    867
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1080
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    563
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    829

Проектирование архитектуры криптоплатёжного шлюза

Платёжный шлюз — это не просто «принимать крипту». Это система которая должна быть устойчива к потере подключения к ноде, к реорганизациям блокчейна, к атакам двойной траты, к курсовым рискам при конвертации в фиат, к недоступности внешних API. И всё это одновременно, пока обрабатываются реальные деньги реальных клиентов.

Проектирование архитектуры до написания кода экономит месяцы переработок.

Фундаментальные требования

Прежде чем рисовать диаграммы, нужно ответить на вопросы которые определяют всё остальное:

Кастодиальная или некастодиальная схема?

  • Кастодиальная: шлюз хранит ключи, управляет кошельками клиентов. Полный контроль, но юридическая ответственность как у финансового учреждения, требования к лицензированию (в ряде юрисдикций — лицензия платёжного оператора).
  • Некастодиальная: платёж идёт напрямую на кошелёк мерчанта, шлюз только мониторит. Нет хранения активов третьих лиц, но мерчант сам управляет ключами.

Какие сети? Каждая сеть — отдельная инфраструктура. Bitcoin UTXO-модель несовместима с EVM account-model. TON — своя VM. Каждая добавляет операционную нагрузку.

Модель расчётов. Мерчант получает крипту as-is, или шлюз конвертирует в фиат? При конвертации появляется курсовой риск и нужна интеграция с биржей/OTC.

Объём и SLA. 100 транзакций/день и 100 000 — разные архитектуры. SLA 99.9% (8.7 часов downtime/год) и 99.99% (52 минуты/год) — принципиально разные требования к избыточности.

Компонентная архитектура

┌─────────────────────────────────────────────────────────────┐
│                    Merchant-facing API                       │
│              REST / Webhooks / SDK библиотеки               │
└───────────────────────┬─────────────────────────────────────┘
                        │
┌───────────────────────▼─────────────────────────────────────┐
│                    Core Services                             │
│                                                             │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │
│  │Invoice Service│  │Address Alloc │  │ Exchange Rate    │  │
│  │(create/query) │  │(HD wallet    │  │ Service          │  │
│  └──────────────┘  │ derivation)  │  └──────────────────┘  │
│                    └──────────────┘                         │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │
│  │Confirmation  │  │Settlement    │  │ Notification     │  │
│  │Tracker       │  │Service       │  │ Service          │  │
│  └──────────────┘  └──────────────┘  └──────────────────┘  │
└───────────┬──────────────────────────────────┬──────────────┘
            │                                  │
┌───────────▼──────────┐          ┌────────────▼──────────────┐
│   Blockchain Layer   │          │       Data Layer           │
│                      │          │                            │
│  BTC Connector       │          │  PostgreSQL (orders,txns)  │
│  EVM Connector       │          │  Redis (rates, sessions)   │
│  TON Connector       │          │  Message Queue (Kafka/RMQ) │
│  TRON Connector      │          └────────────────────────────┘
└──────────────────────┘

Invoice Service

Центральная сущность — invoice. Конечный автомат состояний:

created → address_assigned → payment_detected → 
confirming (N/required) → confirmed → settled | expired | failed
interface Invoice {
  id: string;           // UUID
  merchant_id: string;
  external_order_id: string;
  
  // Что мерчант хочет получить
  requested_currency: 'USD' | 'EUR';
  requested_amount: Decimal;
  
  // Что клиент платит
  payment_currency: 'BTC' | 'ETH' | 'USDT_ERC20' | 'USDT_TRC20';
  payment_network: 'bitcoin' | 'ethereum' | 'tron';
  payment_address: string;
  payment_amount: Decimal;      // в crypto
  exchange_rate: Decimal;       // зафиксированный курс
  exchange_rate_expires_at: Date;
  
  // Состояние
  status: InvoiceStatus;
  received_amount: Decimal;
  tx_hash: string | null;
  confirmations: number;
  required_confirmations: number;
  
  // Временные метки
  created_at: Date;
  expires_at: Date;             // обычно created_at + 15 минут
  confirmed_at: Date | null;
  settled_at: Date | null;
}

Почему exchange_rate_expires_at отдельно от expires_at: клиент может начать новый платёж после истечения курса, но до истечения инвойса — нужно обновить курс, не пересоздавать инвойс.

Address Allocation

HD wallet с BIP-44 деривацией. Маппинг {network, account_index, address_index}address хранится в БД. При создании инвойса берётся следующий свободный address_index.

Критическая проблема: address reuse. Один адрес — один инвойс. Никогда. Даже если инвойс истёк — адрес не переиспользуется, потому что пользователь мог сохранить его и отправить позже.

CREATE TABLE address_pool (
    id BIGSERIAL PRIMARY KEY,
    network VARCHAR(20) NOT NULL,
    coin_type INTEGER NOT NULL,    -- BIP-44 coin type
    address_index BIGINT NOT NULL,
    address VARCHAR(200) NOT NULL,
    allocated_at TIMESTAMPTZ,
    invoice_id UUID REFERENCES invoices(id),
    UNIQUE(network, address_index)
);

-- Pre-generate адреса (batch)
CREATE INDEX idx_address_pool_unallocated 
    ON address_pool (network) 
    WHERE allocated_at IS NULL;

Pre-generation: генерируйте адреса заранее батчами по 1000. Это избегает задержки при генерации в момент создания инвойса.

Blockchain Connector Interface

Все коннекторы реализуют единый интерфейс:

interface BlockchainConnector {
  // Мониторинг
  watchAddress(address: string, callback: (tx: IncomingTransaction) => void): () => void;
  getTransaction(txHash: string): Promise<TransactionDetail>;
  getConfirmations(txHash: string, blockNumber: number): Promise<number>;
  
  // Отправка (для sweep)
  buildSweepTransaction(from: string, to: string, amount: bigint): Promise<UnsignedTx>;
  broadcastTransaction(signedTx: string): Promise<string>;
  
  // Утилиты
  validateAddress(address: string): boolean;
  estimateFee(): Promise<bigint>;
}

Реализации: BitcoinConnector (Electrum protocol), EVMConnector (viem/ethers.js, один инстанс на несколько EVM сетей с разными RPC), TRONConnector (tronweb).

Resilience паттерны для коннекторов

Circuit Breaker — если RPC нода не отвечает, переключаемся на резервную:

class ResilientEVMConnector {
  private rpcUrls: string[];
  private currentIndex = 0;
  private failures: Map<string, number> = new Map();

  private async callWithFallback<T>(fn: (client: PublicClient) => Promise<T>): Promise<T> {
    for (let attempt = 0; attempt < this.rpcUrls.length; attempt++) {
      const url = this.rpcUrls[this.currentIndex];
      try {
        const client = this.getClient(url);
        const result = await fn(client);
        this.failures.set(url, 0);  // сброс счётчика при успехе
        return result;
      } catch (err) {
        const failures = (this.failures.get(url) || 0) + 1;
        this.failures.set(url, failures);
        if (failures >= 3) {
          this.currentIndex = (this.currentIndex + 1) % this.rpcUrls.length;
        }
        if (attempt === this.rpcUrls.length - 1) throw err;
      }
    }
    throw new Error('All RPC endpoints failed');
  }
}

Обработка реорганизаций

Реорг — блоки которые вы уже обработали стали неканоническими. Для шлюза это означает: транзакция которую вы считали подтверждённой — исчезла.

Защита: никогда не помечать инвойс settled при количестве подтверждений меньше безопасного порога. Рекомендуемые пороги:

Сеть Безопасные подтверждения Примерное время
Bitcoin 3 (малые суммы) / 6 (крупные) 30-60 мин
Ethereum 12-15 3-4 мин
Polygon 128 (до checkpoint) 5-7 мин
Arbitrum 1 (optimistic, L2) 15 сек
TRON 20 1 мин

Дополнительно: храните block_hash вместе с tx_hash. При каждой проверке подтверждений верифицируйте что блок с этим хешом всё ещё в canonical chain.

Settlement и Sweep

Sweep — перевод поступивших средств с платёжного адреса на холодный/treasury кошелёк. Автоматизируется через воркер:

async function sweepConfirmedPayments() {
  const confirmed = await db.query(`
    SELECT i.payment_address, i.received_amount, i.payment_currency, i.payment_network
    FROM invoices i
    WHERE i.status = 'confirmed'
      AND i.swept_at IS NULL
      AND i.received_amount > 0
  `);

  for (const invoice of confirmed) {
    const connector = getConnector(invoice.payment_network);
    const fee = await connector.estimateFee();
    const sweepAmount = invoice.received_amount - fee;
    
    if (sweepAmount <= 0) {
      // Сумма меньше комиссии — логируем, не отправляем
      continue;
    }
    
    const unsignedTx = await connector.buildSweepTransaction(
      invoice.payment_address,
      TREASURY_ADDRESSES[invoice.payment_currency],
      sweepAmount
    );
    
    // Подпись через HSM или KMS
    const signedTx = await signingService.sign(invoice.payment_address, unsignedTx);
    const txHash = await connector.broadcastTransaction(signedTx);
    
    await db.query(
      'UPDATE invoices SET swept_at = NOW(), sweep_tx_hash = $1 WHERE id = $2',
      [txHash, invoice.id]
    );
  }
}

Webhook система

Мерчант подписывается на события через webhook URL. Требования к надёжной доставке:

interface WebhookDelivery {
  id: string;
  merchant_id: string;
  invoice_id: string;
  event_type: 'payment.detected' | 'payment.confirmed' | 'payment.settled' | 'payment.expired';
  payload: object;
  status: 'pending' | 'delivered' | 'failed';
  attempts: number;
  next_retry_at: Date;
  delivered_at: Date | null;
}

Retry логика с exponential backoff: 30 сек → 5 мин → 30 мин → 2 часа → 24 часа. После 5 неудачных попыток — статус failed, алерт для поддержки.

HMAC подпись webhook-ов — мерчант верифицирует что запрос от шлюза:

const signature = crypto
  .createHmac('sha256', merchantSecret)
  .update(JSON.stringify(payload))
  .digest('hex');

headers['X-Signature'] = `sha256=${signature}`;

Безопасность

Ключи никогда в application серверах. HSM или KMS (AWS KMS, Google Cloud KMS, HashiCorp Vault). Signing service — изолированный микросервис с минимальными привилегиями.

IP whitelist для webhook endpoint мерчанта — опциональная, но рекомендуемая мера.

Rate limiting на API: создание инвойсов — не более 100/мин на мерчанта (защита от DDoS-генерации адресов).

Audit log всех действий: кто создал инвойс, когда изменился статус, какой воркер провёл sweep. Неизменяемый audit log — через append-only таблицу или внешний SIEM.

Процесс проектирования

День 1: сбор требований — сети, валюты, объёмы, SLA, модель расчётов, юрисдикция.

День 2: проектирование схемы данных — invoice lifecycle, address pool, webhook deliveries. Review с командой.

День 3: blockchain connectors интерфейс, resilience паттерны, реорг-стратегия.

День 4: API контракт (OpenAPI spec), webhook события, SDK для мерчантов.

День 5: security review — key management, signing service изоляция, threat model. Финальная архитектурная документация (ADR для ключевых решений).

Результат проектирования: набор Architecture Decision Records, схема БД, OpenAPI спецификация, диаграммы компонентов и последовательностей. Это основа для разработки команды без постоянных архитектурных вопросов.