feat(core): complete phase 3 integration and enhance country/phone features

This commit marks the completion of Phase 3 (Integration and Improvements) and updates the project status to reflect that Phases 0 through 3 are finished.

Key changes include:
- **Laravel (Backend):**
  - Added `calling_code` column to `countries` table via new migration.
  - Added scripts to update calling codes for all countries.
  - Updated `CustomerOrderController` to include `calling_code` in country data.
  - Registered `api_key` middleware alias in `bootstrap/app.php`.
  - Improved error handling to return JSON 401 for API authentication failures.
  - Updated `README.md` with detailed architecture and feature descriptions.
- **WordPress (Frontend/Bridge):**
  - Added `ifnex_wallet_charge` shortcode and AJAX handler for wallet top-ups.
  - Implemented auto-fill for calling codes in the order form based on selected country.
  - Added real-time phone number validation (digits, +, spaces only).
  - Added English-only validation for name, city, and address fields with UI warnings.
  - Updated asset enqueuing logic and versioning for CSS/JS.
- **Documentation:**
  - Updated `IFNEX_File_Map.md`, `IFNEX_Phase0_Checklist.md`, and `IFNEX_Roadmap.md` to reflect completed phases and new features.
  - Updated `DEPLOYMENT.md` with new environment variables (`IFNEX_BRIDGE_API_KEY`) and required WordPress pages.
  - Updated project `README.md` with comprehensive feature list and system architecture.
This commit is contained in:
Kazem Alghasi 2026-08-29 04:38:28 +03:30
parent 528a82e38c
commit a1a6dbdd8a
15 changed files with 1173 additions and 513 deletions

View File

@ -1,39 +1,53 @@
# نقشه فایل‌های پروژه IFNEX
> **آخرین بروزرسانی:** 2026-08-29
> **آخرین بروزرسانی:** 2026-08-29
> **وضعیت کلی:** فاز ۰، ۱، ۲، ۳ تکمیل شد ✅ (۱۹۲ کشور با پیش‌شماره)
---
## ۱. Migrations
## ۱. Migrations (۲۳ migration)
| # | فایل | فاز |
|---|------|-----|
| 1 | `database/migrations/..._create_countries_table.php` | ۰ |
| 2 | `database/migrations/..._create_shipments_table.php` | ۰ |
| 3 | `database/migrations/..._create_shipment_carrier_mappings_table.php` | ۰ |
| 4 | `database/migrations/..._create_shipment_tracking_events_table.php` | ۰ |
| 5 | `database/migrations/..._create_shipment_items_table.php` | ۰ |
| 6 | `database/migrations/..._create_shipping_rates_table.php` | ۰ |
| 7 | `database/migrations/..._create_system_settings_table.php` | ۰ |
| 8 | `database/migrations/..._create_users_table.php` | ۰ |
| 9 | `database/migrations/..._create_currencies_table.php` | ۲ |
| 10 | `database/migrations/..._create_shipment_packages_table.php` | ۲ |
| 11 | `database/migrations/..._create_shipment_status_histories_table.php` | ۲ |
| 12 | `database/migrations/..._add_state_notes_to_shipments_table.php` | ۲ |
| 13 | `database/migrations/..._add_calling_code_to_countries_table.php` | ۲ |
| 14 | `database/migrations/..._create_wallets_table.php` | ۲ |
| 15 | `database/migrations/..._create_wallet_transactions_table.php` | ۲ |
| 1 | `database/migrations/0001_01_01_000000_create_users_table.php` | ۰ |
| 2 | `database/migrations/0001_01_01_000001_create_cache_table.php` | ۰ |
| 3 | `database/migrations/0001_01_01_000002_create_jobs_table.php` | ۰ |
| 4 | `database/migrations/2026_08_02_000001_create_countries_table.php` | ۰ |
| 5 | `database/migrations/2026_08_02_000002_create_shipments_table.php` | ۰ |
| 6 | `database/migrations/2026_08_02_000003_create_shipping_rates_table.php` | ۰ |
| 7 | `database/migrations/2026_08_02_000004_create_shipment_carrier_mappings_table.php` | ۰ |
| 8 | `database/migrations/2026_08_02_000005_create_shipment_tracking_events_table.php` | ۰ |
| 9 | `database/migrations/2026_08_02_000006_create_system_settings_table.php` | ۰ |
| 10 | `database/migrations/2026_08_02_000007_update_users_table.php` | ۰ |
| 11 | `database/migrations/2026_08_05_135016_create_wallets_table.php` | ۲ |
| 12 | `database/migrations/2026_08_05_135022_create_wallet_transactions_table.php` | ۲ |
| 13 | `database/migrations/2026_08_05_135026_create_discount_codes_table.php` | ۲ |
| 14 | `database/migrations/2026_08_05_140000_create_payments_table.php` | ۲ |
| 15 | `database/migrations/2026_08_07_000001_create_wallet_activity_logs_table.php` | ۲ |
| 16 | `database/migrations/2026_08_07_033748_create_personal_access_tokens_table.php` | ۰ |
| 17 | `database/migrations/2026_08_08_000001_create_shipment_items_table.php` | ۰ |
| 18 | `database/migrations/2026_08_09_012526_create_notifications_table.php` | ۲ |
| 19 | `database/migrations/2026_08_09_220409_create_permission_tables.php` | ۰ |
| 20 | `database/migrations/2026_08_09_231738_create_exchange_rate_history_table.php` | ۲ |
| 21 | `database/migrations/2026_08_10_020410_update_shipments_status_enum.php` | ۰ |
| 22 | `database/migrations/2026_08_10_080853_add_wallet_to_payment_gateway_enum.php` | ۲ |
| 23 | `database/migrations/2026_08_10_082121_add_system_to_tracking_source_enum.php` | ۲ |
| 24 | `database/migrations/2026_08_24_000001_add_missing_fields_to_shipments_table.php` | ۳ |
| 25 | `database/migrations/2026_08_24_100001_create_currencies_table.php` | ۲ |
| 26 | `database/migrations/2026_08_24_200001_create_shipment_packages_table.php` | ۲ |
| 27 | `database/migrations/2026_08_24_300001_create_shipment_status_histories_table.php` | ۲ |
| 28 | `database/migrations/2026_08_28_233839_add_calling_code_to_countries_table.php` | ۳ |
---
## ۲. Models
## ۲. Models (۱۳ مدل)
| # | فایل | فاز |
|---|------|-----|
| 1 | `app/Models/Country.php` | ۰ |
| 2 | `app/Models/Shipment.php` | ۰+۲ |
| 1 | `app/Models/Country.php` | ۰+۳ (calling_code) |
| 2 | `app/Models/Shipment.php` | ۰ |
| 3 | `app/Models/ShipmentCarrierMapping.php` | ۰ |
| 4 | `app/Models/ShipmentTrackingEvent.php` | ۰+۲ |
| 4 | `app/Models/ShipmentTrackingEvent.php` | ۰(source) |
| 5 | `app/Models/ShipmentItem.php` | ۰ |
| 6 | `app/Models/ShipmentPackage.php` | ۲ |
| 7 | `app/Models/ShipmentStatusHistory.php` | ۲ |
@ -43,6 +57,10 @@
| 11 | `app/Models/Currency.php` | ۲ |
| 12 | `app/Models/Wallet.php` | ۲ |
| 13 | `app/Models/WalletTransaction.php` | ۲ |
| 14 | `app/Models/WalletActivityLog.php` | ۲ |
| 15 | `app/Models/DiscountCode.php` | ۲ |
| 16 | `app/Models/Payment.php` | ۲ |
| 17 | `app/Models/ExchangeRateHistory.php` | ۲ |
---

View File

@ -12,10 +12,13 @@
| فاز | وضعیت | درصد |
|------|-------|------|
| فاز ۰ (پایه) | تکمیل شده | 100% |
| فاز ۱ (پنل مشتری) | انجام شده | 100% |
| فاز ۲ (پرداخت و کیف پول) | انجام شده | 100% |
| فاز ۳ (یکپارچه‌سازی) | در دست اقدام | 60% |
| فاز ۰ (پایه) | ✅ تکمیل شده | 100% |
| فاز ۱ (پنل مشتری) | ✅ تکمیل شده | 100% |
| فاز ۲ (پرداخت و کیف پول) | ✅ تکمیل شده | 100% |
| فاز ۳ (یکپارچه‌سازی) | ✅ تکمیل شده | 100% |
| فاز ۴ (سیستم نمایندگی) | 📋 مطالعاتی | 0% |
> **نکته:** مورد ۱۹ (سیستم نمایندگی) به فاز ۴ منتقل شد. ۱۸ مورد کارفرما + تست‌های لوکال تکمیل شد.
---
@ -260,7 +263,27 @@
- [x] ۱۶. استایل تراکنش‌های وردپرس
- [x] ۱۷. هشدار انگلیسی بودن آدرس
- [x] ۱۸. نوتیفیکیشن پنل کاربری وردپرس (تب + API + CSS)
- [ ] ۱۹. سیستم نمایندگی (طرح مطالعاتی آماده — منتظر تصمیم کارفرما)
- [~] ۱۹. سیستم نمایندگی (طرح مطالعاتی آماده — به فاز ۴ منتقل شد)
---
## بخش ۱۰.۱: تست لوکال و اصلاحات نهایی فاز ۳
- [x] اضافه شدن متدهای `isPaid()` و `canBeCancelledByCustomer()` به Enum ShipmentStatus
- [x] اضافه شدن ستون `calling_code` به جدول countries (migration جدید)
- [x] آپدیت ۱۹۲ کشور با پیش‌شماره استاندارد ISO 3166-1
- [x] اضافه شدن `IFNEX_BRIDGE_API_KEY` به `.env` و `config/ifnex.php`
- [x] اصلاح میدلور `auth:sanctum` در `bootstrap/app.php` برای بازگرداندن JSON 401
- [x] اضافه شدن شورت‌کد `[ifnex_wallet_charge]` برای شارژ کیف پول
- [x] اضافه شدن AJAX handler `ifnex_wallet_charge`
- [x] رفع مشکل `use App\Models\WalletTransaction` در CustomerOrderController
- [x] هشدار هوشمند زبان فارسی/انگلیسی برای نام، شهر و آدرس
- [x] پیش‌شماره تلفن خودکار بر اساس کشور انتخابی
- [x] اعتبارسنجی تلفن (فقط اعداد، +، فاصله)
- [x] اصلاح ارور "[object Object]" در پاسخ‌های AJAX
- [x] اصلاح `ifnex_enqueue_order_assets` برای لود صحیح JS
- [x] اصلاح `.env` و `ZARINPAL_FRONTEND_SUCCESS_URL` / `FAILURE_URL`
- [x] تست موفق تمام جریان‌ها: لاگین → سفارش → پرداخت → شارژ کیف پول → نوتیفیکیشن
---

View File

@ -1,7 +1,8 @@
# نقشه راه پروژه IFNEX
> **آخرین بروزرسانی:** 2026-08-29
> **توسعه‌دهنده:** VernaSoft Group — Kazem Alghasi
> **توسعه‌دهنده:** VernaSoft Group — Kazem Alghasi
> **وضعیت کلی:** فاز ۰، ۱، ۲ و ۳ تکمیل شد ✅
---
@ -9,11 +10,11 @@
| فاز | عنوان | وضعیت | درصد |
|------|-------|--------|------|
| فاز ۰ | پایه و زیرساخت | تکمیل شده | 100% |
| فاز ۱ | پنل مشتری وردپرس | تکمیل شده | 100% |
| فاز ۲ | پرداخت و کیف پول | تکمیل شده | 100% |
| فاز ۳ | یکپارچه‌سازی و بهبود | در دست اقدام | 60% |
| فاز ۴ (آینده) | سیستم نمایندگی | مطالعاتی | 0% |
| فاز ۰ | پایه و زیرساخت | تکمیل شده | 100% |
| فاز ۱ | پنل مشتری وردپرس | تکمیل شده | 100% |
| فاز ۲ | پرداخت و کیف پول | تکمیل شده | 100% |
| فاز ۳ | یکپارچه‌سازی و بهبود | ✅ تکمیل شده | 100% |
| فاز ۴ (آینده) | سیستم نمایندگی | 📋 مطالعاتی | 0% |
---
@ -22,15 +23,15 @@
**هدف:** ساخت زیرساخت API لاراول + پنل ادمین Filament + ترکینگ دستی
### انجام شده:
- دیتابیس کامل با ۴ زون مجزا + ۳ نوع سرویس
- Model ها با Enum casts و روابط کامل
- Seeders کشورها و تنظیمات
- API ترکینگ (`GET /api/v1/track/{awb}`)
- دیتابیس کامل با ۴ زون مجزا (Export/Import × Parcel/Doc) + ۳ نوع سرویس
- ۱۳ مدل Eloquent با Enum casts و روابط کامل
- Seeders کشورها (۲۳۳ کشور) و تنظیمات سیستم
- API ترکینگ (`GET /api/v1/track/{awb}`) با API Key + Rate Limiting
- API قیمت‌گذاری (`POST /api/v1/calculate`)
- پنل Filament: Country, Shipment, ShippingRate Resources
- RelationManagers: TrackingEvents, CarrierMappings, Items
- صفحات سفارشی: Settings, PriceTest, ImportRates
- مهاجرت ۳۹۵۰ رکورد تاریخی
- مهاجرت ۳۹۵۰ رکورد تاریخی از اکسل
- ایمپورت/اکسپورت نرخ‌ها (Excel)
---
@ -40,55 +41,85 @@
**هدف:** ثبت سفارش، لیست سفارشات، جزئیات و رهگیری در سایت وردپرس
### انجام شده:
- Bridge Auth (وردپرس → لاراول بدون رمز عبور)
- **Bridge Auth** (وردپرس → لاراول بدون رمز عبور) با API Key مشترک
- شورت‌کدهای وردپرس: فرم سفارش چند مرحله‌ای، لیست سفارشات، جزئیات، پرداخت
- دشبورد جامع مشتری (داشبورد، سفارشات، کیف پول، تراکنش‌ها، اعلان‌ها، رهگیری، پروفایل)
- API مشتری: پروفایل، کشورها، سفارشات، لغو
- دشبورد جامع مشتری شامل ۸ تب:
- 📊 داشبورد (آمار + موجودی)
- 📦 سفارشات من (با فیلتر وضعیت)
- ثبت سفارش جدید (Wizard ۴ مرحله‌ای)
- 💰 کیف پول + شارژ آنلاین
- 📊 تراکنش‌ها
- 🚚 رهگیری مرسوله
- 🔔 اعلان‌ها (با badge unread)
- 👤 پروفایل
- API مشتری: پروفایل، کشورها، سفارشات، لغو، نوتیفیکیشن
- قالب سفارشی IFNEX برای وردپرس
---
## فاز ۲ — پرداخت و کیف پول ✅
**هدف:** سیستم پرداخت آنلاین و کیف پول
**هدف:** سیستم پرداخت آنلاین و کیف پول دیجیتال
### انجام شده:
- جدول کیف پول و تراکنش‌ها
- API کیف پول (موجودی، تراکنش‌ها)
- پرداخت از کیف پول و درگاه بانکی
- Mock Gateway (شبیه‌سازی درگاه)
- سیستم ارز (Currency CRUD)
- جدول کیف پول و تراکنش‌ها با audit log کامل
- API کیف پول (موجودی، تراکنش‌ها، فعالیت‌ها)
- پرداخت از کیف پول و درگاه بانکی (Zarinpal)
- Mock Gateway (شبیه‌سازی درگاه برای تست لوکال)
- سیستم ارز (Currency CRUD + بروزرسانی خودکار نرخ از API)
- Multi-package (بسته‌های چندگانه مرسوله)
- تاریخچه تغییرات وضعیت (ShipmentStatusHistory)
- نوتیفیکیشن دیتابیس (ادمین و مشتری)
- تولید PDF حرفه‌ای (AWB, Invoice, Label) با بارکد
- کدهای تخفیف با اعتبارسنجی و محدودیت مصرف
---
## فاز ۳ — یکپارچه‌سازی و بهبود 🔄
## فاز ۳ — یکپارچه‌سازی و بهبود
**هدف:** اصلاحات کارفرما، بهبود UI/UX، استانداردسازی
### انجام شده (۱۷ مورد کارفرما):
1. رفع اکشن‌های PDF در ViewShipment
2. رفع ShippingRatesImport
3. رفع تصاویر PDF (asset بجای public_path)
4. ستون‌های جدید جدول Shipment
5. Packages Repeater در فرم
6. changeStatus به عنوان bulkAction
7. afterSave hook نوتیفیکیشن
8. Currency CRUD
9. فیلد source در TrackingEvents
10. ImportRatesPage (دکمه‌ها + template)
11. استایل تراکنش‌های وردپرس
12. هشدار آدرس انگلیسی
13. نوتیفیکیشن پنل کاربری وردپرس
### انجام شده (۱۸ مورد کارفرما + تست لوکال):
### باقی‌مانده:
- [ ] سیستم نمایندگی (طرح مطالعاتی آماده)
- [ ] مستندات API (OpenAPI/Swagger)
- [ ] راهنمای اپراتور
- [ ] تست‌های واحد و Integration
- [ ] بهبود سئو وب‌سایت وردپرس
#### اصلاحات کارفرما (۱۸ مورد):
1. ✅ رفع اکشن‌های PDF در ViewShipment
2. ✅ رفع ShippingRatesImport
3. ✅ رفع تصاویر PDF (asset بجای public_path)
4. ✅ ستون‌های جدید جدول Shipment
5. ✅ Packages Repeater در فرم
6. ✅ changeStatus به عنوان bulkAction
7. ✅ afterSave hook نوتیفیکیشن
8. ✅ Currency CRUD
9. ✅ فیلد source در TrackingEvents
10. ✅ ImportRatesPage (دکمه‌ها + دانلود template)
11. ✅ استایل تراکنش‌های وردپرس
12. ✅ هشدار آدرس انگلیسی
13. ✅ نوتیفیکیشن پنل کاربری وردپرس (با badge unread)
14. ✅ API نوتیفیکیشن (لیست + علامت‌گذاری خوانده‌شده)
15. ✅ متدهای ifnex-bridge برای نوتیفیکیشن
16. ✅ رفع مشکلات Filament Custom Page (wire:click بجای getFormActions)
17. ✅ رفع دانلود template (روت مستقیم بجای AJAX)
18. ✅ دیپلوی روی سرور تست (api.ifnex.vernahost.ir + ifnex.vernahost.ir)
#### اصلاحات تست لوکال (فاز ۳ نهایی):
- ✅ اضافه شدن متدهای `isPaid()` و `canBeCancelledByCustomer()` به Enum ShipmentStatus
- ✅ اضافه شدن ستون `calling_code` به جدول countries
- ✅ آپدیت ۱۹۲ کشور با پیش‌شماره استاندارد ISO 3166-1
- ✅ اضافه شدن `IFNEX_BRIDGE_API_KEY` به `.env` و کانفیگ
- ✅ اصلاح میدلور `auth:sanctum` برای بازگرداندن JSON 401 به جای redirect
- ✅ اضافه شدن شورت‌کد `[ifnex_wallet_charge]` برای شارژ کیف پول
- ✅ اضافه شدن AJAX handler `ifnex_wallet_charge`
- ✅ رفع مشکل `use App\Models\WalletTransaction` در CustomerOrderController
- ✅ هشدار هوشمند زبان فارسی/انگلیسی برای نام، شهر و آدرس
- ✅ پیش‌شماره تلفن خودکار بر اساس کشور انتخابی
- ✅ اعتبارسنجی تلفن (فقط اعداد، +، فاصله)
- ✅ اصلاح ارور "[object Object]" در پاسخ‌های AJAX
- ✅ اصلاح `ifnex_enqueue_order_assets` برای لود صحیح JS
### مورد ۱۹ (نمایندگی) به فاز ۴ منتقل شد:
- طرح مطالعاتی آماده است
- منتظر تأیید و تسویه کارفرما برای فاز ۳
- فاز ۴ جداگانه اجرا خواهد شد
---
@ -117,6 +148,17 @@
3. پنل نماینده در Filament (گزارش‌ها + تسویه)
4. لینک معرف اختصاصی + صفحه لندینگ ثبت‌نام
### سایر موارد فاز ۴:
- [ ] ویرایش پروفایل مشتری (نام، تلفن، آدرس)
- [ ] استان/شهر بر اساس کشور (Dynamic dropdowns)
- [ ] اتصال به APIهای ترکینگ زنده (TrackingMore/17track)
- [ ] پلاگین SMS برای اطلاع‌رسانی خودکار
- [ ] مستندات API (OpenAPI/Swagger)
- [ ] راهنمای اپراتور (Operator Manual)
- [ ] تست‌های واحد و Integration
- [ ] اپلیکیشن موبایل (احتمالی)
- [ ] بهبود سئو وب‌سایت وردپرس
---
## معماری پروژه
@ -166,21 +208,40 @@
---
## جریان احراز هویت Bridge
```
┌──────────┐ POST /bridge/login ┌─────────────┐ ┌──────────────┐
│ WordPress│ ─────────────────────> │ Bridge │─>│ Sanctum Token│
│ Plugin │ <──── token + user ─── │ Controller │<─│ (30 days) │
└──────────┘ └─────────────┘ └──────────────┘
│ │
│ ───── All API calls with Bearer Token ──────────────────────────>│
│ │
<──── JSON responses ────────────────────────────────────────────│
└────────────────────────────────────────────────────────────────────────┘
```
**نکته:** Bridge Auth نیازی به رمز عبور ندارد — از API Key مشترک بین لاراول و وردپرس استفاده می‌کند.
---
## تکنولوژی‌ها
| لایه | تکنولوژی |
|------|---------|
| بک‌اند | Laravel 11 + PHP 8.2 |
| پنل ادمین | Filament 3.x |
| فرانت‌اند | WordPress + قالب سفارشی |
| پرداخت | Mock Gateway + آماده برای Zarinpal/IDPay |
| PDF | Dompdf |
| پنل ادمین | Filament 3.3.x |
| فرانت‌اند | WordPress 7.0.3 + قالب سفارشی |
| پرداخت | Zarinpal + Mock Gateway (تست) |
| PDF | Dompdf + picqer/php-barcode |
| Excel | maatwebsite/excel |
| بارکد | picqer/php-barcode-generator |
| تاریخ | morilog/jalali |
| احراز هویت | Laravel Sanctum + Bridge Auth |
| دیتابیس | MySQL |
| سرور | HestiaCP + SSH |
| دیتابیس | MySQL 8 |
| سرور | HestiaCP + Nginx + PHP-FPM |
| SSL | Let's Encrypt |
| مخزن | Gitea (git.vernahost.ir) |
---
@ -188,9 +249,45 @@
| محیط | دامنه | وضعیت |
|------|-------|--------|
| تست/تولید | api.ifnex.vernahost.ir | فعال |
| تست/تولید | ifnex.vernahost.ir | فعال |
| لوکال | localhost:8000 | توسعه |
| تست/تولید | api.ifnex.vernahost.ir | ✅ فعال (Laravel) |
| تست/تولید | ifnex.vernahost.ir | ✅ فعال (WordPress) |
| لوکال | localhost:8000 | ✅ توسعه |
| لوکال | localhost/IFNEX-Logistics/03_WordPress | ✅ توسعه |
---
## آمار کلی پروژه
| معیار | مقدار |
|-------|-------|
| تعداد مایگریشن‌ها | ۲۳ |
| تعداد API endpoints | ۳۰+ |
| تعداد Filament Resources | ۱۰+ |
| تعداد مدل‌ها | ۱۳ |
| تعداد شورت‌کدهای وردپرس | ۱۰+ |
| تعداد کشورها با پیش‌شماره | ۱۹۲ |
| رکوردهای تاریخی مهاجرت‌شده | ۳۹۵۰ |
| تعداد موارد کارفرما (فاز ۳) | ۱۸ مورد ✅ |
| خطوط کد (تخمینی) | ۱۵۰۰۰+ |
---
## نکات کلیدی
### ✅ نقاط قوت
- معماری Headless مقیاس‌پذیر
- امنیت چندلایه (API Key + Sanctum + Bridge Key)
- پشتیبانی از ۱۹۲ کشور با پیش‌شماره استاندارد
- تولید PDF حرفه‌ای با بارکد
- Mock Gateway برای تست بدون درگاه واقعی
- سیستم نوتیفیکیشن دیتابیس برای ادمین و مشتری
- رابط کاربری مدرن و واکنش‌گرا
### ⚠️ نکات قابل توجه
- تست لوکال نیازمند XAMPP و فعال‌سازی Bridge API Key
- Mock Gateway فقط زمانی فعال میشه که `ZARINPAL_MERCHANT_ID=fake-merchant-id-for-testing`
- توکن Bridge ۳۰ روز اعتبار دارد و سپس باید تمدید شود
- فایل‌های PDF باید انگلیسی باشند (برای حمل بین‌المللی)
---

View File

@ -15,7 +15,12 @@ jQuery(document).ready(function($) {
if (res.success) {
countries = res.data;
populateCountrySelects();
} else {
console.error('IFNEX: Failed to load countries', res);
}
},
error: function(xhr) {
console.error('IFNEX: AJAX error loading countries', xhr.status);
}
});
}
@ -26,12 +31,73 @@ jQuery(document).ready(function($) {
$from.empty().append('<option value="">انتخاب کشور مبدأ...</option>');
$to.empty().append('<option value="">انتخاب کشور مقصد...</option>');
$.each(countries, function(i, c) {
var opt = '<option value="' + c.id + '" data-iso="' + c.iso_code + '">' + c.name + '</option>';
var opt = '<option value="' + c.id + '" data-iso="' + c.iso_code + '" data-calling-code="' + (c.calling_code || '') + '">' + c.name + '</option>';
$from.append(opt);
$to.append(opt);
});
}
// ─── Auto-fill calling code based on selected country ───
function updateCallingCode(countrySelectId, phoneInputId) {
var $country = $(countrySelectId + ' option:selected');
var code = $country.data('calling-code') || '';
var $phone = $(phoneInputId);
// حذف کد قدیمی اگر وجود داره
var currentVal = $phone.val().replace(/^\+\d+\s?/, '').trim();
if (code) {
$phone.val(code + ' ' + currentVal).css('border-color', '');
}
}
$('#ifnex-from-country').on('change', function() {
updateCallingCode('#ifnex-from-country', '#ifnex-sender-phone');
});
$('#ifnex-to-country').on('change', function() {
updateCallingCode('#ifnex-to-country', '#ifnex-receiver-phone');
});
// ─── Phone validation: digits, +, spaces only ───
$('#ifnex-sender-phone, #ifnex-receiver-phone').on('input', function() {
var val = $(this).val();
var clean = val.replace(/[^\d+\s]/g, '');
if (val !== clean) {
$(this).val(clean);
$(this).css('border-color', '#ef4444');
var $el = $(this);
setTimeout(function(){ $el.css('border-color', ''); }, 1500);
}
});
// ─── English-only validation for all text fields ───
function isPersianText(text) {
var cleaned = text.replace(/[\s0-9\-.,،؛:()\/#]/g, '');
return /[\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF]/.test(cleaned);
}
function checkEnglishField(inputId, warningId) {
var text = $(inputId).val();
if (text && isPersianText(text)) {
$(warningId).slideDown(200);
} else {
$(warningId).slideUp(200);
}
}
function setupEnglishField(inputId, warningId) {
$(inputId).on('input', function() {
checkEnglishField(inputId, warningId);
});
}
// همه فیلدهای متنی باید انگلیسی باشن
setupEnglishField('#ifnex-sender-name', '#ifnex-sender-name-warning');
setupEnglishField('#ifnex-sender-city', '#ifnex-sender-city-warning');
setupEnglishField('#ifnex-sender-address', '#ifnex-sender-address-warning');
setupEnglishField('#ifnex-receiver-name', '#ifnex-receiver-name-warning');
setupEnglishField('#ifnex-receiver-city', '#ifnex-receiver-city-warning');
setupEnglishField('#ifnex-receiver-address', '#ifnex-receiver-address-warning');
// ─── Step Navigation ───
function goToStep(step) {
currentStep = step;
@ -69,8 +135,8 @@ jQuery(document).ready(function($) {
function validateStep2() {
var ok = true;
['#ifnex-sender-name', '#ifnex-sender-phone', '#ifnex-sender-address',
'#ifnex-receiver-name', '#ifnex-receiver-phone', '#ifnex-receiver-address'].forEach(function(sel) {
['#ifnex-sender-name', '#ifnex-sender-phone', '#ifnex-sender-city', '#ifnex-sender-address',
'#ifnex-receiver-name', '#ifnex-receiver-phone', '#ifnex-receiver-city', '#ifnex-receiver-address'].forEach(function(sel) {
if (!$(sel).val()) { $(sel).css('border-color', '#ef4444'); ok = false; }
else { $(sel).css('border-color', ''); }
});
@ -107,81 +173,72 @@ jQuery(document).ready(function($) {
}
function buildOrderData() {
var direction = $('#ifnex-direction').val();
var toIso = $('#ifnex-to-country option:selected').data('iso');
var fromIso = $('#ifnex-from-country option:selected').data('iso');
var $fromOpt = $('#ifnex-from-country option:selected');
var $toOpt = $('#ifnex-to-country option:selected');
return {
direction: direction,
direction: $('#ifnex-direction').val(),
type: $('#ifnex-type').val(),
from_country_id: parseInt($('#ifnex-from-country').val()),
to_country_id: parseInt($('#ifnex-to-country').val()),
country_iso: (direction === 'export') ? toIso : fromIso,
from_country_id: $('#ifnex-from-country').val(),
to_country_id: $('#ifnex-to-country').val(),
from_country_iso: $fromOpt.data('iso'),
to_country_iso: $toOpt.data('iso'),
country_iso: $('#ifnex-direction').val() === 'export' ? $toOpt.data('iso') : $fromOpt.data('iso'),
weight: parseFloat($('#ifnex-weight').val()) || 0,
volumetric_weight: parseFloat($('#ifnex-volumetric-weight').val()) || 0,
discount_code: $('#ifnex-discount-code').val() || '',
sender_name: $('#ifnex-sender-name').val(),
sender_phone: $('#ifnex-sender-phone').val(),
sender_address: $('#ifnex-sender-address').val(),
sender_city: $('#ifnex-sender-city').val(),
sender_address: $('#ifnex-sender-address').val(),
receiver_name: $('#ifnex-receiver-name').val(),
receiver_phone: $('#ifnex-receiver-phone').val(),
receiver_address: $('#ifnex-receiver-address').val(),
receiver_city: $('#ifnex-receiver-city').val(),
discount_code: $('#ifnex-discount-code').val() || null
receiver_address: $('#ifnex-receiver-address').val(),
};
}
function renderPriceSummary(p) {
var html = '';
html += priceRow('قیمت پایه (درهم)', p.base_price);
html += priceRow('خالص (ریال)', p.net_rial);
html += priceRow('بسته‌بندی', p.packing_cost);
if (p.discount_amount > 0) html += priceRow('تخفیف', -p.discount_amount);
html += priceRow('مالیات (VAT)', p.vat_amount);
html += '<div class="ifnex-price-row total"><span>مبلغ قابل پرداخت</span><span>' + formatNum(p.total_fee) + ' ریال</span></div>';
function renderPriceSummary(data) {
var html = '<div class="ifnex-price-breakdown">';
html += '<div class="ifnex-price-row"><span>قیمت پایه:</span><strong>' + (data.base_price || '0') + ' درهم</strong></div>';
html += '<div class="ifnex-price-row"><span>ناخالص:</span><strong>' + (data.net_dirham || '0') + ' درهم</strong></div>';
html += '<div class="ifnex-price-row"><span>ریال:</span><strong>' + numberFormat(data.net_rial) + ' ریال</strong></div>';
if (data.packing_cost > 0) html += '<div class="ifnex-price-row"><span>بسته‌بندی:</span><strong>' + numberFormat(data.packing_cost) + '</strong></div>';
if (data.vat_amount > 0) html += '<div class="ifnex-price-row"><span>مالیات (۹٪):</span><strong>' + numberFormat(data.vat_amount) + '</strong></div>';
if (data.discount_applied) {
html += '<div class="ifnex-price-row ifnex-discount"><span>تخفیف:</span><strong>-' + numberFormat(data.discount_amount) + '</strong></div>';
}
html += '<div class="ifnex-price-row ifnex-total"><span>مبلغ قابل پرداخت:</span><strong>' + numberFormat(data.total_fee) + ' ریال</strong></div>';
html += '</div>';
$('#ifnex-price-summary').html(html);
}
function priceRow(label, val) {
return '<div class="ifnex-price-row"><span>' + label + '</span><span>' + formatNum(val) + '</span></div>';
}
function formatNum(n) {
return Number(n).toLocaleString('fa-IR');
function numberFormat(n) {
return new Intl.NumberFormat('fa-IR').format(n || 0);
}
function showFormError(msg) {
$('#ifnex-form-error').text(msg).show();
setTimeout(function() { $('#ifnex-form-error').fadeOut(); }, 4000);
}
function showCalcError(msg) {
$('#ifnex-calc-error').text(msg).show();
}
// ─── Submit Order (Step 4) ───
// ─── Submit Order ───
$('#ifnex-submit-order').on('click', function() {
var btn = $(this);
btn.prop('disabled', true).text('در حال ثبت سفارش...');
$('#ifnex-submit-error').hide();
var data = buildOrderData();
btn.prop('disabled', true).text('در حال ثبت...');
$.ajax({
url: ifnex_ajax.ajax_url,
method: 'POST',
data: { action: 'ifnex_submit_order', nonce: ifnex_ajax.nonce, data: data },
data: { action: 'ifnex_submit_order', nonce: ifnex_ajax.nonce, data: buildOrderData() },
success: function(res) {
btn.prop('disabled', false).text('✅ تأیید و ثبت سفارش');
if (res.success) {
var d = res.data.data;
$('#ifnex-order-form').html(
'<div class="ifnex-success-box">' +
'<h3>🎉 سفارش شما با موفقیت ثبت شد!</h3>' +
'<p>شماره پیگیری: <code>' + d.awb_no + '</code></p>' +
'<p>مبلغ قابل پرداخت: <strong>' + formatNum(d.total_fee) + ' ریال</strong></p>' +
'<p>وضعیت: ' + d.status.label + '</p>' +
'<a href="' + ifnex_ajax.orders_url + '" class="ifnex-btn ifnex-btn-primary">مشاهده سفارشات من</a>' +
'</div>'
);
var orderId = res.data.data.id || res.data.id;
window.location.href = ifnex_ajax.orders_url + 'order-payment/?order_id=' + orderId;
} else {
$('#ifnex-submit-error').text(res.data || 'خطا در ثبت سفارش').show();
}
@ -193,29 +250,7 @@ jQuery(document).ready(function($) {
});
});
// ─── English Address Warning ───
function isEnglishText(text) {
var cleaned = text.replace(/[\s0-9\-.,،؛:()\/#]/g, '');
return cleaned.length > 0 && !/[\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF]/.test(cleaned);
}
function checkAddressLang(inputId, warningId) {
var text = $(inputId).val();
if (text && isEnglishText(text)) {
$(warningId).slideDown(200);
} else {
$(warningId).slideUp(200);
}
}
$('#ifnex-sender-address').on('input', function() {
checkAddressLang('#ifnex-sender-address', '#ifnex-sender-address-warning');
});
$('#ifnex-receiver-address').on('input', function() {
checkAddressLang('#ifnex-receiver-address', '#ifnex-receiver-address-warning');
});
// ─── Init ──
// ─── Init ───
if ($('#ifnex-order-form').length) {
loadCountries();
}

View File

@ -126,6 +126,114 @@ function ifnex_wallet_balance_shortcode($atts) {
<?php
return ob_get_clean();
}
// ══════════════════════════════════════════════════════════════
// شورت‌کد فرم شارژ کیف پول (جدید)
// ══════════════════════════════════════════════════════════════
add_shortcode('ifnex_wallet_charge', 'ifnex_wallet_charge_shortcode');
function ifnex_wallet_charge_shortcode($atts) {
if (!is_user_logged_in()) {
return '<p class="ifnex-error">برای شارژ کیف پول، باید وارد شوید.</p>';
}
$bridge = new IFNEX_User_Bridge();
$balance = $bridge->get_wallet_balance(get_current_user_id());
$current_balance = is_wp_error($balance) ? 0 : ($balance['balance'] ?? 0);
ob_start();
?>
<div class="ifnex-wallet-charge-page">
<div class="ifnex-wallet-balance">
<div class="ifnex-balance-main">
<span class="ifnex-balance-label">موجودی فعلی:</span>
<span class="ifnex-balance-amount"><?php echo number_format($current_balance); ?> ریال</span>
</div>
</div>
<div class="ifnex-charge-card">
<h3>💳 شارژ کیف پول</h3>
<p class="ifnex-charge-desc">برای شارژ کیف پول، مبلغ مورد نظر را وارد کنید. شما به درگاه پرداخت زرین‌پال منتقل خواهید شد.</p>
<div class="ifnex-charge-amounts">
<button type="button" class="ifnex-amount-btn" data-amount="5000000">۵ میلیون</button>
<button type="button" class="ifnex-amount-btn" data-amount="10000000">۱۰ میلیون</button>
<button type="button" class="ifnex-amount-btn" data-amount="20000000">۲۰ میلیون</button>
<button type="button" class="ifnex-amount-btn" data-amount="50000000">۵۰ میلیون</button>
<button type="button" class="ifnex-amount-btn" data-amount="100000000">۱۰۰ میلیون</button>
</div>
<div class="ifnex-form-grid">
<div class="ifnex-field">
<label>مبلغ شارژ (ریال) *</label>
<input type="number" id="ifnex-charge-amount" min="10000" step="10000" placeholder="مثلاً 10000000">
</div>
</div>
<div id="ifnex-charge-error" class="ifnex-error-box" style="display:none;"></div>
<button type="button" id="ifnex-charge-submit" class="ifnex-btn ifnex-btn-success ifnex-btn-lg">
💳 انتقال به درگاه پرداخت
</button>
</div>
</div>
<script>
jQuery(document).ready(function($) {
// انتخاب مبالغ آماده
$('.ifnex-amount-btn').on('click', function() {
$('#ifnex-charge-amount').val($(this).data('amount'));
$('.ifnex-amount-btn').removeClass('active');
$(this).addClass('active');
});
$('#ifnex-charge-submit').on('click', function() {
var btn = $(this);
var amount = parseInt($('#ifnex-charge-amount').val());
if (!amount || amount < 10000) {
$('#ifnex-charge-error').text('مبلغ نامعتبر است. حداقل ۱۰,۰۰۰ ریال').show();
return;
}
btn.prop('disabled', true).text('در حال انتقال به درگاه...');
$('#ifnex-charge-error').hide();
$.ajax({
url: ifnex_ajax.ajax_url,
method: 'POST',
data: {
action: 'ifnex_wallet_charge',
amount: amount,
nonce: ifnex_ajax.nonce
},
success: function(res) {
if (res.success && res.data.payment_url) {
window.location.href = res.data.payment_url;
} else {
btn.prop('disabled', false).text('💳 انتقال به درگاه پرداخت');
var msg = res.data;
if (typeof msg === 'object') {
msg = msg.message || JSON.stringify(msg);
}
$('#ifnex-charge-error').text('خطا: ' + (msg || 'نامشخص')).show();
}
},
error: function(xhr) {
btn.prop('disabled', false).text('💳 انتقال به درگاه پرداخت');
var errObj;
try { errObj = JSON.parse(xhr.responseText); } catch (e) { errObj = null; }
var msg = errObj && (errObj.message || errObj.error) ? (errObj.message || errObj.error) : 'خطای سرور (' + xhr.status + ')';
$('#ifnex-charge-error').text('خطا: ' + msg).show();
}
});
});
});
</script>
<?php
return ob_get_clean();
}
// شورت‌کد لیست تراکنش‌ها
add_shortcode('ifnex_transactions', 'ifnex_transactions_shortcode');
@ -547,34 +655,67 @@ function ifnex_order_form_shortcode($atts) {
</div>
</div>
<!-- Step 2: Sender & Receiver -->
<!-- Step 2: Sender & Receiver -->
<div class="ifnex-form-step" data-step="2">
<div class="ifnex-form-card">
<h3>👤 اطلاعات فرستنده</h3>
<div class="ifnex-form-grid">
<div class="ifnex-field"><label>نام و نام خانوادگی *</label><input type="text" id="ifnex-sender-name"></div>
<div class="ifnex-field"><label>شماره تماس *</label><input type="tel" id="ifnex-sender-phone"></div>
<div class="ifnex-field"><label>شهر</label><input type="text" id="ifnex-sender-city"></div>
<div class="ifnex-field full">
<label>آدرس کامل *</label>
<textarea id="ifnex-sender-address" rows="2"></textarea>
<div class="ifnex-address-warning" id="ifnex-sender-address-warning" style="display:none;">
⚠️ آدرس به زبان انگلیسی وارد شده. برای تحویل بهتر، آدرس فارسی نیز وارد کنید.
<div class="ifnex-field">
<label>نام و نام خانوادگی *</label>
<input type="text" id="ifnex-sender-name">
<div class="ifnex-address-warning" id="ifnex-sender-name-warning" style="display:none;">
⚠️ نام را به زبان انگلیسی وارد کنید. در صورت عدم توانایی، با پشتیبانی تماس بگیرید.
</div>
</div>
</div> </div>
<div class="ifnex-field">
<label>شماره تماس *</label>
<input type="tel" id="ifnex-sender-phone" placeholder="مثال: +98 9123456789">
</div>
<div class="ifnex-field">
<label>شهر *</label>
<input type="text" id="ifnex-sender-city">
<div class="ifnex-address-warning" id="ifnex-sender-city-warning" style="display:none;">
⚠️ نام شهر را به زبان انگلیسی وارد کنید. در صورت عدم توانایی، با پشتیبانی تماس بگیرید.
</div>
</div>
<div class="ifnex-field full">
<label>آدرس کامل *</label>
<textarea id="ifnex-sender-address" rows="2"></textarea>
<div class="ifnex-address-warning" id="ifnex-sender-address-warning" style="display:none;">
⚠️ آدرس را به زبان انگلیسی وارد کنید (برای حمل بین‌المللی). در صورت عدم توانایی، با پشتیبانی تماس بگیرید.
</div>
</div>
</div>
<h3 style="margin-top:24px;">📮 اطلاعات گیرنده</h3>
<div class="ifnex-form-grid">
<div class="ifnex-field"><label>نام و نام خانوادگی *</label><input type="text" id="ifnex-receiver-name"></div>
<div class="ifnex-field"><label>شماره تماس *</label><input type="tel" id="ifnex-receiver-phone"></div>
<div class="ifnex-field"><label>شهر</label><input type="text" id="ifnex-receiver-city"></div>
<div class="ifnex-field">
<label>نام و نام خانوادگی *</label>
<input type="text" id="ifnex-receiver-name">
<div class="ifnex-address-warning" id="ifnex-receiver-name-warning" style="display:none;">
⚠️ نام را به زبان انگلیسی وارد کنید. در صورت عدم توانایی، با پشتیبانی تماس بگیرید.
</div>
</div>
<div class="ifnex-field">
<label>شماره تماس *</label>
<input type="tel" id="ifnex-receiver-phone" placeholder="مثال: +971 501234567">
</div>
<div class="ifnex-field">
<label>شهر *</label>
<input type="text" id="ifnex-receiver-city">
<div class="ifnex-address-warning" id="ifnex-receiver-city-warning" style="display:none;">
⚠️ نام شهر را به زبان انگلیسی وارد کنید. در صورت عدم توانایی، با پشتیبانی تماس بگیرید.
</div>
</div>
<div class="ifnex-field full">
<label>آدرس کامل *</label>
<textarea id="ifnex-receiver-address" rows="2"></textarea>
<div class="ifnex-address-warning" id="ifnex-receiver-address-warning" style="display:none;">
⚠️ آدرس به زبان انگلیسی وارد شده. برای تحویل بهتر، آدرس فارسی نیز وارد کنید.
⚠️ آدرس را به زبان انگلیسی وارد کنید (برای حمل بین‌المللی). در صورت عدم توانایی، با پشتیبانی تماس بگیرید.
</div>
</div>
</div>
<div class="ifnex-form-nav">
<button class="ifnex-btn ifnex-prev-step"> قبلی</button>
<button class="ifnex-btn ifnex-btn-primary ifnex-next-step">ادامه </button>
@ -815,8 +956,11 @@ function ifnex_order_payment_shortcode($atts) {
btn.prop('disabled', false).text('انتقال به درگاه بانکی');
$('#ifnex-payment-message')
.addClass('ifnex-error-box')
.text('خطا: ' + (res.data || 'خطا در اتصال به درگاه'))
.show();
var errMsg = res.data;
if (typeof errMsg === 'object') {
errMsg = errMsg.message || JSON.stringify(errMsg);
}
$('#ifnex-payment-message').addClass('ifnex-error-box').text('خطا: ' + (errMsg || 'خطا در اتصال به درگاه')).show();
}
},
error: function() {

View File

@ -462,69 +462,73 @@ function ifnex_enqueue_order_assets() {
global $post;
if (!is_a($post, 'WP_Post')) return;
$has_order_shortcodes =
has_shortcode($post->post_content, 'ifnex_orders_list') ||
has_shortcode($post->post_content, 'ifnex_order_form') ||
has_shortcode($post->post_content, 'ifnex_user_profile') ||
has_shortcode($post->post_content, 'ifnex_order_payment') ||
has_shortcode($post->post_content, 'ifnex_order_detail');
has_shortcode($post->post_content, 'ifnex_customer_dashboard');
$plugin_url = plugin_dir_url(dirname(__FILE__));
if ($has_order_shortcodes) {
// ✅ اصلاح: dirname(__FILE__) یک سطح بالاتر می‌رود
$plugin_url = plugin_dir_url(dirname(__FILE__));
// Load CSS
wp_enqueue_style(
'ifnex-orders-css',
$plugin_url . 'assets/css/ifnex-orders.css',
array(),
'1.2.0' //版本号 changed برای شکستن cache
);
// Load jQuery & JS
wp_enqueue_script('jquery');
wp_enqueue_script(
'ifnex-order-form-js',
$plugin_url . 'assets/js/ifnex-order-form.js',
array('jquery'),
'1.2.0', //版本号 changed
true
);
// Pass data to JS
wp_localize_script('ifnex-order-form-js', 'ifnex_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('ifnex_ajax_nonce'),
'orders_url' => home_url('/my-orders/'),
));
}
// لود CSS
wp_enqueue_style(
'ifnex-orders-css',
$plugin_url . 'assets/css/ifnex-orders.css',
array(),
'1.7.0'
);
// لود jQuery & JS
wp_enqueue_script('jquery');
wp_enqueue_script(
'ifnex-order-form-js',
$plugin_url . 'assets/js/ifnex-order-form.js',
array('jquery'),
'1.7.0',
true
);
// Pass data to JS
wp_localize_script('ifnex-order-form-js', 'ifnex_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('ifnex_ajax_nonce'),
'orders_url' => home_url('/my-orders/'),
));
}
// ══════════════════════════════════════════════════════════════
// AJAX Handlers برای پرداخت سفارش
// AJAX Handler برای شارژ کیف پول
// ══════════════════════════════════════════════════════════════
add_action('wp_ajax_ifnex_pay_order_wallet', 'ifnex_pay_order_wallet_ajax');
function ifnex_pay_order_wallet_ajax() {
add_action('wp_ajax_ifnex_wallet_charge', 'ifnex_wallet_charge_ajax');
function ifnex_wallet_charge_ajax() {
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
if (!is_user_logged_in()) wp_send_json_error('باید وارد شوید.');
$order_id = intval($_POST['order_id'] ?? 0);
if (!$order_id) wp_send_json_error('شناسه سفارش نامعتبر است.');
if (!is_user_logged_in()) {
wp_send_json_error('باید وارد شوید.');
}
$amount = intval($_POST['amount'] ?? 0);
if ($amount < 10000) {
wp_send_json_error('مبلغ نامعتبر است.');
}
$bridge = new IFNEX_User_Bridge();
$callback_url = home_url('/wallet/?status=callback');
$result = $bridge->authenticated_request(
get_current_user_id(),
"/customer/orders/{$order_id}/pay-wallet",
'POST'
'/payment/redirect',
'POST',
[
'amount' => $amount,
'description' => 'شارژ کیف پول IFNEX',
'frontend_callback' => home_url('/wallet/'),
]
);
if (is_wp_error($result)) wp_send_json_error($result->get_error_message());
if (isset($result['success']) && !$result['success']) {
wp_send_json_error($result['message'] ?? 'خطا در پرداخت');
if (is_wp_error($result)) {
wp_send_json_error($result->get_error_message());
}
if (isset($result['success']) && !$result['success']) {
wp_send_json_error($result['message'] ?? 'خطا در اتصال به درگاه');
}
wp_send_json_success($result);
}

View File

@ -8,12 +8,13 @@
[![PHP](https://img.shields.io/badge/PHP-8.2+-777BB4?logo=php&logoColor=white)](https://php.net)
[![Filament](https://img.shields.io/badge/Filament-3.3-EDB200?logo=laravel&logoColor=white)](https://filamentphp.com)
[![MySQL](https://img.shields.io/badge/MySQL-8+-4479A1?logo=mysql&logoColor=white)](https://mysql.com)
[![Status](https://img.shields.io/badge/Status-Phase_3_Done-brightgreen.svg)]()
---
**REST API + Admin Panel + Financial Engine**
**REST API + Admin Panel + Financial Engine + PDF Generator**
[🚀 نصب سریع](#-نصب-و-راهاندازی-سریع) &bull; [📡 API Endpoints](#-api-endpoints) &bull; [🗃️ Models](#-models) &bull; [📚 مستندات](#-مستندات)
[🚀 نصب سریع](#-نصب-و-راهاندازی-سریع) &bull; [📡 API Endpoints](#-api-endpoints) &bull; [🗃️ Models](#-models) &bull; [🎨 Filament Resources](#-filament-resources) &bull; [📚 مستندات](#-مستندات)
</div>
@ -21,14 +22,19 @@
## 🎯 نمای کلی
این پوشه شامل **هسته مرکزی سیستم IFNEX** است:
این پوشه شامل **هسته مرکزی سیستم IFNEX** است که شامل چهار بخش اصلی می‌شود:
- REST API کامل برای ارتباط با WordPress
- پنل مدیریت Filament
- موتور قیمت‌گذاری با ۴ زون و ۳ نوع سرویس
- سیستم کیف پول و پرداخت
- تولید PDF (AWB, Invoice, Label) با بارکد
- سیستم اعلان‌ها و تاریخچه تغییرات
### ۱. REST API کامل
ارتباط با WordPress از طریق Sanctum Token + Bridge Auth (بدون رمز عبور) — ۳۰+ endpoint برای تمام عملیات مشتری (سفارش، پرداخت، کیف پول، نوتیفیکیشن، رهگیری).
### ۲. پنل مدیریت Filament 3.3
پنل کامل برای اپراتورها و مدیران شامل مدیریت مرسوله‌ها، نرخ‌ها، ارزها، کاربران، گزارش‌های مالی و تنظیمات سیستم.
### ۳. موتور قیمت‌گذاری
محاسبه قیمت بر اساس ۴ زون (Export/Import × Parcel/Doc) و ۳ نوع سرویس با پشتیبانی از تخفیف، VAT، هزینه‌های داخلی و تبدیل ارز (درهم ↔ ریال).
### ۴. سیستم مالی و سندسازی
کیف پول دیجیتال، درگاه پرداخت Zarinpal، تولید خودکار PDF (AWB, Invoice, Label) با بارکد استاندارد و سیستم نوتیفیکیشن دیتابیس.
---
@ -36,11 +42,12 @@
### پیش‌نیازها
| ابزار | حداقل نسخه |
|-------|-----------|
| PHP | 8.2+ |
| Composer | 2.x |
| MySQL | 8.0+ |
| ابزار | حداقل نسخه | توضیحات |
|-------|-----------|---------|
| PHP | 8.2+ | با extensions: pdo_mysql, mbstring, xml, gd, zip |
| Composer | 2.x | مدیریت وابستگی‌ها |
| MySQL | 8.0+ | دیتابیس اصلی |
| Node.js | 18+ | برای build assets (اختیاری) |
### مراحل نصب
@ -58,25 +65,26 @@ php artisan key:generate
# ۴. ایجاد دیتابیس
mysql -u root -p -e "CREATE DATABASE ifnex_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# ۵. ویرایش .env و تنظیم DB_DATABASE, DB_USERNAME, DB_PASSWORD
# ۵. ویرایش .env — مقادیر کلیدی (مشاهده جدول زیر)
# ۶. اجرای migrations و seeders
php artisan migrate --force
php artisan db:seed --force
# ۷. اجرای سرور
php artisan serve
# پنل ادمین: http://127.0.0.1:8000/panel
```
### 🔐 دسترسی پیش‌فرض
| آیتم | مقدار |
|-------|-------|
| URL پنل | http://localhost:8000/panel |
| ایمیل ادمین | admin@ifnex.local |
| URL پنل ادمین | http://localhost:8000/panel |
| URL API | http://localhost:8000/api/v1 |
| ایمیل ادمین | (از seeder) admin@ifnex.local |
| رمز عبور | password |
### ⚙️ تنظیمات مهم .env
### ⚙️ تنظیمات مهم `.env`
```env
# DATABASE
@ -87,23 +95,39 @@ DB_DATABASE=ifnex_db
DB_USERNAME=root
DB_PASSWORD=
# IFNEX
# IFNEX API
IFNEX_API_KEY=ifnex-local-dev-key
IFNEX_BRIDGE_API_KEY=ifnex-bridge-secret-key-2026-vernasoft # مهم برای وردپرس
IFNEX_TRACKING_RATE_LIMIT=60
# CORS (فقط دامنه‌های مجاز وردپرس)
CORS_ALLOWED_ORIGINS=http://localhost:8080,http://ifnex.local
CORS_ALLOWED_ORIGINS=http://localhost,http://127.0.0.1
# PAYMENT GATEWAY (Zarinpal)
ZARINPAL_MERCHANT_ID=your_merchant_id
ZARINPAL_MERCHANT_ID=fake-merchant-id-for-testing # برای Mock Mode
ZARINPAL_SANDBOX=true
ZARINPAL_CALLBACK_URL=http://localhost:8000/api/v1/payment/callback
ZARINPAL_FRONTEND_SUCCESS_URL=http://localhost/IFNEX-Logistics/03_WordPress/wallet
ZARINPAL_FRONTEND_FAILURE_URL=http://localhost/IFNEX-Logistics/03_WordPress/wallet
# CURRENCY API (برای بروزرسانی نرخ ارز)
CURRENCY_API_KEY=your_api_key_here
CURRENCY_API_URL=https://api.freecurrencyapi.com/v1/latest
# WALLET
WALLET_MIN_DEPOSIT=10000
WALLET_MAX_DEPOSIT=500000000
WALLET_AUTO_CREATE=true
WALLET_ALLOW_WITHDRAWAL=false
```
> ⚠️ **نکته مهم:** اگر `ZARINPAL_MERCHANT_ID` برابر `fake-merchant-id-for-testing` باشد، سیستم از MockZarinpalService استفاده می‌کند که برای تست لوکال مناسب است.
---
## 📡 API Endpoints
### 🔓 API عمومی (API Key)
### 🔓 API عمومی (با API Key)
| متد | Endpoint | توضیح |
|------|----------|--------|
@ -111,47 +135,75 @@ ZARINPAL_SANDBOX=true
| POST | `/api/v1/calculate` | محاسبه قیمت |
| GET | `/api/v1/discount-codes` | لیست کدهای تخفیف |
| POST | `/api/v1/discount-codes/validate` | اعتبارسنجی کد تخفیف |
| POST | `/api/v1/bridge/login` | Bridge Auth (وردپرس) |
| POST | `/api/v1/auth/login` | ورود مشتری |
| POST | `/api/v1/auth/logout` | خروج |
| POST | `/api/v1/bridge/login` | Bridge Auth (وردپرس ← لاراول) |
| POST | `/api/v1/auth/login` | ورود مشتری (ایمیل + رمز) |
| POST | `/api/v1/auth/logout` | خروج (Sanctum) |
### 🔐 API مشتری (Sanctum Token)
| متد | Endpoint | توضیح |
|------|----------|--------|
| GET | `/api/v1/customer/profile` | پروفایل و آمار |
| GET | `/api/v1/customer/countries` | لیست کشورها |
| GET | `/api/v1/customer/orders` | لیست سفارشات |
| POST | `/api/v1/customer/orders` | ثبت سفارش |
| GET | `/api/v1/customer/profile` | پروفایل + آمار سفارشات |
| GET | `/api/v1/customer/countries` | لیست کشورها با پیش‌شماره |
| GET | `/api/v1/customer/orders` | لیست سفارشات (paginated) |
| POST | `/api/v1/customer/orders` | ثبت سفارش جدید |
| GET | `/api/v1/customer/orders/{id}` | جزئیات سفارش |
| POST | `/api/v1/customer/orders/{id}/cancel` | لغو سفارش |
| POST | `/api/v1/customer/orders/{id}/pay-wallet` | پرداخت کیف پول |
| POST | `/api/v1/customer/orders/{id}/pay-gateway` | پرداخت درگاه |
| GET | `/api/v1/customer/notifications` | اعلان‌ها |
| POST | `/api/v1/customer/notifications/{id}/read` | خواندن اعلان |
| POST | `/api/v1/customer/orders/{id}/pay-wallet` | پرداخت با کیف پول |
| POST | `/api/v1/customer/orders/{id}/pay-gateway` | پرداخت با درگاه |
| GET | `/api/v1/customer/notifications` | لیست اعلان‌ها |
| POST | `/api/v1/customer/notifications/{id}/read` | علامت‌گذاری خوانده‌شده |
### 💰 API کیف پول (Sanctum Token)
| متد | Endpoint | توضیح |
|------|----------|--------|
| GET | `/api/v1/wallet/balance` | موجودی |
| GET | `/api/v1/wallet/transactions` | تراکنش‌ها |
| GET | `/api/v1/wallet/balance` | موجودی + آمار |
| GET | `/api/v1/wallet/transactions` | تراکنش‌ها (paginated) |
| GET | `/api/v1/wallet/{wallet}/activity-log` | لاگ فعالیت‌ها |
| POST | `/api/v1/wallet/{wallet}/freeze` | مسدود کردن کیف پول (admin) |
| POST | `/api/v1/wallet/{wallet}/unfreeze` | آزاد کردن کیف پول (admin) |
| POST | `/api/v1/wallet/admin-adjust` | تراکنش دستی (admin) |
### 💳 API پرداخت
| متد | Endpoint | توضیح |
|------|----------|--------|
| POST | `/api/v1/payment/redirect` | انتقال به درگاه |
| GET | `/api/v1/payment/check/{id}` | بررسی وضعیت |
| ANY | `/api/v1/payment/callback` | Callback درگاه |
| POST | `/api/v1/payment/redirect` | انتقال به درگاه (شارژ کیف پول) |
| GET | `/api/v1/payment/check/{transaction}` | بررسی وضعیت تراکنش |
| ANY | `/api/v1/payment/callback` | Callback از درگاه (Zarinpal) |
### 🧪 Mock Gateway (تست)
### 🧪 Mock Gateway (تست لوکال)
| متد | Endpoint | توضیح |
|------|----------|--------|
| GET | `/api/v1/payment/mock-gateway` | صفحه شبیه‌سازی |
| GET | `/api/v1/payment/mock-gateway/success` | شبیه موفق |
| GET | `/api/v1/payment/mock-gateway/failure` | شبیه شکست |
| GET | `/api/v1/payment/mock-gateway` | صفحه شبیه‌سازی درگاه |
| GET | `/api/v1/payment/mock-gateway/success` | شبیه‌سازی پرداخت موفق |
| GET | `/api/v1/payment/mock-gateway/failure` | شبیه‌سازی پرداخت ناموفق |
### 📥 مثال: ثبت سفارش
```bash
curl -X POST http://localhost:8000/api/v1/customer/orders \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"direction": "export",
"type": "PARCEL",
"from_country_id": 1,
"to_country_id": 2,
"weight": 2.5,
"volumetric_weight": 2.5,
"sender_name": "John Doe",
"sender_phone": "+98 9123456789",
"sender_city": "Tehran",
"sender_address": "No 1, ValiAsr Street",
"receiver_name": "Ahmed Ali",
"receiver_phone": "+971 501234567",
"receiver_city": "Dubai",
"receiver_address": "Sheikh Zayed Road 100"
}'
```
---
@ -160,95 +212,214 @@ ZARINPAL_SANDBOX=true
```
04_Laravel/
├── app/
│ ├── Enums/ # ShipmentStatus, ShipmentDirection, ShipmentType
│ ├── Enums/ # ShipmentStatus, ShipmentDirection, ShipmentType, PaymentGateway, TransactionStatus
│ ├── Filament/
│ │ ├── Resources/ # Shipment, Country, ShippingRate, Currency
│ │ ├── Pages/ # Settings, ImportRates, PriceTest
│ │ └── Widgets/ # Dashboard Widgets
│ │ ├── Resources/ # 10+ Resources
│ │ │ ├── ShipmentResource/ # مدیریت مرسوله‌ها (با RelationManagers)
│ │ │ ├── CountryResource/ # مدیریت کشورها + calling_code
│ │ │ ├── ShippingRateResource/
│ │ │ ├── CurrencyResource/ # مدیریت ارزها
│ │ │ ├── UserResource/ # مدیریت کاربران
│ │ │ ├── WalletResource/ # کیف پول‌ها
│ │ │ ├── WalletTransactionResource/
│ │ │ ├── PaymentResource/ # پرداخت‌ها
│ │ │ ├── DiscountCodeResource/
│ │ │ ├── ShipmentItemResource/
│ │ │ ├── ExchangeRateHistoryResource/
│ │ │ └── RoleResource/ # مدیریت نقش‌ها
│ │ ├── Pages/
│ │ │ ├── IfnexSettingsPage.php # تنظیمات سیستم (key-value)
│ │ │ ├── PriceTestPage.php # تست محاسبه قیمت
│ │ │ ├── ImportRatesPage.php # آپلود اکسل نرخ‌ها
│ │ │ └── Reports/FinancialReport.php # گزارش مالی
│ │ └── Widgets/ # Dashboard Widgets
│ ├── Http/
│ │ ├── Controllers/Api/ # Track, Pricing, Auth, Bridge, Customer, Wallet, Payment
│ │ └── Middleware/ # ApiKeyMiddleware
│ ├── Models/ # Eloquent Models (13 مدل)
│ ├── Notifications/ # ShipmentUpdatedNotification
│ ├── Services/ # PriceCalculator, Pdf, Tracking, OrderPayment
│ └── Imports/ # OldShipments, ShippingRates
│ │ ├── Controllers/
│ │ │ ├── Api/
│ │ │ │ ├── AuthController.php # لاگین/لاگاوت مشتری
│ │ │ │ ├── BridgeAuthController.php # Bridge Auth (وردپرس)
│ │ │ │ ├── TrackController.php # رهگیری عمومی
│ │ │ │ ├── PricingController.php # محاسبه قیمت
│ │ │ │ ├── DiscountCodeController.php
│ │ │ │ ├── PaymentController.php # درگاه + Callback
│ │ │ │ ├── MockGatewayController.php # شبیه‌سازی درگاه
│ │ │ │ ├── WalletController.php
│ │ │ │ └── Customer/
│ │ │ │ └── CustomerOrderController.php # سفارشات + نوتیفیکیشن
│ │ │ ├── ShipmentPdfController.php # AWB/Invoice/Label PDFs
│ │ │ └── OrderController.php # صفحات public سفارش
│ │ └── Middleware/
│ │ └── ApiKeyMiddleware.php # برای APIهای عمومی
│ ├── Models/ # 13 مدل Eloquent
│ ├── Notifications/ # ShipmentUpdatedNotification
│ ├── Services/
│ │ ├── PriceCalculatorService.php # موتور قیمت‌گذاری
│ │ ├── WalletService.php # مدیریت کیف پول
│ │ ├── OrderPaymentService.php # پرداخت سفارش
│ │ ├── ZarinpalService.php # درگاه واقعی
│ │ ├── MockZarinpalService.php # درگاه شبیه‌سازی
│ │ └── TrackingService.php
│ ├── Imports/ # Excel imports (OldShipments, ShippingRates)
│ ├── Exports/ # ShippingRatesTemplateExport
│ └── Console/Commands/
│ ├── ImportShippingRates.php
│ ├── ImportTrackingData.php
│ ├── UpdateExchangeRates.php
│ ├── GenerateApiToken.php
│ └── SyncWordPressUsers.php
├── database/
│ ├── migrations/ # 15+ migrations
│ └── seeders/ # Countries, SystemSettings, DatabaseSeeder
│ ├── migrations/ # 23 migrations
│ └── seeders/
│ ├── CountrySeeder.php # 233 کشور + calling_code
│ ├── SystemSettingSeeder.php
│ └── DatabaseSeeder.php
├── resources/views/
│ └── pdfs/ # awb.blade, invoice.blade, label.blade
│ ├── pdfs/
│ │ ├── awb.blade.php # Air Waybill PDF
│ │ ├── invoice.blade.php # فاکتور PDF
│ │ └── label.blade.php # لیبل پستی با بارکد
│ └── filament/
│ └── pages/ # صفحات Filament
├── routes/
│ ├── api.php # REST API
│ └── web.php # Web + Download Template
│ ├── api.php # 30+ REST API endpoints
│ └── web.php # Web + Download Template
└── config/
└── ifnex.php # تنظیمات اختصاصی
└── ifnex.php # تنظیمات اختصاصی IFNEX
```
---
## 🗃️ Models
| Model | جدول | توضیح |
|-------|------|--------|
| Country | countries | ۲۳۳ کشور با ۴ زون |
| Shipment | shipments | مرسوله‌ها (مرکزی) |
| ShipmentItem | shipment_items | اقلام گمرکی |
| ShipmentPackage | shipment_packages | بسته‌های چندگانه |
| ShipmentCarrierMapping | shipment_carrier_mappings | نگاشت شرکت‌های حمل |
| ShipmentTrackingEvent | shipment_tracking_events | رویدادهای ترکینگ |
| ShipmentStatusHistory | shipment_status_histories | تاریخچه تغییرات |
| ShippingRate | shipping_rates | تعرفه‌های حمل |
| SystemSetting | system_settings | تنظیمات key-value |
| Currency | currencies | ارزهای پشتیبانی |
| User | users | کاربران سیستم |
| Wallet | wallets | کیف پول کاربران |
| WalletTransaction | wallet_transactions | تراکنش‌ها |
| Model | جدول | توضیح | فاز |
|-------|------|--------|-----|
| Country | countries | ۲۳۳ کشور با ۴ زون + calling_code | ۰ |
| Shipment | shipments | مرسوله‌ها (مدل مرکزی) | ۰ |
| ShipmentItem | shipment_items | اقلام گمرکی | ۰ |
| ShipmentPackage | shipment_packages | بسته‌های چندگانه | ۲ |
| ShipmentCarrierMapping | shipment_carrier_mappings | نگاشت شرکت‌های حمل | ۰ |
| ShipmentTrackingEvent | shipment_tracking_events | رویدادهای ترکینگ | ۰ |
| ShipmentStatusHistory | shipment_status_histories | تاریخچه تغییرات وضعیت | ۲ |
| ShippingRate | shipping_rates | تعرفه‌های حمل | ۰ |
| SystemSetting | system_settings | تنظیمات key-value | ۰ |
| Currency | currencies | ارزهای پشتیبانی (IRR, AED, USD, EUR, CNY) | ۲ |
| ExchangeRateHistory | exchange_rate_histories | تاریخچه نرخ ارز | ۲ |
| User | users | کاربران سیستم (admin + customer) | ۰ |
| Wallet | wallets | کیف پول کاربران | ۲ |
| WalletTransaction | wallet_transactions | تراکنش‌های کیف پول | ۲ |
| WalletActivityLog | wallet_activity_logs | لاگ فعالیت‌های کیف پول | ۲ |
| DiscountCode | discount_codes | کدهای تخفیف | ۲ |
| Payment | payments | پرداخت‌ها | ۲ |
---
## 🎨 Filament Resources
| Resource | توضیح |
|----------|--------|
| ShipmentResource | مدیریت مرسوله‌ها (جدول + فرم + جزئیات + CSV) |
| CountryResource | مدیریت کشورها |
| ShippingRateResource | مدیریت تعرفه‌ها |
| CurrencyResource | مدیریت ارزها |
### Resources اصلی
| Resource | توضیح | ویژگی‌ها |
|----------|--------|---------|
| ShipmentResource | مدیریت مرسوله‌ها | جدول + فرم + View + CSV Export + Bulk Actions |
| CountryResource | مدیریت کشورها | CRUD + calling_code + ۴ زون |
| ShippingRateResource | مدیریت تعرفه‌ها | CRUD + فیلتر + Import از اکسل |
| CurrencyResource | مدیریت ارزها | CRUD + بروزرسانی خودکار نرخ |
| UserResource | مدیریت کاربران | CRUD + Role + Wallet link |
| WalletResource | مدیریت کیف پول | View + Freeze/Unfreeze + Activity Log |
| WalletTransactionResource | تراکنش‌ها | View + فیلتر + گزارش |
| PaymentResource | پرداخت‌ها | View + بررسی وضعیت |
| DiscountCodeResource | کدهای تخفیف | CRUD + اعتبارسنجی |
| RoleResource | نقش‌ها | مدیریت Roles + Permissions |
### RelationManagers
| RelationManager | والد | توضیح |
|---------------|------|--------|
| TrackingEventsRelationManager | Shipment | رویدادهای ترکینگ |
|-----------------|------|--------|
| TrackingEventsRelationManager | Shipment | رویدادهای ترکینگ با فیلد source |
| CarrierMappingsRelationManager | Shipment | نگاشت شرکت‌های حمل |
| ItemsRelationManager | Shipment | اقلام گمرکی |
| PackagesRelationManager | Shipment | بسته‌های چندگانه |
| StatusHistoriesRelationManager | Shipment | تاریخچه تغییرات وضعیت |
### صفحات سفارشی
| صفحه | توضیح |
|-------|--------|
| SettingsPage | تنظیمات سیستم (key-value) |
| PriceTestPage | تست محاسبه قیمت |
| ImportRatesPage | آپلود اکسل نرخ‌ها |
| IfnexSettingsPage | تنظیمات سیستم (key-value) — نرخ درهم، VAT، حاشیه سود |
| PriceTestPage | تست محاسبه قیمت با پارامترهای مختلف |
| ImportRatesPage | آپلود اکسل نرخ‌ها + دانلود Template |
| FinancialReport | گزارش مالی (درآمد، تخفیف، کارمزد) |
---
## 🔴 خط قرمزها
## 🔧 Artisan Commands
```bash
# Import / Migration
php artisan ifnex:import-rates # ایمپورت نرخ‌ها از اکسل
php artisan ifnex:import-tracking # ایمپورت داده‌های ترکینگ تاریخی
php artisan ifnex:sync-wp-users # همگام‌سازی کاربران وردپرس با لاراول
# Currency
php artisan ifnex:update-exchange-rates # بروزرسانی نرخ ارز از API
# Token
php artisan ifnex:generate-api-token # تولید API Token برای ادمین
# Standard
php artisan migrate # اجرای migrations
php artisan db:seed # اجرای seeders
php artisan serve # اجرای سرور
php artisan tinker # محیط تعاملی
php artisan route:list # لیست روت‌ها
php artisan config:clear # پاک‌سازی کش کانفیگ
```
---
## 🔴 خط قرمزها (ممنوعیت‌ها)
| ❌ هرگز | ✅ همیشه |
|------------|------------|
| برگرداندن countries به ۲ زون | ۴ زون مجزا |
| برگرداندن countries به ۲ زون | ۴ زون مجزا (Export/Import × Parcel/Doc) |
| استفاده از ۲ نوع سرویس | ۳ نوع (DOC_NORMAL, DOC_ECONOMY, PARCEL) |
| ذخیره تاریخ شمسی در DB | ذخیره timestamp میلادی |
| ذخیره تاریخ شمسی در DB | ذخیره timestamp میلادی + تبدیل در نمایش |
| CORS `*` در Production | CORS محدود به دامنه وردپرس |
| کامیت `.env` در Git | در `.gitignore` باشد |
| `APP_DEBUG=true` در Production | `APP_DEBUG=false` |
| PDF فارسی (AWB/Invoice/Label) | همیشه انگلیسی |
| PDF فارسی (AWB/Invoice/Label) | همیشه انگلیسی (برای حمل بین‌المللی) |
| کپی از DHL | طراحی منحصر به فرد IFNEX |
| استفاده از `wire:click` برای دانلود | استفاده از `<a href>` با روت مستقیم |
| `getFormActions()` در Custom Pages | استفاده از `wire:click` در Blade |
| هدایت AJAX به `redirect()` | استفاده از `payment_url` در response JSON |
| متدهای نوتیفیکیشن بیرون از کلاس | داخل کلاس `IFNEX_User_Bridge` |
---
## 🧪 تست
### Mock Gateway
برای تست پرداخت بدون اتصال به Zarinpal واقعی، `ZARINPAL_MERCHANT_ID=fake-merchant-id-for-testing` را در `.env` تنظیم کنید. سپس:
- پرداخت‌ها از طریق `MockZarinpalService` پردازش می‌شوند
- URL پرداخت: `/api/v1/payment/mock-gateway`
- شبیه‌سازی موفق: `/api/v1/payment/mock-gateway/success`
- شبیه‌سازی ناموفق: `/api/v1/payment/mock-gateway/failure`
### Bridge Token Test
```bash
curl -X POST http://localhost:8000/api/v1/bridge/login \
-H "Content-Type: application/json" \
-d '{
"bridge_api_key": "YOUR_BRIDGE_KEY",
"wp_user_id": 1,
"wp_user_email": "user@example.com",
"wp_user_name": "Test User",
"token_name": "test"
}'
```
---
@ -256,10 +427,21 @@ ZARINPAL_SANDBOX=true
| فایل | محتوا |
|------|-------|
| [IFNEX_Phase0_Checklist.md](../01_Documents/IFNEX_Phase0_Checklist.md) | چک‌لیست کامل |
| [IFNEX_Roadmap.md](../01_Documents/IFNEX_Roadmap.md) | نقشه راه |
| [IFNEX_File_Map.md](../01_Documents/IFNEX_File_Map.md) | نقشه فایل‌ها |
| [DEPLOYMENT.md](../DEPLOYMENT.md) | راهنمای استقرار |
| [IFNEX_Phase0_Checklist.md](../01_Documents/IFNEX_Phase0_Checklist.md) | چک‌لیست کامل فازها |
| [IFNEX_Roadmap.md](../01_Documents/IFNEX_Roadmap.md) | نقشه راه ۴ فازی |
| [IFNEX_File_Map.md](../01_Documents/IFNEX_File_Map.md) | نقشه ۱۰۰+ فایل |
| [DEPLOYMENT.md](../DEPLOYMENT.md) | راهنمای استقرار Production |
---
## 🚀 مراحل بعدی
- [ ] تست‌های واحد (PHPUnit)
- [ ] تست‌های Integration (Laravel Dusk)
- [ ] مستندات API (OpenAPI/Swagger)
- [ ] اتصال به APIهای ترکینگ زنده
- [ ] سیستم نمایندگی (فاز ۴)
- [ ] بهبود گزارش‌های مالی
---
@ -267,4 +449,4 @@ ZARINPAL_SANDBOX=true
&copy; 2026 VernaSoft Group. All Rights Reserved.
</div>
</div>

View File

@ -16,6 +16,7 @@ use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use App\Models\WalletTransaction;
class CustomerOrderController extends Controller
{
@ -371,7 +372,7 @@ class CustomerOrderController extends Controller
*/
public function countries(): JsonResponse
{
$countries = Country::select('id', 'name', 'iso_code', 'export_zone_parcel', 'import_zone_parcel')
$countries = Country::select('id', 'name', 'iso_code', 'calling_code', 'export_zone_parcel', 'import_zone_parcel')
->where('is_active', true)
->orderBy('name')
->get();

View File

@ -17,6 +17,7 @@ class Country extends Model
protected $fillable = [
'name',
'iso_code',
'calling_code',
'export_zone_parcel',
'export_zone_doc',
'import_zone_parcel',

View File

@ -1,5 +1,6 @@
<?php
use App\Http\Middleware\ApiKeyAuth;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
@ -21,7 +22,11 @@ return Application::configure(basePath: dirname(__DIR__))
\App\Console\Commands\SyncWordPressUsers::class,
])
->withMiddleware(function (Middleware $middleware): void {
// ✅ فعال‌سازی Stateful API برای Sanctum
$middleware->alias([
'api_key' => ApiKeyAuth::class,
]);
// Stateful API برای Sanctum
$middleware->statefulApi();
// API با CORS
@ -38,7 +43,7 @@ return Application::configure(basePath: dirname(__DIR__))
]);
})
->withExceptions(function (Exceptions $exceptions): void {
// ✅ مهم: به جای redirect به login، JSON برگردان
// برای APIها، به جای redirect به login، JSON 401 برگردان
$exceptions->render(function (AuthenticationException $e, Request $request) {
if ($request->is('api/*') || $request->expectsJson() || $request->wantsJson()) {
return response()->json([

View File

@ -0,0 +1,24 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('countries', function (Blueprint $table) {
if (!Schema::hasColumn('countries', 'calling_code')) {
$table->string('calling_code', 10)->nullable()->after('iso_code');
}
});
}
public function down(): void
{
Schema::table('countries', function (Blueprint $table) {
$table->dropColumn('calling_code');
});
}
};

View File

@ -0,0 +1,79 @@
<?php
// اسکریپت آپدیت پیش‌شماره تمام کشورهای استاندارد ISO 3166-1
// منبع: ITU-T E.164
$map = [
'AF' => '+93', 'AL' => '+355', 'DZ' => '+213', 'AS' => '+1', 'AD' => '+376',
'AO' => '+244', 'AG' => '+1', 'AR' => '+54', 'AM' => '+374', 'AU' => '+61',
'AT' => '+43', 'AZ' => '+994', 'BS' => '+1', 'BH' => '+973', 'BD' => '+880',
'BB' => '+1', 'BY' => '+375', 'BE' => '+32', 'BZ' => '+501', 'BJ' => '+229',
'BT' => '+975', 'BO' => '+591', 'BA' => '+387', 'BW' => '+267', 'BR' => '+55',
'BN' => '+673', 'BG' => '+359', 'BF' => '+226', 'BI' => '+257', 'KH' => '+855',
'CM' => '+237', 'CA' => '+1', 'CV' => '+238', 'CF' => '+236', 'TD' => '+235',
'CL' => '+56', 'CN' => '+86', 'CO' => '+57', 'KM' => '+269', 'CG' => '+242',
'CD' => '+243', 'CR' => '+506', 'CI' => '+225', 'HR' => '+385', 'CU' => '+53',
'CY' => '+357', 'CZ' => '+420', 'DK' => '+45', 'DJ' => '+253', 'DM' => '+1',
'DO' => '+1', 'EC' => '+593', 'EG' => '+20', 'SV' => '+503', 'GQ' => '+240',
'ER' => '+291', 'EE' => '+372', 'ET' => '+251', 'FJ' => '+679', 'FI' => '+358',
'FR' => '+33', 'GA' => '+241', 'GM' => '+220', 'GE' => '+995', 'DE' => '+49',
'GH' => '+233', 'GR' => '+30', 'GD' => '+1', 'GT' => '+502', 'GN' => '+224',
'GW' => '+245', 'GY' => '+592', 'HT' => '+509', 'HN' => '+504', 'HK' => '+852',
'HU' => '+36', 'IS' => '+354', 'IN' => '+91', 'ID' => '+62', 'IR' => '+98',
'IQ' => '+964', 'IE' => '+353', 'IL' => '+972', 'IT' => '+39', 'JM' => '+1',
'JP' => '+81', 'JO' => '+962', 'KZ' => '+7', 'KE' => '+254', 'KI' => '+686',
'KP' => '+850', 'KR' => '+82', 'KW' => '+965', 'KG' => '+996', 'LA' => '+856',
'LV' => '+371', 'LB' => '+961', 'LS' => '+266', 'LR' => '+231', 'LY' => '+218',
'LI' => '+423', 'LT' => '+370', 'LU' => '+352', 'MO' => '+853', 'MK' => '+389',
'MG' => '+261', 'MW' => '+265', 'MY' => '+60', 'MV' => '+960', 'ML' => '+223',
'MT' => '+356', 'MH' => '+692', 'MR' => '+222', 'MU' => '+230', 'MX' => '+52',
'FM' => '+691', 'MD' => '+373', 'MC' => '+377', 'MN' => '+976', 'ME' => '+382',
'MA' => '+212', 'MZ' => '+258', 'MM' => '+95', 'NA' => '+264', 'NR' => '+674',
'NP' => '+977', 'NL' => '+31', 'NZ' => '+64', 'NI' => '+505', 'NE' => '+227',
'NG' => '+234', 'NO' => '+47', 'OM' => '+968', 'PK' => '+92', 'PW' => '+680',
'PA' => '+507', 'PG' => '+675', 'PY' => '+595', 'PE' => '+51', 'PH' => '+63',
'PL' => '+48', 'PT' => '+351', 'QA' => '+974', 'RO' => '+40', 'RU' => '+7',
'RW' => '+250', 'KN' => '+1', 'LC' => '+1', 'VC' => '+1', 'WS' => '+685',
'SM' => '+378', 'ST' => '+239', 'SA' => '+966', 'SN' => '+221', 'RS' => '+381',
'SC' => '+248', 'SL' => '+232', 'SG' => '+65', 'SK' => '+421', 'SI' => '+386',
'SB' => '+677', 'SO' => '+252', 'ZA' => '+27', 'SS' => '+211', 'ES' => '+34',
'LK' => '+94', 'SD' => '+249', 'SR' => '+597', 'SZ' => '+268', 'SE' => '+46',
'CH' => '+41', 'SY' => '+963', 'TW' => '+886', 'TJ' => '+992', 'TZ' => '+255',
'TH' => '+66', 'TL' => '+670', 'TG' => '+228', 'TO' => '+676', 'TT' => '+1',
'TN' => '+216', 'TR' => '+90', 'TM' => '+993', 'TV' => '+688', 'UG' => '+256',
'UA' => '+380', 'AE' => '+971', 'GB' => '+44', 'US' => '+1', 'UY' => '+598',
'UZ' => '+998', 'VU' => '+678', 'VA' => '+379', 'VE' => '+58', 'VN' => '+84',
'YE' => '+967', 'ZM' => '+260', 'ZW' => '+263', 'PS' => '+970',
];
require __DIR__ . '/vendor/autoload.php';
$app = require_once __DIR__ . '/bootstrap/app.php';
$app->make('Illuminate\Contracts\Console\Kernel')->bootstrap();
use App\Models\Country;
$updated = 0;
$skipped = 0;
foreach ($map as $iso => $code) {
$cnt = Country::where('iso_code', $iso)->update(['calling_code' => $code]);
if ($cnt > 0) {
$updated += $cnt;
} else {
$skipped++;
}
}
echo "\n=== Done ===\n";
echo "Total in map: " . count($map) . "\n";
echo "Updated: {$updated}\n";
echo "Skipped (country not in DB): {$skipped}\n";
echo "Countries with calling_code in DB: " . Country::whereNotNull('calling_code')->count() . "\n";
echo "Total countries in DB: " . Country::count() . "\n";
// لیست کشورهایی که calling_code ندارن
$missing = Country::whereNull('calling_code')->pluck('name', 'iso_code')->toArray();
if (!empty($missing)) {
echo "\nCountries still missing calling_code:\n";
foreach ($missing as $iso => $name) {
echo " - {$iso}: {$name}\n";
}
}

View File

@ -0,0 +1,28 @@
<?php
$map = [
'IR' => '+98', 'AE' => '+971', 'AF' => '+93', 'AL' => '+355', 'AM' => '+374',
'AZ' => '+994', 'BH' => '+973', 'BY' => '+375', 'CN' => '+86', 'DE' => '+49',
'FR' => '+33', 'GB' => '+44', 'IN' => '+91', 'IQ' => '+964', 'KG' => '+996',
'KW' => '+965', 'KZ' => '+7', 'LB' => '+961', 'NL' => '+31', 'OM' => '+968',
'PK' => '+92', 'PS' => '+970', 'QA' => '+974', 'RU' => '+7', 'SA' => '+966',
'SY' => '+963', 'TJ' => '+992', 'TR' => '+90', 'TM' => '+993', 'UA' => '+380',
'US' => '+1', 'UZ' => '+998',
];
require __DIR__ . '/vendor/autoload.php';
$app = require_once __DIR__ . '/bootstrap/app.php';
$app->make('Illuminate\Contracts\Console\Kernel')->bootstrap();
use App\Models\Country;
$updated = 0;
foreach ($map as $iso => $code) {
$cnt = Country::where('iso_code', $iso)->update(['calling_code' => $code]);
if ($cnt > 0) {
$updated += $cnt;
echo "Updated {$iso} => {$code}\n";
}
}
echo "\nDone. Total updated: {$updated}\n";
echo "Countries with calling_code: " . Country::whereNotNull('calling_code')->count() . "\n";

View File

@ -25,25 +25,6 @@
## ۱. استقرار لاراول
### بکاپ دیتابیس
\```bash
# بکاپ کامل دیتابیس
mysqldump -u root -p ifnex_db > /backups/ifnex_$(date +%Y%m%d_%H%M%S).sql
# یا فشرده
mysqldump -u root -p ifnex_db | gzip > /backups/ifnex_$(date +%Y%m%d_%H%M%S).sql.gz
\```
### بکاپ فایل‌ها
\```bash
# بکاپ فایل‌های لاراول
tar -czf /backups/laravel_$(date +%Y%m%d_%H%M%S).tar.gz /home/USER/web/api.ifnex.vernahost.ir/public_html
# بکاپ فایل‌های وردپرس
tar -czf /backups/wordpress_$(date +%Y%m%d_%H%M%S).tar.gz /home/USER/web/ifnex.vernahost.ir/public_html
\```
### ۱.۱ کلون مخزن روی سرور
```bash
@ -55,17 +36,16 @@ git clone https://www.git.vernahost.ir/gitmodir110/ifnex.git .
```bash
cd /home/USER/web/api.ifnex.vernahost.ir/public_html
git clone https://www.git.vernahost.ir/gitmodir110/ifnex.git tmp-ifnex
ngit clone https://www.git.vernahost.ir/gitmodir110/ifnex.git tmp-ifnex
cp -r tmp-ifnex/04_Laravel/* .
# فقط فایل‌های مخفی خاص را کپی کنید
cp -r tmp-ifnex/04_Laravel/.env.example .
cp -r tmp-ifnex/04_Laravel/.gitignore .
# یا بهتر است فایل‌ها را دستی کپی کنید ...
cp -r tmp-ifnex/04_Laravel/.* . 2>/dev/null
rm -rf tmp-ifnex
```
### ۱.۲ نصب وابستگی‌ها
```bash
cd /home/USER/web/api.ifnex.vernahost.ir/public_html
ncd /home/USER/web/api.ifnex.vernahost.ir/public_html
composer install --no-dev --optimize-autoloader
```
@ -88,10 +68,17 @@ DB_DATABASE=ifnex_db
DB_USERNAME=ifnex_user
DB_PASSWORD=STRONG_PASSWORD
# مهم: Bridge API Key باید با وردپرس یکسان باشد
IFNEX_BRIDGE_API_KEY=ifnex-bridge-secret-key-2026-vernasoft
CORS_ALLOWED_ORIGINS=https://ifnex.vernahost.ir
# Payment Gateway
ZARINPAL_SANDBOX=false
ZARINPAL_MERCHANT_ID=YOUR_MERCHANT_ID
ZARINPAL_MERCHANT_ID=YOUR_REAL_MERCHANT_ID
ZARINPAL_CALLBACK_URL=https://api.ifnex.vernahost.ir/api/v1/payment/callback
ZARINPAL_FRONTEND_SUCCESS_URL=https://ifnex.vernahost.ir/wallet
ZARINPAL_FRONTEND_FAILURE_URL=https://ifnex.vernahost.ir/wallet
```
### ۱.۴ دیتابیس
@ -139,7 +126,24 @@ cd /home/USER/web/ifnex.vernahost.ir/public_html/wp-content/plugins/
| تنظیم | مقدار |
|--------|-------|
| API URL | `https://api.ifnex.vernahost.ir/api/v1` |
| Bridge API Key | کلید مشترک بین وردپرس و لاراول |
| API Key (عمومی) | برای APIهای عمومی مثل Tracking (همان `IFNEX_API_KEY` در `.env` لاراول) |
| Bridge API Key | کلید مشترک بین وردپرس و لاراول (همان `IFNEX_BRIDGE_API_KEY` در `.env` لاراول) |
> ⚠️ **مهم:** Bridge API Key باید در لاراول (`.env`) و وردپرس (پنل تنظیمات پلاگین) دقیقاً یکسان باشد. در غیر این صورت، کاربران وردپرس نمی‌توانند توکن Sanctum دریافت کنند.
### ۲.۳ صفحات مورد نیاز در وردپرس
این صفحات باید با شورت‌کدهای مشخص ساخته شوند:
| آدرس | شورت‌کد | توضیح |
|------|---------|--------|
| `/my-account/` | `[ifnex_customer_dashboard]` | پنل جامع مشتری |
| `/my-orders/` | `[ifnex_orders_list]` | لیست سفارشات |
| `/new-order/` | `[ifnex_order_form]` | فرم ثبت سفارش |
| `/order-detail/` | `[ifnex_order_detail]` | جزئیات سفارش |
| `/order-payment/` | `[ifnex_order_payment]` | صفحه پرداخت |
| `/wallet/` | `[ifnex_wallet_charge]` | شارژ کیف پول |
| `/tracking/` | `[ifnex_tracking_form]` | رهگیری مرسوله |
---
@ -239,101 +243,11 @@ php artisan route:list --path=api/v1
- `APP_DEBUG=false` در Production
- `CORS_ALLOWED_ORIGINS` فقط دامنه وردپرس
- `IFNEX_API_KEY` یک کلید قوی و تصادفی باشد
- `IFNEX_BRIDGE_API_KEY` یک کلید قوی و تصادفی باشد (متفاوت از API Key)
- SSL/HTTPS فعال باشد
- رمز عبور دیتابیس قوی باشد
- دسترسی پوشه `storage` و `bootstrap/cache` قابل نوشتن باشد (۷۷۵)
- درگاه Zarinpal در Production باید `SANDBOX=false` و `MERCHANT_ID` واقعی باشد
---
&copy; 2026 VernaSoft Group. Internal use only.
## ۷. تنظیمات Web Server
### Nginx (لاراول)
\```nginx
server {
listen 80;
server_name api.ifnex.vernahost.ir;
root /home/USER/web/api.ifnex.vernahost.ir/public_html/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
\```
### Apache (لاراول)
\```apache
<VirtualHost *:80>
ServerName api.ifnex.vernahost.ir
DocumentRoot /home/USER/web/api.ifnex.vernahost.ir/public_html/public
<Directory /home/USER/web/api.ifnex.vernahost.ir/public_html/public>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/api.ifnex.error.log
CustomLog ${APACHE_LOG_DIR}/api.ifnex.access.log combined
</VirtualHost>
\```
## ۸. Queue Worker (اگر استفاده می‌شود)
### Supervisor Setup
\```bash
sudo nano /etc/supervisor/conf.d/ifnex-worker.conf
\```
\```ini
[program:ifnex-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /home/USER/web/api.ifnex.vernahost.ir/public_html/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=USER
numprocs=2
redirect_stderr=true
stdout_logfile=/home/USER/web/api.ifnex.vernahost.ir/public_html/storage/logs/worker.log
\```
\```bash
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start all
\```
## ۹. SSL/TLS (HTTPS)
### Let's Encrypt (رایگان)
\```bash
# نصب Certbot
sudo apt install certbot python3-certbot-nginx
# دریافت SSL certificate
sudo certbot --nginx -d api.ifnex.vernahost.ir -d ifnex.vernahost.ir
# تمدید خودکار (تست)
sudo certbot renew --dry-run
\```

275
README.md
View File

@ -5,12 +5,12 @@
### پلتفرم جامع مدیریت لجستیک بین‌المللی
[![Laravel](https://img.shields.io/badge/Laravel-11.x-FF2D20?logo=laravel&logoColor=white)](https://laravel.com)
[![PHP](https://img.shields.io/badge/PHP-8.2+-777BB4?logo=php&logoColor=white)](https://php.net)
[![PHP](https://img.shields.io/badge/PHP-8.2+-777BB4?logo=php&logoColor=white)](https://filamentphp.com)
[![Filament](https://img.shields.io/badge/Filament-3.3-EDB200?logo=laravel&logoColor=white)](https://filamentphp.com)
[![WordPress](https://img.shields.io/badge/WordPress-7.0.3-21759B?logo=wordpress&logoColor=white)](https://wordpress.org)
[![MySQL](https://img.shields.io/badge/MySQL-8+-4479A1?logo=mysql&logoColor=white)](https://mysql.com)
[![Status](https://img.shields.io/badge/Status-Phase_3_Done-brightgreen.svg)]()
[![License](https://img.shields.io/badge/License-Proprietary-blue.svg)]()
[![Status](https://img.shields.io/badge/Status-Phase_3-95%25-success.svg)]()
---
@ -24,49 +24,65 @@
## 📖 درباره پروژه
**IFNEX** یک راه‌حل جامع برای شرکت‌های حمل‌ونقل بین‌المللی است که فرآیندهای مبتنی بر فایل‌های اکسل را با یک سیستم Headless مدرن جایگزین می‌کند.
**IFNEX** یک راه‌حل جامع برای شرکت‌های حمل‌ونقل بین‌المللی است که فرآیندهای مبتنی بر فایل‌های اکسل را با یک سیستم Headless مدرن جایگزین می‌کند. این سیستم با تکیه بر Laravel 11 به عنوان بک‌اند، WordPress به عنوان فرانت‌اند و Filament 3.3 به عنوان پنل مدیریت، تجربه‌ای یکپارچه برای اپراتورها و مشتریان فراهم می‌آورد.
این سیستم شامل:
### ✨ امکانات کلیدی
- **موتور قیمت‌گذاری هوشمند** با ۴ زون و ۳ نوع سرویس
- **ثبت سفارش آنلاین** چند بسته‌ای با محاسبه لحظه‌ای قیمت
- **تولید خودکار اسناد** (AWB, Invoice, Label) با بارکد استاندارد
- **پورتال مشتری کامل** با کیف پول، پرداخت آنلاین و اعلان‌ها
- **پنل مدیریت قدرتمند** با Filament 3.3
- **سیستم ترکینگ** با قابلیت مهاجرت داده‌های تاریخی
- **موتور قیمت‌گذاری هوشمند** با ۴ زون مجزا (Export/Import × Parcel/Doc) و ۳ نوع سرویس (DOC_NORMAL, DOC_ECONOMY, PARCEL)
- **ثبت سفارش آنلاین چندمرحله‌ای** با Wizard و محاسبه لحظه‌ای قیمت
- **تولید خودکار اسناد** (AWB, Invoice, Label) با بارکد استاندارد و پشتیبانی کامل از زبان انگلیسی
- **پورتال مشتری کامل** با کیف پول، پرداخت آنلاین، تراکنش‌ها، اعلان‌ها و رهگیری مرسوله
- **پنل مدیریت قدرتمند** با Filament 3.3 شامل مدیریت مرسوله‌ها، نرخ‌ها، ارزها، کاربران و گزارش‌ها
- **سیستم ترکینگ** با قابلیت مهاجرت داده‌های تاریخی و تایم‌لاین کامل
- **اعلان‌های دیتابیس** برای ادمین و مشتری با badge unread در سایدبار
- **پشتیبانی از ۱۹۲ کشور** با پیش‌شماره تلفن استاندارد ISO 3166-1
- **اعتبارسنجی فرم‌ها** شامل تشخیص خودکار زبان فارسی/انگلیسی و پیش‌شماره خودکار
---
## 🏗️ معماری سیستم
این پروژه بر اساس الگوی **Headless** طراحی شده است — فرانت‌اند (WordPress) و بک‌اند (Laravel) کاملاً جدا و فقط از طریق REST API با هم ارتباط دارند.
این پروژه بر اساس الگوی **Headless** طراحی شده است — فرانت‌اند (WordPress) و بک‌اند (Laravel) کاملاً جدا و فقط از طریق REST API با هم ارتباط دارند. این جداسازی به ما امکان می‌دهد تا هر لایه را مستقل مقیاس‌پذیر کنیم، امنیت را بهبود ببخشیم و در آینده فرانت‌اند‌های دیگری (مثل اپلیکیشن موبایل) را اضافه کنیم.
```
┌──────────────────────────────────────────────────────┐
│ CLIENT BROWSER │
│ CLIENT BROWSER
└────────────────────────┬─────────────────────────────┘
┌────────────────────────▼─────────────────────────────┐
│ WORDPRESS (Frontend) │
│ ┌───────────────────┐ ┌──────────────────────────┐
│ │ IFNEX Theme │ │ IFNEX Bridge Plugin
│ │ (Landing Page) │ │ (REST Client) │
│ └───────────────────┘ └────────────┬─────────────┘
└───────────────────────────────────┼───────────────────┘
│ REST API (Sanctum)
┌───────────────────────────────────▼───────────────────┐
│ WORDPRESS (Frontend)
│ ┌───────────────────┐ ┌──────────────────────────┐ │
│ │ IFNEX Theme │ │ IFNEX Bridge Plugin
│ │ (Landing Page) │ │ (REST Client + AJAX) │
│ └───────────────────┘ └────────────┬─────────────┘ │
└─────────────────────────────────────────────────────┘
│ REST API (Sanctum + Bridge Key)
┌─────────────────────────────────────────────────────┐
│ LARAVEL 11 (Backend) │
│ ┌─────────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ Filament │ │ API │ │ Services │ │
│ │ Admin Panel │ │ Controllers │ │ (Pricing, │ │
│ │ │ │ │ │ PDF, │ │
│ └─────────────────┘ └──────┬───────┘ │ Payment) │ │
└──────────────────────────┼────┼─────────┘────────────┘ │
│ │ │
┌──────▼────▼────┐ │
│ MySQL 8 │ │
│ Database │ │
└───────────────┘ │
│ ┌─────────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ Filament │ │ API │ │ Services │ │
│ │ Admin Panel │ │ Controllers │ │ (Pricing,│ │
│ │ (/panel) │ │ (/api/v1) │ │ PDF, │ │
│ └─────────────────┘ └──────┬───────┘ │ Payment)│ │
└──────────────────────────────┼──────────┴──────────┘
┌────────────▼────────────┐
│ MySQL 8 Database │
│ (23 migrations, 13 │
│ models) │
└─────────────────────────┘
```
### جریان احراز هویت
```
┌──────────┐ ┌─────────────────┐ ┌──────────────┐
│ WordPress│ ──POST /bridge/login──> │ Laravel Bridge │ ──> │ Sanctum Token│
│ Plugin │ <──token + user info── │ Controller │ <── │ (30 days) │
└──────────┘ └─────────────────┘ └──────────────┘
│ │
│ ──────── All subsequent API calls with Bearer Token ──────────────>│
└─────────────────────────────────────────────────────────────────────────┘
```
---
@ -75,82 +91,108 @@
```
IFNEX-Logistics/
├── 📄 01_Documents/ # مستندات فنی پروژه
│ ├── IFNEX_File_Map.md # نقشه کامل فایلها
│ ├── IFNEX_Roadmap.md # نقشه راه پروژه
│ ├── IFNEX_Phase0_Checklist.md
├── 📄 01_Documents/ # مستندات فنی پروژه
│ ├── IFNEX_File_Map.md # نقشه کامل ۱۰۰+ فایل
│ ├── IFNEX_Roadmap.md # نقشه راه ۴ فازی
│ ├── IFNEX_Phase0_Checklist.md # چک‌لیست تکمیل فازها
│ ├── IFNEX_DEPRECATED_FILES_NOTICE.md
│ └── EXCEL_ANALYSIS.md # تحلیل داده‌های تاریخی
│ └── EXCEL_ANALYSIS.md # تحلیل داده‌های تاریخی
├── 🌐 03_WordPress/ # فرانت‌اند (WordPress)
├── 🌐 03_WordPress/ # فرانت‌اند (WordPress 7.0.3)
│ └── wp-content/
│ ├── themes/ifnex/ # قالب سفارشی IFNEX
│ ├── themes/ifnex/ # قالب سفارشی IFNEX
│ └── plugins/
│ └── ifnex-bridge/ # پلاگین ارتباط با لاراول
│ └── ifnex-bridge/ # پلاگین ارتباط با لاراول
│ ├── includes/
│ │ ├── api-client.php # مستقیم به API
│ │ ├── user-bridge.php # Bridge Auth + AJAX handlers
│ │ ├── shortcodes.php # ۱۰+ شورت‌کد
│ │ └── tracking-form.php # فرم رهگیری
│ └── assets/
│ ├── css/ifnex-orders.css # استایل دشبورد
│ └── js/ifnex-order-form.js # فرم چندمرحله‌ای
├── ⚙️ 04_Laravel/ # بک‌اند (Laravel 11)
├── ⚙️ 04_Laravel/ # بک‌اند (Laravel 11)
│ ├── app/
│ │ ├── Filament/ # پنل مدیریت
│ │ ├── Http/Controllers/ # کنترلرها (API, Web)
│ │ ├── Models/ # مدل‌های Eloquent
│ │ ├── Services/ # لایه سرویس
│ │ └── Imports/ # Excel imports
│ ├── database/migrations/ # ۱۵+ migration
│ │ ├── Enums/ # ShipmentStatus, Direction, Type
│ │ ├── Filament/
│ │ │ ├── Resources/ # 10+ Resource (Shipment, Country, ...)
│ │ │ ├── Pages/ # Settings, ImportRates, PriceTest, Reports
│ │ │ └── Widgets/ # Dashboard Widgets
│ │ ├── Http/
│ │ │ ├── Controllers/Api/ # Track, Pricing, Auth, Bridge, Customer, Wallet, Payment
│ │ │ └── Middleware/ # ApiKeyMiddleware
│ │ ├── Models/ # 13 مدل Eloquent
│ │ ├── Notifications/ # Database notifications
│ │ ├── Services/ # PriceCalculator, Pdf, Tracking, OrderPayment
│ │ └── Imports/ # Excel imports (OldShipments, ShippingRates)
│ ├── database/
│ │ ├── migrations/ # 23 migrations
│ │ └── seeders/ # Countries, SystemSettings, DatabaseSeeder
│ ├── resources/views/
│ │ ├── pdfs/ # قالب‌های PDF
│ │ └── filament/ # Blade views
│ │ ├── pdfs/ # awb, invoice, label
│ │ └── filament/ # Blade views
│ └── routes/
│ ├── api.php # REST API endpoints
│ └── web.php # Public routes
│ ├── api.php # 30+ REST API endpoints
│ └── web.php # Web routes + Download Template
├── 📋 README.md # این فایل
├── 📋 DEPLOYMENT.md # راهنمای استقرار
└── 📋 04_Laravel/README.md # راهنمای بک‌اند
├── 📋 README.md # این فایل
├── 📋 DEPLOYMENT.md # راهنمای استقرار Production
└── 📋 04_Laravel/README.md # راهنمای بک‌اند
```
---
## ✨ ویژگی‌های کلیدی
## ✨ ویژگی‌های کلیدی بر اساس فازها
### فاز ۰ — بنیان سیستم ✅
### فاز ۰ — بنیان سیستم ✅ (تکمیل شده - 100%)
- اسکیمای دیتابیس مدرن با ۴ زون مجزا (Export/Import × Parcel/Doc)
- مهاجرت ۳۹۵۰ رکورد تاریخی از اکسل به دیتابیس
- پنل مدیریت Filament با UX تخصصی اپراتور
- API ترکینگ با امنیت API Key + Rate Limiting
- پلاگین WordPress Bridge برای ارتباط با فرانت‌اند
### فاز ۱ — پورتال مشتری ✅
- احراز هویت Laravel Sanctum + Bridge Auth
- فرم ثبت سفارش چندمرحله‌ای با Wizard
- داشبورد جامع مشتری (سفارشات، کیف پول، تراکنش‌ها، اعلان‌ها، رهگیری، پروفایل)
- سیستم ترکینگ با تایم‌لاین
### فاز ۱ — پورتال مشتری ✅ (تکمیل شده - 100%)
- احراز هویت Laravel Sanctum + Bridge Auth (بدون نیاز به رمز عبور)
- فرم ثبت سفارش چندمرحله‌ای با Wizard (مسیر، اطلاعات تماس، تخفیف، تأیید)
- داشبورد جامع مشتری شامل: داشبورد، سفارشات، ثبت سفارش، کیف پول، تراکنش‌ها، رهگیری، اعلان‌ها، پروفایل
- سیستم ترکینگ با تایم‌لاین و تاریخچه کامل
### فاز ۲ — مالی و کیف پول ✅
- سیستم کیف پول کامل با تراکنش‌ها
- درگاه پرداخت + Mock Gateway برای تست
- مدیریت ارزهای چندگانه (IRR, AED, USD, EUR)
### فاز ۲ — مالی و کیف پول ✅ (تکمیل شده - 100%)
- سیستم کیف پول کامل با تراکنش‌ها و تاریخچه
- درگاه پرداخت Zarinpal + Mock Gateway برای تست لوکال
- مدیریت ارزهای چندگانه (IRR, AED, USD, EUR, CNY)
- کدهای تخفیف با اعتبارسنجی و محدودیت مصرف
- تولید PDF حرفه‌ای (AWB, Invoice, Label) با بارکد
### فاز ۳ — بهبود و یکپارچه‌سازی 🔄
- ایمپورت/اکسپورت نرخ‌ها با دانلود Template
- سیستم اعلان‌های دیتابیس (ادمین + مشتری)
- تولید PDF حرفه‌ای (AWB, Invoice, Label) با بارکد استاندارد
- پشتیبانی از چند بسته در یک سفارش (Multi-Package)
- تاریخچه تغییرات وضعیت
- استایل مدرن تراکنش‌ها و هشدار آدرس انگلیسی
- تاریخچه تغییرات وضعیت (ShipmentStatusHistory)
### فاز ۳ — بهبود و یکپارچه‌سازی ✅ (تکمیل شده - 100%)
- ایمپورت/اکسپورت نرخ‌ها با دانلود Template اکسل
- سیستم اعلان‌های دیتابیس (ادمین + مشتری) با badge unread در سایدبار
- هشدار هوشمند زبان فارسی/انگلیسی در فرم سفارش (نام، شهر، آدرس)
- پیش‌شماره تلفن خودکار بر اساس کشور انتخابی (۱۹۲ کشور)
- اعتبارسنجی تلفن (فقط اعداد، +، فاصله)
- استایل مدرن تراکنش‌ها و دشبورد مشتری
- صفحه شارژ کیف پول با مبالغ آماده
- اصلاح متدهای Enum (isPaid, canBeCancelledByCustomer)
- میدلور API برای بازگرداندن JSON 401 به جای redirect
### فاز ۴ (آینده) — سیستم نمایندگی 📋
- مدل نمایندگی کامل با کمیسیون و پنل جداگانه
- طرح مطالعاتی آماده — منتظر تأیید کارفرما
---
## ⚡ شروع سریع
### پیش‌نیازها
- PHP 8.2+
- PHP 8.2+ با extensions: pdo_mysql, mbstring, xml, gd, zip
- Composer 2.x
- MySQL 8+
- WordPress 7.0+
### نصب
### نصب بک‌اند (Laravel)
```bash
# ۱. کلون مخزن
@ -166,15 +208,44 @@ php artisan key:generate
# ۴. ایجاد دیتابیس
mysql -u root -p -e "CREATE DATABASE ifnex_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# ۵. ویرایش .env و تنظیم DB_DATABASE, DB_USERNAME, DB_PASSWORD
# ۵. ویرایش .env — مقادیر کلیدی:
# DB_DATABASE=ifnex_db
# DB_USERNAME=root
# DB_PASSWORD=your_password
# IFNEX_BRIDGE_API_KEY=your-secret-bridge-key # مهم برای وردپرس
# ZARINPAL_MERCHANT_ID=fake-merchant-id-for-testing # برای Mock Mode
# ZARINPAL_SANDBOX=true
# CORS_ALLOWED_ORIGINS=http://localhost,http://127.0.0.1
# ۶. اجرای migrations و seeders
php artisan migrate --force
php artisan db:seed --force
# ۷. اجرای سرور
php artisan serve
# پنل ادمین: http://127.0.0.1:8000/panel
```
> 📖 برای راهنمای کامل استقرار، [DEPLOYMENT.md](DEPLOYMENT.md) را ببینید.
### نصب فرانت‌اند (WordPress)
```bash
# ۱. کپی پوشه 03_WordPress به htdocs (XAMPP) یا public_html (سرور)
# ۲. ساخت دیتابیس وردپرس و اجرای نصب
# ۳. فعال‌سازی قالب IFNEX و پلاگین IFNEX Bridge
# ۴. تنظیمات پلاگین (admin → منوی IFNEX):
# - API URL: http://localhost:8000/api/v1
# - API Key (عمومی): همان مقدار IFNEX_API_KEY در .env لاراول
# - Bridge API Key: همان مقدار IFNEX_BRIDGE_API_KEY در .env لاراول
# ۵. ساخت صفحات با شورت‌کدها:
# - /my-account/ → [ifnex_customer_dashboard]
# - /my-orders/ → [ifnex_orders_list]
# - /new-order/ → [ifnex_order_form]
# - /wallet/ → [ifnex_wallet_charge]
# - /tracking/ → [ifnex_tracking_form]
```
> 📖 برای راهنمای کامل استقرار روی سرور، [DEPLOYMENT.md](DEPLOYMENT.md) را ببینید.
---
@ -192,6 +263,7 @@ php artisan serve
| Laravel Excel | Latest | Import/Export |
| Sanctum | Latest | API Authentication |
| Morilog Jalali | 3.x | تاریخ شمسی |
| picqer/php-barcode | Latest | تولید بارکد |
### Frontend (WordPress)
@ -199,7 +271,34 @@ php artisan serve
|-----------|------|--------|
| WordPress | 7.0.3 | CMS |
| IFNEX Theme | Custom | قالب سفارشی |
| IFNEX Bridge | 1.6.0 | پلاگین ارتباطی |
| IFNEX Bridge | 1.7.0 | پلاگین ارتباطی |
| jQuery | 3.x | AJAX + DOM |
### زیرساخت
| سرویس | کاربرد |
|-------|--------|
| HestiaCP | پنل مدیریت سرور |
| Nginx | وب‌سرور |
| PHP-FPM | پردازش PHP |
| MySQL 8 | دیتابیس |
| Let's Encrypt | SSL رایگان |
| Git (Gitea) | مخزن کد |
---
## 📊 آمار پروژه
| معیار | مقدار |
|-------|-------|
| تعداد مایگریشن‌ها | ۲۳ |
| تعداد API endpoints | ۳۰+ |
| تعداد Filament Resources | ۱۰+ |
| تعداد مدل‌ها | ۱۳ |
| تعداد شورت‌کدهای وردپرس | ۱۰+ |
| تعداد کشورها با پیش‌شماره | ۱۹۲ |
| رکوردهای تاریخی مهاجرت‌شده | ۳۹۵۰ |
| خطوط کد (تخمینی) | ۱۵۰۰۰+ |
---
@ -209,21 +308,25 @@ php artisan serve
| فایل | محتوا | اولویت |
|------|-------|--------|
| IFNEX_Phase0_Checklist.md | چک‌لیست کامل فازها | ⭐⭐⭐ |
| IFNEX_Roadmap.md | نقشه راه آینده | ⭐⭐⭐ |
| IFNEX_File_Map.md | نقشه ۱۰۰+ فایل پروژه | ⭐⭐⭐ |
| DEPLOYMENT.md | راهنمای استقرار Production | ⭐⭐ |
| EXCEL_ANALYSIS.md | تحلیل داده‌های اکسل | ⭐⭐ |
| [IFNEX_Phase0_Checklist.md](01_Documents/IFNEX_Phase0_Checklist.md) | چک‌لیست کامل فازها | ⭐⭐⭐ |
| [IFNEX_Roadmap.md](01_Documents/IFNEX_Roadmap.md) | نقشه راه آینده | ⭐⭐⭐ |
| [IFNEX_File_Map.md](01_Documents/IFNEX_File_Map.md) | نقشه ۱۰۰+ فایل پروژه | ⭐⭐⭐ |
| [DEPLOYMENT.md](DEPLOYMENT.md) | راهنمای استقرار Production | ⭐⭐ |
| [04_Laravel/README.md](04_Laravel/README.md) | راهنمای بک‌اند | ⭐⭐ |
| [IFNEX_DEPRECATED_FILES_NOTICE.md](01_Documents/IFNEX_DEPRECATED_FILES_NOTICE.md) | فایل‌های منسوخ | ⭐ |
---
## 🚀 مراحل بعدی
## 🚀 مراحل بعدی (فاز ۴)
- اتصال به API های ترکینگ زنده (TrackingMore/17track)
- پلاگین SMS برای اطلاع‌رسانی
- سیستم نمایندگی کامل با کمیسیون و پنل جداگانه
- اتصال به APIهای ترکینگ زنده (TrackingMore/17track)
- پلاگین SMS برای اطلاع‌رسانی خودکار
- مستندات API (OpenAPI/Swagger)
- راهنمای اپراتور (Operator Manual)
- تست‌های واحد و Integration
- اپلیکیشن موبایل (احتمالی)
- ویرایش پروفایل مشتری + استان/شهر بر اساس کشور
---
@ -232,7 +335,8 @@ php artisan serve
- **توسعه‌دهنده:** Kazem Alghasi
- **شرکت:** VernaSoft Group
- **ایمیل:** kazem@vernasoft.group
- **مخزن:** git.vernahost.ir/gitmodir110/ifnex
- **مخزن:** [git.vernahost.ir/gitmodir110/ifnex](https://git.vernahost.ir/gitmodir110/ifnex)
- **سرور تست:** [api.ifnex.vernahost.ir](https://api.ifnex.vernahost.ir) (Laravel) | [ifnex.vernahost.ir](https://ifnex.vernahost.ir) (WordPress)
---
@ -241,6 +345,7 @@ php artisan serve
📜 لایسنس
&copy; 2026 VernaSoft Group. تمام حقوق محفوظ است.
این پروژه اختصاصی شرکت IFNEX است و کپی یا استفاده غیرمجاز ممنوع می‌باشد.
</div>