Files
remnawave-minishop/CLAUDE.md
T

14 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Telegram bot for selling and managing Remnawave VPN subscriptions. Built with Aiogram 3.x (async), SQLAlchemy 2.x (async ORM), and PostgreSQL. Supports multiple payment providers (YooKassa, CryptoPay, FreeKassa, Platega, SeverPay, Telegram Stars) and integrates with the Remnawave panel API.

Common Development Commands

Running the Bot

# Using Docker Compose (recommended)
docker compose up -d

# View logs
docker compose logs -f remnawave-tg-shop

# Stop
docker compose down

# Local development (requires PostgreSQL running)
python main.py

Environment Setup

# Copy example environment file
cp .env.example .env

# Edit .env with your configuration
# Required: BOT_TOKEN, ADMIN_IDS, WEBHOOK_BASE_URL, POSTGRES_*, PANEL_API_URL, PANEL_API_KEY

Database Operations

The bot auto-creates tables on startup via init_db() in db/database_setup.py. No manual migrations needed for initial setup.

Architecture Overview

Layered Architecture

Handlers (routing, input validation)
    ↓
Services (business logic, orchestration)
    ↓
DAL (data access layer - pure async functions)
    ↓
Models (SQLAlchemy ORM)
    ↓
PostgreSQL

Core Components

Entry Point: main.pybot/main_bot.py::run_bot()

Initialization Flow:

  1. Load .env via config/settings.py (Pydantic)
  2. Initialize database connection (db/database_setup.py)
  3. Create dispatcher with middlewares (bot/app/controllers/dispatcher_controller.py)
  4. Build all services via factory (bot/app/factories/build_services.py)
  5. Register routers (bot/routers.py)
  6. Start AIOHTTP web server for webhooks (bot/app/web/web_server.py)

Middleware Pipeline (Execution Order)

All middlewares are outer middlewares applied at dispatcher level:

  1. DBSessionMiddleware - Provides session to handlers, auto-commits/rollbacks
  2. I18nMiddleware - Sets current_language and i18n_instance in handler data
  3. ProfileSyncMiddleware - Syncs Telegram user profile to local DB
  4. BanCheckMiddleware - Blocks banned users
  5. ChannelSubscriptionMiddleware - Enforces required channel subscription (if configured)
  6. ActionLoggerMiddleware - Logs all user actions to message_logs table

Router Hierarchy

root_router (Private chat filter)
├── user_router_aggregate
│   ├── start_router (CommandStart)
│   ├── payment_router (payment flow callbacks)
│   ├── subscription_router (my_subscription callbacks)
│   ├── trial_router
│   ├── referral_router
│   ├── promo_user_router
│   └── payment method routers (yookassa, stars, crypto, etc.)
├── inline_mode.router
└── admin_main_filtered_router (AdminFilter)
    ├── admin_router_aggregate
    ├── admin_common_router
    ├── admin_payments_router
    ├── admin_promo_routers
    ├── admin_stats_router
    ├── admin_logs_router
    ├── admin_broadcast_router
    ├── admin_ads_router
    ├── admin_sync_router
    └── admin_user_management_router

Key Subsystems

1. Service Layer (bot/services/)

All services are created once at startup in build_core_services() and injected into dispatcher. Handlers access them via data["service_name"].

Core Services:

  • PanelApiService - REST client for Remnawave panel API (user CRUD, device management, stats)
  • SubscriptionService - Subscription lifecycle management (trial, paid, renewals, traffic packages)
  • YooKassaService - Primary payment provider with auto-renewal support
  • StarsService, CryptoPayService, FreeKassaService, PlategaService, SeverPayService - Alternative payment providers
  • PanelWebhookService - Handles panel subscription events (expiry notifications, auto-renew triggers)
  • ReferralService - Referral tracking and bonus distribution
  • PromoCodeService - Promo code validation and activation
  • LknpdService - Tax receipt generation (nalog.ru integration)

Service Wiring Pattern:

# Services are cross-injected after creation
subscription_service.yookassa_service = yookassa_service
panel_webhook_service.subscription_service = subscription_service

2. Data Access Layer (db/dal/)

Pure async functions (no classes). Each module provides CRUD operations for specific entities.

Key DAL Modules:

  • user_dal.py - User CRUD, statistics
  • subscription_dal.py - Subscription upsert, status tracking, expiry queries
  • payment_dal.py - Payment records, financial statistics
  • promo_code_dal.py - Promo code management, activation tracking
  • user_billing_dal.py - Saved payment methods for auto-renewal

Common Pattern:

async def get_user_by_id(session: AsyncSession, user_id: int) -> Optional[User]:
    stmt = select(User).where(User.user_id == user_id)
    result = await session.execute(stmt)
    return result.scalar_one_or_none()

3. Database Models (db/models.py)

Key Entities:

  • User - Telegram user profile, panel linkage, referral tree
  • Subscription - Panel-synced subscription state (active/inactive, expiry, traffic, auto-renew flag)
  • Payment - Payment transaction records (status, provider, amount, promo code linkage)
  • PromoCode / PromoCodeActivation - Promo codes with usage limits
  • UserBilling / UserPaymentMethod - Saved payment methods for auto-renewal
  • MessageLog - Audit trail of all user/admin actions
  • PanelSyncStatus - Tracks automatic panel sync state
  • AdCampaign / AdAttribution - Ad tracking

Important Relationships:

  • User → Subscription (1:Many)
  • User → Payment (1:Many)
  • User → User (self-referencing for referral tree via referred_by_id)
  • Subscription has panel_user_uuid and panel_subscription_uuid for sync

4. Webhook Handling (bot/app/web/web_server.py)

Single AIOHTTP application hosts multiple webhook routes:

Path Handler Purpose
/{BOT_TOKEN} SimpleRequestHandler Telegram updates
/webhook/yookassa yookassa_webhook_route YooKassa payment status
/webhook/cryptopay cryptopay_webhook_route CryptoPay transactions
/webhook/freekassa freekassa_webhook_route FreeKassa payments
/webhook/platega platega_webhook_route Platega payments
/webhook/severpay severpay_webhook_route SeverPay payments
/webhook/panel panel_webhook_route Panel subscription events

Security: All webhooks verify signatures (HMAC-SHA256 for panel, provider-specific for payment systems).

5. Panel Integration

Remnawave API (bot/services/panel_api_service.py):

  • REST API client with Bearer token auth
  • User CRUD operations
  • Device management (HWID disconnect)
  • Subscription link generation
  • System/bandwidth stats

User Linking Logic: When activating subscription, the bot ensures a panel user exists:

  1. Check local DB for panel_user_uuid
  2. If missing, search panel by telegramId
  3. If not found, search by username pattern tg_{telegram_id}
  4. If still missing, create new panel user with configured settings
  5. Save returned UUID to local DB

Panel Webhook Events (bot/services/panel_webhook_service.py):

  • user.expires_in_72_hours → Send notification
  • user.expires_in_48_hours → Send notification (special if auto-renew enabled)
  • user.expires_in_24_hoursTrigger auto-renewal if YooKassa + saved card
  • user.expired → Send expiration notice
  • user.expired_24_hours_ago → Follow-up reminder

6. Payment Flow

Standard Payment Flow:

  1. User selects subscription duration
  2. Handler creates Payment record (status: pending_{provider})
  3. Service creates payment with provider API
  4. User redirected to payment page
  5. Provider sends webhook on completion
  6. Webhook handler verifies signature, updates Payment status
  7. If succeeded: call subscription_service.activate_subscription()
    • Ensures panel user exists/linked
    • Creates/updates Subscription record
    • Updates panel user expiry and traffic
    • Enables auto-renew if payment method saved
  8. Send success notification with config link

Auto-Renewal Flow (YooKassa only):

  1. Panel webhook triggers 24h before expiry
  2. PanelWebhookService checks if auto-renew enabled
  3. Calls subscription_service.charge_subscription_renewal()
  4. Uses saved payment_method_id for off-session charge
  5. If successful: suppress 24h notification, create new Payment
  6. If failed: send notification to user

Important Architectural Patterns

1. Dependency Injection via Factory

All services created in build_core_services(), stored in dispatcher:

dp["panel_service"] = panel_service
# Handlers access:
panel_service = data["panel_service"]

2. Upsert Pattern for Panel Sync

Uses panel_subscription_uuid as idempotency key to prevent duplicate subscriptions:

async def upsert_subscription(session, payload):
    existing = await get_subscription_by_panel_subscription_uuid(...)
    if existing:
        # Update fields
    else:
        # Create new

3. FSM for Multi-Step Flows

State machine for complex interactions (defined in bot/states/):

# Set state
await state.set_state(UserPromoStates.waiting_for_promo_code)

# Next handler checks state, processes, clears
if await state.get_state() == UserPromoStates.waiting_for_promo_code:
    # Process promo code
    await state.clear()

4. Notification Suppression

Track last_notification_sent per subscription to avoid spam:

if last_notification_sent is None or date(last_notification_sent) < date(now):
    # Send notification
    await update_last_notification_sent(subscription)

Configuration

All settings loaded via Pydantic from .env (see .env.example for full reference).

Critical Settings:

  • BOT_TOKEN - Telegram bot token
  • ADMIN_IDS - Comma-separated admin Telegram IDs
  • WEBHOOK_BASE_URL - External URL for webhooks (HTTPS required)
  • PANEL_API_URL, PANEL_API_KEY - Remnawave panel access
  • PANEL_WEBHOOK_SECRET - HMAC signature verification
  • Payment provider credentials (YooKassa, CryptoPay, etc.)
  • Pricing: RUB_PRICE_1_MONTH, STARS_PRICE_1_MONTH, etc.

Sales Modes:

  • Time-based (default): User buys N months subscription
  • Traffic-based (traffic_sale_mode): User buys X GB with far-future expiry

Development Guidelines

Adding New Payment Provider

  1. Create service in bot/services/{provider}_service.py implementing:
    • create_payment() - Generate payment URL/data
    • Webhook handler function
  2. Add webhook route in bot/app/web/web_server.py
  3. Add service to build_core_services() factory
  4. Create handler router in bot/handlers/user/subscription/payments_{provider}.py
  5. Register router in bot/routers.py
  6. Add pricing settings to config/settings.py
  7. Update payment method selection in bot/handlers/user/subscription/payment_methods.py

Adding New Admin Feature

  1. Create handler in bot/handlers/admin/{feature}.py
  2. Add keyboard buttons in bot/keyboards/inline/admin_keyboards.py
  3. Register router in bot/routers.py under admin_main_filtered_router
  4. Use AdminFilter() to protect routes
  5. Access services via data["service_name"]

Database Changes

The project uses SQLAlchemy models without formal migrations. To add fields:

  1. Update model in db/models.py
  2. Add corresponding DAL functions in db/dal/
  3. Drop and recreate tables in dev (bot auto-creates on startup)
  4. For production, manually ALTER tables or use db/migrator.py as template

Localization

Translations stored in locales/{lang}/LC_MESSAGES/messages.json. To add strings:

  1. Add key-value to both ru and en files
  2. Access in handlers via: i18n.get("key_name")
  3. I18n instance available in handler data: data["i18n_instance"]

Testing Checklist

When making changes, verify:

  • Webhooks are reachable (use ngrok for local testing)
  • Payment flow completes end-to-end
  • Panel user creation/linking works
  • Subscription activation updates both DB and panel
  • Auto-renewal triggers correctly
  • Notifications send at proper times
  • Admin panel functions accessible only to admins
  • Banned users cannot access bot
  • Referral bonuses apply correctly
  • Promo codes validate and activate

Deployment

Production Setup:

  1. Configure reverse proxy (Nginx/HAProxy) to route HTTPS webhooks to container
  2. Set WEBHOOK_BASE_URL to external domain
  3. Run docker compose up -d
  4. Verify webhook registration in logs
  5. Add bot as admin to required channel (if using REQUIRED_CHANNEL_ID)
  6. Test payment flow with small amount

Webhook Paths:

  • Telegram: https://yourdomain.com/{BOT_TOKEN}
  • YooKassa: https://yourdomain.com/webhook/yookassa
  • Panel: https://yourdomain.com/webhook/panel
  • Other providers: https://yourdomain.com/webhook/{provider}

Troubleshooting

Bot not responding:

  • Check webhook is set correctly (logs show webhook URL on startup)
  • Verify HTTPS is working for WEBHOOK_BASE_URL
  • Check docker compose logs for errors

Payment not completing:

  • Verify webhook route is accessible from provider
  • Check signature verification is passing
  • Look for errors in payment webhook handler logs
  • Ensure provider_payment_id is unique (duplicate payments are rejected)

Panel sync issues:

  • Check PANEL_API_KEY is valid
  • Verify panel user exists with matching UUID
  • Check panel webhook secret matches
  • Review panel_sync_status table for last sync time

Auto-renewal not working:

  • Only works with YooKassa
  • User must have saved payment method (user_billing or user_payment_methods table)
  • Subscription must have auto_renew_enabled = true
  • Panel webhook must trigger 24h before expiry