Introduce a comprehensive address management system allowing users to
save, manage, and reuse sender and receiver addresses during the
ordering process. This feature bridges the WordPress frontend with the
Laravel backend API.
Key changes:
- Backend (Laravel):
- Added `AddressBookEntry` model and migration for storing addresses
with role (sender/receiver), usage tracking, and default status.
- Implemented `AddressBookController` with full CRUD functionality
secured via Sanctum and ownership validation.
- Integrated address usage tracking and upsert logic into the
`CustomerOrderController` to prevent duplicate entries.
- Added 18 new feature tests covering ownership, limits, and
validation.
- Frontend (WordPress):
- Added a new "Address Book" tab in the user dashboard with card-based
UI, filtering, and SweetAlert2 integration.
- Enhanced the order form with dropdown selectors for saved addresses
and "save this address" checkboxes for both sender and receiver.
- Implemented secure AJAX proxies in `user-bridge.php` with nonce
and sanitization.
- Documentation:
- Updated `AGENT.md` and `CLIENT_DELIVERY.md` to reflect phase 3.9
completion and updated the project roadmap.
- Removed obsolete `README-INSTALL.md`.
1143 lines
42 KiB
PHP
1143 lines
42 KiB
PHP
<?php
|
|
/**
|
|
* IFNEX User Bridge
|
|
* مدیریت ارتباط کاربران وردپرس با لاراول
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
class IFNEX_User_Bridge {
|
|
private $api_url;
|
|
private $api_key;
|
|
|
|
public function __construct() {
|
|
$this->api_url = rtrim(get_option('ifnex_api_url', 'http://localhost:8000/api/v1'), '/');
|
|
$this->api_key = get_option('ifnex_api_key', '');
|
|
}
|
|
|
|
/**
|
|
* دریافت توکن Sanctum از لاراول برای کاربر وردپرس
|
|
*/
|
|
public function get_user_token($user_id) {
|
|
$token = get_user_meta($user_id, 'ifnex_laravel_token', true);
|
|
$token_expiry = get_user_meta($user_id, 'ifnex_laravel_token_expiry', true);
|
|
|
|
// اگر توکن معتبر وجود دارد، از آن استفاده کن
|
|
if ($token && $token_expiry && strtotime($token_expiry) > time()) {
|
|
return $token;
|
|
}
|
|
|
|
// دریافت اطلاعات کاربر وردپرس
|
|
$wp_user = get_userdata($user_id);
|
|
if (!$wp_user) {
|
|
return false;
|
|
}
|
|
|
|
// درخواست توکن از Bridge endpoint (جدید)
|
|
$token = $this->request_bridge_token(
|
|
$user_id,
|
|
$wp_user->user_email,
|
|
$wp_user->display_name
|
|
);
|
|
|
|
if ($token) {
|
|
update_user_meta($user_id, 'ifnex_laravel_token', $token);
|
|
update_user_meta($user_id, 'ifnex_laravel_token_expiry', date('Y-m-d H:i:s', strtotime('+30 days')));
|
|
}
|
|
|
|
return $token;
|
|
}
|
|
|
|
/**
|
|
* درخواست توکن از Bridge endpoint جدید
|
|
* این روش نیازی به رمز عبور ندارد و از API Key مشترک استفاده میکند
|
|
*/
|
|
private function request_bridge_token($wp_user_id, $email, $name) {
|
|
$url = $this->api_url . '/bridge/login';
|
|
$bridge_key = get_option('ifnex_bridge_api_key', '');
|
|
|
|
if (empty($bridge_key)) {
|
|
error_log('IFNEX: Bridge API Key is not configured in WordPress settings');
|
|
return false;
|
|
}
|
|
|
|
$args = array(
|
|
'headers' => array(
|
|
'Content-Type' => 'application/json',
|
|
'Accept' => 'application/json',
|
|
),
|
|
'body' => json_encode(array(
|
|
'bridge_api_key' => $bridge_key,
|
|
'wp_user_id' => $wp_user_id,
|
|
'wp_user_email' => $email,
|
|
'wp_user_name' => $name,
|
|
'token_name' => 'wordpress-bridge-' . get_current_blog_id(),
|
|
)),
|
|
'timeout' => 30,
|
|
);
|
|
|
|
$response = wp_remote_post($url, $args);
|
|
|
|
if (is_wp_error($response)) {
|
|
error_log('IFNEX Bridge Error: ' . $response->get_error_message());
|
|
return false;
|
|
}
|
|
|
|
$body = wp_remote_retrieve_body($response);
|
|
$data = json_decode($body, true);
|
|
|
|
if (isset($data['token'])) {
|
|
return $data['token'];
|
|
}
|
|
|
|
error_log('IFNEX Bridge Token Error: ' . ($data['message'] ?? 'Unknown error'));
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* ارسال درخواست احراز هویتشده به API لاراول
|
|
*/
|
|
public function authenticated_request($user_id, $endpoint, $method = 'GET', $data = array()) {
|
|
$token = $this->get_user_token($user_id);
|
|
|
|
if (!$token) {
|
|
return new WP_Error('no_token', 'توکن احراز هویت یافت نشد.');
|
|
}
|
|
|
|
$url = $this->api_url . $endpoint;
|
|
$method = strtoupper($method);
|
|
|
|
$args = array(
|
|
'headers' => array(
|
|
'Authorization' => 'Bearer ' . $token,
|
|
'Accept' => 'application/json',
|
|
'Content-Type' => 'application/json',
|
|
),
|
|
'timeout' => 30,
|
|
'method' => $method, // ✅ همیشه متد درخواستی استفاده شود
|
|
);
|
|
|
|
// ✅ اصلاح: بر اساس متد، data را اضافه کن
|
|
if (in_array($method, ['POST', 'PUT', 'PATCH'])) {
|
|
// حتی اگر data خالی باشد، body را خالی بفرست
|
|
$args['body'] = json_encode($data ?: new \stdClass());
|
|
} elseif ($method === 'GET' && !empty($data)) {
|
|
// برای GET، data را به URL اضافه کن
|
|
$url = add_query_arg($data, $url);
|
|
}
|
|
|
|
$response = wp_remote_request($url, $args);
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
$body = wp_remote_retrieve_body($response);
|
|
$code = wp_remote_retrieve_response_code($response);
|
|
$data = json_decode($body, true);
|
|
|
|
// لاگ برای دیباگ
|
|
error_log("IFNEX API [{$method}] {$endpoint} => Status: {$code}");
|
|
|
|
if ($code === 401) {
|
|
$this->invalidate_token($user_id);
|
|
return new WP_Error(
|
|
'token_expired',
|
|
'توکن منقضی شده است. لطفاً دوباره وارد شوید.'
|
|
);
|
|
}
|
|
|
|
// اگر پاسخ JSON نیست
|
|
if (!is_array($data)) {
|
|
return new WP_Error('invalid_response', 'پاسخ نامعتبر از سرور: ' . substr($body, 0, 200));
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* باطل کردن توکن
|
|
*/
|
|
public function invalidate_token($user_id) {
|
|
delete_user_meta($user_id, 'ifnex_laravel_token');
|
|
delete_user_meta($user_id, 'ifnex_laravel_token_expiry');
|
|
}
|
|
|
|
/**
|
|
* دریافت موجودی کیف پول کاربر
|
|
*/
|
|
public function get_wallet_balance($user_id) {
|
|
$result = $this->authenticated_request($user_id, '/wallet/balance');
|
|
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
|
|
return array(
|
|
'balance' => $result['balance'] ?? 0,
|
|
'total_deposited' => $result['total_deposited'] ?? 0,
|
|
'total_withdrawn' => $result['total_withdrawn'] ?? 0,
|
|
'is_frozen' => $result['is_frozen'] ?? false,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* دریافت تراکنشهای کاربر
|
|
*/
|
|
public function get_transactions($user_id, $per_page = 15) {
|
|
$result = $this->authenticated_request($user_id, '/wallet/transactions', 'GET', array(
|
|
'per_page' => $per_page,
|
|
));
|
|
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* رهگیری مرسوله
|
|
*/
|
|
public function track_shipment($awb_no) {
|
|
if (empty($this->api_key)) {
|
|
return new WP_Error('no_api_key', 'API Key تنظیم نشده است.');
|
|
}
|
|
|
|
$url = $this->api_url . '/track/' . urlencode($awb_no);
|
|
|
|
$args = array(
|
|
'headers' => array(
|
|
'Authorization' => 'Bearer ' . $this->api_key,
|
|
'Accept' => 'application/json',
|
|
),
|
|
'timeout' => 30,
|
|
);
|
|
|
|
$response = wp_remote_get($url, $args);
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
$body = wp_remote_retrieve_body($response);
|
|
$data = json_decode($body, true);
|
|
|
|
if ($response['response']['code'] === 404) {
|
|
return new WP_Error('not_found', 'مرسولهای با این کد پیدا نشد.');
|
|
}
|
|
|
|
if ($response['response']['code'] !== 200) {
|
|
return new WP_Error('api_error', 'خطا در ارتباط با API: ' . ($data['message'] ?? 'Unknown error'));
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════════════
|
|
// Customer Orders API (جدید)
|
|
// ══════════════════════════════════════════════════════════════
|
|
|
|
/**
|
|
* دریافت پروفایل و آمار کاربر
|
|
*/
|
|
public function get_customer_profile($user_id) {
|
|
$result = $this->authenticated_request($user_id, '/customer/profile');
|
|
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* دریافت لیست کشورها
|
|
*/
|
|
public function get_countries($user_id) {
|
|
$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;
|
|
}
|
|
|
|
$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'];
|
|
}
|
|
|
|
/**
|
|
* دریافت لیست سفارشات کاربر
|
|
*/
|
|
public function get_customer_orders($user_id, $per_page = 15, $status = null) {
|
|
$params = ['per_page' => $per_page];
|
|
|
|
if ($status) {
|
|
$params['status'] = $status;
|
|
}
|
|
|
|
$result = $this->authenticated_request($user_id, '/customer/orders', 'GET', $params);
|
|
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* دریافت جزئیات یک سفارش
|
|
*/
|
|
public function get_customer_order($user_id, $shipment_id) {
|
|
$result = $this->authenticated_request($user_id, "/customer/orders/{$shipment_id}");
|
|
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* ثبت سفارش جدید
|
|
*/
|
|
public function create_customer_order($user_id, $order_data) {
|
|
$result = $this->authenticated_request($user_id, '/customer/orders', 'POST', $order_data);
|
|
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* ارسال مجدد سفارش پس از درخواست اصلاح
|
|
*/
|
|
public function resubmit_customer_order($user_id, $shipment_id, $order_data) {
|
|
$result = $this->authenticated_request(
|
|
$user_id,
|
|
"/customer/orders/{$shipment_id}/resubmit",
|
|
'POST',
|
|
$order_data
|
|
);
|
|
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* لغو سفارش
|
|
*/
|
|
public function cancel_customer_order($user_id, $shipment_id) {
|
|
$result = $this->authenticated_request($user_id, "/customer/orders/{$shipment_id}/cancel", 'POST');
|
|
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* دریافت نوتیفیکیشنهای کاربر
|
|
*/
|
|
public function get_notifications($user_id, $per_page = 20) {
|
|
$result = $this->authenticated_request($user_id, '/customer/notifications', 'GET', array(
|
|
'per_page' => $per_page,
|
|
));
|
|
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* علامتگذاری نوتیفیکیشن به عنوان خواندهشده
|
|
*/
|
|
public function mark_notification_read($user_id, $notification_id) {
|
|
$result = $this->authenticated_request($user_id, "/customer/notifications/{$notification_id}/read", 'POST');
|
|
return $result;
|
|
}
|
|
}
|
|
|
|
// AJAX Handler برای لغو سفارش
|
|
add_action('wp_ajax_ifnex_cancel_order', 'ifnex_cancel_order_ajax');
|
|
|
|
function ifnex_cancel_order_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->cancel_customer_order(
|
|
get_current_user_id(),
|
|
$order_id
|
|
);
|
|
|
|
if (is_wp_error($result)) {
|
|
wp_send_json_error($result->get_error_message());
|
|
}
|
|
|
|
wp_send_json_success('سفارش لغو شد.');
|
|
}
|
|
|
|
|
|
// AJAX Handler برای دریافت سفارش جهت اصلاح و ارسال مجدد
|
|
add_action(
|
|
'wp_ajax_ifnex_get_resubmit_order',
|
|
'ifnex_get_resubmit_order_ajax'
|
|
);
|
|
|
|
function ifnex_get_resubmit_order_ajax() {
|
|
|
|
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
|
|
|
|
if (!is_user_logged_in()) {
|
|
wp_send_json_error('برای اصلاح سفارش باید وارد حساب خود شوید.');
|
|
}
|
|
|
|
$order_id = absint($_POST['order_id'] ?? 0);
|
|
|
|
if (!$order_id) {
|
|
wp_send_json_error('شناسه سفارش نامعتبر است.');
|
|
}
|
|
|
|
$user_id = get_current_user_id();
|
|
|
|
$bridge = new IFNEX_User_Bridge();
|
|
|
|
/*
|
|
* سفارش از طریق endpoint احرازشده مشتری دریافت میشود.
|
|
* بنابراین مالکیت سفارش نیز در Laravel بررسی میشود.
|
|
*/
|
|
$result = $bridge->get_customer_order(
|
|
$user_id,
|
|
$order_id
|
|
);
|
|
|
|
if (is_wp_error($result)) {
|
|
wp_send_json_error($result->get_error_message());
|
|
}
|
|
|
|
if (empty($result['data']) || !is_array($result['data'])) {
|
|
wp_send_json_error('اطلاعات سفارش یافت نشد.');
|
|
}
|
|
|
|
$order = $result['data'];
|
|
$review = $order['review'] ?? [];
|
|
|
|
/*
|
|
* فقط سفارشی که آخرین تصمیم آن changes_requested است
|
|
* قابلیت اصلاح و ارسال مجدد دارد.
|
|
*/
|
|
if (($review['state'] ?? '') !== 'changes_requested') {
|
|
wp_send_json_error(
|
|
'این سفارش در حال حاضر قابل اصلاح و ارسال مجدد نیست.'
|
|
);
|
|
}
|
|
|
|
if (empty($review['can_resubmit'])) {
|
|
wp_send_json_error(
|
|
'امکان ارسال مجدد این سفارش فعال نیست.'
|
|
);
|
|
}
|
|
|
|
$order_id = $order['id'] ?? $order_id;
|
|
|
|
wp_send_json_success([
|
|
'id' => $order_id,
|
|
|
|
'direction' => $order['direction'] ?? '',
|
|
'type' => $order['type'] ?? '',
|
|
|
|
'from_country' => $order['from_country'] ?? null,
|
|
'to_country' => $order['to_country'] ?? null,
|
|
|
|
'sender' => $order['sender'] ?? null,
|
|
'receiver' => $order['receiver'] ?? null,
|
|
|
|
'packages' => $order['packages'] ?? [],
|
|
'items' => $order['items'] ?? [],
|
|
|
|
'review' => $review,
|
|
]);
|
|
}
|
|
|
|
// AJAX Handler برای ارسال مجدد سفارش
|
|
add_action('wp_ajax_ifnex_resubmit_order', 'ifnex_resubmit_order_ajax');
|
|
|
|
function ifnex_resubmit_order_ajax() {
|
|
|
|
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
|
|
|
|
if (!is_user_logged_in()) {
|
|
wp_send_json_error('برای ارسال مجدد سفارش باید وارد حساب خود شوید.');
|
|
}
|
|
|
|
$order_id = absint($_POST['order_id'] ?? 0);
|
|
|
|
if (!$order_id) {
|
|
wp_send_json_error('شناسه سفارش نامعتبر است.');
|
|
}
|
|
|
|
$data = $_POST['data'] ?? [];
|
|
|
|
/*
|
|
* بسته به نحوه ارسال jQuery ممکن است data
|
|
* به صورت array یا JSON string دریافت شود.
|
|
*/
|
|
if (is_string($data)) {
|
|
$decoded = json_decode(wp_unslash($data), true);
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
wp_send_json_error('دادههای سفارش نامعتبر هستند.');
|
|
}
|
|
|
|
$data = $decoded;
|
|
}
|
|
|
|
if (!is_array($data) || empty($data)) {
|
|
wp_send_json_error('دادههای سفارش ارسال نشده است.');
|
|
}
|
|
|
|
$bridge = new IFNEX_User_Bridge();
|
|
|
|
$result = $bridge->resubmit_customer_order(
|
|
get_current_user_id(),
|
|
$order_id,
|
|
$data
|
|
);
|
|
|
|
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);
|
|
}
|
|
|
|
// Enqueue AJAX script
|
|
add_action('wp_enqueue_scripts', 'ifnex_enqueue_ajax_script');
|
|
|
|
function ifnex_enqueue_ajax_script() {
|
|
global $post;
|
|
|
|
if (is_a($post, 'WP_Post') && has_shortcode($post->post_content, 'ifnex_orders_list')) {
|
|
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'),
|
|
));
|
|
}
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════════════
|
|
// AJAX Handlers برای فرم سفارش
|
|
// ══════════════════════════════════════════════════════════════
|
|
|
|
add_action('wp_ajax_ifnex_get_countries', 'ifnex_get_countries_ajax');
|
|
function ifnex_get_countries_ajax() {
|
|
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
|
|
if (!is_user_logged_in()) wp_send_json_error('باید وارد شوید.');
|
|
|
|
$bridge = new IFNEX_User_Bridge();
|
|
$result = $bridge->get_countries(get_current_user_id());
|
|
|
|
if (is_wp_error($result)) wp_send_json_error($result->get_error_message());
|
|
wp_send_json_success($result);
|
|
}
|
|
|
|
add_action('wp_ajax_ifnex_calculate_price', 'ifnex_calculate_price_ajax');
|
|
function ifnex_calculate_price_ajax() {
|
|
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
|
|
if (!is_user_logged_in()) wp_send_json_error('باید وارد شوید.');
|
|
|
|
$data = $_POST['data'] ?? [];
|
|
if (empty($data)) wp_send_json_error('دادهای ارسال نشده.');
|
|
|
|
// تبدیل direction برای PriceCalculator
|
|
$payload = array(
|
|
'direction' => ($data['direction'] === 'export') ? 'Outbound' : 'Inbound',
|
|
'type' => $data['type'],
|
|
'country_iso' => $data['country_iso'],
|
|
'weight' => floatval($data['weight']),
|
|
'volumetric_weight' => floatval($data['volumetric_weight'] ?? 0),
|
|
);
|
|
if (!empty($data['discount_code'])) $payload['discount_code'] = $data['discount_code'];
|
|
|
|
$api_url = rtrim(get_option('ifnex_api_url', 'http://localhost:8000/api/v1'), '/');
|
|
$response = wp_remote_post($api_url . '/calculate', array(
|
|
'headers' => array('Content-Type' => 'application/json', 'Accept' => 'application/json'),
|
|
'body' => json_encode($payload),
|
|
'timeout' => 30,
|
|
));
|
|
|
|
if (is_wp_error($response)) wp_send_json_error($response->get_error_message());
|
|
|
|
$body = json_decode(wp_remote_retrieve_body($response), true);
|
|
if ($response['response']['code'] !== 200) {
|
|
wp_send_json_error($body['message'] ?? 'خطا در محاسبه قیمت');
|
|
}
|
|
wp_send_json_success($body);
|
|
}
|
|
|
|
add_action('wp_ajax_ifnex_submit_order', 'ifnex_submit_order_ajax');
|
|
function ifnex_submit_order_ajax() {
|
|
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
|
|
if (!is_user_logged_in()) wp_send_json_error('باید وارد شوید.');
|
|
|
|
$data = $_POST['data'] ?? [];
|
|
if (empty($data)) wp_send_json_error('دادهای ارسال نشده.');
|
|
|
|
$bridge = new IFNEX_User_Bridge();
|
|
$result = $bridge->create_customer_order(get_current_user_id(), $data);
|
|
|
|
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);
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════════════
|
|
// AJAX Handlers دفترچه آدرس (فاز ۳.۹)
|
|
// پروکسی امن به API لاراول — کاربر فقط به رکوردهای خودش دسترسی دارد
|
|
// (Ownership در سمت لاراول با توکن Sanctum تضمین میشود)
|
|
// ══════════════════════════════════════════════════════════════
|
|
|
|
add_action('wp_ajax_ifnex_get_addresses', 'ifnex_get_addresses_ajax');
|
|
function ifnex_get_addresses_ajax() {
|
|
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
|
|
if (!is_user_logged_in()) wp_send_json_error('باید وارد شوید.');
|
|
|
|
$args = array();
|
|
if (!empty($_POST['role']) && in_array($_POST['role'], ['sender', 'receiver'], true)) {
|
|
$args['role'] = sanitize_key($_POST['role']);
|
|
}
|
|
if (!empty($_POST['search'])) {
|
|
$args['search'] = sanitize_text_field($_POST['search']);
|
|
}
|
|
|
|
$bridge = new IFNEX_User_Bridge();
|
|
$result = $bridge->authenticated_request(get_current_user_id(), '/addresses', 'GET', $args);
|
|
|
|
if (is_wp_error($result)) wp_send_json_error($result->get_error_message());
|
|
wp_send_json_success($result);
|
|
}
|
|
|
|
add_action('wp_ajax_ifnex_save_address', 'ifnex_save_address_ajax');
|
|
function ifnex_save_address_ajax() {
|
|
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
|
|
if (!is_user_logged_in()) wp_send_json_error('باید وارد شوید.');
|
|
|
|
$data = $_POST['data'] ?? [];
|
|
if (empty($data)) wp_send_json_error('دادهای ارسال نشده.');
|
|
|
|
// پاکسازی ورودیها قبل از ارسال به API
|
|
$clean = array(
|
|
'role' => in_array($data['role'] ?? '', ['sender', 'receiver'], true) ? $data['role'] : 'receiver',
|
|
'label' => sanitize_text_field($data['label'] ?? ''),
|
|
'name' => sanitize_text_field($data['name'] ?? ''),
|
|
'phone' => sanitize_text_field($data['phone'] ?? ''),
|
|
'state' => sanitize_text_field($data['state'] ?? ''),
|
|
'city' => sanitize_text_field($data['city'] ?? ''),
|
|
'postal_code' => sanitize_text_field($data['postal_code'] ?? ''),
|
|
'national_id' => sanitize_text_field($data['national_id'] ?? ''),
|
|
'address' => sanitize_textarea_field($data['address'] ?? ''),
|
|
);
|
|
if (!empty($data['email'])) $clean['email'] = sanitize_email($data['email']);
|
|
if (!empty($data['is_default'])) $clean['is_default'] = true;
|
|
|
|
$bridge = new IFNEX_User_Bridge();
|
|
$user_id = get_current_user_id();
|
|
|
|
if (!empty($data['id'])) {
|
|
// ویرایش رکورد موجود
|
|
$address_id = intval($data['id']);
|
|
if ($address_id <= 0) wp_send_json_error('شناسه آدرس نامعتبر است.');
|
|
$result = $bridge->authenticated_request($user_id, '/addresses/' . $address_id, 'PUT', $clean);
|
|
} else {
|
|
// افزودن رکورد جدید
|
|
$result = $bridge->authenticated_request($user_id, '/addresses', 'POST', $clean);
|
|
}
|
|
|
|
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_delete_address', 'ifnex_delete_address_ajax');
|
|
function ifnex_delete_address_ajax() {
|
|
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
|
|
if (!is_user_logged_in()) wp_send_json_error('باید وارد شوید.');
|
|
|
|
$address_id = intval($_POST['id'] ?? 0);
|
|
if ($address_id <= 0) wp_send_json_error('شناسه آدرس نامعتبر است.');
|
|
|
|
$bridge = new IFNEX_User_Bridge();
|
|
$result = $bridge->authenticated_request(get_current_user_id(), '/addresses/' . $address_id, 'DELETE');
|
|
|
|
if (is_wp_error($result)) wp_send_json_error($result->get_error_message());
|
|
wp_send_json_success($result);
|
|
}
|
|
|
|
add_action('wp_ajax_ifnex_set_default_address', 'ifnex_set_default_address_ajax');
|
|
function ifnex_set_default_address_ajax() {
|
|
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
|
|
if (!is_user_logged_in()) wp_send_json_error('باید وارد شوید.');
|
|
|
|
$address_id = intval($_POST['id'] ?? 0);
|
|
if ($address_id <= 0) wp_send_json_error('شناسه آدرس نامعتبر است.');
|
|
|
|
$bridge = new IFNEX_User_Bridge();
|
|
$result = $bridge->authenticated_request(get_current_user_id(), '/addresses/' . $address_id . '/default', 'POST');
|
|
|
|
if (is_wp_error($result)) wp_send_json_error($result->get_error_message());
|
|
wp_send_json_success($result);
|
|
}
|
|
|
|
// Enqueue scripts & styles برای شورتکدهای جدید
|
|
add_action('wp_enqueue_scripts', 'ifnex_enqueue_order_assets');
|
|
function ifnex_enqueue_order_assets() {
|
|
global $post;
|
|
if (!is_a($post, 'WP_Post')) return;
|
|
|
|
$plugin_url = trailingslashit(plugins_url('', dirname(__FILE__)));
|
|
|
|
// لود CSS — وابسته به CSS قالب
|
|
wp_enqueue_style(
|
|
'ifnex-orders-css',
|
|
$plugin_url . 'assets/css/ifnex-orders.css',
|
|
array('ifnex-main'),
|
|
ifnex_asset_version('assets/css/ifnex-orders.css')
|
|
);
|
|
|
|
// لود jQuery & JS
|
|
wp_enqueue_script('jquery');
|
|
// SweetAlert2 — پاپآپ خطاهای فرم سفارش (فاز ۳.۸) — باندل محلی، بدون وابستگی به CDN
|
|
wp_enqueue_script(
|
|
'sweetalert2',
|
|
$plugin_url . 'assets/js/sweetalert2.all.min.js',
|
|
array(),
|
|
'11.14.5',
|
|
true
|
|
);
|
|
wp_enqueue_script(
|
|
'ifnex-order-form-js',
|
|
$plugin_url . 'assets/js/ifnex-order-form.js',
|
|
array('jquery', 'sweetalert2'),
|
|
ifnex_asset_version('assets/js/ifnex-order-form.js'),
|
|
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-account/?tab=orders'),
|
|
));
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════════════
|
|
// AJAX Handler برای شارژ کیف پول
|
|
// ══════════════════════════════════════════════════════════════
|
|
|
|
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('باید وارد شوید.');
|
|
}
|
|
|
|
$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(),
|
|
'/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'] ?? 'خطا در اتصال به درگاه');
|
|
}
|
|
|
|
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');
|
|
if (!is_user_logged_in()) wp_send_json_error('باید وارد شوید.');
|
|
|
|
$order_id = intval($_POST['order_id'] ?? 0);
|
|
$callback_url = sanitize_url($_POST['callback_url'] ?? '');
|
|
|
|
if (!$order_id) wp_send_json_error('شناسه سفارش نامعتبر است.');
|
|
if (!$callback_url) wp_send_json_error('آدرس بازگشت نامعتبر است.');
|
|
|
|
$bridge = new IFNEX_User_Bridge();
|
|
$result = $bridge->authenticated_request(
|
|
get_current_user_id(),
|
|
"/customer/orders/{$order_id}/pay-gateway",
|
|
'POST',
|
|
['frontend_callback' => $callback_url]
|
|
);
|
|
|
|
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);
|
|
}
|
|
|
|
// Enqueue برای صفحه پرداخت
|
|
add_action('wp_enqueue_scripts', 'ifnex_enqueue_payment_assets');
|
|
function ifnex_enqueue_payment_assets() {
|
|
global $post;
|
|
if (!is_a($post, 'WP_Post')) return;
|
|
|
|
if (has_shortcode($post->post_content, 'ifnex_order_payment')) {
|
|
$plugin_url = trailingslashit(plugins_url('', dirname(__FILE__)));
|
|
wp_enqueue_style(
|
|
'ifnex-orders-css',
|
|
$plugin_url . 'assets/css/ifnex-orders.css',
|
|
array('ifnex-main'),
|
|
ifnex_asset_version('assets/css/ifnex-orders.css')
|
|
);
|
|
wp_enqueue_script('jquery');
|
|
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');
|
|
}
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════════════
|
|
// لود سراسری CSS و متغیرهای AJAX برای همه صفحات IFNEX
|
|
// این تابع همیشه اجرا میشود تا هیچ شورتکدی بدون استایل نماند
|
|
// ══════════════════════════════════════════════════════════════
|
|
|
|
add_action('wp_enqueue_scripts', 'ifnex_enqueue_global_assets', 20);
|
|
|
|
function ifnex_enqueue_global_assets() {
|
|
$plugin_url = trailingslashit(plugins_url('', dirname(__FILE__)));
|
|
|
|
// لود CSS اصلی — وابسته به CSS قالب تا بعد از آن لود شود
|
|
wp_enqueue_style(
|
|
'ifnex-orders-css',
|
|
$plugin_url . 'assets/css/ifnex-orders.css',
|
|
array('ifnex-main'),
|
|
ifnex_asset_version('assets/css/ifnex-orders.css')
|
|
);
|
|
|
|
// لود jQuery
|
|
wp_enqueue_script('jquery');
|
|
|
|
// متغیرهای AJAX برای همه صفحات
|
|
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');
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════════════
|
|
// AJAX Handler برای دانلود مدارک PDF سفارش
|
|
// ══════════════════════════════════════════════════════════════
|
|
|
|
add_action('wp_ajax_ifnex_download_order_pdf', 'ifnex_download_order_pdf_ajax');
|
|
|
|
function ifnex_download_order_pdf_ajax() {
|
|
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
|
|
if (!is_user_logged_in()) {
|
|
wp_send_json_error('باید وارد شوید.');
|
|
}
|
|
|
|
$order_id = intval($_POST['order_id'] ?? 0);
|
|
$download_type = sanitize_text_field($_POST['download_type'] ?? '');
|
|
|
|
if (!$order_id) wp_send_json_error('شناسه سفارش نامعتبر است.');
|
|
if (!in_array($download_type, ['awb', 'invoice', 'label', 'import-invoice'])) wp_send_json_error('نوع فایل نامعتبر است.');
|
|
|
|
$bridge = new IFNEX_User_Bridge();
|
|
$user_id = get_current_user_id();
|
|
|
|
// بررسی مالکیت سفارش
|
|
$order = $bridge->get_customer_order($user_id, $order_id);
|
|
if (is_wp_error($order)) wp_send_json_error($order->get_error_message());
|
|
|
|
// ساخت URL دانلود از API لاراول
|
|
$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 = $bridge->get_user_token($user_id); // با تجدید خودکار در صورت انقضا/حذف
|
|
if (!$token) wp_send_json_error('توکن احراز هویت در دسترس نیست — لطفاً دوباره وارد شوید.');
|
|
|
|
$response = wp_remote_get($download_url, array(
|
|
'headers' => array(
|
|
'Authorization' => 'Bearer ' . $token,
|
|
'Accept' => 'application/pdf',
|
|
),
|
|
'timeout' => 30,
|
|
));
|
|
|
|
if (is_wp_error($response)) wp_send_json_error('خطا در ارتباط با سرور.');
|
|
|
|
$status_code = wp_remote_retrieve_response_code($response);
|
|
if ($status_code !== 200) wp_send_json_error('خطا در دانلود فایل.');
|
|
|
|
$file_content = wp_remote_retrieve_body($response);
|
|
$filename = strtoupper($download_type) . '-' . $order['data']['awb_no'] . '.pdf';
|
|
|
|
// ارسال فایل به مرورگر
|
|
nocache_headers();
|
|
header('Content-Type: application/pdf');
|
|
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
|
header('Content-Length: ' . strlen($file_content));
|
|
echo $file_content;
|
|
exit;
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════════════
|
|
// AJAX Handler برای دریافت لیست تعهدنامههای سفارش
|
|
// ══════════════════════════════════════════════════════════════
|
|
|
|
add_action('wp_ajax_ifnex_get_commitment_forms', 'ifnex_get_commitment_forms_ajax');
|
|
|
|
function ifnex_get_commitment_forms_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();
|
|
$user_id = get_current_user_id();
|
|
|
|
$order = $bridge->get_customer_order($user_id, $order_id);
|
|
if (is_wp_error($order)) wp_send_json_error($order->get_error_message());
|
|
|
|
$direction = strtolower($order['data']['direction'] ?? 'export');
|
|
$api_url = get_option('ifnex_api_url', 'http://localhost:8000/api/v1');
|
|
$url = rtrim($api_url, '/') . "/customer/orders/{$order_id}/commitment-forms";
|
|
|
|
$token = $bridge->get_user_token($user_id); // با تجدید خودکار در صورت انقضا/حذف
|
|
if (!$token) wp_send_json_error('توکن احراز هویت در دسترس نیست — لطفاً دوباره وارد شوید.');
|
|
|
|
$response = wp_remote_get($url, array(
|
|
'headers' => array(
|
|
'Authorization' => 'Bearer ' . $token,
|
|
),
|
|
'timeout' => 30,
|
|
));
|
|
|
|
if (is_wp_error($response)) wp_send_json_error('خطا در ارتباط با سرور.');
|
|
|
|
$body = json_decode(wp_remote_retrieve_body($response), true);
|
|
if (!$body || !isset($body['success'])) wp_send_json_error('خطا در دریافت اطلاعات.');
|
|
|
|
wp_send_json_success($body['data']);
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════════════
|
|
// AJAX Handler برای آپلود فرم تعهدنامه امضاشده
|
|
// ══════════════════════════════════════════════════════════════
|
|
|
|
add_action('wp_ajax_ifnex_upload_commitment_form', 'ifnex_upload_commitment_form_ajax');
|
|
|
|
function ifnex_upload_commitment_form_ajax() {
|
|
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
|
|
if (!is_user_logged_in()) {
|
|
wp_send_json_error('باید وارد شوید.');
|
|
}
|
|
|
|
$order_id = intval($_POST['order_id'] ?? 0);
|
|
$form_id = intval($_POST['form_id'] ?? 0);
|
|
|
|
if (!$order_id) wp_send_json_error('شناسه سفارش نامعتبر است.');
|
|
if (!$form_id) wp_send_json_error('شناسه فرم نامعتبر است.');
|
|
if (!isset($_FILES['file'])) wp_send_json_error('فایل ارسال نشده است.');
|
|
|
|
$bridge = new IFNEX_User_Bridge();
|
|
$user_id = get_current_user_id();
|
|
|
|
$order = $bridge->get_customer_order($user_id, $order_id);
|
|
if (is_wp_error($order)) wp_send_json_error($order->get_error_message());
|
|
|
|
$file = $_FILES['file'];
|
|
$allowed_types = array('application/pdf', 'image/jpeg', 'image/jpg', 'image/png');
|
|
$max_size = 5 * 1024 * 1024; // 5MB
|
|
|
|
if (!in_array($file['type'], $allowed_types)) wp_send_json_error('فرمت فایل مجاز نیست. فقط PDF, JPG, PNG.');
|
|
if ($file['size'] > $max_size) wp_send_json_error('حجم فایل نباید بیشتر از 5MB باشد.');
|
|
|
|
// آپلود فایل به وردپرس
|
|
require_once(ABSPATH . 'wp-admin/includes/file.php');
|
|
require_once(ABSPATH . 'wp-admin/includes/media.php');
|
|
require_once(ABSPATH . 'wp-admin/includes/image.php');
|
|
|
|
$upload = wp_handle_upload($file, array('test_form' => false));
|
|
if (isset($upload['error'])) wp_send_json_error('خطا در آپلود: ' . $upload['error']);
|
|
|
|
$file_path = str_replace(wp_get_upload_dir()['basedir'] . '/', '', $upload['file']);
|
|
$file_url = $upload['url'];
|
|
|
|
// ارسال به API لاراول
|
|
$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 = $bridge->get_user_token($user_id); // با تجدید خودکار در صورت انقضا/حذف
|
|
if (!$token) wp_send_json_error('توکن احراز هویت در دسترس نیست — لطفاً دوباره وارد شوید.');
|
|
|
|
$multipart = new CURLFile($upload['file'], $file['type'], $file['name']);
|
|
$post_fields = array(
|
|
'file' => $multipart,
|
|
'notes' => sanitize_textarea_field($_POST['notes'] ?? ''),
|
|
);
|
|
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
curl_setopt($ch, CURLOPT_POST, 1);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
|
'Authorization: Bearer ' . $token,
|
|
));
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
|
|
$response = curl_exec($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($http_code !== 200 && $http_code !== 201) wp_send_json_error('خطا در ارسال فایل به سرور.');
|
|
|
|
$result = json_decode($response, true);
|
|
if (!$result || !isset($result['success'])) wp_send_json_error('خطا در پردازش پاسخ سرور.');
|
|
|
|
wp_send_json_success($result['data']);
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
// AJAX: پرداخت فاکتور دستی از کیف پول
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
add_action('wp_ajax_ifnex_pay_invoice', 'ifnex_pay_invoice_ajax');
|
|
function ifnex_pay_invoice_ajax() {
|
|
check_ajax_referer('ifnex_ajax_nonce', 'nonce');
|
|
if (!is_user_logged_in()) {
|
|
wp_send_json_error('باید وارد شوید.');
|
|
}
|
|
|
|
$invoice_id = intval($_POST['invoice_id'] ?? 0);
|
|
if (!$invoice_id) {
|
|
wp_send_json_error('شناسه فاکتور نامعتبر است.');
|
|
}
|
|
|
|
$bridge = new IFNEX_User_Bridge();
|
|
$result = $bridge->authenticated_request(
|
|
get_current_user_id(),
|
|
"/customer/invoices/{$invoice_id}/pay",
|
|
'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);
|
|
}
|