# معماری پلتفرم «گسترده»

این سند تصمیم‌های معماری را ثبت می‌کند. قبل از هر تغییر ساختاری این سند را به‌روز کنید.

## ۱. اصول

| اصل | پیاده‌سازی |
|---|---|
| Telegram فقط Interface است | `app/Telegram/Handlers/*` فقط ورودی را می‌خواند، Service را صدا می‌زند و پاسخ را رندر می‌کند. هیچ SQL یا قانون مالی در Handler نیست. |
| SQL فقط در Repository | هر ماژول یک یا چند `*Repository` دارد. Serviceها SQL نمی‌نویسند. |
| پول فقط از طریق Ledger | تنها `WalletService` حق تغییر جدول `wallets` را دارد و هر تغییر یک ردیف `wallet_transactions` می‌سازد. |
| عملیات حساس Idempotent | کلید یکتا (`idempotency_key`) در DB + تغییر وضعیت شرطی (`UPDATE … WHERE status = ?`). |
| مجوزها Server-Side | هر متد در `app/Admin/*Service` ابتدا `Authorizer::authorize()` را صدا می‌زند؛ پنهان بودن دکمه کافی نیست. |
| API خارجی = Adapter | `BotApi`، `PaymentGatewayInterface`، `RateProviderInterface`، `TronGridClient`، `TonCenterClient`. |
| کار سنگین = Queue | Webhook فقط اعتبارسنجی و `push` می‌کند؛ Workerها پردازش می‌کنند. |
| Secret خارج از سورس | `.env`؛ توکن ربات‌ها و داده‌های حساس با AES-256-GCM در DB رمز می‌شوند. |
| تغییر Schema = Migration | `database/migrations/*` با Schema Builder داخلی (MySQL/MariaDB و SQLite برای تست). |

## ۲. لایه‌ها

```text
public/index.php ─► Core\Http\Router ─► Controllers (Webhook, PaymentCallback, Health, AdminApi)
                                             │
                               Queue (Redis) ◄┘
                                             │
bin/console queue:work ─► Core\Queue\Worker ─► Jobs ─► Telegram\Dispatcher ─► Handlers / Flows
                                                   └─► Services ─► Repositories ─► Database
bin/console schedule:run ─► routes/schedule.php ─► Jobs (دوره‌ای)
```

```text
app/
├── Core/            هسته بدون وابستگی به دامنه: Container, Config, Database, Redis, Queue, Cache,
│                    Lock, RateLimiter, Logger, Http, Events, I18n, Console, Settings
├── Controllers/     ورودی‌های HTTP
├── Console/         فرمان‌های bin/console
├── Jobs/            واحدهای کار صف
├── Workers/         تعریف Workerها و Health
├── Telegram/        BotApi، Dispatcher، State، Flowها، Handlerها، Middlewareها
├── Bots/            رجیستری Multi-Bot
├── Users/ Wallet/ Channels/ Campaigns/ Orders/ Payments/ Withdrawals/
├── Referrals/ Kyc/ Support/ Notifications/ Broadcast/ Ranking/ Reports/
├── Admin/           RBAC، Audit، سرویس‌های مدیریتی، Export
└── Security/        Encryption، Fraud Detection، Anti-Abuse
```

هر ماژول دامنه شامل `Models`، `Repository`، `Service` و در صورت نیاز `Events` است.

## ۳. وابستگی‌ها

هسته **هیچ وابستگی اجباری Composer ندارد** (سطح حمله‌ی Supply-Chain برای یک سیستم مالی کوچک می‌ماند):

- `ext-pdo`, `ext-pdo_mysql`, `ext-curl`, `ext-mbstring`, `ext-openssl`, `ext-bcmath`, `ext-json`
- Redis با یک کلاینت RESP داخلی (`Core\Redis\RedisClient`) — به `ext-redis` نیاز نیست.
- `phpunit/phpunit` فقط در `require-dev`. بدون Composer هم `php bin/console test` با Runner داخلی اجرا می‌شود.
- `bootstrap/autoload.php` اگر `vendor/autoload.php` نباشد، PSR-4 را خودش انجام می‌دهد.

## ۴. مدل پول

- واحد پایه: **تومان**، عدد صحیح (`BIGINT`). هیچ `float` در مسیر مالی وجود ندارد. مقادیر رمزارزی `DECIMAL(36,18)` و با `bcmath` محاسبه می‌شوند.
- کیف پول سه سطل دارد:
  - `available` — قابل خرج.
  - `pending` — درآمد در دوره‌ی نگهداری (Hold)؛ هنوز قابل خرج نیست.
  - `withdrawable` — بخشی از `available` که از **درآمد** آمده و قابل برداشت است. واریز درگاه قابل خرج است ولی (به‌صورت پیش‌فرض) قابل برداشت نیست تا «واریز ← برداشت» برای پولشویی/سوءاستفاده از درگاه ممکن نباشد.
  - قاعده: همیشه `0 ≤ withdrawable ≤ available`. در هر برداشت از `available` ابتدا بخش غیرقابل‌برداشت خرج می‌شود.
- هر عملیات: `BEGIN` ← قفل ردیف کیف پول (`SELECT … FOR UPDATE`) ← بررسی کلید Idempotency ← اعتبارسنجی ← `UPDATE wallets` ← `INSERT wallet_transactions` ← `COMMIT`.
- قفل چند کیف پول همیشه به ترتیب صعودی `user_id` (جلوگیری از Deadlock).

## ۵. Idempotency

| عملیات | کلید |
|---|---|
| Webhook | `SET NX` روی `upd:{bot}:{update_id}` + ایندکس یکتای `telegram_updates(bot_id, update_id)` |
| تأیید پرداخت | قفل ردیف `payments` + گذار `pending→paid` + کلید Ledger `payment:{id}` |
| TX Hash | ایندکس یکتای `payments.tx_hash` |
| سفارش | `orders.idempotency_key` یکتا (توکن ساخته‌شده در مرحله‌ی تأیید Flow) |
| برداشت | `withdrawals.idempotency_key` یکتا + قفل کیف پول |
| بازپرداخت | کلید Ledger مثل `withdrawal:{id}:refund`، `order:{id}:refund`، `campaign:{id}:refund` |
| Join | یکتایی `campaign_members(campaign_id, telegram_user_id)` + `UPDATE … WHERE completed_count < capacity` |
| تکمیل کمپین | `UPDATE campaigns SET status='completed' WHERE id=? AND status='active'` (فقط یک برنده) |
| تسویه‌ی درآمد | `UPDATE campaign_members SET earning_status='settled' WHERE id=? AND earning_status='pending'` |

## ۶. Multi-Bot

جدول `bots` هر ربات را با توکن رمزشده، `webhook_secret`، چت‌های مدیریتی و `settings/features` اختصاصی نگه می‌دارد. آدرس Webhook هر ربات `/webhook/{bot_key}` است. `BotContext` در تمام مسیر پردازش حمل می‌شود و تنظیمات به ترتیب «ربات ← سراسری ← پیش‌فرض» خوانده می‌شوند. کاربر و کیف پول بین ربات‌ها مشترک‌اند و `bot_users` عضویت هر کاربر در هر ربات را نگه می‌دارد.

## ۷. توسعه‌پذیری

- **نوع تبلیغ جدید**: یک کلاس با `CampaignTypeInterface` و ثبت آن در `config/campaigns.php`.
- **درگاه جدید**: یک کلاس با `PaymentGatewayInterface` و ثبت در `config/payments.php`.
- **روش برداشت جدید**: `WithdrawalMethodInterface` و ثبت در `config/payments.php`.
- **زبان جدید**: `resources/messages/{locale}.php`.
- **Worker جدید**: یک `Job` و یک خط در `routes/schedule.php` یا `deploy/supervisor/gostardeh.conf`.
