Files
formipay/includes/Order.php
dwindown e8fbfb14c1 fix: prevent asset conflicts between React and Grid.js versions
Add coexistence checks to all enqueue methods to prevent loading
both React and Grid.js assets simultaneously.

Changes:
- ReactAdmin.php: Only enqueue React assets when ?react=1
- Init.php: Skip Grid.js when React active on admin pages
- Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0
- Customer.php, Product.php, License.php: Add coexistence checks

Now the toggle between Classic and React versions works correctly.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 17:02:14 +07:00

1249 lines
47 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace Formipay;
use Formipay\Traits\SingletonTrait;
if ( ! defined( 'ABSPATH' ) ) exit;
class Order {
use SingletonTrait;
private $form_id;
private $form_data;
private $order_data;
private $order_details;
private $chosen_currency; // reserved (not used yet)
private $currency; // 3-letter currency code from request (e.g., IDR, USD)
/**
* Initializes the plugin by setting filters and administration functions.
*/
protected function __construct() {
add_action( 'init', [$this, 'create_db'] );
add_action( 'wp_ajax_formipay_submission', [$this, 'retrieve_form_data'] );
add_action( 'wp_ajax_nopriv_formipay_submission', [$this, 'retrieve_form_data'] );
add_action( 'admin_menu', [$this, 'add_menu'] );
add_action( 'admin_enqueue_scripts', [$this, 'enqueue'] );
add_action( 'wp_ajax_formipay_get_all_forms', [$this, 'formipay_get_all_forms'] );
add_action( 'wp_ajax_formipay_orders_get_choices', [$this, 'formipay_orders_get_choices'] );
add_action( 'wp_ajax_formipay-tabledata-orders', [$this, 'formipay_tabledata_orders'] );
add_action( 'wp_ajax_formipay-delete-order', [$this, 'formipay_delete_order'] );
add_action( 'wp_ajax_formipay-bulk-delete-order', [$this, 'formipay_bulk_delete_order'] );
add_action( 'wp_ajax_formipay_load_order_data', [$this, 'formipay_load_order_data'] );
add_action( 'wp_ajax_formipay_change_order_status', [$this, 'formipay_change_order_status'] );
add_action( 'wp_ajax_formipay_check_editable_field', [$this, 'formipay_check_editable_field'] );
add_action( 'wp_ajax_formipay_update_editable_field_data', [$this, 'formipay_update_editable_field_data'] );
add_action( 'wp_ajax_formipay_update_digital_access', [$this, 'formipay_update_digital_access'] );
add_filter( 'formipay/order/thankyou-screen-content', [$this, 'process_response_message'], 999, 5 );
add_filter( 'formipay/order/get', [$this, 'get_order_by_id'], 10, 2 );
}
public function create_db() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$create[] = "CREATE TABLE `{$wpdb->base_prefix}formipay_orders` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`created_date` datetime DEFAULT CURRENT_TIMESTAMP,
`updated_date` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`form_id` int,
`user_id` int,
`customer_id` int,
`items` text,
`total` float(10, 2) DEFAULT 0,
`status` varchar(20) DEFAULT 'on-hold',
`form_data` text,
`payment_gateway` text,
`meta_data` text,
PRIMARY KEY (`id`)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($create);
}
public function retrieve_form_data() {
// Verify nonce
if ( empty($_POST['nonce']) || ! wp_verify_nonce( sanitize_text_field(wp_unslash($_POST['nonce'])), 'formipay_order_submit' ) ) {
wp_send_json_error([
'message' => 'Nonce verification failed'
]);
}
// Sanitize and unslash inputs explicitly
$form_id = isset($_REQUEST['form_id']) ? intval(wp_unslash($_REQUEST['form_id'])) : 0;
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$raw_data = isset($_REQUEST['data']) ? wp_unslash($_REQUEST['data']) : [];
$form_data = is_array($raw_data) ? formipay_sanitize_array($raw_data) : sanitize_text_field($raw_data);
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$order_meta_data = isset($_REQUEST['meta_data']) ? wp_unslash($_REQUEST['meta_data']) : [];
$purpose = isset($_REQUEST['purpose']) ? sanitize_text_field(wp_unslash($_REQUEST['purpose'])) : '';
$this->currency = isset($_REQUEST['currency']) ? sanitize_text_field( wp_unslash($_REQUEST['currency']) ) : (string) formipay_default_currency('code');
$this->form_id = $form_id;
// Let everyone modify this data (sanitize inside filters if needed)
$order_data = apply_filters('formipay/order/process-data', $form_data, $form_id);
$this->order_data = $order_data;
$order_details = $this->process_order_details();
$this->order_details = $order_details;
$total = $this->process_order_total();
if ($purpose === 'calculate') {
$order_data['total'] = $total;
$order_data['items'] = $order_details;
$order_data['form_data'] = formipay_get_post_meta($form_id);
wp_send_json($order_data);
}
$status = ($total == 0) ? 'completed' : 'on-hold';
$submit_args = [
'form_id' => $form_id,
'items' => $order_details,
'total' => $total,
'status' => $status,
'form_data' => $order_data,
'payment_gateway' => isset($order_data['payment_gateway']) ? sanitize_text_field($order_data['payment_gateway']) : '',
'meta_data' => $order_meta_data,
];
$submit = $this->submit($submit_args);
$order_date = formipay_date('Y-m-d H:i:s');
$form_settings = formipay_get_post_meta($form_id, 'formipay_settings');
$global_settings = get_option('formipay_settings');
$thankyou_link = (!empty($global_settings['thankyou_link'])) ? $global_settings['thankyou_link'] : 'thankyou';
if ($submit !== false) {
$order_data = formipay_get_order($submit);
$this->order_data = apply_filters('formipay/order/result', $order_data);
do_action('formipay/order/new', $this->order_data);
do_action('formipay/order/on-hold', $this->order_data);
do_action('formipay/notification/order', $this->order_data);
$submit_action_type = formipay_get_post_meta($form_id, 'submit_action_type');
$success_message = formipay_get_post_meta($form_id, 'success_response_content');
$success_message = $this->process_response_content($success_message, 'thankyou');
$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', wp_json_encode([$order_data['id'] => $unique_id]), time() + 86400, '/', '', is_ssl(), true);
if (
!empty($this->order_data['redirect_url']) &&
$submit_action_type !== 'whatsapp'
) {
$redirect_url = esc_url_raw($this->order_data['redirect_url']);
} else {
switch ($submit_action_type) {
case 'redirect':
$redirect_url = esc_url_raw(formipay_get_post_meta($form_id, 'redirect_url'));
break;
case 'whatsapp':
$admin_number = sanitize_text_field(formipay_get_post_meta($form_id, 'whatsapp_admin'));
$whatsapp_message_format = formipay_get_post_meta($form_id, 'whatsapp_message');
$redirect_url = 'https://wa.me/' . $admin_number . '?text=' . rawurlencode($this->process_response_content($whatsapp_message_format, $submit_action_type));
break;
default:
$redirect_url = esc_url_raw($thankyou_url);
break;
}
}
wp_send_json_success([
'message' => wp_kses_post($success_message),
'response_type' => formipay_get_post_meta($form_id, 'response_type'),
'action_type' => $submit_action_type,
'url' => $redirect_url,
]);
}
$message = formipay_get_post_meta($form_id, 'failed_response_content');
$message = str_replace('{{error_message}}', $submit->error_message(), $message);
wp_send_json_error(['message' => wp_kses_post($message)]);
}
public function process_order_shortcodes($submit_action_type) {
$buyer_name = '';
if(!empty(formipay_get_post_meta($this->form_id, 'buyer_name'))){
$buyer_name_field = formipay_get_post_meta($this->form_id, 'buyer_name');
$buyer_name = $this->order_data['form_data'][$buyer_name_field];
}
$shortcodes = [
'buyer_name' => $buyer_name,
'product_name' => html_entity_decode(get_the_title($this->form_id)),
'order_id' => $this->order_data['id'],
'order_date' => $this->order_data['created_date'],
'order_total' => formipay_price_format($this->order_data['total'], $this->form_id),
'order_status' => $this->order_data['status'],
'order_details' => $this->render_order_details($submit_action_type),
'form_submission' => $this->render_form_submit($submit_action_type)
];
$shortcodes = apply_filters( 'formipay/order/shortcodes', $shortcodes, $this->form_id, $this->order_data, $submit_action_type );
return $shortcodes;
}
public function process_order_details() {
$currency_codes = [];
$allowed_currencies = formipay_get_post_meta($this->form_id, 'allowed_currencies');
if(!empty($allowed_currencies)){
$parse_currencies = json_decode($allowed_currencies, true);
foreach($parse_currencies as $currency_data){
$parse_currency = explode(':::', $currency_data);
$currency_codes[] = $parse_currency[0];
}
}
$details = [];
// Ensure currency code is present; fallback to form default currency code
if (empty($this->currency)) {
$default_currency_full = formipay_get_post_meta($this->form_id, 'default_currencies'); // e.g., "IDR:::Indonesian rupiah:::Rp"
$parts = explode(':::', (string) $default_currency_full);
$this->currency = $parts[0] ?? 'IDR';
}
// Attached static products (qty = 1 each in this case)
$products = formipay_get_post_meta($this->form_id, 'static_products');
if (!empty($products)) {
$products = array_filter(array_map('absint', explode(',', (string) $products)));
foreach ($products as $product_id) {
$regular_key = 'setting_product_price_regular_' . $this->currency;
$sale_key = 'setting_product_price_sale_' . $this->currency;
$regular_price = formipay_get_post_meta($product_id, $regular_key);
$sale_price = formipay_get_post_meta($product_id, $sale_key);
$price = ($sale_price !== '' && $sale_price !== null) ? (float) $sale_price : (float) $regular_price;
$details[] = [
'item' => html_entity_decode(get_the_title($product_id)),
'amount' => $price,
'qty' => 1,
'subtotal' => $price,
'context' => 'product',
];
}
}
// Static items (fees/bonuses), currency-aware amounts
$raw_items = formipay_get_post_meta($this->form_id, 'static_items');
if (!empty($raw_items)) {
$items = json_decode((string) $raw_items, true) ?: [];
foreach ($items as $it) {
$label = $it['label'] ?? 'Item';
$qty = (int) ($it['quantity'] ?? 1);
$key = 'amount_' . $this->currency;
$amt = (float) ($it[$key] ?? 0);
$details[] = [
'item' => $label,
'amount' => $amt,
'qty' => $qty,
'subtotal' => $amt * $qty,
'context' => 'item',
];
}
}
$details = apply_filters('formipay/order/order-details', $details, $this->form_id, $this->order_data );
return $details;
}
public function process_order_total() {
$detail_total = 0.00;
if(!empty($this->order_details)){
foreach ($this->order_details as $detail) {
$detail_total += $detail['subtotal'];
}
}
$order_total = apply_filters('formipay/order/set-total', $detail_total, $this->form_id, $this->order_data);
if($order_total <= 0){
$order_total = 0;
}
return $order_total;
}
public function process_response_content($content, $submit_action_type){
$shortcodes = $this->process_order_shortcodes($submit_action_type);
// $target = [];
// $replace_to_be = [];
$replacements = [];
if(!empty($shortcodes)){
foreach($shortcodes as $key => $value){
// $target[] = '{{'.$key.'}}';
// $replace_to_be[] = $value;
$replacements['{{'.$key.'}}'] = $value;
}
}
// $content = str_replace($target, $replace_to_be, $content);
$content = strtr($content, $replacements);
if($submit_action_type == 'whatsapp'){
$content = str_replace(
[
PHP_EOL, "\n", ' '
],
[
'%0A', '%0A', '%20'
],
$content
);
}
return $content;
}
public function render_order_details($submit_action_type) {
if($submit_action_type == 'thankyou'){
ob_start();
if(!empty($this->order_details)){
?>
<table id="order-details">
<tbody>
<?php
foreach($this->order_details as $detail){
$qty = '';
if(isset($detail['qty'])){
$qty = ' <i class="bi bi-x"></i> '.$detail['qty'];
}
?>
<tr>
<th><?php echo esc_html($detail['item'] . $qty); ?></th>
<td><?php echo esc_html(formipay_price_format($detail['subtotal'], $this->form_id)); ?></td>
</tr>
<?php
}
?>
<tr>
<th><?php echo esc_html__( 'Total', 'formipay' ); ?></th>
<td><b><?php echo esc_html(formipay_price_format($this->order_data['total'], $this->form_id)); ?></b></td>
</tr>
</tbody>
</table>
<?php
}
$content = ob_get_contents();
ob_get_clean();
}elseif($submit_action_type == 'whatsapp'){
$target_length = 24;
$message_format = formipay_get_post_meta($this->form_id, 'whatsapp_message');
$order_details_message = '';
if(!empty($this->order_details)){
foreach($this->order_details as $detail){
$qty = '';
if(isset($detail['qty']) && !empty($detail['qty'])){
$qty = $detail['qty'].'×';
}
$subtotal = formipay_price_format($detail['subtotal'], $this->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($this->order_data['total'], $this->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."%0A%0A".'Total'.$dots.$total;
}
$order_details_message .= '';
$content = '%60%60%60'.$order_details_message.'%60%60%60';
}else{
$content = apply_filters( 'formipay/order/order-details/shortcode-render', '', $this->form_id, $this->order_details, $submit_action_type );
}
return $content;
}
public function render_form_submit($submit_action_type) {
ob_start();
if(!empty($this->order_data)){
$field_config = formipay_get_post_meta($this->order_data['form_id'], 'formipay_settings');
$skip_row = array(
'form_id', 'payment', 'id', 'status', 'total', 'created_date', 'redirect_url', 'items', 'form_data', 'meta_data'
);
$rows = [];
foreach($this->order_data['form_data'] as $field_name => $field_value){
$field_value = $field_value['value'];
// $field_label = $field_config['fields'][$field_name.'_config']['label'];
if(in_array($field_name, $skip_row)){
continue;
}
switch ($field_name) {
case 'form_id':
$field_label = __( 'Form ID', 'formipay' );
break;
case 'qty':
$field_label = __( 'Quantity', 'formipay' );
break;
case 'coupon_code':
$field_label = __( 'Coupon Code', 'formipay' );
break;
case 'payment':
$field_label = __( 'Payment', 'formipay' );
break;
case 'payment_gateway':
$field_label = __( 'Payment Gateway', 'formipay' );
$field_value = ucwords(str_replace('_', ' ', $field_value));
break;
case 'id':
$field_label = __( 'Order ID', 'formipay' );
break;
case 'total':
$field_label = __( 'Grand Total', 'formipay' );
$field_value = formipay_price_format($field_value, $this->form_id);
break;
case 'status':
$field_label = __( 'Order Status', 'formipay' );
break;
case 'created_date':
$field_label = __( 'Order Date', 'formipay' );
break;
default:
$field_label = $field_config['fields'][$field_name.'_config']['label'];
if(is_array($field_value)){
$field_value = $field_value['value'];
}
if(filter_var($field_value, FILTER_VALIDATE_EMAIL)){
$field_value = $field_value;
}elseif(strpos($field_value, ',') !== false){
$values = explode(',', $field_value);
$field_value = '';
foreach($values as $value){
$field_value .= '<span class="checkbox-span">'.$value.'</span> ';
}
}else{
$field_value = ucwords(str_replace(['-','_'], ' ', $field_value));
}
break;
}
$rows[] = [
'label' => $field_label,
'value' => $field_value
];
}
if($submit_action_type == 'thankyou') {
?>
<table id="order-submission-details">
<tbody>
<tr>
<th><?php echo esc_html__('Product', 'formipay'); ?></th>
<td><?php echo esc_html(get_the_title($this->form_id)); ?></td>
</tr>
<?php
foreach($rows as $row) {
?>
<tr>
<th><?php echo esc_html($row['label']); ?></th>
<td><?php echo esc_html($row['value']); ?></td>
</tr>
<?php
}
?>
</tbody>
</table>
<?php
ob_get_clean();
$content = ob_get_contents();
}elseif($submit_action_type == 'whatsapp') {
$content = '';
foreach($rows as $row){
$content .= "%0A".rawurlencode($row['label'].': *'.$row['value'].'*');
}
}
}
return $content;
}
public function submit($args) {
$args = wp_parse_args( $args, [
'form_id' => 0,
'items' => [],
'total' => 0,
'status' => 'on-hold',
'form_data' => [],
'payment_gateway' => '',
'meta_data' => []
] );
global $wpdb;
$table = $wpdb->prefix . 'formipay_orders';
$insert_data = [
'created_date' => formipay_date('Y-m-d H:i:s'),
'form_id' => intval($args['form_id']),
'items' => maybe_serialize($args['items']),
'total' => floatval($args['total']),
'status' => sanitize_text_field( $args['status'] ),
'form_data' => maybe_serialize($args['form_data']),
'payment_gateway' => sanitize_text_field($args['payment_gateway']),
'meta_data' => maybe_serialize($args['meta_data'])
];
if($args['form_id'] > 0){
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->insert($table, $insert_data);
return $wpdb->insert_id;
}
return false;
}
public function get($order_id = 0) {
global $wpdb;
$table = $wpdb->prefix .'formipay_orders';
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 `id` = %d", $table, $order_id
)
);
}
return $get;
}
public function get_order_by_id($data, $order_id){
global $wpdb;
$table = $wpdb->prefix .'formipay_orders';
if($order_id == 0){
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$data = $wpdb->get_results(
$wpdb->prepare("SELECT * FROM %i", $table),
ARRAY_A
);
}else{
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$data = $wpdb->get_row(
$wpdb->prepare("SELECT * FROM %i WHERE `id` = %d", $table, $order_id),
ARRAY_A);
}
return $data;
}
public function get_by_form_id($form_id, $search='', $limit=false, $offset=false) {
global $wpdb;
$table = $wpdb->prefix .'formipay_orders';
if(false == $limit && false == $offset){
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$get = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM %i WHERE `form_id` = %d", $table, $form_id
)
);
}else{
if('' == $search){
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$get = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM %i WHERE `form_id` = %d ORDER BY `id` DESC LIMIT %d, %d",
$table, $form_id, $offset, $limit
)
);
}else{
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$get = $wpdb->get_results(
$wpdb->prepare("SELECT * FROM %i WHERE `form_id` = %d AND `form_data` LIKE %s ORDER BY `id` DESC LIMIT %d, %d",
$table, $form_id, '%'.$search.'%',$offset, $limit)
);
}
}
return $get;
}
public function add_menu() {
add_submenu_page(
'formipay',
__( 'Orders', 'formipay' ),
__( 'Orders', 'formipay' ),
'manage_options',
'formipay-orders',
[$this, 'orders_page']
);
}
public function orders_page() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$order_id = isset($_GET['order_id']) ? intval($_GET['order_id']) : 0;
$page = $order_id ? 'order-detail' : 'orders';
// Coexistence mode: check query param or setting for React version
$use_react = isset($_GET['react']) || get_option('formipay_use_react_admin', false);
if ($use_react) {
// New React version
printf(
'<div id="formipay-admin-root" data-formipay-mount="%s"></div>',
esc_attr($page)
);
} else {
// Classic Grid.js version
if ($order_id) {
include_once FORMIPAY_PATH . 'admin/page-order-details.php';
} else {
include_once FORMIPAY_PATH . 'admin/page-orders.php';
}
}
}
public function enqueue() {
global $current_screen;
if (!$current_screen) return;
// Check coexistence mode - if React is active, don't load classic assets
$use_react = isset($_GET['react']) || get_option('formipay_use_react_admin', false);
if ($use_react) {
return;
}
// Classic Grid.js assets would be loaded here
// (Original enqueue code was commented out, may need restoration)
}
public function formipay_get_all_forms() {
$all_forms = get_posts([
'post_type' => 'formipay-form',
'posts_per_page' => -1
]);
$forms = [];
if(!empty($all_forms)){
foreach($all_forms as $form){
$forms[$form->ID] = get_the_title($form->ID);
}
}
wp_send_json( $forms );
}
public function get_all_field_name($formid) {
$meta = get_post_meta($formid, 'formipay_settings', true);
$headers = $meta['fields'];
$table_header = [];
if(!empty($headers)){
foreach($headers as $config => $header){
$header_id = $header['field_id'];
$table_header[$header_id] = $header['label'];
}
}
return $table_header;
}
public function formipay_orders_get_choices() {
check_ajax_referer( 'formipay-admin', '_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']) ) : '';
$results = [];
if($type == 'form'){
$args = [
'post_type' => 'formipay-form',
'posts_per_page' => -1,
];
if('' !== $search) {
$args['s'] = $search;
}
$get = get_posts($args);
if(!empty($get)){
foreach($get as $post){
$results[] = [
'value' => $post->ID,
'label' => $post->post_title
];
}
}
}elseif($type == 'currency'){
$countries = formipay_currency_array();
foreach($countries as $country){
$country_name = strtolower($country['name']);
$country_search = strtolower($search);
if(strpos($country_name, $country_search) !== false || $country_search == strtolower($country['code'])){
$results[] = [
'value' => $country['code'],
// phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage
'label' => '<img src="'.formipay_get_flag_by_currency($country['code']).'" height="18" style="margin-bottom: -3px;"> '.$country['name']
];
}
}
}
wp_send_json($results);
}
public function update($order_id, $args) {
global $wpdb;
$table_name = $wpdb->prefix . 'formipay_orders';
// Get current data safely
$recent_data = $this->get($order_id);
if (!$recent_data) {
return new WP_Error('invalid_order', __('Order not found', 'formipay'));
}
// Parse args with type safety
$args = wp_parse_args($args, [
'form_id' => (int)$recent_data->form_id,
'items' => maybe_unserialize($recent_data->items) ?: [],
'total' => (float)$recent_data->total,
'status' => sanitize_text_field($recent_data->status),
'form_data' => maybe_unserialize($recent_data->form_data) ?: [],
'payment_gateway' => sanitize_text_field($recent_data->payment_gateway),
'meta_data' => maybe_unserialize($recent_data->meta_data) ?: []
]);
// Prepare update log
$update_time = current_time('mysql');
$user_id = get_current_user_id();
$changes = [];
$status_changed = false;
// Compare values properly (unserialized)
$compare_fields = [
'items' => ['before' => maybe_unserialize($recent_data->items), 'after' => $args['items']],
'total' => ['before' => (float)$recent_data->total, 'after' => (float)$args['total']],
'status' => ['before' => $recent_data->status, 'after' => $args['status']],
'form_data' => ['before' => maybe_unserialize($recent_data->form_data), 'after' => $args['form_data']],
'payment_gateway' => ['before' => $recent_data->payment_gateway, 'after' => $args['payment_gateway']]
];
foreach ($compare_fields as $field => $values) {
if ($values['before'] !== $values['after']) {
$changes[$field] = [
'before' => $this->sanitize_log_value($values['before']),
'after' => $this->sanitize_log_value($values['after'])
];
if ($field === 'status') {
$status_changed = true;
}
}
}
// Update meta_data with log
if (!empty($changes)) {
$args['meta_data']['update_log'][] = [
'timestamp' => $update_time,
'user_id' => $user_id,
'changes' => $changes
];
}
// Prepare database update
$new_args = [
'form_id' => (int)$args['form_id'],
'items' => maybe_serialize($args['items']),
'total' => (float)$args['total'],
'status' => sanitize_text_field($args['status']),
'form_data' => maybe_serialize($args['form_data']),
'payment_gateway' => sanitize_text_field($args['payment_gateway']),
'meta_data' => maybe_serialize($args['meta_data'])
];
$formats = [
'%d', // form_id
'%s', // items
'%f', // total
'%s', // status
'%s', // form_data
'%s', // payment_gateway
'%s' // meta_data
];
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$updated = $wpdb->update(
$table_name,
$new_args,
['id' => (int)$order_id],
$formats,
['%d']
);
// Trigger actions if status changed
if ($status_changed && $updated) {
$order_data = $this->get($order_id);
do_action("formipay/order/{$args['status']}", $order_data);
do_action('formipay/notification/order', $order_data);
}
return $updated;
}
public function delete($order_id) {
global $wpdb;
return $wpdb->delete(
$wpdb->prefix . 'formipay_orders',
['id' => $order_id],
['%d'],
);
}
private function sanitize_log_value($value) {
if (is_array($value)) {
return array_map([$this, 'sanitize_log_value'], $value);
}
return sanitize_text_field(wp_unslash($value));
}
public function formipay_tabledata_orders() {
check_ajax_referer( 'formipay-admin', '_wpnonce' );
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
];
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'];
}
}
}
$where = [];
$params = [];
// ID filter
if (!empty(intval($_REQUEST['order_id']))) {
$where[] = "`id` = %d";
$params[] = intval($_REQUEST['order_id']);
} else {
$where[] = "`id` > %d";
$params[] = 0;
}
// Status filter
if (isset($_REQUEST['order_status']) && 'all' !== $_REQUEST['order_status']) {
$where[] = "`status` = %s";
$params[] = sanitize_text_field(wp_unslash($_REQUEST['order_status']));
}
// Product filter
if (isset($_REQUEST['product']) && 0 < intval($_REQUEST['product'])) {
$where[] = "`form_id` = %d";
$params[] = intval($_REQUEST['product']);
}
// Combine WHERE clause
$where_sql = implode(' AND ', $where);
$sql = "SELECT * FROM {$table} WHERE $where_sql ORDER BY `id` DESC";
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$get_all_orders = $wpdb->get_results($wpdb->prepare($sql, ...$params));
// For paginated query:
$limit = intval($_REQUEST['limit'] ?? 10);
$offset = intval($_REQUEST['offset'] ?? 0);
$sql_paginated = $sql . " LIMIT %d OFFSET %d";
$params_paginated = array_merge($params, [$limit, $offset]);
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$get_filtered_orders = $wpdb->get_results($wpdb->prepare($sql_paginated, ...$params_paginated));
$orders = [];
// Process filtered orders
if (!empty($get_filtered_orders)) {
foreach ($get_filtered_orders as $key => $order) {
$currency = explode(':::', formipay_get_post_meta($order->form_id, 'product_currency'));
$code = !empty($currency[0]) ? $currency[0] : '';
// Prepare order data
$orders[] = [
'ID' => $order->id,
'form' => get_the_title($order->form_id),
'date' => $order->created_date,
'payment_gateway' => ucwords(str_replace('_', ' ', $order->payment_gateway)),
'status' => ucwords(str_replace('-', ' ', $order->status)),
'total' => [
'flag' => formipay_get_flag_by_currency($code),
'name' => $code,
'value' => formipay_price_format($order->total, $order->form_id),
]
];
}
}
// Prepare response data
$response = [
'results' => $orders,
'total' => count($get_all_orders),
'posts_report' => $order_status
];
wp_send_json($response);
}
public function formipay_delete_order() {
check_ajax_referer( 'formipay-admin', '_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);
if(is_wp_error( $delete )){
wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ),
'message' => esc_html__( 'Failed to delete ortder. Please try again.', 'formipay' ),
'icon' => 'error'
] );
}
wp_send_json_success( [
'title' => esc_html__( 'Deleted', 'formipay' ),
'message' => esc_html__( 'Order is deleted permanently.', 'formipay' ),
'icon' => 'success'
] );
}
public function formipay_bulk_delete_order() {
check_ajax_referer( 'formipay-admin', '_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' ),
'message' => esc_html__( 'There is no coupon selected. Please try again.', 'formipay' ),
'icon' => 'error'
] );
}
$ids = isset($_REQUEST['ids']) ? $_REQUEST['ids'] : [];
$success = 0;
$failed = 0;
$report = __( 'Done.', 'formipay' );
if(!empty($ids)){
foreach($ids as $id){
$delete = $this->delete($id);
if(is_wp_error( $delete )){
$failed++;
}else{
$success++;
}
}
}
if($success > 0){
$report .= sprintf( __( ' Deleted %d coupon(s).', 'formipay' ), $success);
}
if($failed > 0){
$report .= sprintf( __( ' Failed %d coupon(s).', 'formipay' ), $failed);
}
wp_send_json_success( [
'title' => esc_html__( 'Done!', 'formipay' ),
'message' => $report,
'icon' => 'info'
] );
}
public function formipay_load_order_data() {
check_ajax_referer( 'formipay-admin', '_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));
}
public function formipay_change_order_status() {
check_ajax_referer( 'formipay-admin', '_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']) ) : '';
if ($order_id > 0 && $status !== '') {
$update = $this->update($order_id, ['status' => $status]);
if (is_wp_error($update)) {
wp_send_json_error([
'message' => $update->get_error_message(),
'code' => $update->get_error_code(),
]);
}
wp_send_json_success([
'message' => sprintf(
esc_html__('Change status order of #%d is success', 'formipay'),
$order_id
),
]);
}
wp_send_json_error([
'message' => sprintf(
esc_html__('Change status order of #%d is failed', 'formipay'),
$order_id
),
]);
}
public function formipay_check_editable_field() {
check_ajax_referer( 'formipay-admin', '_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;
$get_order_data = formipay_get_order($order_id);
$form_id = $get_order_data['form_id'];
$field_types = formipay_field_type_collection();
$form_field = formipay_get_post_meta($form_id, 'formipay_settings');
$form_field = $form_field['fields'];
unset($field_types['divider']);
unset($field_types['page_break']);
unset($field_types['country_list']);
foreach($form_field as $key => $field){
if(array_key_exists($field['field_type'], $field_types)){
$skip = false;
if(in_array($field['field_type'], ['select', 'checkbox', 'radio'])){
$options = $field['field_options'];
if(!empty($options)){
foreach($options as $option){
if('' !== $option['amount']){
if(isset($get_order_data['status']) && $get_order_data['status'] == 'completed'){
$skip = true;
}
}
}
}
}
if(false == $skip){
$editable_field[$key] = $field;
}
}
}
wp_send_json($editable_field);
}
public function formipay_update_editable_field_data() {
check_ajax_referer( 'formipay-admin', '_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'] ) : [];
$data = is_array($raw_data) ? formipay_sanitize_array($raw_data) : sanitize_text_field($raw_data);
$new_values = [];
if(!empty($data)){
foreach($data as $_data){
$name = $_data['name'];
$new_values[$name] = $_data['value'];
}
}
$get_order_data = formipay_get_order($order_id);
if(!empty($get_order_data)){
$current_form_data = $get_order_data['form_data'];
$new_form_data = [];
foreach($current_form_data as $current_data){
$field_name = $current_data['name'];
$field_value = $current_data['value'];
if(isset($new_values[$field_name])) {
$field_value = $new_values[$field_name];
}
$new_form_data[$field_name] = $field_value;
}
$update_form_data = $this->update($order_id, ['form_data' => $new_form_data]);
if (is_wp_error($update_form_data)) {
wp_send_json_error([
'message' => $update_form_data->get_error_message(),
'code' => $update_form_data->get_error_code(),
]);
}
wp_send_json_success( [
'message' => esc_html__( 'Succesfully change transaction data', 'formipay' )
] );
}
wp_send_json_error( [
'message' => esc_html__( 'Error. Data is not available or not able to be changed. Try again.', 'formipay' )
] );
}
public function formipay_update_digital_access() {
check_ajax_referer( 'formipay-admin', '_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']) ) : '';
$get_order_data = $this->get($order_id);
$meta_data = maybe_unserialize($get_order_data->meta_data);
$meta_data['access_method'] = $method;
if($method == 'static_password'){
$meta_data['access_password'] = $password;
}
$update_meta_data = $this->update($order_id, ['meta_data' => $meta_data]);
if(is_wp_error($update_meta_data)){
wp_send_json_error([
'message' => $update_meta_data->get_error_message(),
'code' => $update_meta_data->get_error_code(),
]);
}
wp_send_json_success( [
'message' => esc_html__( 'Succesfully set access data', 'formipay' )
] );
}
}