fix: resolve all Week 2 performance & security issues (F1.10–F1.19)

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 <noreply@anthropic.com>
This commit is contained in:
dwindown
2026-04-17 19:52:01 +07:00
parent be9a1a0a86
commit 66e7b37f92
15 changed files with 341 additions and 868 deletions

View File

@@ -26,11 +26,13 @@ function formipay_field_type_collection() {
} }
function formipay_currency_array() { function formipay_currency_array() {
static $cache = null;
if ($cache !== null) {
return $cache;
}
$json = file_get_contents(FORMIPAY_PATH . 'admin/assets/json/currencies.json'); $json = file_get_contents(FORMIPAY_PATH . 'admin/assets/json/currencies.json');
$array = json_decode($json, true); $cache = json_decode($json, true);
return $array; return $cache;
} }
function formipay_is_multi_currency_active() { function formipay_is_multi_currency_active() {
@@ -146,22 +148,26 @@ function get_global_currency_array() {
} }
function formipay_country_array() { function formipay_country_array() {
static $cache = null;
if ($cache !== null) {
return $cache;
}
$json = file_get_contents(FORMIPAY_PATH . 'admin/assets/json/country.json'); $json = file_get_contents(FORMIPAY_PATH . 'admin/assets/json/country.json');
$array = json_decode($json, true); $cache = json_decode($json, true);
return $array; return $cache;
} }
function formipay_get_flag_by_currency($currency) { function formipay_get_flag_by_currency($currency) {
static $flags = null;
if(strpos($currency, ':::')){ if(strpos($currency, ':::')){
$currency = explode(':::', $currency); $currency = explode(':::', $currency);
$currency = $currency[0]; $currency = $currency[0];
} }
if ($flags === null) {
$json = file_get_contents(FORMIPAY_PATH . 'admin/assets/json/flags.json'); $json = file_get_contents(FORMIPAY_PATH . 'admin/assets/json/flags.json');
$array = json_decode($json, true); $flags = json_decode($json, true);
foreach($array as $country){ }
foreach($flags as $country){
if($currency == $country['code']){ if($currency == $country['code']){
return $country['flag']; return $country['flag'];
} }

View File

@@ -50,6 +50,20 @@ spl_autoload_register(function ($class) {
\Formipay\Init::get_instance(); \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) { // function formipay_add_defer_attribute($tag, $handle) {
// if ( 'product-details' === $handle ) { // if ( 'product-details' === $handle ) {
// // Add defer attribute // // Add defer attribute

View File

@@ -434,6 +434,10 @@ class Access {
check_ajax_referer( 'formipay-admin-access-nonce', '_wpnonce' ); check_ajax_referer( 'formipay-admin-access-nonce', '_wpnonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
$args = [ $args = [
'post_type' => 'formipay-access', 'post_type' => 'formipay-access',
'posts_per_page' => -1, 'posts_per_page' => -1,
@@ -524,6 +528,10 @@ class Access {
check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' ); 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'])) { if (!isset($_POST['search']) || empty($_POST['search'])) {
wp_send_json_error( __('No search term provided.', 'formipay') ); wp_send_json_error( __('No search term provided.', 'formipay') );
} }
@@ -554,6 +562,10 @@ class Access {
check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' ); check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
if( !empty($_REQUEST['title']) ){ if( !empty($_REQUEST['title']) ){
$title = sanitize_text_field( wp_unslash($_REQUEST['title']) ); $title = sanitize_text_field( wp_unslash($_REQUEST['title']) );
$post_id = wp_insert_post( [ $post_id = wp_insert_post( [
@@ -582,6 +594,10 @@ class Access {
check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' ); check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
if( empty($_REQUEST['id']) ){ if( empty($_REQUEST['id']) ){
wp_send_json_error( [ wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ), 'title' => esc_html__( 'Failed', 'formipay' ),
@@ -613,6 +629,10 @@ class Access {
check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' ); check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
if( empty($_REQUEST['ids']) ){ if( empty($_REQUEST['ids']) ){
wp_send_json_error( [ wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ), 'title' => esc_html__( 'Failed', 'formipay' ),
@@ -656,6 +676,10 @@ class Access {
check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' ); check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
if( empty($_REQUEST['id']) ){ if( empty($_REQUEST['id']) ){
wp_send_json_error( [ wp_send_json_error( [
'title' => esc_html__('Failed', 'formipay'), 'title' => esc_html__('Failed', 'formipay'),

View File

@@ -453,6 +453,10 @@ class Coupon {
check_ajax_referer( 'formipay-frontend-nonce', 'security' ); 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']) ) : ''; $code = isset($_REQUEST['code']) ? sanitize_text_field( wp_unslash($_REQUEST['code']) ) : '';
$form_id = isset($_REQUEST['form']) ? intval( $_REQUEST['form'] ) : 0; $form_id = isset($_REQUEST['form']) ? intval( $_REQUEST['form'] ) : 0;
@@ -566,6 +570,10 @@ class Coupon {
check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' ); check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
$args = [ $args = [
'post_type' => 'formipay-coupon', 'post_type' => 'formipay-coupon',
'posts_per_page' => -1, 'posts_per_page' => -1,
@@ -670,6 +678,10 @@ class Coupon {
check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' ); 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'])) { if (!isset($_POST['search']) || empty($_POST['search'])) {
wp_send_json_error( __('No search term provided.', 'formipay') ); wp_send_json_error( __('No search term provided.', 'formipay') );
} }
@@ -700,6 +712,10 @@ class Coupon {
check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' ); 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']) ) : ''; $code = isset($_REQUEST['title']) ? sanitize_text_field( wp_unslash($_REQUEST['title']) ) : '';
if( !empty($code) && '' !== $code ){ if( !empty($code) && '' !== $code ){
@@ -731,6 +747,10 @@ class Coupon {
check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' ); 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_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0;
$delete = wp_delete_post($post_id, true); $delete = wp_delete_post($post_id, true);
@@ -755,6 +775,10 @@ class Coupon {
check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' ); check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
if( empty($_REQUEST['ids']) ){ if( empty($_REQUEST['ids']) ){
wp_send_json_error( [ wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ), 'title' => esc_html__( 'Failed', 'formipay' ),
@@ -798,6 +822,10 @@ class Coupon {
check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' ); 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_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0;
$post = get_post($post_id); $post = get_post($post_id);
if (!$post) { if (!$post) {

View File

@@ -233,11 +233,40 @@ class Customer {
check_ajax_referer( 'formipay-admin-access-nonce', '_wpnonce' ); check_ajax_referer( 'formipay-admin-access-nonce', '_wpnonce' );
$get_all_customers = $this->get(); if ( ! current_user_can( 'manage_options' ) ) {
$customers = []; wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
if(!empty($get_all_customers)){ global $wpdb;
foreach($get_all_customers as $customer){ $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[] = [ $customers[] = [
'ID' => $customer->id, 'ID' => $customer->id,
'name' => $customer->name, 'name' => $customer->name,
@@ -248,10 +277,9 @@ class Customer {
} }
} }
// Prepare response data
$response = [ $response = [
'results' => $customers, 'results' => $customers,
'total' => count($get_all_customers), 'total' => (int) $total,
'posts_report' => $customers 'posts_report' => $customers
]; ];
@@ -263,6 +291,10 @@ class Customer {
check_ajax_referer( 'formipay-form-editor', '_wpnonce' ); 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; $form_id = isset($_REQUEST['post']) ? intval($_REQUEST['post']) : 0;
$saved = [ $saved = [

View File

@@ -1525,6 +1525,10 @@ class Form {
check_ajax_referer( 'formipay-form-editor', '_wpnonce' ); check_ajax_referer( 'formipay-form-editor', '_wpnonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
if(isset($_GET['post_id'])){ if(isset($_GET['post_id'])){
$post_id = intval($_GET['post_id']); $post_id = intval($_GET['post_id']);
$formipay_post_meta = get_post_meta($post_id, 'formipay_settings', true); $formipay_post_meta = get_post_meta($post_id, 'formipay_settings', true);
@@ -1545,6 +1549,10 @@ class Form {
check_ajax_referer( 'formipay-admin-post', '_wpnonce' ); check_ajax_referer( 'formipay-admin-post', '_wpnonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
global $wpdb; global $wpdb;
// Initialize default args // Initialize default args
@@ -1623,6 +1631,10 @@ class Form {
check_ajax_referer( 'formipay-admin-post', '_wpnonce' ); 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']) ) : ''; $title = isset($_REQUEST['title']) ? sanitize_text_field( wp_unslash($_REQUEST['title']) ) : '';
if( !empty($title) && '' !== $title ){ if( !empty($title) && '' !== $title ){
@@ -1692,6 +1704,10 @@ class Form {
check_ajax_referer( 'formipay-admin-post', '_wpnonce' ); 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']) : ''; $form_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : '';
$delete = wp_delete_post($form_id, true); $delete = wp_delete_post($form_id, true);
@@ -1716,6 +1732,10 @@ class Form {
check_ajax_referer( 'formipay-admin-post', '_wpnonce' ); check_ajax_referer( 'formipay-admin-post', '_wpnonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
if( empty($_REQUEST['ids']) ){ if( empty($_REQUEST['ids']) ){
wp_send_json_error( [ wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ), 'title' => esc_html__( 'Failed', 'formipay' ),
@@ -1759,6 +1779,10 @@ class Form {
check_ajax_referer( 'formipay-admin-post', '_wpnonce' ); 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_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : '';
$post = get_post($post_id); $post = get_post($post_id);
if (!$post) { if (!$post) {

View File

@@ -734,6 +734,41 @@ class Paypal extends Payment {
return new \WP_Error('invalid_webhook', 'Invalid webhook payload'); 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 // Example: get order_id from custom_id or invoice_number in resource
$resource = $event['resource']; $resource = $event['resource'];
$order_id = null; $order_id = null;

View File

@@ -1,821 +0,0 @@
<?php
namespace Formipay\Integration;
use Formipay\Traits\SingletonTrait;
use Formipay\Payment\Payment;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;
class Paypal extends Payment {
use SingletonTrait;
private $gateway = 'paypal';
protected function __construct() {
parent::__construct();
add_action( 'init', [$this, 'create_db'] );
add_action( 'formipay/order/new', [$this, 'add_transaction'], 99 );
add_filter( 'formipay/form-config/tab:payments/gateways', [$this, 'add_gateway'] );
add_filter( 'formipay/form-config/tab:payments', [$this, 'add_paypal_settings'] );
add_filter( 'formipay/global-settings/tab:payment', [$this, 'add_settings'] );
add_filter( 'formipay/payment-list/paypal/label', [$this, 'set_frontend_label'] );
add_filter( 'formipay/payment-list/paypal/logo', [$this, 'set_frontend_logo'] );
add_filter( 'formipay/order/result', [$this, 'process_payment'], 99 );
add_action( 'parse_query', [$this, 'check_parse_query'] );
add_filter( 'formipay/order/payment-details/paypal', [$this, 'render_payment_details'], 999, 4 );
add_filter('formipay/notification/order/email/action-button', [$this, 'render_email_action_button'], 99, 3 );
}
public function create_db() {
global $wpdb;
$charset_collate = $wpdb->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();
?>
<a id="upload-transfer-receipt" class="formipay-submit-button" href="<?php echo esc_url($meta['payer_action_url']) ?>">
<i class="bi bi-paypal"></i> <?php echo esc_html__( 'Pay Now', 'formipay' ); ?>
</a>
<?php
$render = ob_get_contents();
ob_get_clean();
}elseif($submit_action_type == 'whatsapp'){
$slug = 'thankyou';
if(!empty($formipay_settings['thankyou_link'])){
$slug = $formipay_settings['thankyou_link'];
}
// translators: %s is the Paypal payment URL.
$message = sprintf( __('> 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;
}
}

View File

@@ -123,6 +123,10 @@ class License {
public function tabledata() { public function tabledata() {
check_ajax_referer('formipay-admin-licenses', '_wpnonce'); check_ajax_referer('formipay-admin-licenses', '_wpnonce');
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
global $wpdb; global $wpdb;
$table = $wpdb->prefix . 'formipay_licenses'; $table = $wpdb->prefix . 'formipay_licenses';
@@ -200,6 +204,10 @@ class License {
public function delete() { public function delete() {
check_ajax_referer('formipay-admin-licenses', '_wpnonce'); check_ajax_referer('formipay-admin-licenses', '_wpnonce');
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
if (empty($_REQUEST['id'])) { if (empty($_REQUEST['id'])) {
wp_send_json_error([ wp_send_json_error([
'title' => esc_html__('Failed', 'formipay'), 'title' => esc_html__('Failed', 'formipay'),
@@ -232,6 +240,10 @@ class License {
public function bulk_delete() { public function bulk_delete() {
check_ajax_referer('formipay-admin-licenses', '_wpnonce'); 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'] : []; $ids = isset($_REQUEST['ids']) ? (array) $_REQUEST['ids'] : [];
if (empty($ids)) { if (empty($ids)) {
wp_send_json_error([ wp_send_json_error([

View File

@@ -148,7 +148,7 @@ class Order {
$unique_id = isset($order_meta_data['session_id']) ? sanitize_text_field($order_meta_data['session_id']) : ''; $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)); $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 ( if (
!empty($this->order_data['redirect_url']) && !empty($this->order_data['redirect_url']) &&
@@ -768,6 +768,10 @@ class Order {
check_ajax_referer( 'formipay-order-details', '_wpnonce' ); 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']) ) : ''; $type = isset($_REQUEST['type']) ? sanitize_text_field( wp_unslash($_REQUEST['type']) ) : '';
$search = isset($_REQUEST['search']) ? sanitize_text_field( wp_unslash($_REQUEST['search']) ) : ''; $search = isset($_REQUEST['search']) ? sanitize_text_field( wp_unslash($_REQUEST['search']) ) : '';
@@ -932,7 +936,16 @@ class Order {
check_ajax_referer( 'formipay-order-details', '_wpnonce' ); 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 = [ $order_status = [
'all' => 0, 'all' => 0,
@@ -941,16 +954,14 @@ class Order {
'payment-confirm' => 0 'payment-confirm' => 0
]; ];
// Count order statuses if (!empty($status_counts)) {
if (!empty($all_orders)) { foreach ($status_counts as $row) {
foreach ($all_orders as $order) { $order_status['all'] += (int) $row['cnt'];
$order_status['all']++; if (isset($order_status[$row['status']])) {
$order_status[$order->status]++; $order_status[$row['status']] = (int) $row['cnt'];
}
} }
} }
global $wpdb;
$table = $wpdb->prefix . 'formipay_orders';
$where = []; $where = [];
$params = []; $params = [];
@@ -1028,6 +1039,10 @@ class Order {
check_ajax_referer( 'formipay-order-details', '_wpnonce' ); 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; $order_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0;
$delete = $this->delete($order_id); $delete = $this->delete($order_id);
@@ -1052,6 +1067,10 @@ class Order {
check_ajax_referer( 'formipay-order-details', '_wpnonce' ); check_ajax_referer( 'formipay-order-details', '_wpnonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
if( empty($_REQUEST['ids']) ){ if( empty($_REQUEST['ids']) ){
wp_send_json_error( [ wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ), 'title' => esc_html__( 'Failed', 'formipay' ),
@@ -1095,6 +1114,10 @@ class Order {
check_ajax_referer( 'formipay-order-details', '_wpnonce' ); 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; $order_id = isset($_REQUEST['order']) ? intval($_REQUEST['order']) : 0;
wp_send_json(formipay_get_order($order_id)); wp_send_json(formipay_get_order($order_id));
@@ -1105,6 +1128,10 @@ class Order {
check_ajax_referer( 'formipay-order-details', '_wpnonce' ); 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; $order_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0;
$status = isset($_REQUEST['status']) ? sanitize_text_field( wp_unslash($_REQUEST['status']) ) : ''; $status = isset($_REQUEST['status']) ? sanitize_text_field( wp_unslash($_REQUEST['status']) ) : '';
@@ -1139,6 +1166,10 @@ class Order {
check_ajax_referer( 'formipay-order-details', '_wpnonce' ); check_ajax_referer( 'formipay-order-details', '_wpnonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
$editable_field = []; $editable_field = [];
$order_id = isset($_REQUEST['order_id']) ? intval($_REQUEST['order_id']) : 0; $order_id = isset($_REQUEST['order_id']) ? intval($_REQUEST['order_id']) : 0;
@@ -1183,6 +1214,10 @@ class Order {
check_ajax_referer( 'formipay-order-details', '_wpnonce' ); 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; $order_id = isset($_REQUEST['order_id']) ? intval($_REQUEST['order_id']) : 0;
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$raw_data = isset($_REQUEST['new_values']) ? wp_unslash( $_REQUEST['new_values'] ) : []; $raw_data = isset($_REQUEST['new_values']) ? wp_unslash( $_REQUEST['new_values'] ) : [];
@@ -1234,6 +1269,10 @@ class Order {
check_ajax_referer( 'formipay-order-details', '_wpnonce' ); 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; $order_id = isset($_REQUEST['order_id']) ? intval($_REQUEST['order_id']) : 0;
$method = isset($_REQUEST['method']) ? sanitize_text_field( wp_unslash($_REQUEST['method']) ) : ''; $method = isset($_REQUEST['method']) ? sanitize_text_field( wp_unslash($_REQUEST['method']) ) : '';
$password = isset($_REQUEST['password']) ? sanitize_text_field( wp_unslash($_REQUEST['password']) ) : ''; $password = isset($_REQUEST['password']) ? sanitize_text_field( wp_unslash($_REQUEST['password']) ) : '';

View File

@@ -220,7 +220,6 @@ abstract class Payment {
'top' 'top'
); );
flush_rewrite_rules();
} }

View File

@@ -591,6 +591,10 @@ class Product {
check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' ); check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
global $wpdb; global $wpdb;
// Initialize default args // Initialize default args
@@ -707,6 +711,10 @@ class Product {
check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' ); 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']) ) : ''; $title = isset($_REQUEST['title']) ? sanitize_text_field( wp_unslash($_REQUEST['title']) ) : '';
if( !empty($title) && '' !== $title ){ if( !empty($title) && '' !== $title ){
@@ -738,6 +746,10 @@ class Product {
check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' ); 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']) ) : ''; $search = isset($_REQUEST['search']) ? sanitize_text_field( wp_unslash($_REQUEST['search']) ) : '';
$countries = formipay_currency_array(); $countries = formipay_currency_array();
@@ -762,6 +774,10 @@ class Product {
check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' ); 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']) : ''; $form_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : '';
$delete = wp_delete_post($form_id, true); $delete = wp_delete_post($form_id, true);
@@ -786,6 +802,10 @@ class Product {
check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' ); check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => 'Unauthorized' ] );
}
if( empty($_REQUEST['ids']) ){ if( empty($_REQUEST['ids']) ){
wp_send_json_error( [ wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ), 'title' => esc_html__( 'Failed', 'formipay' ),
@@ -828,6 +848,10 @@ class Product {
check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' ); 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_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : '';
$post = get_post($post_id); $post = get_post($post_id);
if (!$post) { if (!$post) {

View File

@@ -68,7 +68,7 @@ class Render {
} }
ob_start(); ob_start();
$timezone = get_option('timezone_string', 'Asia/Jakarta'); $timezone = wp_timezone_string();
$require_login = formipay_get_post_meta($post_id, 'require_login') === 'on'; $require_login = formipay_get_post_meta($post_id, 'require_login') === 'on';
$scheduled = formipay_get_post_meta($post_id, 'schedule_toggle') === 'on'; $scheduled = formipay_get_post_meta($post_id, 'schedule_toggle') === 'on';

View File

@@ -41,7 +41,6 @@ class Thankyou {
'top' 'top'
); );
flush_rewrite_rules();
} }
@@ -425,7 +424,7 @@ class Thankyou {
$cookie = $this->get_cookie(); $cookie = $this->get_cookie();
$cookie[$order_id] = $order_meta_data['session_id']; $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'){ if($method == 'magic_link'){
do_action('formipay/notification/access/email', $order); do_action('formipay/notification/access/email', $order);
@@ -679,14 +678,10 @@ class Thankyou {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$raw_value = wp_unslash($_COOKIE['fp_access']); $raw_value = wp_unslash($_COOKIE['fp_access']);
// Validate before unserializing $decoded = json_decode($raw_value, true);
if (is_serialized($raw_value)) { // Use WordPress core function
$maybe = maybe_unserialize($raw_value);
// Type-check and sanitize array contents if (is_array($decoded)) {
if (is_array($maybe)) { $cookie = array_map('sanitize_text_field', $decoded);
$cookie = array_map('sanitize_text_field', $maybe);
}
} }
} }
return $cookie; return $cookie;

62
uninstall.php Normal file
View File

@@ -0,0 +1,62 @@
<?php
/**
* Formipay Uninstall
*
* Removes all plugin data when the plugin is uninstalled via WordPress admin.
*/
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}
global $wpdb;
// Drop custom tables
$tables = [
'formipay_orders',
'formipay_customers',
'formipay_bank_transfer_trx',
'formipay_paypal_trx',
'formipay_notification_log',
'formipay_tokens',
];
foreach ( $tables as $table_suffix ) {
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$wpdb->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();