Перейти к содержанию

Deposits Flow - Критический путь депозитов

Дата обновления: 2025-11-13 Архитектура: Integration-Only (НИКАКИХ WALLET ОПЕРАЦИЙ)

Архитектурные принципы

  • ✅ ОБЯЗАТЕЛЬНО: Crypto2B API для всех депозитных операций
  • ✅ ОБЯЗАТЕЛЬНО: Webhooks от Crypto2B для уведомлений
  • ❌ ЗАПРЕТ: HD Wallet, приватные ключи, собственный blockchain мониторинг
  • ✅ ОБЯЗАТЕЛЬНО: transactions table как single source of truth
  • ✅ ОБЯЗАТЕЛЬНО: Immutable deposit records
  • ✅ ОБЯЗАТЕЛЬНО: Email обязателен для всех пользователей

Компоненты системы

Backend Stack

Компонент Файл Роль
Crypto2B Service backend/integrations/crypto2b/service.go Интеграция с Crypto2B API
Webhook Handler backend/integrations/crypto2b/webhook_handler.go Обработка Crypto2B webhooks
Router backend/shared/routing/deposit_router.go Депозитные endpoints
Repository backend/shared/repository/crypto2b_repo.go Crypto2B транзакции
Models backend/shared/models/ User, Transaction, Crypto2BTransaction

Frontend Stack

Компонент Файл Роль
Hook /frontend/user-app/src/hooks/useDeposits.ts React data fetching
Service /frontend/shared/lib/services/crypto2b-service.ts Crypto2B API интеграция
Components /frontend/user-app/src/components/deposits/ UI компоненты

External Infrastructure

  • Crypto2B API: Управляет всеми crypto операциями
  • Поддерживаемые сети: TRC20 (USDT), ERC20 (USDC), BSC, Polygon
  • Webhooks: Real-time уведомления от Crypto2B
  • Rate Limit: 10 запросов/секунду

API Endpoints

Пользовательские операции:

  • GET /api/user/deposits/address - Получение депозитного адреса от Crypto2B
  • GET /api/user/deposits - Список депозитов пользователя
  • GET /api/user/balance - Баланс пользователя (только tracking)
  • GET /api/user/transactions?type=deposit - Депозитные транзакции

Webhook endpoints:

  • POST /api/webhooks/crypto2b - Webhook от Crypto2B для депозитов

Административные операции:

  • GET /api/admin/crypto2b/status - Статус Crypto2B интеграции
  • GET /api/admin/deposits/list - Просмотр всех депозитов
  • GET /api/admin/deposits/stats - Статистика депозитов

Детальный Flow

1. Получение депозитного адреса от Crypto2B

sequenceDiagram
    participant U as User
    participant F as Frontend
    participant B as Backend
    participant C2B as Crypto2B API
    participant DB as Database

    U->>F: Запрос депозитного адреса
    F->>B: GET /api/user/deposits/address
    B->>C2B: POST /channels/take
    Note right of C2B: {"currency": "USDT", "network": "TRC20"}
    C2B-->>B: {address, tag, expires, channel_id}
    B->>DB: Сохранить crypto2b_transactions
    DB-->>B: Запись создана
    B-->>F: {address, qr_code, instructions, expires}
    F->>F: Отображение QR кода и адреса

2. Обработка депозита через Crypto2B webhook

sequenceDiagram
    participant User as 👤 Пользователь
    participant TRC20 as 🟢 Tron Network
    participant C2B as 💳 Crypto2B API
    participant B as 🚀 Backend
    participant DB as 🗄️ Database
    participant F as 📱 Frontend

    User->>TRC20: Отправка USDT на адрес
    TRC20->>C2B: Подтверждение транзакции
    C2B->>C2B: Обработка депозита

    C2B->>B: POST /api/webhooks/crypto2b
    Note right of C2B: {"event": "deposit_confirmed", - "channel_id": "...", "amount": "100.00"}

    B->>B: Верификация webhook signature
    B->>DB: INSERT transactions (type='deposit')
    B->>DB: UPDATE user balance
    B->>DB: UPDATE crypto2b_transactions status

    B->>F: WebSocket уведомление
    F->>F: Real-time обновление UI

    B-->>C2B: 200 OK (идемпотентно)

3. Автоматическое создание инвестиции

sequenceDiagram
    participant WH as WebhookHandler
    participant Val as Validator
    participant DB as Database
    participant IS as InvestmentService
    participant Not as NotificationService

    WH->>Val: Валидация Crypto2B webhook
    Val->>Val: Проверка amount > minimum (500 USD)
    Val->>Val: Проверка supported currency (USDT)
    Val->>Val: Проверка webhook signature

    alt Валидация успешна
        Val-->>WH: Webhook valid

        WH->>DB: BEGIN TRANSACTION
        WH->>DB: INSERT INTO transactions
        Note right of DB: id, user_id, type='deposit', - amount, currency, crypto2b_id, - status='confirmed'
        WH->>DB: UPDATE user balance += amount
        WH->>DB: UPDATE crypto2b_transactions

        WH->>IS: Автосоздание инвестиции (10% APY)
        IS->>DB: INSERT INTO investments
        WH->>DB: COMMIT TRANSACTION

        WH->>Not: Email: депозит получен + инвестиция создана
        Not-->>WH: Notification sent

    else Валидация провалена
        Val-->>WH: Webhook invalid
        WH->>DB: LOG failed webhook
        Note right of DB: Анализ проблем
    end

Crypto2B Integration Details

API Integration

Получение депозитного адреса:

func GetDepositAddress(userID uuid.UUID, currency string) (*DepositAddress, error) {
    // 1. Подготовка запроса к Crypto2B
    request := &crypto2b.ChannelRequest{
        Currency: currency, // "USDT"
        Network:  "TRC20",  // TRC20 для USDT
    }

    // 2. API вызов
    response, err := crypto2bClient.TakeChannel(request)
    if err != nil {
        return nil, fmt.Errorf("crypto2b API error: %w", err)
    }

    // 3. Сохранение channel_id для webhook обработки
    err = saveCrypto2BTransaction(userID, response.ChannelID, "pending")
    if err != nil {
        return nil, fmt.Errorf("database error: %w", err)
    }

    return &DepositAddress{
        Address:   response.Address,
        Tag:       response.Tag,
        Expires:   response.ExpiresAt,
        ChannelID: response.ChannelID,
    }, nil
}

Webhook Security

✅ ЧТО ПРОВЕРЯЕМ:

  • HMAC signature для каждого webhook
  • Идемпотентность обработки (защита от дублей)
  • Rate limiting (не более 100 webhooks/минуту)
  • Валидацию структуры данных

❌ ЧТО НЕ ДЕЛАЕМ:

  • Не храним Crypto2B API ключи в коде
  • Не обрабываем webhooks без signature
  • Не повторно обрабатываем тот же crypto2b_id

✅ КАК ЗАЩИЩАЕМ:

  • API ключи в environment variables
  • Webhook signature verification
  • Comprehensive logging всех операций
  • Automatic retry with exponential backoff

База данных схема

Transactions Table (Single Source of Truth)

CREATE TABLE transactions (
    id UUID PRIMARY KEY,
    user_id UUID NOT NULL REFERENCES users(id),
    type VARCHAR(20) NOT NULL, -- 'deposit', 'withdrawal', 'investment'
    currency VARCHAR(10) NOT NULL, -- 'USDT', 'USDC'
    amount DECIMAL(18,8) NOT NULL CHECK (amount > 0),

    -- Integration tracking (НЕ blockchain данные)
    external_id VARCHAR(255), -- Crypto2B transaction ID
    external_provider VARCHAR(50) DEFAULT 'crypto2b',

    -- Статус и метаданные
    status VARCHAR(20) NOT NULL DEFAULT 'pending',
    fee DECIMAL(18,8),

    -- Аудит
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),
    processed_at TIMESTAMP,

    -- Integration-Only constraints
    CONSTRAINT valid_transaction_type CHECK (type IN ('deposit', 'withdrawal', 'investment')),
    CONSTRAINT valid_status CHECK (status IN ('pending', 'confirmed', 'failed', 'reversed')),
    CONSTRAINT valid_currency CHECK (currency IN ('USDT', 'USDC')),
    CONSTRAINT valid_provider CHECK (external_provider IN ('crypto2b', 'fordefi'))
);

-- Индексы для эффективного поиска
CREATE INDEX idx_transactions_user_type ON transactions(user_id, type);
CREATE INDEX idx_transactions_external_id ON transactions(external_id);
CREATE INDEX idx_transactions_status ON transactions(status);
CREATE INDEX idx_transactions_created_at ON transactions(created_at DESC);

Crypto2B Transactions Table

CREATE TABLE crypto2b_transactions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id),
    saga_transaction_id UUID REFERENCES transactions(id),

    -- Crypto2B специфичные данные
    crypto2b_id VARCHAR(255) UNIQUE, -- External ID from Crypto2B
    channel_id VARCHAR(255), -- Channel для депозитных адресов
    operation_type VARCHAR(20) NOT NULL, -- 'deposit' | 'withdrawal'
    status VARCHAR(50) NOT NULL, -- 'pending' | 'confirmed' | 'failed'

    -- Финансовые данные
    amount DECIMAL(36,18) NOT NULL,
    currency VARCHAR(10) NOT NULL, -- 'USDT', 'USDC'
    network VARCHAR(50), -- 'TRC20', 'ERC20', 'BSC'

    -- Адресные данные (для логирования)
    address VARCHAR(255), -- Депозитный адрес
    tag VARCHAR(100), -- Memo/Tag если нужен

    -- Webhook и API данные
    external_data JSONB, -- Full API response
    webhook_received_at TIMESTAMPTZ,

    -- Аудит
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW(),

    -- Constraints для Integration-Only
    CONSTRAINT valid_operation_type CHECK (operation_type IN ('deposit', 'withdrawal')),
    CONSTRAINT valid_currency_c2b CHECK (currency IN ('USDT', 'USDC')),
    CONSTRAINT valid_status_c2b CHECK (status IN ('pending', 'confirmed', 'failed', 'expired'))
);

-- Индексы для эффективного поиска
CREATE INDEX idx_crypto2b_user_id ON crypto2b_transactions(user_id);
CREATE INDEX idx_crypto2b_external_id ON crypto2b_transactions(crypto2b_id);
CREATE INDEX idx_crypto2b_channel ON crypto2b_transactions(channel_id);
CREATE INDEX idx_crypto2b_status ON crypto2b_transactions(status);

User Balances (Derived Data)

CREATE TABLE user_balances (
    user_id UUID PRIMARY KEY REFERENCES users(id),
    usdt_balance DECIMAL(18,8) DEFAULT 0 CHECK (usdt_balance >= 0),
    usdc_balance DECIMAL(18,8) DEFAULT 0 CHECK (usdc_balance >= 0),

    -- Метаданные баланса
    last_deposit_at TIMESTAMP,
    last_withdrawal_at TIMESTAMP,
    updated_at TIMESTAMP DEFAULT NOW(),

    -- Аудит для обнаружения расхождений
    total_deposits DECIMAL(18,8) DEFAULT 0,
    total_withdrawals DECIMAL(18,8) DEFAULT 0,
    total_investments DECIMAL(18,8) DEFAULT 0,

    -- Инвестиционные данные (Integration-Only)
    invested_amount DECIMAL(18,8) DEFAULT 0, -- Автоинвестированная сумма
    fixed_apy DECIMAL(5,2) DEFAULT 10.00 -- Единая стратегия 10% APY
);

-- Trigger для автоматического обновления балансов
CREATE OR REPLACE FUNCTION update_user_balance()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.type = 'deposit' AND NEW.status = 'confirmed' THEN
        INSERT INTO user_balances (user_id) VALUES (NEW.user_id)
        ON CONFLICT (user_id) DO NOTHING;

        -- Обновление баланса в зависимости от валюты
        IF NEW.currency = 'USDT' THEN
            UPDATE user_balances
            SET usdt_balance = usdt_balance + NEW.amount,
                total_deposits = total_deposits + NEW.amount,
                last_deposit_at = NEW.created_at,
                updated_at = NOW()
            WHERE user_id = NEW.user_id;
        ELSIF NEW.currency = 'USDC' THEN
            UPDATE user_balances
            SET usdc_balance = usdc_balance + NEW.amount,
                total_deposits = total_deposits + NEW.amount,
                last_deposit_at = NEW.created_at,
                updated_at = NOW()
            WHERE user_id = NEW.user_id;
        END IF;
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_update_balance
    AFTER INSERT ON transactions
    FOR EACH ROW
    EXECUTE FUNCTION update_user_balance();

External Integration Configuration

Crypto2B Configuration

# config/integrations.yaml - Integration-Only архитектура
external_services:
  crypto2b:
    enabled: true
    base_url: "https://api.crypto2b.com"
    rate_limit:
      requests_per_second: 10
      burst: 20

    # Поддерживаемые валюты и сети
    supported_currencies:
      USDT:
        networks: ["TRC20", "ERC20", "BSC"]
        primary_network: "TRC20"
        min_deposit: "10.0"
      USDC:
        networks: ["ERC20", "BSC", "POLYGON"]
        primary_network: "ERC20"
        min_deposit: "10.0"

    # Webhook конфигурация
    webhook:
      endpoint: "/api/webhooks/crypto2b"
      timeout: "30s"
      retry_attempts: 3
      verify_signature: true

    # Security
    api_keys:
      dev:
        public_key: "${CRYPTO2B_DEV_PUBLIC_KEY}"
        private_key: "${CRYPTO2B_DEV_PRIVATE_KEY}"

Crypto2B Webhook Events

Deposit Confirmed Event:

{
  "event": "deposit_confirmed",
  "data": {
    "id": "c2b_tx_12345",
    "channel_id": "ch_67890",
    "currency": "USDT",
    "network": "TRC20",
    "amount": "100.00",
    "address": "TQrZ...",
    "tag": null,
    "tx_hash": "0x123...",
    "confirmations": 19,
    "status": "confirmed"
  },
  "timestamp": "2025-11-13T14:30:00Z",
  "signature": "sha256_hmac_signature"
}

Webhook обработка:

func ProcessCrypto2BWebhook(webhook *Crypto2BWebhook) error {
    // 1. Верификация HMAC signature
    if !verifyCrypto2BSignature(webhook) {
        return fmt.Errorf("invalid webhook signature")
    }

    // 2. Идемпотентность - проверка дублей
    existing, err := findCrypto2BTransaction(webhook.Data.ID)
    if err == nil && existing != nil {
        return nil // Уже обработан
    }

    // 3. Создание transaction записи
    transaction := &Transaction{
        UserID:           webhook.UserID,
        Type:             "deposit",
        Currency:         webhook.Data.Currency,
        Amount:           webhook.Data.Amount,
        ExternalID:       webhook.Data.ID,
        ExternalProvider: "crypto2b",
        Status:           "confirmed",
    }

    // 4. Автоматическое создание инвестиции (Integration-Only)
    if webhook.Data.Amount >= 500.0 { // Minimum investment
        investment := &Investment{
            UserID:    webhook.UserID,
            Amount:    webhook.Data.Amount,
            Strategy:  "fixed_10_apy", // Single strategy
            Status:    "active",
        }
        return createDepositWithInvestment(transaction, investment)
    }

    return createDeposit(transaction)
}

Error Handling и Recovery

Типичные ошибки и их обработка

1. Crypto2B API недоступен

func retryApiCall(ctx context.Context, operation func() error) error {
    backoff := time.Second
    maxBackoff := 60 * time.Second

    for i := 0; i < MAX_RETRIES; i++ {
        if err := operation(); err != nil {
            log.Error("Crypto2B API error", "attempt", i+1, "error", err)

            if i == MAX_RETRIES-1 {
                return fmt.Errorf("crypto2b API permanently failed: %w", err)
            }

            time.Sleep(backoff)
            backoff = min(backoff*2, maxBackoff)
            continue
        }
        return nil
    }
    return nil
}

2. Пропущенные webhooks

func ReconcileMissedDeposits() error {
    // Проверяем pending crypto2b_transactions старше 1 часа
    staleTransactions := findStaleTransactions(time.Hour)

    for _, tx := range staleTransactions {
        // Проверяем статус через Crypto2B API
        status, err := crypto2bClient.CheckTransaction(tx.Crypto2BID)
        if err != nil {
            continue
        }

        if status.Status == "confirmed" && tx.Status != "confirmed" {
            // Обработать как успешный депозит
            processLateDeposit(tx, status)
        }
    }
    return nil
}

3. Дублированные webhooks

-- Уникальный индекс предотвращает дублирование по crypto2b_id
CREATE UNIQUE INDEX idx_crypto2b_external_id_unique
ON crypto2b_transactions(crypto2b_id) WHERE crypto2b_id IS NOT NULL;

4. Балансовые расхождения

func AuditBalances() error {
    users := getAllUsers()

    for _, user := range users {
        // Считаем confirmed депозиты через transactions
        calculatedBalance := sumConfirmedTransactions(user.ID, "deposit") -
                           sumConfirmedTransactions(user.ID, "withdrawal")

        storedBalance := getUserBalance(user.ID)

        if abs(calculatedBalance - storedBalance) > BALANCE_TOLERANCE {
            log.Warn("Balance discrepancy detected",
                "user_id", user.ID,
                "calculated", calculatedBalance,
                "stored", storedBalance)

            // Alert операторов для ручной проверки
            alertOperators("BALANCE_MISMATCH", user.ID)
        }
    }
    return nil
}

Real-time User Experience

Frontend Integration

useDeposits Hook:

export function useDeposits(userId: string) {
    const [deposits, setDeposits] = useState<Deposit[]>([]);
    const [depositAddress, setDepositAddress] = useState<DepositAddress | null>(null);
    const [loading, setLoading] = useState(false);

    // 1. Получить депозитный адрес от Crypto2B
    const requestDepositAddress = async (currency: string = 'USDT') => {
        setLoading(true);
        try {
            const address = await crypto2bService.getDepositAddress(userId, currency);
            setDepositAddress(address);
        } catch (error) {
            showErrorNotification('Ошибка получения адреса: ' + error.message);
        } finally {
            setLoading(false);
        }
    };

    // 2. Real-time подписка на депозиты через WebSocket
    useEffect(() => {
        const ws = new WebSocket('/api/user/deposits/stream');

        ws.onmessage = (event) => {
            const notification = JSON.parse(event.data);
            if (notification.userId === userId && notification.type === 'deposit_confirmed') {
                setDeposits(prev => [notification.deposit, ...prev]);

                // Показать успешное уведомление + автоинвестиция
                showDepositSuccessNotification(notification.deposit, notification.investment);
            }
        };

        return () => ws.close();
    }, [userId]);

    return {
        deposits,
        depositAddress,
        loading,
        requestDepositAddress
    };
}

UX Flow для пользователя (Integration-Only)

  1. Пользователь открывает Deposits страницу
  2. Система запрашивает депозитный адрес у Crypto2B
  3. Показывается QR код с адресом USDT TRC20 + инструкции
  4. Пользователь отправляет USDT на адрес
  5. Crypto2B обнаруживает депозит → отправляет webhook
  6. Real-time обновление: "Депозит получен! Инвестиция создана с 10% APY!"

Мониторинг и Алерты

Критические метрики

Операционные метрики:

  • Deposits per hour/day (через Crypto2B webhooks)
  • Webhook response time и успешность обработки
  • Failed deposits ratio
  • Crypto2B API response time и доступность

Безопасность метрики:

  • Large deposits (> $1000 USDT equivalent)
  • Webhook signature verification failures
  • Duplicate crypto2b_id attempts
  • Unusual deposit patterns по времени/суммам

Алерты:

alerts:
  - name: "Crypto2B API Down"
    condition: "crypto2b_api_response_time > 30s"
    severity: "critical"

  - name: "Large Deposit"
    condition: "deposit_amount > 1000_USDT"
    severity: "warning"

  - name: "Webhook Processing Failure"
    condition: "webhook_failure_rate > 5%"
    severity: "high"

  - name: "Balance Discrepancy"
    condition: "abs(calculated_balance - stored_balance) > 0.01"
    severity: "high"

Testing Strategy

Unit Tests

  • Crypto2B API client functions
  • Webhook signature verification
  • Deposit validation logic
  • Balance calculation accuracy
  • JSON parsing и serialization

Integration Tests

  • End-to-end deposit flow через Crypto2B
  • Webhook processing accuracy
  • Database consistency
  • Real Crypto2B API integration

Load Tests

  • High-volume webhook processing
  • Crypto2B API rate limiting
  • Database performance under load
  • WebSocket connection limits

Связь с другими системами

Authentication Integration

  • Email MANDATORY для всех пользователей
  • User ID из authentication system для Crypto2B channel creation
  • См. также: Authentication Flow

Investment Integration

  • Автоматическое создание инвестиций при депозите ≥ $500
  • Single strategy: 10% APY (Integration-Only)

Operator Notifications

  • Email уведомления операторам о крупных депозитах
  • Telegram бот для мгновенных алертов

Связанная документация

Security Checklist

При изменениях в deposits flow (Integration-Only):

  • ✅ Crypto2B API ключи защищены (environment variables)
  • ❌ НЕТ приватных ключей в БД (только external integration)
  • ✅ Только supported currencies (USDT/USDC)
  • ✅ Минимальные суммы депозитов соблюдены ($10 minimum)
  • ✅ Webhook signature verification работает
  • ✅ Transaction immutability сохранена
  • ✅ Balance calculation корректна
  • ✅ Идемпотентность webhook обработки
  • ✅ Error handling не раскрывает sensitive data
  • ✅ Comprehensive logging включено
  • ✅ Real-time мониторинг webhook processing

Архитектурный контроль (Integration-Only):

  • Single source of truth в transactions table
  • Immutable записи (только INSERT, не UPDATE)
  • Consistency между calculated и stored балансами
  • Clean separation между integration layer и business logic
  • Email обязателен для всех пользователей
  • Автоматическое создание инвестиций с single strategy (10% APY)