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.
653 lines
25 KiB
PHP
653 lines
25 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\Customer;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Shipment;
|
|
use App\Models\ShipmentItem;
|
|
use App\Models\Country;
|
|
use App\Enums\ShipmentStatus;
|
|
use App\Enums\ShipmentDirection;
|
|
use App\Enums\ShipmentType;
|
|
use App\Services\PriceCalculatorService;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
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
|
|
{
|
|
public function __construct(
|
|
protected PriceCalculatorService $calculator
|
|
) {}
|
|
|
|
/**
|
|
* لیست سفارشات کاربر
|
|
* GET /api/v1/customer/orders
|
|
*/
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'per_page' => ['nullable', 'integer', 'min:1', 'max:100'],
|
|
'status' => ['nullable', 'string'],
|
|
'page' => ['nullable', 'integer', 'min:1'],
|
|
]);
|
|
|
|
$user = Auth::user();
|
|
$query = Shipment::query()
|
|
->where('user_id', $user->id)
|
|
->with(['fromCountry', 'toCountry', 'items'])
|
|
->orderBy('created_at', 'desc');
|
|
|
|
if (!empty($validated['status'])) {
|
|
$query->where('status', $validated['status']);
|
|
}
|
|
|
|
$shipments = $query->paginate($validated['per_page'] ?? 15);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => $shipments->map(function ($shipment) {
|
|
return $this->formatShipment($shipment);
|
|
}),
|
|
'pagination' => [
|
|
'current_page' => $shipments->currentPage(),
|
|
'last_page' => $shipments->lastPage(),
|
|
'per_page' => $shipments->perPage(),
|
|
'total' => $shipments->total(),
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* ثبت سفارش جدید
|
|
* POST /api/v1/customer/orders
|
|
*/
|
|
public function store(Request $request): JsonResponse
|
|
{
|
|
try {
|
|
$validated = $request->validate([
|
|
// مسیر و نوع
|
|
'direction' => ['required', 'in:export,import'],
|
|
'type' => ['required', 'in:DOC_NORMAL,DOC_ECONOMY,PARCEL'],
|
|
'from_country_id' => ['required', 'exists:countries,id'],
|
|
'to_country_id' => ['required', 'exists:countries,id'],
|
|
|
|
// وزن و ابعاد
|
|
'weight' => ['required', 'numeric', 'min:0.1'],
|
|
'volumetric_weight' => ['nullable', 'numeric', 'min:0'],
|
|
'dimensions' => ['nullable', 'string', 'max:100'],
|
|
|
|
// اطلاعات فرستنده
|
|
'sender_name' => ['required', 'string', 'max:255'],
|
|
'sender_company' => ['nullable', 'string', 'max:255'],
|
|
'sender_phone' => ['required', 'string', 'max:50'],
|
|
'sender_email' => ['nullable', 'email', 'max:255'],
|
|
'sender_address' => ['required', 'string', 'max:1000'],
|
|
'sender_city' => ['nullable', 'string', 'max:255'],
|
|
'sender_zip' => ['nullable', 'string', 'max:20'],
|
|
'sender_id_number' => ['nullable', 'string', 'max:50'],
|
|
|
|
// اطلاعات گیرنده
|
|
'receiver_name' => ['required', 'string', 'max:255'],
|
|
'receiver_company' => ['nullable', 'string', 'max:255'],
|
|
'receiver_phone' => ['required', 'string', 'max:50'],
|
|
'receiver_email' => ['nullable', 'email', 'max:255'],
|
|
'receiver_address' => ['required', 'string', 'max:1000'],
|
|
'receiver_city' => ['nullable', 'string', 'max:255'],
|
|
'receiver_zip' => ['nullable', 'string', 'max:20'],
|
|
'receiver_id_number' => ['nullable', 'string', 'max:50'],
|
|
|
|
// خدمات اضافی
|
|
'extra_service' => ['nullable', 'numeric', 'min:0'],
|
|
'packing_cost' => ['nullable', 'numeric', 'min:0'],
|
|
'domestic_pickup' => ['nullable', 'numeric', 'min:0'],
|
|
'domestic_delivery' => ['nullable', 'numeric', 'min:0'],
|
|
'warehousing_cost' => ['nullable', 'numeric', 'min:0'],
|
|
|
|
// تخفیف
|
|
'discount_code' => ['nullable', 'string', 'max:50'],
|
|
|
|
// اقلام گمرکی
|
|
'items' => ['nullable', 'array', 'max:9'],
|
|
'items.*.description' => ['required_with:items', 'string', 'max:500'],
|
|
'items.*.hs_code' => ['required_with:items', 'string', 'max:20'],
|
|
'items.*.quantity' => ['required_with:items', 'integer', 'min:1'],
|
|
'items.*.unit_price' => ['required_with:items', 'numeric', 'min:0'],
|
|
]);
|
|
} catch (ValidationException $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در اعتبارسنجی اطلاعات',
|
|
'errors' => $e->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$user = Auth::user();
|
|
|
|
// بررسی اینکه کاربر کیف پول فعال دارد
|
|
if (!$user->wallet || $user->wallet->is_frozen) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'کیف پول شما غیرفعال است. لطفاً با پشتیبانی تماس بگیرید.',
|
|
], 403);
|
|
}
|
|
|
|
// دریافت کشور مقصد برای محاسبه قیمت
|
|
$destinationCountry = Country::findOrFail(
|
|
$validated['direction'] === 'export'
|
|
? $validated['to_country_id']
|
|
: $validated['from_country_id']
|
|
);
|
|
|
|
// محاسبه قیمت
|
|
try {
|
|
$pricingData = [
|
|
'direction' => $validated['direction'] === 'export' ? 'Outbound' : 'Inbound',
|
|
'type' => $validated['type'],
|
|
'country_iso' => $destinationCountry->iso_code,
|
|
'weight' => (float) $validated['weight'],
|
|
'volumetric_weight' => (float) ($validated['volumetric_weight'] ?? 0),
|
|
'extra_service' => (float) ($validated['extra_service'] ?? 0),
|
|
'packing_cost' => (float) ($validated['packing_cost'] ?? 0),
|
|
'domestic_pickup' => (float) ($validated['domestic_pickup'] ?? 0),
|
|
'domestic_delivery' => (float) ($validated['domestic_delivery'] ?? 0),
|
|
'warehousing_cost' => (float) ($validated['warehousing_cost'] ?? 0),
|
|
'discount_code' => $validated['discount_code'] ?? null,
|
|
];
|
|
|
|
$priceResult = $this->calculator->calculate($pricingData);
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در محاسبه قیمت: ' . $e->getMessage(),
|
|
], 400);
|
|
}
|
|
|
|
// ساخت شماره AWB (فرمت: IFN-YYYY-XXXXX)
|
|
$awbNo = $this->generateAwbNumber();
|
|
|
|
// ذخیره سفارش
|
|
try {
|
|
$shipment = DB::transaction(function () use ($validated, $user, $priceResult, $awbNo) {
|
|
$chargeableWeight = max(
|
|
(float) $validated['weight'],
|
|
(float) ($validated['volumetric_weight'] ?? 0)
|
|
);
|
|
|
|
$shipment = Shipment::create([
|
|
'user_id' => $user->id,
|
|
'awb_no' => $awbNo,
|
|
'direction' => $validated['direction'],
|
|
'type' => $validated['type'],
|
|
'status' => ShipmentStatus::PendingPayment,
|
|
|
|
// وزن
|
|
'weight' => $validated['weight'],
|
|
'volumetric_weight' => $validated['volumetric_weight'] ?? null,
|
|
'chargeable_weight' => $chargeableWeight,
|
|
'dimensions' => $validated['dimensions'] ?? null,
|
|
|
|
// کشورها
|
|
'from_country_id' => $validated['from_country_id'],
|
|
'to_country_id' => $validated['to_country_id'],
|
|
|
|
// اطلاعات مالی (از محاسبه)
|
|
'shipping_price' => $priceResult['net_dirham'],
|
|
'extra_service' => $priceResult['extra_service'],
|
|
'packing_cost' => $priceResult['packing_cost'],
|
|
'domestic_pickup' => $priceResult['domestic_pickup'],
|
|
'domestic_delivery' => $priceResult['domestic_delivery'],
|
|
'warehousing_cost' => $priceResult['warehousing_cost'],
|
|
'vat_amount' => $priceResult['vat_amount'],
|
|
'discount' => $priceResult['discount_amount'],
|
|
'total_fee' => $priceResult['total_fee'],
|
|
'net_dirham' => $priceResult['net_dirham'],
|
|
'net_rial' => $priceResult['net_rial'],
|
|
|
|
// فرستنده
|
|
'sender_name' => $validated['sender_name'],
|
|
'sender_company' => $validated['sender_company'] ?? null,
|
|
'sender_phone' => $validated['sender_phone'],
|
|
'sender_email' => $validated['sender_email'] ?? null,
|
|
'sender_address' => $validated['sender_address'],
|
|
'sender_city' => $validated['sender_city'] ?? null,
|
|
'sender_zip' => $validated['sender_zip'] ?? null,
|
|
'sender_id_number' => $validated['sender_id_number'] ?? null,
|
|
|
|
// گیرنده
|
|
'receiver_name' => $validated['receiver_name'],
|
|
'receiver_company' => $validated['receiver_company'] ?? null,
|
|
'receiver_phone' => $validated['receiver_phone'],
|
|
'receiver_email' => $validated['receiver_email'] ?? null,
|
|
'receiver_address' => $validated['receiver_address'],
|
|
'receiver_city' => $validated['receiver_city'] ?? null,
|
|
'receiver_zip' => $validated['receiver_zip'] ?? null,
|
|
'receiver_id_number' => $validated['receiver_id_number'] ?? null,
|
|
]);
|
|
|
|
// ذخیره اقلام گمرکی
|
|
if (!empty($validated['items'])) {
|
|
foreach ($validated['items'] as $index => $item) {
|
|
ShipmentItem::create([
|
|
'shipment_id' => $shipment->id,
|
|
'row_number' => $index + 1,
|
|
'description' => $item['description'],
|
|
'hs_code' => $item['hs_code'],
|
|
'quantity' => $item['quantity'],
|
|
'unit_price' => $item['unit_price'],
|
|
'total_usd' => $item['quantity'] * $item['unit_price'],
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $shipment;
|
|
});
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'سفارش شما با موفقیت ثبت شد. لطفاً برای تکمیل، پرداخت را انجام دهید.',
|
|
'data' => $this->formatShipment($shipment->load(['fromCountry', 'toCountry', 'items'])),
|
|
'pricing' => $priceResult,
|
|
], 201);
|
|
|
|
} catch (\Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'خطا در ثبت سفارش: ' . $e->getMessage(),
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* جزئیات یک سفارش
|
|
* GET /api/v1/customer/orders/{shipment}
|
|
*/
|
|
public function show(Shipment $shipment): JsonResponse
|
|
{
|
|
$user = Auth::user();
|
|
|
|
// بررسی مالکیت
|
|
if ($shipment->user_id !== $user->id) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'شما به این سفارش دسترسی ندارید.',
|
|
], 403);
|
|
}
|
|
|
|
$shipment->load(['fromCountry', 'toCountry', 'items', 'trackingEvents']);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => $this->formatShipment($shipment, detailed: true),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* لغو سفارش (فقط در وضعیت pending_payment)
|
|
* POST /api/v1/customer/orders/{shipment}/cancel
|
|
*/
|
|
public function cancel(Shipment $shipment): JsonResponse
|
|
{
|
|
$user = Auth::user();
|
|
|
|
if ($shipment->user_id !== $user->id) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'شما به این سفارش دسترسی ندارید.',
|
|
], 403);
|
|
}
|
|
|
|
if (!$shipment->status->canBeCancelledByCustomer()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'این سفارش در وضعیتی نیست که بتوان آن را لغو کرد.',
|
|
], 400);
|
|
}
|
|
|
|
$shipment->update(['status' => ShipmentStatus::Cancelled]);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'سفارش با موفقیت لغو شد.',
|
|
'data' => $this->formatShipment($shipment->fresh()),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* پروفایل و آمار کاربر
|
|
* GET /api/v1/customer/profile
|
|
*/
|
|
public function profile(Request $request): JsonResponse
|
|
{
|
|
$user = Auth::user();
|
|
$user->load(['wallet']);
|
|
|
|
// آمار سفارشات
|
|
$stats = [
|
|
'total_orders' => Shipment::where('user_id', $user->id)->count(),
|
|
'pending_orders' => Shipment::where('user_id', $user->id)
|
|
->where('status', ShipmentStatus::PendingPayment)
|
|
->count(),
|
|
'active_orders' => Shipment::where('user_id', $user->id)
|
|
->whereIn('status', [
|
|
ShipmentStatus::Processed,
|
|
ShipmentStatus::PickedUp,
|
|
ShipmentStatus::InTransit,
|
|
ShipmentStatus::OutForDelivery,
|
|
])
|
|
->count(),
|
|
'delivered_orders' => Shipment::where('user_id', $user->id)
|
|
->where('status', ShipmentStatus::Delivered)
|
|
->count(),
|
|
'total_spent' => Shipment::where('user_id', $user->id)
|
|
->where('status', '!=', ShipmentStatus::PendingPayment)
|
|
->where('status', '!=', ShipmentStatus::Cancelled)
|
|
->sum('total_fee'),
|
|
];
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'user' => [
|
|
'id' => $user->id,
|
|
'name' => $user->name,
|
|
'email' => $user->email,
|
|
'phone' => $user->phone,
|
|
'member_since' => $user->created_at->format('Y-m-d'),
|
|
],
|
|
'wallet' => $user->wallet ? [
|
|
'balance' => $user->wallet->balance,
|
|
'is_frozen' => $user->wallet->is_frozen,
|
|
] : null,
|
|
'stats' => $stats,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* لیست کشورها برای فرم
|
|
* GET /api/v1/customer/countries
|
|
*/
|
|
public function countries(): JsonResponse
|
|
{
|
|
$countries = Country::select('id', 'name', 'iso_code', 'calling_code', 'export_zone_parcel', 'import_zone_parcel')
|
|
->where('is_active', true)
|
|
->orderBy('name')
|
|
->get();
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => $countries,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* فرمت کردن Shipment برای خروجی API
|
|
*/
|
|
private function formatShipment(Shipment $shipment, bool $detailed = false): array
|
|
{
|
|
$data = [
|
|
'id' => $shipment->id,
|
|
'awb_no' => $shipment->awb_no,
|
|
'direction' => $shipment->direction?->value,
|
|
'type' => $shipment->type?->value,
|
|
'status' => [
|
|
'value' => $shipment->status?->value,
|
|
'label' => $shipment->status?->label(),
|
|
'color' => $shipment->status?->color(),
|
|
'is_paid' => $shipment->status?->isPaid(),
|
|
'can_cancel' => $shipment->status?->canBeCancelledByCustomer(),
|
|
],
|
|
'from_country' => $shipment->fromCountry ? [
|
|
'id' => $shipment->fromCountry->id,
|
|
'name' => $shipment->fromCountry->name,
|
|
'iso_code' => $shipment->fromCountry->iso_code,
|
|
] : null,
|
|
'to_country' => $shipment->toCountry ? [
|
|
'id' => $shipment->toCountry->id,
|
|
'name' => $shipment->toCountry->name,
|
|
'iso_code' => $shipment->toCountry->iso_code,
|
|
] : null,
|
|
'weight' => $shipment->weight,
|
|
'chargeable_weight' => $shipment->chargeable_weight,
|
|
'total_fee' => $shipment->total_fee,
|
|
'net_rial' => $shipment->net_rial,
|
|
'net_dirham' => $shipment->net_dirham,
|
|
'created_at' => $shipment->created_at->toIso8601String(),
|
|
'created_at_jalali' => $this->toJalali($shipment->created_at),
|
|
];
|
|
|
|
if ($detailed) {
|
|
$data['sender'] = [
|
|
'name' => $shipment->sender_name,
|
|
'company' => $shipment->sender_company,
|
|
'phone' => $shipment->sender_phone,
|
|
'email' => $shipment->sender_email,
|
|
'address' => $shipment->sender_address,
|
|
'city' => $shipment->sender_city,
|
|
];
|
|
$data['receiver'] = [
|
|
'name' => $shipment->receiver_name,
|
|
'company' => $shipment->receiver_company,
|
|
'phone' => $shipment->receiver_phone,
|
|
'email' => $shipment->receiver_email,
|
|
'address' => $shipment->receiver_address,
|
|
'city' => $shipment->receiver_city,
|
|
];
|
|
$data['financial'] = [
|
|
'shipping_price' => $shipment->shipping_price,
|
|
'extra_service' => $shipment->extra_service,
|
|
'packing_cost' => $shipment->packing_cost,
|
|
'domestic_pickup' => $shipment->domestic_pickup,
|
|
'domestic_delivery' => $shipment->domestic_delivery,
|
|
'warehousing_cost' => $shipment->warehousing_cost,
|
|
'vat_amount' => $shipment->vat_amount,
|
|
'discount' => $shipment->discount,
|
|
'total_fee' => $shipment->total_fee,
|
|
];
|
|
$data['items'] = $shipment->items->map(fn($item) => [
|
|
'row' => $item->row_number,
|
|
'description' => $item->description,
|
|
'hs_code' => $item->hs_code,
|
|
'quantity' => $item->quantity,
|
|
'unit_price' => $item->unit_price,
|
|
'total' => $item->total_usd,
|
|
]);
|
|
$data['tracking_events'] = $shipment->trackingEvents->map(fn($event) => [
|
|
'date' => $event->event_date,
|
|
'description' => $event->event_description,
|
|
'location' => $event->location,
|
|
]);
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* نوتیفیکیشنهای کاربر
|
|
* GET /api/v1/customer/notifications
|
|
*/
|
|
public function notifications(Request $request): JsonResponse
|
|
{
|
|
$user = Auth::user();
|
|
|
|
$notifications = $user->notifications()
|
|
->orderBy('created_at', 'desc')
|
|
->paginate($request->per_page ?? 20);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => collect($notifications->items())->map(function ($notification) {
|
|
$data = $notification->data;
|
|
return [
|
|
'id' => $notification->id,
|
|
'type' => $notification->type,
|
|
'title' => $data['title'] ?? 'اعلان',
|
|
'body' => $data['message'] ?? '',
|
|
'data' => $data,
|
|
'read_at' => $notification->read_at?->toIso8601String(),
|
|
'created_at' => $notification->created_at->toIso8601String(),
|
|
'created_at_jalali' => $this->toJalali($notification->created_at),
|
|
];
|
|
}),
|
|
'unread_count' => $user->unreadNotifications()->count(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* خواندن نوتیفیکیشن
|
|
* POST /api/v1/customer/notifications/{notification}/read
|
|
*/
|
|
public function markNotificationRead(Request $request, $notification): JsonResponse
|
|
{
|
|
$user = Auth::user();
|
|
|
|
$notif = $user->notifications()->where('id', $notification)->first();
|
|
|
|
if (!$notif) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'نوتیفیکیشن یافت نشد.',
|
|
], 404);
|
|
}
|
|
|
|
$notif->markAsRead();
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'خوانده شد.',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* تولید شماره AWB
|
|
*/
|
|
private function generateAwbNumber(): string
|
|
{
|
|
do {
|
|
$number = 'IFN-' . date('Y') . '-' . str_pad(random_int(10000, 99999), 5, '0', STR_PAD_LEFT);
|
|
} while (Shipment::where('awb_no', $number)->exists());
|
|
|
|
return $number;
|
|
}
|
|
|
|
/**
|
|
* تبدیل تاریخ به شمسی
|
|
*/
|
|
private function toJalali($date): string
|
|
{
|
|
if (!$date) return '—';
|
|
|
|
try {
|
|
return \Morilog\Jalali\Jalalian::fromCarbon($date)->format('Y/m/d H:i');
|
|
} catch (\Exception $e) {
|
|
return $date->format('Y-m-d H:i');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* پرداخت سفارش از کیف پول
|
|
* POST /api/v1/customer/orders/{shipment}/pay-wallet
|
|
*/
|
|
public function payFromWallet(Shipment $shipment): JsonResponse
|
|
{
|
|
$user = Auth::user();
|
|
|
|
// بررسی مالکیت
|
|
if ($shipment->user_id !== $user->id) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'شما به این سفارش دسترسی ندارید.',
|
|
], 403);
|
|
}
|
|
|
|
// بررسی وضعیت
|
|
if ($shipment->status !== ShipmentStatus::PendingPayment) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'این سفارش در وضعیت قابل پرداخت نیست.',
|
|
], 400);
|
|
}
|
|
|
|
$wallet = $user->wallet;
|
|
if (!$wallet) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'کیف پول شما وجود ندارد.',
|
|
], 400);
|
|
}
|
|
|
|
$paymentService = app(\App\Services\OrderPaymentService::class);
|
|
$result = $paymentService->payFromWallet($shipment, $wallet);
|
|
|
|
if ($result['success']) {
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => $result['message'],
|
|
'data' => $this->formatShipment($shipment->fresh()->load(['fromCountry', 'toCountry'])),
|
|
]);
|
|
}
|
|
|
|
return response()->json($result, 400);
|
|
}
|
|
|
|
/**
|
|
* شروع پرداخت از درگاه
|
|
* POST /api/v1/customer/orders/{shipment}/pay-gateway
|
|
*/
|
|
public function payViaGateway(Request $request, Shipment $shipment): JsonResponse
|
|
{
|
|
$user = Auth::user();
|
|
|
|
// بررسی مالکیت
|
|
if ($shipment->user_id !== $user->id) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'شما به این سفارش دسترسی ندارید.',
|
|
], 403);
|
|
}
|
|
|
|
// بررسی وضعیت
|
|
if ($shipment->status !== ShipmentStatus::PendingPayment) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'این سفارش در وضعیت قابل پرداخت نیست.',
|
|
], 400);
|
|
}
|
|
|
|
$validated = $request->validate([
|
|
'frontend_callback' => ['required', 'url', 'max:500'],
|
|
]);
|
|
|
|
$wallet = $user->wallet;
|
|
if (!$wallet) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'کیف پول شما وجود ندارد.',
|
|
], 400);
|
|
}
|
|
|
|
$paymentService = app(\App\Services\OrderPaymentService::class);
|
|
$result = $paymentService->initiateGatewayPayment($shipment, $wallet);
|
|
|
|
if ($result['success']) {
|
|
// ذخیره callback URL در metadata تراکنش
|
|
$transaction = WalletTransaction::find($result['transaction_id']);
|
|
if ($transaction) {
|
|
$metadata = $transaction->metadata ?? [];
|
|
$metadata['frontend_callback'] = $validated['frontend_callback'];
|
|
$transaction->update(['metadata' => $metadata]);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'payment_url' => $result['payment_url'],
|
|
'transaction_id' => $result['transaction_id'],
|
|
]);
|
|
}
|
|
|
|
return response()->json($result, 400);
|
|
}
|
|
} |