ifnex/04_Laravel/app/Services/OrderPaymentService.php
Kazem Alghasi b25bfda597 feat(core): enhance order processing and bridge integration
Refactor the order and shipment lifecycle across WordPress and Laravel,
improving multi-package handling, payment automation, and frontend
reliability.

- Laravel:
  - Rename `package_number` to `package_no` and `description` to
    `content_description` in `ShipmentPackage` model and controller.
  - Update `PaymentController` to automatically transition approved
    shipments to `Processed` status upon successful payment.
  - Adjust `OrderPaymentService` to validate against `Approved` status
    instead of `PendingPayment`.
  - Expose `/countries` endpoint as a public route (unauthenticated).
  - Remove obsolete `read_excel.php` utility.

- WordPress (Bridge Plugin & Theme):
  - Implement AJAX handler for wallet-based order payments.
  - Update `ifnex-order-form.js` to support multi-package input names
    and auto-select Iran based on shipment direction.
  - Improve error handling and feedback in the order form and country
    loading logic.
  - Add automatic tracking submission when an `awb` parameter is
    present in the URL.
  - Update CSS with `!important` flags to ensure correct visibility
    of form steps and dashboard elements.
  - Implement cache-busting for plugin assets and prevent OPcache
    stale files via header controls.
  - Optimize theme logo loading with eager loading and explicit
    dimensions.
2026-09-09 12:03:58 +03:30

224 lines
8.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Services;
use App\Enums\PaymentGateway;
use App\Enums\ShipmentStatus;
use App\Enums\TransactionStatus;
use App\Enums\TransactionType;
use App\Models\Shipment;
use App\Models\ShipmentTrackingEvent;
use App\Models\Wallet;
use App\Models\WalletTransaction;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class OrderPaymentService
{
/**
* پرداخت از کیف پول
*/
public function payFromWallet(Shipment $shipment, Wallet $wallet): array
{
// بررسی موجودی
if (!$wallet->hasSufficientBalance($shipment->total_fee)) {
return [
'success' => false,
'message' => 'موجودی کیف پول کافی نیست.',
'required' => $shipment->total_fee,
'available' => $wallet->balance,
];
}
try {
return DB::transaction(function () use ($shipment, $wallet) {
// ۱. ثبت تراکنش کسر از کیف پول
$transaction = WalletTransaction::create([
'wallet_id' => $wallet->id,
'amount' => -$shipment->total_fee,
'balance_before' => $wallet->balance,
'balance_after' => $wallet->balance - $shipment->total_fee,
'type' => TransactionType::ORDER_PAYMENT,
'status' => TransactionStatus::COMPLETED,
'gateway' => PaymentGateway::WALLET,
'description' => "پرداخت سفارش {$shipment->awb_no}",
'transactionable_type' => Shipment::class,
'transactionable_id' => $shipment->id,
'ip_address' => request()->ip(),
'user_agent' => request()->userAgent(),
]);
// ۲. به‌روزرسانی موجودی کیف پول
$wallet->update([
'balance' => $wallet->balance - $shipment->total_fee,
'total_withdrawn' => $wallet->total_withdrawn + $shipment->total_fee,
]);
// ۳. تغییر status سفارش به processed
$shipment->update(['status' => ShipmentStatus::Processed]);
// ۴. ثبت tracking event (با همه فیلدهای احتمالی)
$this->createTrackingEvent($shipment, 'پرداخت تکمیل شد (از کیف پول)');
Log::info('Order paid from wallet', [
'shipment_id' => $shipment->id,
'awb_no' => $shipment->awb_no,
'amount' => $shipment->total_fee,
'wallet_id' => $wallet->id,
]);
return [
'success' => true,
'message' => 'پرداخت با موفقیت انجام شد.',
'transaction_id' => $transaction->id,
'new_status' => 'processed',
];
});
} catch (\Exception $e) {
Log::error('Wallet payment failed', [
'shipment_id' => $shipment->id,
'error' => $e->getMessage(),
]);
return [
'success' => false,
'message' => 'خطا در پرداخت: ' . $e->getMessage(),
];
}
}
/**
* شروع پرداخت از درگاه
*/
public function initiateGatewayPayment(Shipment $shipment, Wallet $wallet): array
{
try {
// ۱. ایجاد تراکنش pending
$transaction = WalletTransaction::create([
'wallet_id' => $wallet->id,
'amount' => $shipment->total_fee,
'balance_before' => $wallet->balance,
'balance_after' => $wallet->balance,
'type' => TransactionType::ORDER_PAYMENT,
'status' => TransactionStatus::PENDING,
'gateway' => PaymentGateway::ZARINPAL,
'description' => "پرداخت سفارش {$shipment->awb_no}",
'transactionable_type' => Shipment::class,
'transactionable_id' => $shipment->id,
'ip_address' => request()->ip(),
'user_agent' => request()->userAgent(),
'metadata' => [
'shipment_id' => $shipment->id,
'awb_no' => $shipment->awb_no,
'is_order_payment' => true,
],
]);
// ۲. درخواست به درگاه
$gatewayService = app()->make(\App\Services\ZarinpalService::class);
if (config('ifnex.zarinpal.merchant_id') === 'fake-merchant-id-for-testing'
|| config('ifnex.zarinpal.sandbox', true)) {
$gatewayService = app()->make(\App\Services\MockZarinpalService::class);
}
$paymentData = $gatewayService->requestPayment(
amount: $shipment->total_fee,
description: "پرداخت سفارش {$shipment->awb_no}",
mobile: $shipment->user->phone,
email: $shipment->user->email
);
// ۳. ذخیره authority در metadata
$metadata = $transaction->metadata ?? [];
$metadata['zarinpal_authority'] = $paymentData['authority'];
$transaction->update(['metadata' => $metadata]);
return [
'success' => true,
'payment_url' => $paymentData['payment_url'],
'transaction_id' => $transaction->id,
'authority' => $paymentData['authority'],
];
} catch (\Exception $e) {
Log::error('Gateway payment initiation failed', [
'shipment_id' => $shipment->id,
'error' => $e->getMessage(),
]);
return [
'success' => false,
'message' => 'خطا در اتصال به درگاه: ' . $e->getMessage(),
];
}
}
/**
* تکمیل پرداخت بعد از callback موفق
*/
public function completeGatewayPayment(WalletTransaction $transaction): bool
{
try {
return DB::transaction(function () use ($transaction) {
$shipment = Shipment::find($transaction->transactionable_id);
if (!$shipment || $shipment->status !== ShipmentStatus::Approved) {
return false;
}
// ۱. به‌روزرسانی تراکنش
$transaction->update([
'status' => TransactionStatus::COMPLETED,
'balance_after' => $transaction->wallet->balance,
]);
// ۲. تغییر status سفارش
$shipment->update(['status' => ShipmentStatus::Processed]);
// ۳. ثبت tracking event
$this->createTrackingEvent($shipment, 'پرداخت تکمیل شد (از درگاه بانکی)');
Log::info('Order paid via gateway', [
'shipment_id' => $shipment->id,
'awb_no' => $shipment->awb_no,
'transaction_id' => $transaction->id,
]);
return true;
});
} catch (\Exception $e) {
Log::error('Complete gateway payment failed', [
'transaction_id' => $transaction->id,
'error' => $e->getMessage(),
]);
return false;
}
}
/**
* ساخت Tracking Event با ساختار دقیق جدول
*/
private function createTrackingEvent(Shipment $shipment, string $description): void
{
try {
ShipmentTrackingEvent::create([
'shipment_id' => $shipment->id,
'event_date' => now()->toDateString(), // YYYY-MM-DD
'event_time' => now()->format('H:i:s'), // HH:MM:SS ← فیلد جدید
'event_description' => $description,
'location' => 'سیستم',
'delivery_status' => 'processed', // ← اصلاح شد (status وجود نداشت)
'source' => 'manual', // مقدار معتبر enum TrackingSource
]);
} catch (\Exception $e) {
// اگر tracking event ثبت نشد، فقط لاگ کن (تراکنش را خراب نکن)
Log::warning('Failed to create tracking event', [
'shipment_id' => $shipment->id,
'error' => $e->getMessage(),
]);
}
}
}