# عیب‌یابی

اولین قدم همیشه:

```bash
sudo -u gostardeh php bin/console health:check
tail -n 50 storage/logs/errors-$(date -u +%F).log
sudo supervisorctl status 'gostardeh:*'
```

لاگ‌ها در `storage/logs/{channel}-YYYY-MM-DD.log` (تاریخ UTC). هر خط: `[زمان] [کد پیگیری] channel.LEVEL پیام {context}`. کدی که کاربر می‌بیند (`کد پیگیری`) را در لاگ `telegram` یا `errors` جستجو کنید:

```bash
grep -r "a1b2c3d4e5f6" storage/logs/
```

## ربات پاسخ نمی‌دهد

| بررسی | فرمان | اگر مشکل بود |
|---|---|---|
| Webhook | `php bin/console webhook:set --info` | `url` خالی یا اشتباه ← `webhook:set`. `last error` مثل `SSL error` / `Wrong response from the webhook: 500` ← Nginx / گواهی / PHP-FPM |
| پاسخ سرور | `curl -i -X POST https://<دامنه>/webhook/main` | باید `403` بدهد. `404` ← کلید ربات اشتباه یا ربات ثبت نشده؛ `502/504` ← PHP-FPM؛ `500` ← `storage/logs/errors-*.log` |
| صف | `health:check` (ردیف `queue` و `workers`) | `no worker for: updates` ← `supervisorctl start 'gostardeh:*'` |
| Redis | `redis-cli -a … ping` | سرویس یا رمز `REDIS_PASSWORD` |
| به‌روزرسانی‌های معوق | `pending updates` در `--info` | Workerها کم‌اند: `numprocs` صف `updates` را بالا ببرید |

`403` در لاگ `security` با پیام `Webhook request with invalid secret` یعنی Secret تلگرام با دیتابیس یکی نیست ← دوباره `webhook:set`.

## Join / Leave ثبت نمی‌شود

1. ربات باید در کانال **ادمین** با دسترسی «Invite users via link» باشد. اگر نباشد وضعیت کانال به «در انتظار تأیید» برمی‌گردد و کمپین متوقف می‌شود.
2. `webhook:set --info` باید `chat_member` را در `allowed updates` نشان دهد؛ اگر نه، `webhook:set` را دوباره اجرا کنید.
3. عضو باید از **لینک اختصاصی ناشر** وارد شده باشد؛ ورود با لینک عمومی کانال شمرده نمی‌شود (ولی در آمار کانال می‌آید).
4. Worker صف `joins` در حال اجراست؟
5. هر شخص در هر کمپین فقط یک‌بار شمرده می‌شود؛ ناشر و مالک از لینک خودشان شمرده نمی‌شوند.
6. لاگ: `storage/logs/campaigns-*.log` (با `APP_DEBUG=true` نتیجه‌ی هر Join نوشته می‌شود: `joined` / `duplicate` / `rejected`).

## پرداخت انجام شده ولی کیف پول شارژ نشده

1. پنل ← جستجو ← `p:<شماره>` یا هش تراکنش ← رویدادها را ببینید.
2. «🔄 بررسی مجدد از درگاه» (دوبار شارژ نمی‌کند).
3. `storage/logs/payment-*.log`:

| پیام | علت |
|---|---|
| `Gateway verify failed` | درگاه/شبکه در دسترس نبوده؛ Worker دوباره تلاش می‌کند |
| `callback_rejected` در رویدادها | امضای IPN نامعتبر (Secret اشتباه در `.env`) |
| وضعیت `failed` با `authority mismatch` | Callback با Authority دیگری آمده (جعل یا اشتباه) |
| TRON در `pending` می‌ماند | مبلغ دقیق واریز نشده، شبکه‌ی اشتباه، یا هنوز به `TRON_MIN_CONFIRMATIONS` نرسیده |
| TON در `pending` می‌ماند | Comment/Memo وارد نشده یا اشتباه است |
| `payment.rate_unavailable` | نرخ از Nobitex گرفته نشد و `rate.*` هم صفر است ← نرخ دستی را در تنظیمات وارد کنید |

4. اگر واقعاً پول به حساب شما رسیده ولی قابل تطبیق خودکار نیست (مثلاً مبلغ متفاوت): «اصلاح موجودی» با ذکر شماره‌ی پرداخت در دلیل.

## درآمد ناشر «در انتظار» مانده

درآمد پس از `campaign.hold_hours` (برای «عضو ماندگار» سه برابر) تسویه می‌شود و `CampaignMaintenanceJob` هر دقیقه اجرا می‌شود. بررسی: ردیف `scheduler` در `health:check` و Worker صف `campaigns`.

```bash
grep -c "schedule:run" /etc/cron.d/gostardeh
sudo -u gostardeh php bin/console schedule:run --verbose
```

## Jobهای ناموفق

```bash
php bin/console queue:failed                  # فهرست + متن خطا
php bin/console queue:failed --retry=17       # پس از رفع علت
php bin/console queue:failed --retry=all
```

علت در `storage/logs/workers-*.log` با پیام `Job failed permanently` و Stack Trace کوتاه آمده است.

## خطاهای رایج

| نشانه | علت و راه‌حل |
|---|---|
| `APP_KEY must be a base64 encoded 32-byte key` | `php bin/console key:generate` (فقط نصب تازه) یا بازگرداندن `.env` از پشتیبان |
| `Decryption failed` | `APP_KEY` با کلیدِ زمان رمزنگاری یکی نیست ← `.env` درست را برگردانید؛ برای توکن ربات `bot:add` را دوباره اجرا کنید |
| `No bot registered` | `php bin/console bot:add` |
| `SQLSTATE[HY000] [2002]` | MySQL در دسترس نیست / `DB_HOST` اشتباه |
| `SQLSTATE[HY000] [1045]` | نام کاربری یا رمز دیتابیس |
| `Redis connection failed` | Redis خاموش است یا `REDIS_HOST/PORT` اشتباه |
| `Redis error: NOAUTH` / `WRONGPASS` | `REDIS_PASSWORD` |
| `Telegram … [401]: Unauthorized` | توکن ربات باطل شده ← توکن جدید در `.env` و `bot:add` |
| `Telegram … [429]` در لاگ | محدودیت تلگرام؛ سیستم خودش صبر و دوباره ارسال می‌کند. اگر زیاد است `TELEGRAM_RATE_PER_SECOND` را کم کنید |
| `Check constraint 'chk_wallets_…' is violated` | تلاشی برای منفی‌کردن موجودی جلوگیری شده؛ **نباید** در کار عادی رخ دهد — لاگ `wallet` و کد پیگیری را بررسی و گزارش کنید |
| کاربر: «تعداد درخواست‌ها زیاد است» | Flood Control؛ در صورت نیاز `config/telegram.php → user_rate / user_burst` |
| دکمه‌های قدیمی: «این مرحله منقضی شده» | State پس از ۳۰ دقیقه منقضی شده؛ کاربر از منو دوباره شروع کند |
| تغییر تنظیمات اعمال نمی‌شود | حداکثر ۵ ثانیه صبر کنید؛ تغییر `.env` نیاز به `supervisorctl restart 'gostardeh:*'` و `systemctl reload php8.3-fpm` دارد |
| تغییر کد اعمال نمی‌شود | OPcache (`validate_timestamps=0`) ← `systemctl reload php8.3-fpm` و ری‌استارت Workerها (یا `scripts/deploy.sh`) |

## بررسی سلامت مالی

این دو پرس‌وجو باید **هیچ ردیفی** برنگردانند:

```sql
-- موجودی هر کیف پول = جمع Ledger
SELECT w.user_id, w.available, COALESCE(SUM(CASE WHEN t.direction = 'credit' THEN t.amount ELSE -t.amount END), 0) AS ledger
FROM wallets w LEFT JOIN wallet_transactions t ON t.user_id = w.user_id AND t.bucket = 'available'
GROUP BY w.user_id, w.available HAVING w.available <> ledger;

-- هیچ پرداختی دوبار شارژ نشده
SELECT reference_id, COUNT(*) FROM wallet_transactions
WHERE type = 'deposit' AND reference_type = 'payment' GROUP BY reference_id HAVING COUNT(*) > 1;
```

## پشتیبان‌گیری کار نمی‌کند

`storage/logs/backup.log` و `application-*.log`. رایج‌ترین علت‌ها: نبودن `mysqldump` (`apt install mysql-client` یا مسیر در `BACKUP_MYSQLDUMP`)، نداشتن دسترسی `LOCK TABLES`/`SELECT` برای کاربر دیتابیس، پر بودن دیسک (ردیف `disk` در `health:check`).
