@@ -742,12 +749,37 @@ function ifnex_order_payment_shortcode($atts) {
$wallet_balance = is_wp_error($balance) ? 0 : ($balance['balance'] ?? 0);
$can_pay_wallet = $wallet_balance >= $data['total_fee'];
+ // بررسی نتیجه پرداخت از درگاه (callback)
+ $payment_status = sanitize_text_field($_GET['status'] ?? '');
+ $payment_message = sanitize_text_field($_GET['message'] ?? '');
+ $payment_ref_id = sanitize_text_field($_GET['ref_id'] ?? '');
+
ob_start();
?>
💳 پرداخت سفارش
+
+
+
✅ پرداخت با موفقیت انجام شد!
+
کد پیگیری تراکنش:
+
مبلغ: ریال
+
سفارش شما در حال پردازش است.
+
مشاهده سفارشات
+
+
+
+
❌ پرداخت ناموفق بود
+
+
+
+
+
+
شماره پیگیری:
@@ -797,7 +829,7 @@ function ifnex_order_payment_shortcode($atts) {
-
+
diff --git a/03_WordPress/wp-content/plugins/ifnex-bridge/includes/user-bridge.php b/03_WordPress/wp-content/plugins/ifnex-bridge/includes/user-bridge.php
index 7b7e728..57c94dd 100644
--- a/03_WordPress/wp-content/plugins/ifnex-bridge/includes/user-bridge.php
+++ b/03_WordPress/wp-content/plugins/ifnex-bridge/includes/user-bridge.php
@@ -1,4 +1,7 @@
authenticated_request($user_id, '/customer/countries');
-
- if (is_wp_error($result)) {
- return $result;
+ $api_url = rtrim(get_option('ifnex_api_url', 'http://localhost:8000/api/v1'), '/');
+ $response = wp_remote_get($api_url . '/countries', array(
+ 'headers' => array('Accept' => 'application/json'),
+ 'timeout' => 15,
+ ));
+
+ if (is_wp_error($response)) {
+ return $response;
}
-
- return $result['data'] ?? [];
+
+ $body = json_decode(wp_remote_retrieve_body($response), true);
+ $code = wp_remote_retrieve_response_code($response);
+
+ if ($code !== 200 || empty($body['data'])) {
+ return new WP_Error('countries_error', 'خطا در دریافت لیست کشورها');
+ }
+
+ return $body['data'];
}
/**
@@ -462,14 +476,14 @@ function ifnex_enqueue_order_assets() {
global $post;
if (!is_a($post, 'WP_Post')) return;
- $plugin_url = plugin_dir_url(dirname(__FILE__));
+ $plugin_url = trailingslashit(plugins_url('', dirname(__FILE__)));
- // لود CSS
+ // لود CSS — وابسته به CSS قالب
wp_enqueue_style(
- 'ifnex-orders-css',
- $plugin_url . 'assets/css/ifnex-orders.css',
- array(),
- '1.7.0'
+ 'ifnex-orders-css',
+ $plugin_url . 'assets/css/ifnex-orders.css',
+ array('ifnex-main'),
+ '2.0.0-' . time()
);
// لود jQuery & JS
@@ -478,7 +492,7 @@ function ifnex_enqueue_order_assets() {
'ifnex-order-form-js',
$plugin_url . 'assets/js/ifnex-order-form.js',
array('jquery'),
- '1.7.0',
+ '2.0.0-' . time(),
true
);
@@ -486,7 +500,7 @@ function ifnex_enqueue_order_assets() {
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/'),
+ 'orders_url' => home_url('/my-account/?tab=orders'),
));
}
@@ -532,6 +546,41 @@ function ifnex_wallet_charge_ajax() {
wp_send_json_success($result);
}
+// ══════════════════════════════════════════════════════════════
+// AJAX Handler برای پرداخت سفارش از کیف پول
+// ══════════════════════════════════════════════════════════════
+
+add_action('wp_ajax_ifnex_pay_order_wallet', 'ifnex_pay_order_wallet_ajax');
+
+function ifnex_pay_order_wallet_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('شناسه سفارش نامعتبر است.');
+ }
+
+ $bridge = new IFNEX_User_Bridge();
+ $result = $bridge->authenticated_request(
+ get_current_user_id(),
+ "/customer/orders/{$order_id}/pay-wallet",
+ 'POST'
+ );
+
+ 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);
+}
+
add_action('wp_ajax_ifnex_pay_order_gateway', 'ifnex_pay_order_gateway_ajax');
function ifnex_pay_order_gateway_ajax() {
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
@@ -566,14 +615,19 @@ function ifnex_enqueue_payment_assets() {
if (!is_a($post, 'WP_Post')) return;
if (has_shortcode($post->post_content, 'ifnex_order_payment')) {
- $plugin_url = plugin_dir_url(dirname(__FILE__));
- wp_enqueue_style('ifnex-orders-css', $plugin_url . 'assets/css/ifnex-orders.css', array(), '1.4.0');
+ $plugin_url = trailingslashit(plugins_url('', dirname(__FILE__)));
+ wp_enqueue_style(
+ 'ifnex-orders-css',
+ $plugin_url . 'assets/css/ifnex-orders.css',
+ array('ifnex-main'),
+ '2.0.0-' . time()
+ );
wp_enqueue_script('jquery');
- wp_localize_script('jquery', 'ifnex_ajax', array(
- 'ajax_url' => admin_url('admin-ajax.php'),
- 'nonce' => wp_create_nonce('ifnex_ajax_nonce'),
- 'orders_url' => home_url('/my-orders/'),
- ));
+ wp_add_inline_script('jquery', 'var ifnex_ajax = ' . wp_json_encode(array(
+ 'ajax_url' => admin_url('admin-ajax.php'),
+ 'nonce' => wp_create_nonce('ifnex_ajax_nonce'),
+ 'orders_url' => home_url('/my-account/?tab=orders'),
+ )) . ';', 'before');
}
}
@@ -585,24 +639,24 @@ function ifnex_enqueue_payment_assets() {
add_action('wp_enqueue_scripts', 'ifnex_enqueue_global_assets', 20);
function ifnex_enqueue_global_assets() {
- $plugin_url = plugin_dir_url(dirname(__FILE__));
+ $plugin_url = trailingslashit(plugins_url('', dirname(__FILE__)));
- // لود CSS اصلی (نسخه را بالا بردیم تا cache مرورگر بشکند)
+ // لود CSS اصلی — وابسته به CSS قالب تا بعد از آن لود شود
wp_enqueue_style(
'ifnex-orders-css',
$plugin_url . 'assets/css/ifnex-orders.css',
- array(),
- '1.6.0'
+ array('ifnex-main'),
+ '2.0.0-' . time()
);
// لود jQuery
wp_enqueue_script('jquery');
// متغیرهای AJAX برای همه صفحات
- wp_localize_script('jquery', 'ifnex_ajax', array(
+ wp_add_inline_script('jquery', 'var ifnex_ajax = ' . wp_json_encode(array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('ifnex_ajax_nonce'),
'orders_url' => home_url('/my-account/?tab=orders'),
'home_url' => home_url('/'),
- ));
+ )) . ';', 'before');
}
\ No newline at end of file
diff --git a/03_WordPress/wp-content/themes/ifnex/assets/js/main.js b/03_WordPress/wp-content/themes/ifnex/assets/js/main.js
index ffbb91a..f6e4e03 100644
--- a/03_WordPress/wp-content/themes/ifnex/assets/js/main.js
+++ b/03_WordPress/wp-content/themes/ifnex/assets/js/main.js
@@ -97,4 +97,32 @@
});
});
+ // ─── Hero Tracking Form ───
+ const trackingForm = document.querySelector('.tracking-form');
+ if (trackingForm) {
+ const trackingInput = trackingForm.querySelector('.tracking-input');
+ const trackingBtn = trackingForm.querySelector('.tracking-btn');
+
+ function submitTracking() {
+ var awb = trackingInput.value.trim();
+ if (!awb) {
+ trackingInput.focus();
+ return;
+ }
+ var baseUrl = (typeof ifnexConfig !== 'undefined' && ifnexConfig.homeUrl) ? ifnexConfig.homeUrl : '/';
+ window.location.href = baseUrl.replace(/\/$/, '') + '/tracking/?awb=' + encodeURIComponent(awb);
+ }
+
+ if (trackingBtn) {
+ trackingBtn.addEventListener('click', submitTracking);
+ }
+
+ trackingInput.addEventListener('keydown', function(e) {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ submitTracking();
+ }
+ });
+ }
+
})();
\ No newline at end of file
diff --git a/03_WordPress/wp-content/themes/ifnex/functions.php b/03_WordPress/wp-content/themes/ifnex/functions.php
index 3e02731..4d9e8c0 100644
--- a/03_WordPress/wp-content/themes/ifnex/functions.php
+++ b/03_WordPress/wp-content/themes/ifnex/functions.php
@@ -176,6 +176,9 @@ function ifnex_get_logo_html($context = 'header') {
$custom_logo = wp_get_attachment_image($logo_id, 'full', false, [
'class' => 'logo-image',
'alt' => get_bloginfo('name'),
+ 'loading' => 'eager',
+ 'width' => 200,
+ 'height' => 50,
]);
if ($custom_logo) {
diff --git a/04_Laravel/app/Http/Controllers/Api/Customer/CustomerOrderController.php b/04_Laravel/app/Http/Controllers/Api/Customer/CustomerOrderController.php
index b3b67b2..4cbe7ba 100644
--- a/04_Laravel/app/Http/Controllers/Api/Customer/CustomerOrderController.php
+++ b/04_Laravel/app/Http/Controllers/Api/Customer/CustomerOrderController.php
@@ -266,24 +266,24 @@ class CustomerOrderController extends Controller
ShipmentPackage::create([
'shipment_id' => $shipment->id,
- 'package_number' => $index + 1,
+ 'package_no' => $index + 1,
'weight' => $pkg['weight'],
'volumetric_weight' => $volWeight,
'chargeable_weight' => $chargeable,
'dimensions' => $pkg['dimensions'] ?? null,
- 'description' => $pkg['description'] ?? null,
+ 'content_description' => $pkg['description'] ?? null,
]);
}
} else {
// حالت قدیمی: یه بسته با وزن کلی سفارش
ShipmentPackage::create([
'shipment_id' => $shipment->id,
- 'package_number' => 1,
+ 'package_no' => 1,
'weight' => $validated['weight'],
'volumetric_weight' => $validated['volumetric_weight'] ?? 0,
'chargeable_weight' => $validated['chargeable_weight'],
'dimensions' => $validated['dimensions'] ?? null,
- 'description' => null,
+ 'content_description' => null,
]);
}
diff --git a/04_Laravel/app/Http/Controllers/Api/PaymentController.php b/04_Laravel/app/Http/Controllers/Api/PaymentController.php
index f8b1468..0a9944e 100644
--- a/04_Laravel/app/Http/Controllers/Api/PaymentController.php
+++ b/04_Laravel/app/Http/Controllers/Api/PaymentController.php
@@ -213,7 +213,14 @@ class PaymentController extends Controller
gatewayReferenceId: $verification['ref_id'] ?? $authority
);
- // ─── NEW: اگر تراکنش مربوط به سفارش است، status سفارش را تغییر بده ─
+ // اگر تراکنش مربوط به سفارش است، status سفارش را تغییر بده
+ $metadata = $transaction->metadata ?? [];
+ if (!empty($metadata['is_order_payment']) && !empty($metadata['shipment_id'])) {
+ $shipment = \App\Models\Shipment::find($metadata['shipment_id']);
+ if ($shipment && $shipment->status === \App\Enums\ShipmentStatus::Approved) {
+ $shipment->update(['status' => \App\Enums\ShipmentStatus::Processed]);
+ }
+ }
Log::info('Payment completed successfully', [
'transaction_id' => $transaction->id,
diff --git a/04_Laravel/app/Models/ShipmentPackage.php b/04_Laravel/app/Models/ShipmentPackage.php
index c1d0c97..f2430cd 100644
--- a/04_Laravel/app/Models/ShipmentPackage.php
+++ b/04_Laravel/app/Models/ShipmentPackage.php
@@ -9,12 +9,12 @@ class ShipmentPackage extends Model
{
protected $fillable = [
'shipment_id',
- 'package_number',
+ 'package_no',
'weight',
'volumetric_weight',
'chargeable_weight',
'dimensions',
- 'description',
+ 'content_description',
];
protected $casts = [
diff --git a/04_Laravel/app/Services/OrderPaymentService.php b/04_Laravel/app/Services/OrderPaymentService.php
index c4c12d7..704a517 100644
--- a/04_Laravel/app/Services/OrderPaymentService.php
+++ b/04_Laravel/app/Services/OrderPaymentService.php
@@ -163,7 +163,7 @@ class OrderPaymentService
return DB::transaction(function () use ($transaction) {
$shipment = Shipment::find($transaction->transactionable_id);
- if (!$shipment || $shipment->status !== ShipmentStatus::PendingPayment) {
+ if (!$shipment || $shipment->status !== ShipmentStatus::Approved) {
return false;
}
diff --git a/04_Laravel/read_excel.php b/04_Laravel/read_excel.php
deleted file mode 100644
index ac271d2..0000000
--- a/04_Laravel/read_excel.php
+++ /dev/null
@@ -1,26 +0,0 @@
-load(__DIR__ . '/../01_Documents/Sheet ENG invoice.xlsx');
-$sheet = $spreadsheet->getActiveSheet();
-
-echo 'Sheet: ' . $sheet->getTitle() . PHP_EOL;
-echo 'Rows: ' . $sheet->getHighestRow() . PHP_EOL;
-
-// Use toArray for cleaner output
-$data = $sheet->toArray(null, true, true, true);
-
-echo PHP_EOL . '=== CONTENT ===' . PHP_EOL;
-
-foreach ($data as $rowIndex => $row) {
- $line = '';
- foreach ($row as $colIndex => $value) {
- if ($value !== null && trim((string)$value) !== '') {
- $line .= $colIndex . '=' . trim((string)$value) . ' | ';
- }
- }
- if ($line) echo 'Row' . $rowIndex . ': ' . $line . PHP_EOL;
-}
diff --git a/04_Laravel/routes/api.php b/04_Laravel/routes/api.php
index 9386452..1c8d28f 100644
--- a/04_Laravel/routes/api.php
+++ b/04_Laravel/routes/api.php
@@ -29,6 +29,13 @@ Route::middleware([ApiKeyMiddleware::class])->prefix('v1')->group(function () {
Route::get('/track/{awb_no}', [TrackController::class, 'show']);
});
+// ══════════════════════════════════════════════════════════════
+// API عمومی (بدون نیاز به احراز هویت)
+// ══════════════════════════════════════════════════════════════
+Route::prefix('v1')->group(function () {
+ Route::get('/countries', [CustomerOrderController::class, 'countries']);
+});
+
// ══════════════════════════════════════════════════════════════
// Auth API (عمومی - برای دریافت توکن)
// ══════════════════════════════════════════════════════════════
@@ -60,8 +67,8 @@ Route::middleware(['auth:sanctum'])->prefix('v1')->group(function () {
// پروفایل و آمار
Route::get('/profile', [CustomerOrderController::class, 'profile']);
- // لیست کشورها
- Route::get('/countries', [CustomerOrderController::class, 'countries']);
+ // لیست کشورها (عمومی - بدون نیاز به احراز هویت)
+ // این endpoint در گروه auth:sanctum نیست
// سفارشات
Route::get('/orders', [CustomerOrderController::class, 'index']);