fix(bridge): resolve corrupted PDF downloads and improve token handling

Relocate PDF and commitment form download logic from shortcode execution
to the `template_redirect` hook to prevent HTML output from corrupting
binary streams.

Key improvements:
- Moved download interception to `template_redirect` to ensure clean
  headers and prevent partial file downloads caused by early buffer
  output.
- Replaced direct `get_user_meta` calls with `IFNEX_User_Bridge::get_user_token`
  to support automatic token renewal and improved authentication reliability.
- Added PDF signature validation (`%PDF` header check) to ensure
  integrity of remote file responses.
- Updated Laravel base controller to include standard `AuthorizesRequests`
  and `ValidatesRequests` traits.
This commit is contained in:
Kazem Alghasi 2026-10-03 18:15:46 +03:30
parent 91984dbde3
commit e5cc6a9d15
4 changed files with 61 additions and 32 deletions

View File

@ -1069,22 +1069,9 @@ function ifnex_order_detail_shortcode($atts) {
}
// Handle PDF download
$download_type = sanitize_text_field($_GET['download'] ?? '');
if ($download_type && in_array($download_type, ['awb', 'invoice', 'label', 'import-invoice'])) {
ifnex_handle_pdf_download($order_id, $download_type);
return '';
}
// Handle commitment form downloads (template / signed) — proxied با توکن لاراول
if (in_array($download_type, ['commitment-template', 'commitment-signed'], true)) {
$form_id = intval($_GET['form_id'] ?? 0);
$type = ($download_type === 'commitment-signed') ? 'signed' : 'template';
ifnex_handle_commitment_download($order_id, $form_id, $type);
return '';
}
// ⚠️ هندلر دانلودها (PDF و تعهدنامه) به template_redirect منتقل شده —
// اجرای آن داخل شورت‌کد یعنی بعد از چاپ HTML هدر قالب؛ خروجی باینری به
// HTML می‌چسبد و با Content-Length ناهم‌خوان، PDF نیمه‌تمام دانلود می‌شود.
$bridge = new IFNEX_User_Bridge();
$user_id = get_current_user_id();
@ -1411,6 +1398,32 @@ function uploadCommitmentForm(orderId, formId, input) {
return ob_get_clean();
}
// ══════════════════════════════════════════════════════════════
// رهگیری درخواست‌های دانلود قبل از هر خروجی صفحه (template_redirect)
// اجرای دانلود داخل شورت‌کد یعنی بعد از چاپ HTML هدر قالب — بایت‌های HTML
// به ابتدای فایل می‌چسبند و با Content-Length ناهم‌خوان، فایل ناقص می‌رسد.
// ══════════════════════════════════════════════════════════════
add_action('template_redirect', 'ifnex_intercept_download_request', 1);
function ifnex_intercept_download_request(): void {
$download = sanitize_text_field($_GET['download'] ?? '');
$order_id = intval($_GET['order_id'] ?? 0);
if (!$download || !$order_id) return;
if (in_array($download, ['awb', 'invoice', 'label', 'import-invoice'], true)) {
ifnex_handle_pdf_download($order_id, $download); // exit داخل تابع
}
if (in_array($download, ['commitment-template', 'commitment-signed'], true)) {
ifnex_handle_commitment_download(
$order_id,
intval($_GET['form_id'] ?? 0),
$download === 'commitment-signed' ? 'signed' : 'template'
); // exit داخل تابع
}
}
// ══════════════════════════════════════════════════════════════
// آیکون‌های SVG مدرن (سبک Heroicons)
// ══════════════════════════════════════════════════════════════
@ -1426,8 +1439,8 @@ function ifnex_handle_pdf_download($order_id, $download_type) {
$api_url = get_option('ifnex_api_url', 'http://localhost:8000/api/v1');
$download_url = rtrim($api_url, '/') . "/customer/orders/{$order_id}/pdf/{$download_type}";
$token = get_user_meta($user_id, 'ifnex_laravel_token', true);
if (!$token) wp_die('توکن احراز هویت یافت نشد.');
$token = $bridge->get_user_token($user_id); // با تجدید خودکار در صورت انقضا/حذف
if (!$token) wp_die('توکن احراز هویت در دسترس نیست — لطفاً دوباره وارد شوید.');
$response = wp_remote_get($download_url, array(
'headers' => array(
@ -1443,8 +1456,17 @@ function ifnex_handle_pdf_download($order_id, $download_type) {
if ($status_code !== 200) wp_die('خطا در دانلود فایل.');
$file_content = wp_remote_retrieve_body($response);
// پاسخ باید PDF خام باشد؛ وگرنه به‌جای فایل خراب، پیام واضح نمایش بده
if (substr($file_content, 0, 4) !== '%PDF') {
wp_die('پاسخ سرور یک فایل PDF معتبر نبود — لطفاً دوباره تلاش کنید.');
}
$filename = strtoupper($download_type) . '-' . $order['data']['awb_no'] . '.pdf';
// پاک‌سازی همه بافرهای خروجی تا جریان فایل تمیز و هم‌طول با Content-Length ارسال شود
while (ob_get_level()) { ob_end_clean(); }
nocache_headers();
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="' . $filename . '"');
@ -1471,8 +1493,8 @@ function ifnex_handle_commitment_download($order_id, $form_id, $type = 'template
$endpoint = ($type === 'signed') ? 'download' : 'template';
$download_url = rtrim($api_url, '/') . "/customer/orders/{$order_id}/commitment-forms/{$form_id}/{$endpoint}";
$token = get_user_meta($user_id, 'ifnex_laravel_token', true);
if (!$token) wp_die('توکن احراز هویت یافت نشد.');
$token = $bridge->get_user_token($user_id); // با تجدید خودکار در صورت انقضا/حذف
if (!$token) wp_die('توکن احراز هویت در دسترس نیست — لطفاً دوباره وارد شوید.');
$response = wp_remote_get($download_url, array(
'headers' => array(
@ -1493,6 +1515,12 @@ function ifnex_handle_commitment_download($order_id, $form_id, $type = 'template
}
$file_content = wp_remote_retrieve_body($response);
// پاسخ باید فایل باینری باشد؛ شروع با '<' یعنی HTML/خطا برگشته
if ($file_content === '' || $file_content[0] === '<') {
wp_die('پاسخ سرور یک فایل معتبر نبود — لطفاً دوباره تلاش کنید.');
}
$awb = $order['data']['awb_no'] ?? ('order-' . $order_id);
// تشخیص نوع محتوا و نام فایل از روی هدر Laravel
@ -1511,6 +1539,9 @@ function ifnex_handle_commitment_download($order_id, $form_id, $type = 'template
$filename .= '.' . $ext;
}
// پاک‌سازی همه بافرهای خروجی تا جریان فایل تمیز و هم‌طول با Content-Length ارسال شود
while (ob_get_level()) { ob_end_clean(); }
nocache_headers();
header('Content-Type: ' . $ctype);
header('Content-Disposition: attachment; filename="' . $filename . '"');

View File

@ -855,8 +855,8 @@ function ifnex_download_order_pdf_ajax() {
$download_url = rtrim($api_url, '/') . "/customer/orders/{$order_id}/pdf/{$download_type}";
// ارسال درخواست به لاراول با توکن احراز هویت
$token = get_user_meta($user_id, 'ifnex_laravel_token', true);
if (!$token) wp_send_json_error('توکن احراز هویت یافت نشد.');
$token = $bridge->get_user_token($user_id); // با تجدید خودکار در صورت انقضا/حذف
if (!$token) wp_send_json_error('توکن احراز هویت در دسترس نیست — لطفاً دوباره وارد شوید.');
$response = wp_remote_get($download_url, array(
'headers' => array(
@ -908,8 +908,8 @@ function ifnex_get_commitment_forms_ajax() {
$api_url = get_option('ifnex_api_url', 'http://localhost:8000/api/v1');
$url = rtrim($api_url, '/') . "/customer/orders/{$order_id}/commitment-forms";
$token = get_user_meta($user_id, 'ifnex_laravel_token', true);
if (!$token) wp_send_json_error('توکن احراز هویت یافت نشد.');
$token = $bridge->get_user_token($user_id); // با تجدید خودکار در صورت انقضا/حذف
if (!$token) wp_send_json_error('توکن احراز هویت در دسترس نیست — لطفاً دوباره وارد شوید.');
$response = wp_remote_get($url, array(
'headers' => array(
@ -973,8 +973,8 @@ function ifnex_upload_commitment_form_ajax() {
$api_url = get_option('ifnex_api_url', 'http://localhost:8000/api/v1');
$url = rtrim($api_url, '/') . "/customer/orders/{$order_id}/commitment-forms/{$form_id}/upload";
$token = get_user_meta($user_id, 'ifnex_laravel_token', true);
if (!$token) wp_send_json_error('توکن احراز هویت یافت نشد.');
$token = $bridge->get_user_token($user_id); // با تجدید خودکار در صورت انقضا/حذف
if (!$token) wp_send_json_error('توکن احراز هویت در دسترس نیست — لطفاً دوباره وارد شوید.');
$multipart = new CURLFile($upload['file'], $file['type'], $file['name']);
$post_fields = array(

View File

@ -2,12 +2,10 @@
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
abstract class Controller
{
//
use AuthorizesRequests, ValidatesRequests;
}
class CustomerOrderController
{
// ...
}

Binary file not shown.