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- Получение депозитного адреса от Crypto2BGET /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)¶
- Пользователь открывает Deposits страницу
- Система запрашивает депозитный адрес у Crypto2B
- Показывается QR код с адресом USDT TRC20 + инструкции
- Пользователь отправляет USDT на адрес
- Crypto2B обнаруживает депозит → отправляет webhook
- 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 бот для мгновенных алертов
Связанная документация¶
- ADR: Integration-Only Architecture
- ADR: Crypto2B Integration
- ADR: Database Schema Design
- Диаграмма: Deposit Flow
- Backend: Crypto2B Integration модуль
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)