Разработка платформы выплат в криптовалюте

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1306 услуг
Разработка платформы выплат в криптовалюте
Сложный
от 2 недель до 3 месяцев
Часто задаваемые вопросы

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

Этапы блокчейн-разработки

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

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

Разработка платформы оплаты в криптовалюте

Разница между "добавить кнопку оплаты криптовалютой" и "построить платёжную платформу" — принципиальная. В первом случае интегрируется третий сервис с его гарантиями и ограничениями. Во втором — строится собственная инфраструктура: генерация адресов, мониторинг транзакций, управление ключами, settlement, reconciliation. Эта страница — про второй вариант: когда объём, регуляторные требования или бизнес-модель не позволяют идти через посредника.

Почему строят собственную платформу

Типичные причины:

  • Объём — при >$10M/месяц комиссия 0.5–1% процессинга становится дороже собственной инфраструктуры
  • Контроль над данными — финансовые данные не должны уходить к третьим сторонам
  • Кастомная логика — условные платежи, эскроу, рекуррентные платежи, сплит платежей
  • White-label — платформа для других бизнесов, собственный процессинг обязателен
  • Регуляторные требования — некоторые юрисдикции требуют лицензии, несовместимые с использованием иностранного процессора

Архитектура: ключевые компоненты

Клиент → Payment API → Invoice Service → Address Generator
                                       → Blockchain Monitor
                                       → Settlement Engine
                                       → Merchant Webhook

HD кошельки и генерация адресов

Основа платформы — иерархические детерминированные кошельки (BIP32/BIP44). Один master seed генерирует неограниченное число дочерних адресов — по одному уникальному адресу на каждый инвойс:

m / purpose' / coin_type' / account' / change / index
m / 44'      / 60'        / 0'       / 0      / invoice_id
from hdwallet import HDWallet
from hdwallet.symbols import ETH

def generate_payment_address(merchant_id: int, invoice_id: int) -> str:
    """
    Детерминированная генерация адреса: один invoice = один адрес
    merchant_id: account index (BIP44 level 3)
    invoice_id: address index (BIP44 level 5)
    """
    wallet = HDWallet(symbol=ETH)
    wallet.from_xprivate_key(MASTER_XPRIV)
    wallet.from_path(f"m/44'/60'/{merchant_id}'/0/{invoice_id}")
    return wallet.p2pkh_address()

Критически важно: приватный ключ от master seed должен храниться в HSM (Hardware Security Module) или минимум в AWS KMS / HashiCorp Vault. Никаких plaintext ключей в конфигурационных файлах или БД.

Для публичной генерации адресов (без доступа к приватному ключу) — xpub достаточно:

# Только xpub для watch-only адресов
wallet.from_xpublic_key(MERCHANT_XPUB)
wallet.from_path(f"m/0/{invoice_id}")  # relative от xpub
address = wallet.p2pkh_address()

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

Два подхода к отслеживанию входящих платежей:

Polling через RPC — простое, работает везде:

import asyncio
from web3 import Web3

async def monitor_invoice(
    address: str,
    expected_amount: int,  # в wei
    invoice_id: str,
    timeout_seconds: int = 3600
):
    w3 = Web3(Web3.HTTPProvider(ETH_RPC_URL))
    start_block = w3.eth.block_number
    deadline = time.time() + timeout_seconds

    while time.time() < deadline:
        # Проверяем баланс адреса
        balance = w3.eth.get_balance(address, "latest")
        if balance >= expected_amount:
            # Ждём confirmations
            await wait_for_confirmations(address, expected_amount, confirmations=6)
            await mark_invoice_paid(invoice_id, balance)
            return

        await asyncio.sleep(15)  # раз в ~блок

    await mark_invoice_expired(invoice_id)

WebSocket subscriptions — для минимальной латентности:

from web3 import AsyncWeb3
from web3.providers import WebsocketProviderV2

async def subscribe_to_address(address: str, callback):
    async with AsyncWeb3(WebsocketProviderV2("wss://eth-mainnet.g.alchemy.com/v2/KEY")) as w3:
        subscription_id = await w3.eth.subscribe(
            "logs",
            {"address": address}  # получаем все logs для адреса
        )
        async for response in w3.socket.process_subscriptions():
            if response["subscription"] == subscription_id:
                await callback(response["result"])

Для ERC-20 платежей мониторинг сложнее: нужно слушать Transfer события токен-контракта с фильтром по to адресу, а не ETH баланс.

Работа с несколькими сетями и токенами

Production платформа поддерживает минимум: ETH/USDT/USDC на Ethereum, USDT/USDC на Polygon и Arbitrum (дешевле gas), BTC нативно. Архитектура мультисети:

@dataclass
class NetworkConfig:
    chain_id: int
    rpc_url: str
    confirmations_required: int  # 1 для L2, 6 для ETH mainnet
    supported_tokens: dict[str, str]  # symbol -> contract address

NETWORKS = {
    "ethereum": NetworkConfig(
        chain_id=1,
        rpc_url=ETH_RPC,
        confirmations_required=6,
        supported_tokens={"USDT": "0xdAC17...", "USDC": "0xA0b86..."}
    ),
    "arbitrum": NetworkConfig(
        chain_id=42161,
        rpc_url=ARB_RPC,
        confirmations_required=1,
        supported_tokens={"USDT": "0xFd086...", "USDC": "0xFF970..."}
    ),
    # ...
}

Settlement: вывод средств

Собранные платежи нужно агрегировать и выводить мерчанту. Два паттерна:

Sweeping — периодический перевод со всех адресов на hot wallet:

async def sweep_address(from_address: str, to_address: str, token: str):
    private_key = await kms.get_key(derive_key_path(from_address))

    if token == "ETH":
        balance = w3.eth.get_balance(from_address)
        gas_estimate = 21000
        gas_price = w3.eth.gas_price
        amount = balance - (gas_estimate * gas_price)
        if amount <= 0:
            return

        tx = {
            "to": to_address,
            "value": amount,
            "gas": gas_estimate,
            "gasPrice": gas_price,
            "nonce": w3.eth.get_transaction_count(from_address),
            "chainId": 1
        }
    else:
        # ERC-20 sweep: сначала ETH на gas, затем transfer токена
        await fund_gas(from_address)
        tx = build_erc20_transfer(token, from_address, to_address)

    signed = w3.eth.account.sign_transaction(tx, private_key)
    tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
    return tx_hash.hex()

Batch payments — для выплат мерчантам, использовать Multicall или специализированные batch transfer контракты (например, Disperse.app паттерн) — один газ на N переводов.

Работа с ERC-20: proблема gas

Слабое место ERC-20 платежей: адрес получения пуст (нет ETH для gas). Перед sweep нужно отправить небольшое количество ETH для оплаты газа трансфера токена. Это создаёт dust проблему: ETH остаётся после sweep.

Решение — EIP-2612 Permit: токены с permit (USDC, DAI) позволяют подписывать разрешение на transfer без on-chain approve транзакции. Можно sweep токены одной транзакцией с подписью, без предварительного funding:

// USDC permit + transferFrom в одном вызове
function sweepWithPermit(
    address token,
    address from,
    address to,
    uint256 amount,
    uint256 deadline,
    uint8 v, bytes32 r, bytes32 s
) external {
    IERC20Permit(token).permit(from, address(this), amount, deadline, v, r, s);
    IERC20(token).transferFrom(from, to, amount);
}

Обменные курсы и ценообразование

Инвойс создаётся в фиатной валюте (USD/EUR), сумма в крипте вычисляется динамически. Для стейблкоинов — курс 1:1 с небольшим буфером. Для волатильных активов (ETH, BTC):

def calculate_crypto_amount(fiat_amount: Decimal, currency: str, crypto: str) -> dict:
    # Агрегируем цену из нескольких источников
    prices = await asyncio.gather(
        fetch_binance_price(f"{crypto}USDT"),
        fetch_coinbase_price(f"{crypto}-USD"),
        fetch_chainlink_price(CHAINLINK_FEEDS[crypto])
    )
    median_price = statistics.median(prices)

    crypto_amount = fiat_amount / median_price
    # Добавляем буфер на случай движения цены (1-2%)
    crypto_amount_with_buffer = crypto_amount * Decimal("1.015")

    return {
        "amount": crypto_amount_with_buffer,
        "rate": median_price,
        "expires_at": datetime.now() + timedelta(minutes=15),
        "rate_locked": True
    }

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

KYT (Know Your Transaction) — проверка адресов на санкционные списки и связи с mixers/dark markets. Провайдеры: Chainalysis, Elliptic, TRM Labs. API интегрируется в воронку платежа:

async def check_address_risk(address: str) -> RiskScore:
    response = await chainalysis_client.post("/v2/address/identifications", json={"address": address})
    risk = response.json()
    if risk["risk"] in ["HIGH", "SEVERE"]:
        await flag_for_review(address)
    return RiskScore(level=risk["risk"], categories=risk["categories"])

Transaction limits — автоматические лимиты по объёму для AML: дневной лимит на кошелёк, порог для ручной проверки, обязательная верификация для крупных транзакций.

Reconciliation — ежедневная сверка: сумма выплаченных мерчантам средств + комиссии платформы + остатки на sweep адресах должны совпадать с суммой всех подтверждённых входящих транзакций. Любое расхождение — алерт.

Инфраструктура и надёжность

Нода vs провайдер: для платежной платформы с требованиями к надёжности — собственная нода или Alchemy/Infura с fallback на другой провайдер. Single point of failure неприемлем.

from web3 import Web3
from web3.middleware import ExceptionRetryMiddleware

providers = [
    Web3.HTTPProvider(ALCHEMY_URL),
    Web3.HTTPProvider(INFURA_URL),
    Web3.HTTPProvider(QUICKNODE_URL),
]

def get_w3_with_fallback():
    for provider in providers:
        w3 = Web3(provider)
        w3.middleware_onion.add(ExceptionRetryMiddleware, retries=3)
        if w3.is_connected():
            return w3
    raise RuntimeError("All providers unavailable")

Idempotency: все операции должны быть идемпотентны — повторная обработка webhook или retry payment check не должны создавать дублирующие выплаты. Уникальный idempotency_key на каждую операцию.

Сроки

Фаза Содержание Срок
Архитектура и HD wallet Дизайн системы, генерация адресов, KMS интеграция 1–2 нед
Мониторинг транзакций Polling/WebSocket мониторинг, confirmations 1–2 нед
Settlement engine Sweep логика, ERC-20 permit, batch payments 2–3 нед
Merchant API REST API, webhooks, dashboard 2–3 нед
Compliance KYT интеграция, лимиты, reconciliation 1–2 нед
Тестирование Testnet, stress testing, security review 2–3 нед

Минимально жизнеспособная платформа с ETH/USDC на одной сети: 6–8 недель. Полная мультисетевая платформа с compliance: 3–5 месяцев.