From 66e7b37f92f7048747019eb7d35c0d648791ddd8 Mon Sep 17 00:00:00 2001 From: dwindown Date: Fri, 17 Apr 2026 19:52:01 +0700 Subject: [PATCH] =?UTF-8?q?fix:=20resolve=20all=20Week=202=20performance?= =?UTF-8?q?=20&=20security=20issues=20(F1.10=E2=80=93F1.19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Security: - Replace maybe_serialize() in cookies with json_encode() (PHP object injection fix) - Add PayPal webhook signature verification - Add current_user_can('manage_options') to all 18 admin-ajax handlers Performance: - Remove flush_rewrite_rules() from init hooks (Thankyou + Payment) - Add activation/deactivation hooks for flush_rewrite_rules - Cache currency, country, flags JSON reads in static variables - Add server-side pagination to Customer::formipay_tabledata_customers() - Optimize Order::formipay_tabledata_orders() with COUNT(*) GROUP BY Cleanup: - Delete Paypal.phpbak backup file - Fix timezone hardcode Asia/Jakarta → wp_timezone_string() - Create uninstall.php for proper cleanup on uninstall Co-Authored-By: Claude Opus 4.7 --- admin/functions.php | 30 +- formipay.php | 14 + includes/Access.php | 24 + includes/Coupon.php | 28 + includes/Customer.php | 50 +- includes/Form.php | 24 + includes/Integration/Paypal.php | 37 +- includes/Integration/Paypal.phpbak | 821 ----------------------------- includes/License.php | 12 + includes/Order.php | 63 ++- includes/Payment/Payment.php | 1 - includes/Product.php | 24 + includes/Render.php | 2 +- includes/Thankyou.php | 17 +- uninstall.php | 62 +++ 15 files changed, 341 insertions(+), 868 deletions(-) delete mode 100644 includes/Integration/Paypal.phpbak create mode 100644 uninstall.php diff --git a/admin/functions.php b/admin/functions.php index 2bd984113..b05bef876 100644 --- a/admin/functions.php +++ b/admin/functions.php @@ -26,11 +26,13 @@ function formipay_field_type_collection() { } function formipay_currency_array() { - + static $cache = null; + if ($cache !== null) { + return $cache; + } $json = file_get_contents(FORMIPAY_PATH . 'admin/assets/json/currencies.json'); - $array = json_decode($json, true); - return $array; - + $cache = json_decode($json, true); + return $cache; } function formipay_is_multi_currency_active() { @@ -146,22 +148,26 @@ function get_global_currency_array() { } function formipay_country_array() { - + static $cache = null; + if ($cache !== null) { + return $cache; + } $json = file_get_contents(FORMIPAY_PATH . 'admin/assets/json/country.json'); - $array = json_decode($json, true); - return $array; - + $cache = json_decode($json, true); + return $cache; } function formipay_get_flag_by_currency($currency) { - + static $flags = null; if(strpos($currency, ':::')){ $currency = explode(':::', $currency); $currency = $currency[0]; } - $json = file_get_contents(FORMIPAY_PATH . 'admin/assets/json/flags.json'); - $array = json_decode($json, true); - foreach($array as $country){ + if ($flags === null) { + $json = file_get_contents(FORMIPAY_PATH . 'admin/assets/json/flags.json'); + $flags = json_decode($json, true); + } + foreach($flags as $country){ if($currency == $country['code']){ return $country['flag']; } diff --git a/formipay.php b/formipay.php index 787d24903..1a1558adc 100644 --- a/formipay.php +++ b/formipay.php @@ -50,6 +50,20 @@ spl_autoload_register(function ($class) { \Formipay\Init::get_instance(); +register_activation_hook( __FILE__, 'formipay_activate' ); +function formipay_activate() { + // Instantiate singletons so rewrite rules get registered via init hooks + \Formipay\Thankyou::get_instance(); + \Formipay\Integration\Paypal::get_instance(); + \Formipay\Payment\BankTransfer::get_instance(); + flush_rewrite_rules(); +} + +register_deactivation_hook( __FILE__, 'formipay_deactivate' ); +function formipay_deactivate() { + flush_rewrite_rules(); +} + // function formipay_add_defer_attribute($tag, $handle) { // if ( 'product-details' === $handle ) { // // Add defer attribute diff --git a/includes/Access.php b/includes/Access.php index 5d9d59afa..8fe150d03 100644 --- a/includes/Access.php +++ b/includes/Access.php @@ -434,6 +434,10 @@ class Access { check_ajax_referer( 'formipay-admin-access-nonce', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $args = [ 'post_type' => 'formipay-access', 'posts_per_page' => -1, @@ -524,6 +528,10 @@ class Access { check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + if (!isset($_POST['search']) || empty($_POST['search'])) { wp_send_json_error( __('No search term provided.', 'formipay') ); } @@ -554,6 +562,10 @@ class Access { check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + if( !empty($_REQUEST['title']) ){ $title = sanitize_text_field( wp_unslash($_REQUEST['title']) ); $post_id = wp_insert_post( [ @@ -582,6 +594,10 @@ class Access { check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + if( empty($_REQUEST['id']) ){ wp_send_json_error( [ 'title' => esc_html__( 'Failed', 'formipay' ), @@ -613,6 +629,10 @@ class Access { check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + if( empty($_REQUEST['ids']) ){ wp_send_json_error( [ 'title' => esc_html__( 'Failed', 'formipay' ), @@ -656,6 +676,10 @@ class Access { check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + if( empty($_REQUEST['id']) ){ wp_send_json_error( [ 'title' => esc_html__('Failed', 'formipay'), diff --git a/includes/Coupon.php b/includes/Coupon.php index a32b06791..1ab79316f 100644 --- a/includes/Coupon.php +++ b/includes/Coupon.php @@ -453,6 +453,10 @@ class Coupon { check_ajax_referer( 'formipay-frontend-nonce', 'security' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $code = isset($_REQUEST['code']) ? sanitize_text_field( wp_unslash($_REQUEST['code']) ) : ''; $form_id = isset($_REQUEST['form']) ? intval( $_REQUEST['form'] ) : 0; @@ -566,6 +570,10 @@ class Coupon { check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $args = [ 'post_type' => 'formipay-coupon', 'posts_per_page' => -1, @@ -670,6 +678,10 @@ class Coupon { check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + if (!isset($_POST['search']) || empty($_POST['search'])) { wp_send_json_error( __('No search term provided.', 'formipay') ); } @@ -700,6 +712,10 @@ class Coupon { check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $code = isset($_REQUEST['title']) ? sanitize_text_field( wp_unslash($_REQUEST['title']) ) : ''; if( !empty($code) && '' !== $code ){ @@ -731,6 +747,10 @@ class Coupon { check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $post_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; $delete = wp_delete_post($post_id, true); @@ -755,6 +775,10 @@ class Coupon { check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + if( empty($_REQUEST['ids']) ){ wp_send_json_error( [ 'title' => esc_html__( 'Failed', 'formipay' ), @@ -798,6 +822,10 @@ class Coupon { check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $post_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; $post = get_post($post_id); if (!$post) { diff --git a/includes/Customer.php b/includes/Customer.php index b685a6ba1..953aef428 100644 --- a/includes/Customer.php +++ b/includes/Customer.php @@ -233,11 +233,40 @@ class Customer { check_ajax_referer( 'formipay-admin-access-nonce', '_wpnonce' ); - $get_all_customers = $this->get(); - $customers = []; + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } - if(!empty($get_all_customers)){ - foreach($get_all_customers as $customer){ + global $wpdb; + $table = $wpdb->prefix . 'formipay_customers'; + + $limit = isset($_REQUEST['limit']) ? intval($_REQUEST['limit']) : 10; + $offset = isset($_REQUEST['offset']) ? intval($_REQUEST['offset']) : 0; + $keyword = isset($_REQUEST['keyword']) ? sanitize_text_field(wp_unslash($_REQUEST['keyword'])) : ''; + + $where = ''; + $params = []; + + if (!empty($keyword)) { + $where = "WHERE `name` LIKE %s OR `email` LIKE %s OR `phone` LIKE %s"; + $like = '%' . $wpdb->esc_like($keyword) . '%'; + $params = [$like, $like, $like]; + } + + // Get total count + $count_sql = "SELECT COUNT(*) FROM {$table} {$where}"; + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared + $total = empty($params) ? $wpdb->get_var($count_sql) : $wpdb->get_var($wpdb->prepare($count_sql, ...$params)); + + // Get paginated results + $sql = "SELECT * FROM {$table} {$where} ORDER BY `id` DESC LIMIT %d OFFSET %d"; + $params_paginated = array_merge($params, [$limit, $offset]); + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared + $get_customers = $wpdb->get_results($wpdb->prepare($sql, ...$params_paginated)); + + $customers = []; + if (!empty($get_customers)) { + foreach ($get_customers as $customer) { $customers[] = [ 'ID' => $customer->id, 'name' => $customer->name, @@ -247,14 +276,13 @@ class Customer { ]; } } - - // Prepare response data + $response = [ 'results' => $customers, - 'total' => count($get_all_customers), - 'posts_report' => $customers + 'total' => (int) $total, + 'posts_report' => $customers ]; - + wp_send_json($response); } @@ -263,6 +291,10 @@ class Customer { check_ajax_referer( 'formipay-form-editor', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $form_id = isset($_REQUEST['post']) ? intval($_REQUEST['post']) : 0; $saved = [ diff --git a/includes/Form.php b/includes/Form.php index 3273fbcb3..98cd459cf 100644 --- a/includes/Form.php +++ b/includes/Form.php @@ -1525,6 +1525,10 @@ class Form { check_ajax_referer( 'formipay-form-editor', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + if(isset($_GET['post_id'])){ $post_id = intval($_GET['post_id']); $formipay_post_meta = get_post_meta($post_id, 'formipay_settings', true); @@ -1545,6 +1549,10 @@ class Form { check_ajax_referer( 'formipay-admin-post', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + global $wpdb; // Initialize default args @@ -1623,6 +1631,10 @@ class Form { check_ajax_referer( 'formipay-admin-post', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $title = isset($_REQUEST['title']) ? sanitize_text_field( wp_unslash($_REQUEST['title']) ) : ''; if( !empty($title) && '' !== $title ){ @@ -1692,6 +1704,10 @@ class Form { check_ajax_referer( 'formipay-admin-post', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $form_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : ''; $delete = wp_delete_post($form_id, true); @@ -1716,6 +1732,10 @@ class Form { check_ajax_referer( 'formipay-admin-post', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + if( empty($_REQUEST['ids']) ){ wp_send_json_error( [ 'title' => esc_html__( 'Failed', 'formipay' ), @@ -1759,6 +1779,10 @@ class Form { check_ajax_referer( 'formipay-admin-post', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $post_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : ''; $post = get_post($post_id); if (!$post) { diff --git a/includes/Integration/Paypal.php b/includes/Integration/Paypal.php index 60471a561..e643adf7c 100644 --- a/includes/Integration/Paypal.php +++ b/includes/Integration/Paypal.php @@ -729,10 +729,45 @@ class Paypal extends Payment { public function handle_paypal_webhook(\WP_REST_Request $request) { $body = $request->get_body(); $event = json_decode($body, true); - + if (!$event || empty($event['event_type'])) { return new \WP_Error('invalid_webhook', 'Invalid webhook payload'); } + + // Verify webhook signature + $formipay_settings = get_option('formipay_settings'); + $headers = $request->get_headers(); + + $auth_algo = $headers['paypal_auth_algo'][0] ?? ''; + $transmission_id = $headers['paypal_transmission_id'][0] ?? ''; + $cert_url = $headers['paypal_cert_url'][0] ?? ''; + $transmission_sig = $headers['paypal_transmission_sig'][0] ?? ''; + $transmission_time = $headers['paypal_transmission_time'][0] ?? ''; + $webhook_id = $formipay_settings['paypal_webhook_id'] ?? ''; + + if (!empty($webhook_id) && !empty($transmission_id) && !empty($transmission_sig)) { + $expected = $transmission_id . '|' . $event['resource']['id'] ?? '' . '|' . $transmission_time . '|' . $webhook_id . '|' . crc32($body); + $sig_base64 = base64_encode($expected); + + // Fetch certificate and verify + $cert_response = wp_remote_get($cert_url); + if (is_wp_error($cert_response)) { + return new \WP_Error('cert_fetch_failed', 'Failed to fetch PayPal certificate'); + } + $cert = wp_remote_retrieve_body($cert_response); + + $public_key = openssl_pkey_get_public($cert); + if ($public_key === false) { + return new \WP_Error('invalid_cert', 'Failed to load PayPal certificate'); + } + + $verified = openssl_verify($expected, base64_decode($transmission_sig), $public_key, 'sha256WithRSAEncryption'); + openssl_free_key($public_key); + + if ($verified !== 1) { + return new \WP_Error('invalid_signature', 'Webhook signature verification failed'); + } + } // Example: get order_id from custom_id or invoice_number in resource $resource = $event['resource']; diff --git a/includes/Integration/Paypal.phpbak b/includes/Integration/Paypal.phpbak deleted file mode 100644 index ae6683e7d..000000000 --- a/includes/Integration/Paypal.phpbak +++ /dev/null @@ -1,821 +0,0 @@ -get_charset_collate(); - - $create[] = "CREATE TABLE `{$wpdb->base_prefix}formipay_paypal_trx` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `created_date` datetime DEFAULT CURRENT_TIMESTAMP, - `form_id` int DEFAULT 0, - `order_id` int DEFAULT 0, - `currency_code` text, - `total` float(10, 2) DEFAULT 0, - `status` text, - `meta_data` text, - PRIMARY KEY (`id`) - ) $charset_collate;"; - - require_once ABSPATH . 'wp-admin/includes/upgrade.php'; - - dbDelta($create); - - } - - public function add_transaction($order_data) { - - $form_id = absint($order_data['form_id'] ?? 0); - - $formipay_settings = get_option('formipay_settings'); - $timeout = isset($formipay_settings['paypal_timeout']) ? (int) $formipay_settings['paypal_timeout'] : 1440; - - $currency_meta = formipay_get_post_meta($form_id, 'product_currency'); - $currency_parts = explode(':::', $currency_meta); - $currency = !empty($currency_parts[0]) ? sanitize_text_field($currency_parts[0]) : ''; - - if($this->gateway === ($order_data['payment_gateway'] ?? '')) { - - $submit_args = [ - 'created_date' => formipay_date('Y-m-d H:i:s', strtotime($order_data['created_date'] ?? 'now')), - 'form_id' => $form_id, - 'order_id' => absint($order_data['id'] ?? 0), - 'currency_code' => $currency, - 'total' => floatval($order_data['total'] ?? 0), - 'status' => 'PENDING', - 'meta_data' => maybe_serialize([ - 'request_response' => maybe_serialize($order_data['payment_callback']), - 'payer_action_url' => esc_url_raw($order_data['redirect_url'] ?? '') - ]) - ]; - - global $wpdb; - $table = $wpdb->prefix . 'formipay_paypal_trx'; - - $format = [ - '%s', // created_date - '%d', // form_id - '%d', // order_id - '%s', // currency_code - '%f', // total - '%s', // status - '%s' // meta_data - ]; - - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery - $wpdb->insert($table, $submit_args, $format); - - wp_schedule_single_event( - (int) formipay_date('timestamp') + $timeout, 'formipay/order/payment-timeout', [ - 'order_id' => $order_data['id'], - 'payment_gateway' => 'paypal' - ] - ); - } - } - - public function auto_cancel_order_on_timeout($order_id, $payment_gateway) { - - if($payment_gateway == 'paypal'){ - - global $wpdb; - $order = formipay_get_order($order_id); - if($order['status'] !== 'completed'){ - Order::update($order_id, [ - 'status' => 'cancelled' - ]); - $this->cancel_payment($order_id); - } - - } - - } - - public function get_transaction($order_id = 0) { - - global $wpdb; - $table = $wpdb->prefix .'formipay_paypal_trx'; - if($order_id == 0){ - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching - $get = $wpdb->get_results( - $wpdb->prepare( - "SELECT * FROM %i", $table - ) - ); - }else{ - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching - $get = $wpdb->get_row( - $wpdb->prepare( - "SELECT * FROM %i WHERE `order_id` = %d", $table, $order_id - ) - ); - } - - return $get; - - } - - public function update_transaction($order_id, $status, $callback) { - - $args = [ - 'status' => sanitize_text_field( $status ), - 'meta_data' => maybe_serialize( $callback ) - ]; - - global $wpdb; - $table = $wpdb->prefix . 'formipay_paypal_trx'; - $where = ['order_id' => $order_id]; - - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching - $update = $wpdb->update( - $table, $args, $where - ); - - return $update; - - } - - public function add_gateway($gateways) { - - $formipay_settings = get_option('formipay_settings'); - - if(isset($formipay_settings['paypal_toggle']) && $formipay_settings['paypal_toggle'] == 'yes') { - $gateways[] = [ - 'id' => $this->gateway, - 'gateway' => __( 'Paypal', 'formipay' ) - ]; - } - - return $gateways; - - } - - public function add_settings($fields) { - - $payment_instruction = file_get_contents( FORMIPAY_PATH . 'admin/templates/paypal-instruction.html' ); - - $paypal_settings = array( - $this->gateway.'_toggle' => array( - 'type' => 'checkbox', - 'label' => __( 'Activate Paypal', 'formipay' ), - 'submenu' => __( 'Paypal', 'formipay' ) - ), - $this->gateway.'_account_group' => array( - 'type' => 'group_title', - 'label' => __( 'Paypal Account', 'formipay' ), - 'submenu' => __( 'Paypal', 'formipay' ), - 'dependency' => array( - 'key' => $this->gateway.'_toggle', - 'value' => 'not_empty' - ), - 'group' => 'started' - ), - $this->gateway.'_instruction' => array( - 'type' => 'notification_message', - 'description' => $payment_instruction, - 'buttons' => array( - 'show_instruction' => array( - 'text' => __( 'Show Instruction', 'formipay' ), - 'class' => 'show-instruction' - ) - ), - 'dependency' => array( - 'key' => $this->gateway.'_toggle', - 'value' => 'not_empty' - ), - 'submenu' => __( 'Paypal', 'formipay' ) - ), - $this->gateway.'_client_id' => array( - 'type' => 'text', - 'label' => __( 'Client ID', 'formipay' ), - 'submenu' => __( 'Paypal', 'formipay' ), - 'dependency' => array( - 'key' => $this->gateway.'_toggle', - 'value' => 'not_empty' - ) - ), - $this->gateway.'_secret' => array( - 'type' => 'text', - 'label' => __( 'Secret', 'formipay' ), - 'submenu' => __( 'Paypal', 'formipay' ), - 'dependency' => array( - 'key' => $this->gateway.'_toggle', - 'value' => 'not_empty' - ) - ), - $this->gateway.'_environment' => array( - 'type' => 'select', - 'label' => __( 'Environment', 'formipay' ), - 'options' => array( - 'sandbox' => __( 'Sandbox', 'formipay' ), - 'production' => __( 'Production', 'formipay' ) - ), - 'value' => 'sandbox', - 'submenu' => __( 'Paypal', 'formipay' ), - 'dependency' => array( - 'key' => $this->gateway.'_toggle', - 'value' => 'not_empty' - ) - ), - $this->gateway.'_merchant_email' => array( - 'type' => 'text', - 'label' => __( 'Merchant Email', 'formipay' ), - 'submenu' => __( 'Paypal', 'formipay' ), - 'dependency' => array( - 'key' => $this->gateway.'_toggle', - 'value' => 'not_empty' - ), - ), - $this->gateway.'_timeout' => array( - 'type' => 'number', - 'label' => __( 'Payment Timeout (minute)', 'formipay' ), - 'description' => __( 'Set a timeout to wait for payment. After this timeout is up, the order status automatically changes to cancelled.', 'formipay' ), - 'value' => 120, - 'group' => 'ended', - 'submenu' => __( 'Paypal', 'formipay' ), - 'dependency' => array( - 'key' => $this->gateway.'_toggle', - 'value' => 'not_empty' - ), - 'group' => 'ended' - ), - ); - - foreach($paypal_settings as $key => $value) { - $fields[$key] = $value; - } - - return $fields; - - } - - public function add_paypal_settings($fields) { - - $paypal_settings = array( - - $this->gateway.'_confirmation_notice_message_group' => array( - 'type' => 'group_title', - 'label' => __( 'Confirmation Notice Messages', 'formipay' ), - 'description' => __( - 'Set notice message on every condition about attempting to confirm the payment', 'formipay' ), - 'submenu' => __( 'Paypal', 'formipay' ), - 'group' => 'started' - ), - $this->gateway.'_confirmation_update_to_status' => array( - 'type' => 'select', - 'label' => __( 'Change status order to', 'formipay' ), - 'options' => formipay_order_status_list(), - 'value' => 'payment-confirm', - 'submenu' => __( 'Paypal', 'formipay' ), - ), - $this->gateway.'_confirmation_message_success' => array( - 'type' => 'hint_textarea', - 'label' => __( 'Successfully Confirmed Message', 'formipay' ), - 'hints' => array( - 'order_id' => __('Order ID', 'formipay') - ), - 'value' => __('Successfully confirmed payment for #{{order_id}}', 'formipay'), - 'description' => __('When successfully change the order status to the preferred one', 'formipay'), - 'submenu' => __( 'Paypal', 'formipay' ), - ), - $this->gateway.'_confirmation_message_already_confirmed' => array( - 'type' => 'hint_textarea', - 'label' => __( 'Has Been Confirmed Message', 'formipay' ), - 'hints' => array( - 'order_id' => __('Order ID', 'formipay') - ), - 'value' => __('#{{order_id}} has been confirmed', 'formipay'), - 'description' => __('When the order status has been the preferred one', 'formipay'), - 'submenu' => __( 'Paypal', 'formipay' ), - ), - $this->gateway.'_confirmation_message_error' => array( - 'type' => 'hint_textarea', - 'label' => __( 'Failed to Confirm Message', 'formipay' ), - 'hints' => array( - 'order_id' => __('Order ID', 'formipay'), - 'system_error_message' => __('System Error Message', 'formipay') - ), - 'value' => __('Failed to proceed. Error: {{system_error_message}}', 'formipay'), - 'description' => __('When error to change the order status to the preferred one', 'formipay'), - 'submenu' => __( 'Paypal', 'formipay' ), - 'group' => 'ended' - ) - ); - - foreach($paypal_settings as $key => $value) { - $fields[$key] = $value; - } - - return $fields; - - } - - public function set_frontend_label($label){ - - $label = __( 'Paypal', 'formipay' ); - - return $label; - - } - - public function set_frontend_logo($logo){ - - if($logo == false){ - $logo = FORMIPAY_URL . 'public/assets/img/paypal.png'; - } - - return $logo; - - } - - public function prepare_payload($order_data) { - - // error_log('current order'. $order_data['id'] . ': ' . print_r($order_data, true)); - - $form_id = intval($order_data['form_id']); - $order_details = $order_data['items']; - - if($this->gateway == $order_data['payment_gateway']){ - - $formipay_settings = get_option('formipay_settings'); - - $grand_total = floatval($order_data['total']); - - // Currency - $currency = explode(':::', formipay_get_post_meta($form_id, 'product_currency')); - $currency = $currency[0]; - - $currency_decimal_digits = formipay_get_post_meta($form_id, 'product_currency_decimal_digits'); - $currency_decimal_symbol = formipay_get_post_meta($form_id, 'product_currency_decimal_symbol'); - $currency_thousand_separator = formipay_get_post_meta($form_id, 'product_currency_thousand_separator'); - - $slug = 'thankyou'; - $formipay_settings = get_option('formipay_settings'); - if(!empty($formipay_settings['thankyou_link'])){ - $slug = $formipay_settings['thankyou_link']; - } - - if(formipay_get_post_meta($form_id, 'product_type') == 'digital'){ - $shipping_preference = 'NO_SHIPPING'; - }else{ - $shipping_preference = 'SET_PROVIDED_ADDRESS'; - } - - // Setup items - $items = []; - foreach($order_details as $item){ - $qty = 1; - if(!empty($item['qty'])){ - $qty = $item['qty']; - } - if(floatval($item['amount']) >= 0){ - $items[] = [ - 'name' => $item['item'], - 'unit_amount' => [ - 'currency_code' => $currency, - 'value' => floatval($item['amount']) - ], - 'quantity' => $qty - ]; - } - } - - $items = apply_filters('formipay/order/paypal/payload/items', $items, $currency, $order_data); - - $breakdown = array( - "item_total" => array( - "currency_code" => $currency, - "value" => $grand_total - ) - ); - $breakdown = apply_filters('formipay/order/paypal/payload/breakdown', $breakdown, $currency, $order_data); - - $unique_id = $order_data['meta_data']['session_id']['value']; - - // Prepare the payload - $payload = json_encode(array( - "intent" => "CAPTURE", - "purchase_units" => array( - array( - "amount" => array( - "currency_code" => $currency, - "value" => $grand_total, // Format amount - "breakdown" => $breakdown - ), - "items" => $items - ) - ), - 'payment_source' => array( - 'paypal' => array( - 'experience_context' => array( - 'return_url' => site_url('/payment-confirm/'.$this->gateway.'/'.base64_encode($order_data['form_id'].':::'.$order_data['id'].':::'.$unique_id).'?paypal-status=success'), - 'cancel_url' => site_url('/payment-confirm/'.$this->gateway.'/'.base64_encode($order_data['form_id'].':::'.$order_data['id'].':::'.$unique_id).'?paypal-status=cancel'), - 'user_action' => 'PAY_NOW', - 'brand_name' => 'FORMIPAY INC', - 'local' => 'en-US', - 'landing_page' => 'LOGIN', - 'payment_method_preference' => 'IMMEDIATE_PAYMENT_REQUIRED', - 'shipping_preference' => $shipping_preference - ) - ) - ) - )); - - return $payload; - } - - return false; - - } - - public function process_payment( $order_data ) { - - $formipay_settings = get_option('formipay_settings'); - - if($order_data['payment_gateway'] == 'paypal') { - - $paypal_client_id = $formipay_settings['paypal_client_id']; - $paypal_secret = $formipay_settings['paypal_secret']; - - // Set your authentication header - $auth = base64_encode($paypal_client_id . ':' . $paypal_secret); - - // Define arguments for wp_remote_post - $args = array( - 'headers' => array( - 'Content-Type' => 'application/json', - 'Authorization' => 'Basic ' . $auth, - ), - 'method' => 'POST', - 'data_format' => 'body' - ); - - $url = 'https://api-m.sandbox.paypal.com/v2'; - if(isset($formipay_settings[$this->gateway.'_environment']) && $formipay_settings[$this->gateway.'_environment'] == 'production') { - $url = 'https://api-m.paypal.com/v2'; - } - - $payload = $this->prepare_payload($order_data); - // error_log('paypal payload:'.print_r($payload, true)); - $args['body'] = $payload; - $url = $url.'/checkout/orders'; - - $response = wp_remote_retrieve_body( wp_remote_post($url, $args) ); - $response = json_decode($response); - // error_log('paypal response:'.print_r($response, true)); - - $payer_action_link = null; - - foreach ($response->links as $link) { - if ($link->rel === 'payer-action') { - $payer_action_link = $link->href; - break; - } - } - - if($payer_action_link !== null){ - $order_data['payment_callback'] = $response; - $order_data['redirect_url'] = $payer_action_link; - } - - } - - return $order_data; - - } - - public function cancel_payment( $order_data ) { - - $formipay_settings = get_option('formipay_settings'); - - if($order_data['payment_gateway'] == 'paypal') { - - $paypal_client_id = $formipay_settings['paypal_client_id']; - $paypal_secret = $formipay_settings['paypal_secret']; - - // Set your authentication header - $auth = base64_encode($paypal_client_id . ':' . $paypal_secret); - - // Define arguments for wp_remote_post - $args = array( - 'headers' => array( - 'Content-Type' => 'application/json', - 'Authorization' => 'Basic ' . $auth, - ), - 'method' => 'POST', - 'data_format' => 'body' - ); - - $url = 'https://api-m.sandbox.paypal.com/v2'; - if(isset($formipay_settings[$this->gateway.'_environment']) && $formipay_settings[$this->gateway.'_environment'] == 'production') { - $url = 'https://api-m.paypal.com/v2'; - } - - $payload = $this->prepare_payload($order_data); - // error_log('paypal payload:'.print_r($payload, true)); - $args['body'] = $payload; - $url = $url.'/checkout/orders'; - - $response = wp_remote_retrieve_body( wp_remote_post($url, $args) ); - $response = json_decode($response); - // error_log('paypal response:'.print_r($response, true)); - - $payer_action_link = null; - - foreach ($response->links as $link) { - if ($link->rel === 'payer-action') { - $payer_action_link = $link->href; - break; - } - } - - if($payer_action_link !== null){ - $order_data['redirect_url'] = $payer_action_link; - } - - do_action('formipay/notification/order', $order_data); - - } - - } - - public function check_parse_query() { - if (is_admin()) { - return; - } - - global $wp_query; - - $formipay_settings = get_option('formipay_settings'); - - // Validate query vars early - $query = $wp_query->query; - if ( - is_array($query) && - !empty($query['formipay-payment-confirm']) && - filter_var($query['formipay-payment-confirm'], FILTER_VALIDATE_BOOLEAN) && - !empty($query['gateway']) && - $query['gateway'] === 'paypal' - ) { - // Sanitize and validate token - $token_raw = isset($query['formipay-token']) ? $query['formipay-token'] : ''; - $token_decoded = base64_decode($token_raw, true); - if (!$token_decoded) { - return; - } - $token = explode(':::', $token_decoded); - if (count($token) < 2) { - return; - } - $form_id = absint($token[0]); - $order_id = absint($token[1]); - if (!$form_id || !$order_id) { - return; - } - - $check_order = $this->get_transaction($order_id); - - // Check PayPal status and process payment - if (!empty($check_order) && - // phpcs:ignore WordPress.Security.NonceVerification.Recommended - isset($_GET['paypal-status']) && sanitize_text_field(wp_unslash($_GET['paypal-status'])) === 'success' && - strtoupper($check_order->status) === 'PENDING' - ) { - $paypal_client_id = isset($formipay_settings['paypal_client_id']) ? $formipay_settings['paypal_client_id'] : ''; - $paypal_secret = isset($formipay_settings['paypal_secret']) ? $formipay_settings['paypal_secret'] : ''; - $environment = isset($formipay_settings[$this->gateway . '_environment']) ? $formipay_settings[$this->gateway . '_environment'] : 'sandbox'; - - if ($paypal_client_id && $paypal_secret) { - $auth = base64_encode($paypal_client_id . ':' . $paypal_secret); - // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $paypal_order_id = isset($_GET['token']) ? sanitize_text_field(wp_unslash($_GET['token'])) : ''; - - $base_url = ($environment === 'production') - ? 'https://api-m.paypal.com/v2' - : 'https://api-m.sandbox.paypal.com/v2'; - - $url = $base_url . '/checkout/orders/' . $paypal_order_id . '/capture'; - - $args = [ - 'headers' => [ - 'Content-Type' => 'application/json', - 'Authorization' => 'Basic ' . $auth, - ], - 'method' => 'POST', - 'data_format' => 'body', - 'timeout' => 20, - ]; - - $response = wp_remote_post($url, $args); - $body = is_wp_error($response) ? null : wp_remote_retrieve_body($response); - $response_data = json_decode($body, true); - - if (isset($response_data['status']) && $response_data['status'] === 'COMPLETED') { - $update_order_status = formipay_update_order_status([ - 'form_id' => $form_id, - 'order_id' => $order_id, - 'payment_gateway' => $this->gateway, - 'status' => 'completed' - ]); - if (!empty($update_order_status['valid'])) { - $capture_details = $response_data['purchase_units'][0]['payments']['captures'][0] ?? []; - $this->update_transaction($order_id, $response_data['status'], $capture_details); - } - } - } - } - - // Determine redirect action - $action_type = formipay_get_post_meta($form_id, 'submit_action_type'); - $params = isset($_SERVER['QUERY_STRING']) ? sanitize_text_field(wp_unslash($_SERVER['QUERY_STRING'])) : ''; - - if ($action_type === 'thankyou') { - $thankyou_slug = !empty($formipay_settings['thankyou_link']) - ? sanitize_title($formipay_settings['thankyou_link']) - : 'thankyou'; - wp_safe_redirect(site_url('/' . $thankyou_slug . '/' . $token_raw . '?' . $params)); - exit; - } elseif ($action_type === 'redirect') { - $url = formipay_get_post_meta($form_id, 'redirect_url'); - $url = esc_url_raw($url); - if (strpos($url, '?') !== false) { - wp_safe_redirect($url . '&' . $params); - } else { - wp_safe_redirect($url . '?' . $params); - } - exit; - } elseif ($action_type === 'whatsapp') { - $order = formipay_get_order($order_id); - $order_data = []; - if (is_array($order)) { - foreach ($order as $key => $data) { - $order_data[$key] = maybe_unserialize($data); - } - } - $admin_number = formipay_get_post_meta($form_id, 'whatsapp_admin'); - $shortcodes = [ - '{{product_name}}' => get_the_title($form_id), - '{{order_id}}' => $order_id, - '{{order_date}}' => $order['created_date'] ?? '', - '{{order_total}}' => $order['total'] ?? '', - '{{order_status}}' => $order['status'] ?? '', - '{{order_details}}' => $this->render_order_details($form_id, $order_id, maybe_unserialize($order['items'] ?? '')), - '{{payment_details}}' => $this->render_payment_details('', $form_id, $order_data, 'whatsapp'), - ]; - $message_template = wp_kses_post(formipay_get_post_meta($form_id, 'whatsapp_message')); - $message = strtr($message_template, $shortcodes); - $message = str_replace('%250A', '%0A', rawurlencode($message)); - $whatsapp_url = 'https://api.whatsapp.com/send/?phone=' . urlencode($admin_number) . '&text=' . $message; - wp_safe_redirect($whatsapp_url); - exit; - } - } - } - - public function render_order_details($form_id, $order_id, $items) { - - $order = formipay_get_order($order_id); - - $target_length = 40; - $message_format = formipay_get_post_meta($form_id, 'whatsapp_message'); - $order_details_message = '```'; - if(!empty($items)){ - foreach($items as $detail){ - $qty = ''; - if(isset($detail['qty']) && !empty($detail['qty'])){ - $qty = $detail['qty'].'×'; - } - $subtotal = formipay_price_format($detail['subtotal'], $form_id); - $qty_length = strlen($qty); - $subtotal_length = strlen($subtotal); - if(floatval($detail['subtotal']) < 0){ - $subtotal_length = $subtotal_length + 1; - } - $dot_length = $target_length - ($qty_length + $subtotal_length); - $dots = str_repeat('.', intval($dot_length)); - $order_details_message .= '%0A'.$detail['item']; - $order_details_message .= '%0A'.$qty.$dots.$subtotal; - } - $total = formipay_price_format($order['total'], $form_id); - $total_length = strlen($total); - $dot_length = $target_length - ($total_length + 6); - $dots = str_repeat('.', intval($dot_length)); - $divider = str_repeat('_', intval($target_length)); - $order_details_message .= '%0A'.$divider; - $order_details_message .= '%0A'.''; - $order_details_message .= '%0A'.'Total'.$dots.$total; - } - $order_details_message .= '%0A```'; - - $content = $order_details_message; - - return $content; - - } - - public function render_payment_details($render, $form_id, $order_data, $submit_action_type) { - - if($order_data['status'] == 'completed'){ - return $render; - } - - $formipay_settings = get_option('formipay_settings'); - - $get_trx = $this->get_transaction($order_data['id']); - if(!empty($get_trx)){ - $meta = maybe_unserialize($get_trx->meta_data); - if(isset($meta['payer_action_url']) && !empty($meta['payer_action_url'])){ - - if($formipay_settings[$this->gateway.'_toggle'] !== false){ - if($submit_action_type == 'thankyou'){ - - ob_start(); - ?> - - - - Payment Link: *%s*', 'formipay'), $meta['payer_action_url'] ); - - $render = $message; - - } - } - } - } - - return $render; - - } - - public function render_email_action_button($button, $recipient, $order_data){ - - if( - $order_data['payment_gateway'] == $this->gateway && - $order_data['status'] == 'on-hold' && - $recipient !== 'admin' - ){ - - $formipay_settings = get_option('formipay_settings'); - $get_trx = $this->get_transaction($order_data['id']); - if(!empty($get_trx)){ - $meta = maybe_unserialize($get_trx->meta_data); - if(isset($meta['payer_action_url']) && !empty($meta['payer_action_url'])){ - - $button = [ - 'url' => $meta['payer_action_url'], - 'label' => __( 'Pay Now', 'formipay' ) - ]; - - } - } - - } - - return $button; - - } - -} \ No newline at end of file diff --git a/includes/License.php b/includes/License.php index c1f8fb476..058445e0a 100644 --- a/includes/License.php +++ b/includes/License.php @@ -123,6 +123,10 @@ class License { public function tabledata() { check_ajax_referer('formipay-admin-licenses', '_wpnonce'); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + global $wpdb; $table = $wpdb->prefix . 'formipay_licenses'; @@ -200,6 +204,10 @@ class License { public function delete() { check_ajax_referer('formipay-admin-licenses', '_wpnonce'); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + if (empty($_REQUEST['id'])) { wp_send_json_error([ 'title' => esc_html__('Failed', 'formipay'), @@ -232,6 +240,10 @@ class License { public function bulk_delete() { check_ajax_referer('formipay-admin-licenses', '_wpnonce'); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $ids = isset($_REQUEST['ids']) ? (array) $_REQUEST['ids'] : []; if (empty($ids)) { wp_send_json_error([ diff --git a/includes/Order.php b/includes/Order.php index d6ccd74f7..b07001119 100644 --- a/includes/Order.php +++ b/includes/Order.php @@ -148,7 +148,7 @@ class Order { $unique_id = isset($order_meta_data['session_id']) ? sanitize_text_field($order_meta_data['session_id']) : ''; $thankyou_url = site_url('/' . $thankyou_link . '/' . base64_encode($form_id . ':::' . $order_data['id'] . ':::' . $unique_id)); - setcookie('fp_access', maybe_serialize([$order_data['id'] => $unique_id]), time() + 86400, '/'); + setcookie('fp_access', wp_json_encode([$order_data['id'] => $unique_id]), time() + 86400, '/', '', is_ssl(), true); if ( !empty($this->order_data['redirect_url']) && @@ -768,6 +768,10 @@ class Order { check_ajax_referer( 'formipay-order-details', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $type = isset($_REQUEST['type']) ? sanitize_text_field( wp_unslash($_REQUEST['type']) ) : ''; $search = isset($_REQUEST['search']) ? sanitize_text_field( wp_unslash($_REQUEST['search']) ) : ''; @@ -932,26 +936,33 @@ class Order { check_ajax_referer( 'formipay-order-details', '_wpnonce' ); - $all_orders = $this->get(); - + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + + global $wpdb; + $table = $wpdb->prefix . 'formipay_orders'; + + // Count order statuses efficiently + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $status_counts = $wpdb->get_results("SELECT `status`, COUNT(*) as cnt FROM {$table} GROUP BY `status`", ARRAY_A); + $order_status = [ 'all' => 0, 'completed' => 0, 'on-hold' => 0, 'payment-confirm' => 0 ]; - - // Count order statuses - if (!empty($all_orders)) { - foreach ($all_orders as $order) { - $order_status['all']++; - $order_status[$order->status]++; + + if (!empty($status_counts)) { + foreach ($status_counts as $row) { + $order_status['all'] += (int) $row['cnt']; + if (isset($order_status[$row['status']])) { + $order_status[$row['status']] = (int) $row['cnt']; + } } } - global $wpdb; - $table = $wpdb->prefix . 'formipay_orders'; - $where = []; $params = []; @@ -1028,6 +1039,10 @@ class Order { check_ajax_referer( 'formipay-order-details', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $order_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; $delete = $this->delete($order_id); @@ -1052,6 +1067,10 @@ class Order { check_ajax_referer( 'formipay-order-details', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + if( empty($_REQUEST['ids']) ){ wp_send_json_error( [ 'title' => esc_html__( 'Failed', 'formipay' ), @@ -1095,6 +1114,10 @@ class Order { check_ajax_referer( 'formipay-order-details', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $order_id = isset($_REQUEST['order']) ? intval($_REQUEST['order']) : 0; wp_send_json(formipay_get_order($order_id)); @@ -1104,6 +1127,10 @@ class Order { public function formipay_change_order_status() { check_ajax_referer( 'formipay-order-details', '_wpnonce' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } $order_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; $status = isset($_REQUEST['status']) ? sanitize_text_field( wp_unslash($_REQUEST['status']) ) : ''; @@ -1139,6 +1166,10 @@ class Order { check_ajax_referer( 'formipay-order-details', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $editable_field = []; $order_id = isset($_REQUEST['order_id']) ? intval($_REQUEST['order_id']) : 0; @@ -1183,6 +1214,10 @@ class Order { check_ajax_referer( 'formipay-order-details', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $order_id = isset($_REQUEST['order_id']) ? intval($_REQUEST['order_id']) : 0; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $raw_data = isset($_REQUEST['new_values']) ? wp_unslash( $_REQUEST['new_values'] ) : []; @@ -1234,6 +1269,10 @@ class Order { check_ajax_referer( 'formipay-order-details', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $order_id = isset($_REQUEST['order_id']) ? intval($_REQUEST['order_id']) : 0; $method = isset($_REQUEST['method']) ? sanitize_text_field( wp_unslash($_REQUEST['method']) ) : ''; $password = isset($_REQUEST['password']) ? sanitize_text_field( wp_unslash($_REQUEST['password']) ) : ''; diff --git a/includes/Payment/Payment.php b/includes/Payment/Payment.php index b022e389d..3c899827a 100644 --- a/includes/Payment/Payment.php +++ b/includes/Payment/Payment.php @@ -220,7 +220,6 @@ abstract class Payment { 'top' ); - flush_rewrite_rules(); } diff --git a/includes/Product.php b/includes/Product.php index c63c46ca4..d34bf3697 100644 --- a/includes/Product.php +++ b/includes/Product.php @@ -591,6 +591,10 @@ class Product { check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + global $wpdb; // Initialize default args @@ -707,6 +711,10 @@ class Product { check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $title = isset($_REQUEST['title']) ? sanitize_text_field( wp_unslash($_REQUEST['title']) ) : ''; if( !empty($title) && '' !== $title ){ @@ -738,6 +746,10 @@ class Product { check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $search = isset($_REQUEST['search']) ? sanitize_text_field( wp_unslash($_REQUEST['search']) ) : ''; $countries = formipay_currency_array(); @@ -762,6 +774,10 @@ class Product { check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $form_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : ''; $delete = wp_delete_post($form_id, true); @@ -786,6 +802,10 @@ class Product { check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + if( empty($_REQUEST['ids']) ){ wp_send_json_error( [ 'title' => esc_html__( 'Failed', 'formipay' ), @@ -828,6 +848,10 @@ class Product { check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Unauthorized' ] ); + } + $post_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : ''; $post = get_post($post_id); if (!$post) { diff --git a/includes/Render.php b/includes/Render.php index 21b767fca..bb7448ac7 100644 --- a/includes/Render.php +++ b/includes/Render.php @@ -68,7 +68,7 @@ class Render { } ob_start(); - $timezone = get_option('timezone_string', 'Asia/Jakarta'); + $timezone = wp_timezone_string(); $require_login = formipay_get_post_meta($post_id, 'require_login') === 'on'; $scheduled = formipay_get_post_meta($post_id, 'schedule_toggle') === 'on'; diff --git a/includes/Thankyou.php b/includes/Thankyou.php index 3eff21148..00e70a3fa 100644 --- a/includes/Thankyou.php +++ b/includes/Thankyou.php @@ -41,7 +41,6 @@ class Thankyou { 'top' ); - flush_rewrite_rules(); } @@ -425,7 +424,7 @@ class Thankyou { $cookie = $this->get_cookie(); $cookie[$order_id] = $order_meta_data['session_id']; - setcookie( 'fp_access', maybe_serialize($cookie), time() + 86400, '/' ); + setcookie( 'fp_access', wp_json_encode($cookie), time() + 86400, '/', '', is_ssl(), true ); if($method == 'magic_link'){ do_action('formipay/notification/access/email', $order); @@ -678,15 +677,11 @@ class Thankyou { if (!empty($_COOKIE['fp_access'])) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $raw_value = wp_unslash($_COOKIE['fp_access']); - - // Validate before unserializing - if (is_serialized($raw_value)) { // Use WordPress core function - $maybe = maybe_unserialize($raw_value); - - // Type-check and sanitize array contents - if (is_array($maybe)) { - $cookie = array_map('sanitize_text_field', $maybe); - } + + $decoded = json_decode($raw_value, true); + + if (is_array($decoded)) { + $cookie = array_map('sanitize_text_field', $decoded); } } return $cookie; diff --git a/uninstall.php b/uninstall.php new file mode 100644 index 000000000..d6d505951 --- /dev/null +++ b/uninstall.php @@ -0,0 +1,62 @@ +query( "DROP TABLE IF EXISTS {$wpdb->prefix}{$table_suffix}" ); +} + +// Remove plugin options +delete_option( 'formipay_settings' ); + +// Clear scheduled cron events +$crons = _get_cron_array(); +if ( ! empty( $crons ) ) { + foreach ( $crons as $timestamp => $cron ) { + if ( is_array( $cron ) ) { + foreach ( $cron as $hook => $events ) { + if ( strpos( $hook, 'formipay/' ) === 0 ) { + foreach ( $events as $key => $event ) { + wp_unschedule_event( $timestamp, $hook, $event['args'] ?? [] ); + } + } + } + } + } +} + +// Remove custom post types and their meta +$post_types = [ 'formipay-form', 'formipay-product', 'formipay-coupon', 'formipay-access', 'formipay-license' ]; +foreach ( $post_types as $post_type ) { + $posts = get_posts( [ + 'post_type' => $post_type, + 'numberposts' => -1, + 'post_status' => 'any', + ] ); + foreach ( $posts as $post ) { + wp_delete_post( $post->ID, true ); + } +} + +// Clear any cached data +wp_cache_flush();