diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 8051fd6..3b3ff08 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ # PayWeb_WooCommerce -## PayGate WooCommerce plugin v1.4.7 for WooCommerce v8.3.1 -This is the Paygate plugin for WooCommerce. Please feel free to contact the Payfast support team at support@payfast.io should you require any assistance. +## PayGate WooCommerce plugin v1.4.8 for WooCommerce v8.9.1 + +This is the Paygate plugin for WooCommerce. Please feel free to contact the Payfast support team at support@payfast.io +should you require any assistance. ## Installation + [![How To Setup PayGate PayWeb for WooCommerce](https://appinlet.com/wp-content/uploads/2021/01/How-To-Setup-PayGate-PayWeb-for-WooCommerce.jpg)](https://www.youtube.com/watch?v=MMcEG7FmoEM "How To Setup PayGate PayWeb for WooCommerce") -Please navigate to the [releases page](https://github.com/PayGate/PayWeb_WooCommerce/releases), download the latest release (v1.4.7) and unzip. You will them be able to follow the integration guide PDF which is included in the zip. +Please navigate to the [releases page](https://github.com/PayGate/PayWeb_WooCommerce/releases), download the latest +release (v1.4.8) and unzip. You will them be able to follow the integration guide PDF which is included in the zip. ## Collaboration diff --git a/changelog.txt b/changelog.txt index 405e306..c3d1fe2 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,8 @@ *** Paygate for WooCommerce Changelog *** += 1.4.8 - 2024-05-28 = + * Tested on WooCommerce 8.9.1, PHP 8.1 and WordPress 6.5.3. + * Fix payment types compatibility. + = 1.4.7 - 2023-11-22 = * Tested on WooCommerce 8.3.1, PHP 8.0 and WordPress 6.4.1. * Add support for HPOS and Blocks. diff --git a/classes/WC_Gateway_PayGate.php b/classes/WC_Gateway_PayGate.php index 0078614..2202bb6 100644 --- a/classes/WC_Gateway_PayGate.php +++ b/classes/WC_Gateway_PayGate.php @@ -1,13 +1,13 @@ '; - const SCRIPT_TAG = '";'; - const SCRIPT_WIN_TOP_LOCAT_HREF = ''; + const SCRIPT_WIN_TOP_LOCAT_HREF = ' HTML; @@ -248,7 +238,7 @@ public function generate_paygate_form($order_id) * * @since 1.0.0 */ - public function check_paygate_response() + public function check_paygate_response(): void { $this->logging ? self::$wc_logger->add('paygatepayweb', 'Redirect POST: ' . json_encode($_POST)) : ''; // Sanitise GET and POST arrays @@ -256,11 +246,11 @@ public function check_paygate_response() $get = $this->sanitizeFields($_GET); // Only process if IPN is disabled - if ( ! (isset($get['gid']) && isset($post[self::PAY_REQUEST_ID]))) { + if (!(isset($get['gid']) && isset($post[self::PAY_REQUEST_ID]))) { die(); } - if ( ! $order_id = $get['gid']) { + if (!$order_id = $get['gid']) { wp_redirect(get_permalink(wc_get_page_id('myaccount'))); } @@ -269,7 +259,7 @@ public function check_paygate_response() } $transient = get_transient('PAYGATE_PAYWEB_RED_' . $post[self::PAY_REQUEST_ID]); - if($transient !== false) { + if ($transient !== false) { exit(); } set_transient('PAYGATE_PAYWEB_RED_' . $post[self::PAY_REQUEST_ID], $post[self::PAY_REQUEST_ID], 15); @@ -281,7 +271,7 @@ public function check_paygate_response() $status = isset($post[self::TRANSACTION_STATUS]) ? $post[self::TRANSACTION_STATUS] : ""; $reference = $this->getOrderReference($order); - if ( ! $this->validateChecksum($post, $reference) && !$order->is_paid()) { + if (!$this->validateChecksum($post, $reference) && !$order->is_paid()) { $order->update_status(self::PENDING, __('Checksum failed')); exit(); } @@ -309,7 +299,7 @@ public function check_paygate_response() parse_str($response['body'], $parsed_response); - if((int)$status === 1) { + if ((int)$status === 1) { $this->vaultCard($parsed_response, $customer_id); } @@ -327,13 +317,11 @@ public function check_paygate_response() * * @since 1.0.0 */ - public function check_paygate_notify_response() + + public function check_paygate_notify_response(): void { // Log notify response for debugging purposes - if($this->logging) { - self::$wc_logger->add('paygatepayweb', 'Notify POST: ' . json_encode($_POST)); - self::$wc_logger->add('paygatepayweb', 'Notify GET: ' . json_encode($_GET)); - } + $this->logIPNRequest(); // Tell Paygate notify we have received echo 'OK'; @@ -342,7 +330,7 @@ public function check_paygate_notify_response() exit(); } - if ( ! isset($_POST)) { + if (!isset($_POST)) { exit(); } @@ -375,24 +363,11 @@ public function check_paygate_notify_response() } // Verify security signature - if ( ! $this->validateChecksumNotify($paygate_data)) { - if($this->logging){ - self::$wc_logger->add('paygatepayweb', 'Failed to validate checksum with data ' . json_encode($paygate_data)); - } - if ( ! $order->has_status(self::FAILED)) { - $order->add_order_note('Failed Response via Notify, ' . $this->error_desc . self::BR); - $order->update_status(self::PENDING, __('Checksum failed in notify')); - } - exit(); - } else { - if($this->logging){ - self::$wc_logger->add('paygatepayweb', 'Validated checksum with data ' . json_encode($paygate_data)); - } - } + $this->verifySecuritySignature($paygate_data, $order); $customer_id = $order->get_customer_id(); - if((int)$paygate_data[self::TRANSACTION_STATUS] === 1) { + if ((int)$paygate_data[self::TRANSACTION_STATUS] === 1) { $this->vaultCard($paygate_data, $customer_id); } @@ -400,9 +375,9 @@ public function check_paygate_notify_response() exit(); } - $transaction_id = isset($paygate_data[self::TRANSACTION_ID]) ? $paygate_data[self::TRANSACTION_ID] : ""; - $result_desc = isset($paygate_data[self::RESULT_DESC]) ? $paygate_data[self::RESULT_DESC] : ""; - $pay_request_id = isset($paygate_data[self::PAY_REQUEST_ID]) ? $paygate_data[self::PAY_REQUEST_ID] : ""; + $transaction_id = $paygate_data[self::TRANSACTION_ID] ?? ""; + $result_desc = $paygate_data[self::RESULT_DESC] ?? ""; + $pay_request_id = $paygate_data[self::PAY_REQUEST_ID] ?? ""; switch ((int)$paygate_data[self::TRANSACTION_STATUS]) { case 1: @@ -420,8 +395,8 @@ public function check_paygate_notify_response() exit; break; default: - if ( ! $order->has_status(self::PENDING)) { - if($this->logging){ + if (!$order->has_status(self::PENDING)) { + if ($this->logging) { self::$wc_logger->add('paygatepayweb', 'Reached default in switch statement'); } $order->add_order_note( @@ -442,7 +417,7 @@ public function check_paygate_notify_response() * * @return mixed */ - public function paywebQuery($payRequestId, $order) + public function paywebQuery($payRequestId, $order): mixed { $reference = $this->getOrderReference($order); @@ -478,9 +453,9 @@ public function paywebQuery($payRequestId, $order) */ public function paywebQueryText($response, $payRequestId) { - $transactionStatus = ! empty($response[self::TRANSACTION_STATUS]) ? $response[self::TRANSACTION_STATUS] : 'Null'; - $resultCode = ! empty($response[self::RESULT_CODE]) ? $response[self::RESULT_CODE] : 'Null'; - $resultDesc = ! empty($response[self::RESULT_DESC]) ? $response[self::RESULT_DESC] : 'Null'; + $transactionStatus = !empty($response[self::TRANSACTION_STATUS]) ? $response[self::TRANSACTION_STATUS] : 'Null'; + $resultCode = !empty($response[self::RESULT_CODE]) ? $response[self::RESULT_CODE] : 'Null'; + $resultDesc = !empty($response[self::RESULT_DESC]) ? $response[self::RESULT_DESC] : 'Null'; return << @@ -495,9 +470,81 @@ public function paywebQueryText($response, $payRequestId) * * @return string */ - public function paywebQueryStatus($response) + public function paywebQueryStatus($response): string + { + return !empty($response[self::TRANSACTION_STATUS]) ? $response[self::TRANSACTION_STATUS] : 'Null'; + } + + /** + * @param $t + * @param true $newPaymentMethod + * + * @return true + */ + public function isPaymentMethodNew($t, $newPaymentMethod): bool + { + if (isset($t) && $t === '1') { + $newPaymentMethod = true; + } + + return $newPaymentMethod; + } + + /** + * @return void + */ + public function logIPNRequest(): void + { + if ($this->logging) { + self::$wc_logger->add('paygatepayweb', 'Notify POST: ' . json_encode($_POST)); + self::$wc_logger->add('paygatepayweb', 'Notify GET: ' . json_encode($_GET)); + } + } + + /** + * @param array $paygate_data + * @param $order + * + * @return void + */ + public function verifySecuritySignature(array $paygate_data, $order): void + { + if (!$this->validateChecksumNotify($paygate_data)) { + if ($this->logging) { + self::$wc_logger->add( + 'paygatepayweb', + 'Failed to validate checksum with data ' . json_encode($paygate_data) + ); + } + if (!$order->has_status(self::FAILED)) { + $order->add_order_note('Failed Response via Notify, ' . $this->error_desc . self::BR); + $order->update_status(self::PENDING, __('Checksum failed in notify')); + } + exit(); + } else { + if ($this->logging) { + self::$wc_logger->add('paygatepayweb', 'Validated checksum with data ' . json_encode($paygate_data)); + } + } + } + + /** + * @param $order + * + * @return void + */ + public function alternativeCartMechanism($order): void { - return ! empty($response[self::TRANSACTION_STATUS]) ? $response[self::TRANSACTION_STATUS] : 'Null'; + if (count($order->get_items()) > 0) { + foreach ($order->get_items() as $product) { + $product_id = isset($product['product_id']) ? (int)$product['product_id'] : 0; + $quantity = isset($product['quantity']) ? (int)$product['quantity'] : 1; + $variation_id = isset($product['variation_id']) ? (int)$product['variation_id'] : 0; + $variation = isset($product['variation']) ? (array)$product['variation'] : array(); + WC()->cart->add_to_cart($product_id, $quantity, $variation_id, $variation); + } + WC()->cart->calculate_totals(); + } } /** @@ -505,7 +552,7 @@ public function paywebQueryStatus($response) * * @return string */ - protected function getOrderReference($order) + protected function getOrderReference($order): string { $order_id = $order->get_id(); $reference = $order_id . '-' . $order->get_order_number(); @@ -517,7 +564,7 @@ protected function getOrderReference($order) // Set reference if custom order meta is set if ($this->order_meta_reference != '') { $reference_meta = $order->get_meta(sanitize_key($this->order_meta_reference), true); - $reference .= ! empty($reference_meta) ? '-' . $reference_meta : ''; + $reference .= !empty($reference_meta) ? '-' . $reference_meta : ''; } return $reference; @@ -526,11 +573,11 @@ protected function getOrderReference($order) /** * @return bool */ - protected function setVaultableMethod() + protected function setVaultableMethod(): bool { - $wcsession = WC()->session; + $wcsession = WC()->session; $subpaymentmethod = $wcsession->get(self::SUB_PAYMENT_METHOD); - $vaultableMethod = false; + $vaultableMethod = false; if (isset($subpaymentmethod) && $subpaymentmethod != '') { $this->data_to_send['PAY_METHOD'] = substr($subpaymentmethod, 0, 2); if (isset($this->paymentTypes[$subpaymentmethod]) && $this->paymentTypes[$subpaymentmethod] != '') { @@ -559,16 +606,7 @@ protected function processOrderFinal($status, $order, $transaction_id, $result_d { if ($this->settings[self::ALTERNATECARTHANDLING] == 'yes') { // Alternative cart mechanism - if (count($order->get_items()) > 0) { - foreach ($order->get_items() as $product) { - $product_id = isset($product['product_id']) ? (int)$product['product_id'] : 0; - $quantity = isset($product['quantity']) ? (int)$product['quantity'] : 1; - $variation_id = isset($product['variation_id']) ? (int)$product['variation_id'] : 0; - $variation = isset($product['variation']) ? (array)$product['variation'] : array(); - WC()->cart->add_to_cart($product_id, $quantity, $variation_id, $variation); - } - WC()->cart->calculate_totals(); - } + $this->alternativeCartMechanism($order); } switch ($status) { case 1: @@ -587,13 +625,13 @@ protected function processOrderFinal($status, $order, $transaction_id, $result_d break; default: if ($this->settings[self::DISABLENOTIFY] == 'yes') { - if ( ! $order->has_status(self::PENDING)) { + if (!$order->has_status(self::PENDING)) { $order->add_order_note( 'Response via ' . $this->settings[self::PAYMENT_TYPE] . ', RESULT_DESC: ' . $result_desc . self::PAYGATE_TRANS_ID . $transaction_id . self::PAY_REQUEST_ID_TEXT . $pay_request_id . self::BR ); if (!$order->is_paid()) { - $order->update_status(self::PENDING); - } + $order->update_status(self::PENDING); + } } $this->add_notice( @@ -611,10 +649,10 @@ protected function processOrderFinal($status, $order, $transaction_id, $result_d /** * @param $redirect_link */ - protected function redirectAfterOrder($redirect_link) + protected function redirectAfterOrder($redirect_link): void { $redirect_link = str_replace('&', '&', $redirect_link); - wp_redirect($redirect_link); + wp_redirect($redirect_link); } /** @@ -622,7 +660,7 @@ protected function redirectAfterOrder($redirect_link) * @param $transaction_id * @param $pay_request_id */ - protected function processOrderFinalSuccess($order, $transaction_id, $pay_request_id, $notify = 'redirect') + protected function processOrderFinalSuccess($order, $transaction_id, $pay_request_id, $notify = 'redirect'): void { WC()->cart->empty_cart(); @@ -632,7 +670,7 @@ protected function processOrderFinalSuccess($order, $transaction_id, $pay_reques $order->add_order_note( 'Response via Redirect: Transaction successful
Paygate Trans Id: ' . $transaction_id . self::PAY_REQUEST_ID_TEXT . $pay_request_id . self::BR ); - if ( ! $order->has_status(self::PROCESSING) && ! $order->has_status(self::COMPLETED)) { + if (!$order->has_status(self::PROCESSING) && !$order->has_status(self::COMPLETED)) { $order->payment_complete(); } } @@ -642,7 +680,7 @@ protected function processOrderFinalSuccess($order, $transaction_id, $pay_reques $order->add_order_note( 'Response via Notify: Transaction successful
Paygate Trans Id: ' . $transaction_id . self::PAY_REQUEST_ID_TEXT . $pay_request_id . self::BR ); - if ( ! $order->has_status(self::PROCESSING) && ! $order->has_status(self::COMPLETED)) { + if (!$order->has_status(self::PROCESSING) && !$order->has_status(self::COMPLETED)) { $order->payment_complete(); } } @@ -661,17 +699,17 @@ protected function processOrderFinalSuccess($order, $transaction_id, $pay_reques * @param $transaction_id * @param $pay_request_id */ - protected function processOrderFinalCancel($order, $transaction_id, $pay_request_id) + protected function processOrderFinalCancel($order, $transaction_id, $pay_request_id): void { if ($this->settings[self::DISABLENOTIFY] == 'yes') { - if ( ! $order->has_status(self::FAILED)) { + if (!$order->has_status(self::FAILED)) { $order->add_order_note( - 'Response via Redirect: User cancelled transaction
' . self::PAY_REQUEST_ID_TEXT . $pay_request_id . self::BR + 'Response via Redirect: User cancelled transaction
' . self::PAY_REQUEST_ID_TEXT . $pay_request_id . self::BR ); $order->update_status(self::FAILED); } } else { - if ( ! $order->has_status(self::FAILED)) { + if (!$order->has_status(self::FAILED)) { $order->add_order_note( 'Response via Notify, User cancelled transaction
' . self::PAY_REQUEST_ID_TEXT . $pay_request_id . self::BR ); @@ -689,17 +727,17 @@ protected function processOrderFinalCancel($order, $transaction_id, $pay_request * @param $pay_request_id * @param $result_desc */ - protected function processOrderFinalFail($order, $transaction_id, $pay_request_id, $result_desc) + protected function processOrderFinalFail($order, $transaction_id, $pay_request_id, $result_desc): void { if ($this->settings[self::DISABLENOTIFY] == 'yes') { - if ( ! $order->has_status(self::FAILED)) { + if (!$order->has_status(self::FAILED)) { $order->add_order_note( 'Response via Redirect, RESULT_DESC: ' . $result_desc . self::PAY_REQUEST_ID_TEXT . $pay_request_id . self::BR ); $order->update_status(self::FAILED); } } else { - if ( ! $order->has_status(self::FAILED)) { + if (!$order->has_status(self::FAILED)) { $order->add_order_note( 'Response via Notify, RESULT_DESC: ' . $result_desc . self::PAY_REQUEST_ID_TEXT . $pay_request_id . self::BR ); @@ -715,7 +753,7 @@ protected function processOrderFinalFail($order, $transaction_id, $pay_request_i * @param $parsed_response * @param $customer_id */ - protected function vaultCard($parsed_response, $customer_id) + protected function vaultCard($parsed_response, $customer_id): void { if ($this->payVault == 'yes') { $this->vaultCard = get_post_meta( @@ -737,11 +775,11 @@ protected function vaultCard($parsed_response, $customer_id) * * @return bool */ - protected function validateChecksum($post, $reference) + protected function validateChecksum($post, $reference): bool { $pay_request_id = $post[self::PAY_REQUEST_ID]; - $status = isset($post[self::TRANSACTION_STATUS]) ? $post[self::TRANSACTION_STATUS] : ""; - $checksum = isset($post[self::CHECKSUM]) ? $post[self::CHECKSUM] : ""; + $status = $post[self::TRANSACTION_STATUS] ?? ""; + $checksum = $post[self::CHECKSUM] ?? ""; $checksum_source = $this->merchant_id . $pay_request_id . $status . $reference . $this->encryption_key; $test_checksum = md5($checksum_source); @@ -757,8 +795,8 @@ protected function validateChecksumNotify($paygate_data) $checkSumParams .= $val; continue; } - if($key === 'AUTH_CODE') { - if($val === 'null') { + if ($key === 'AUTH_CODE') { + if ($val === 'null') { $checkSumParams .= ''; } else { $checkSumParams .= $val; @@ -819,7 +857,7 @@ protected function saveToken($data, $customer_id) } } - if ( ! $exists) { + if (!$exists) { $token = new WC_Payment_Token_CC(); $token->set_token($this->vaultId); diff --git a/gateway-paygate.php b/gateway-paygate.php index eded8a2..c904b7e 100755 --- a/gateway-paygate.php +++ b/gateway-paygate.php @@ -5,17 +5,17 @@ * Description: Receive payments using the South African Paygate payments provider. * Author: Payfast (Pty) Ltd * Author URI: https://payfast.io/ - * Version: 1.4.7 + * Version: 1.4.8 * Requires at least: 5.6 - * Tested up to: 6.4.1 - * WC tested up to: 8.3.1 + * Tested up to: 6.5.3 + * WC tested up to: 8.9.1 * WC requires at least: 6.0 * Requires PHP: 8.0 * * Developer: App Inlet (Pty) Ltd * Developer URI: https://www.appinlet.com/ * - * Copyright: © 2023 Payfast (Pty) Ltd. + * Copyright: © 2024 Payfast (Pty) Ltd. * License: GNU General Public License v3.0 * License URI: http://www.gnu.org/licenses/gpl-3.0.html * Text Domain: paygate-payweb-for-woocommerce @@ -29,6 +29,7 @@ * @since 1.0.0 * @noinspection PhpUnused */ + use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry; function woocommerce_paygate_init() @@ -102,33 +103,35 @@ function paygate_add_cron_hook() function paygate_remove_cron_hook() { - while(wp_next_scheduled('paygate_query_cron_hook')) { + while (wp_next_scheduled('paygate_query_cron_hook')) { wp_clear_scheduled_hook('paygate_query_cron_hook'); } } -add_action( 'before_woocommerce_init', 'woocommerce_paygatepayweb_declare_hpos_compatibility' ); +add_action('before_woocommerce_init', 'woocommerce_paygatepayweb_declare_hpos_compatibility'); /** * Declares support for HPOS. * * @return void */ -function woocommerce_paygatepayweb_declare_hpos_compatibility() { - if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) { - \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true ); +function woocommerce_paygatepayweb_declare_hpos_compatibility() +{ + if (class_exists('\Automattic\WooCommerce\Utilities\FeaturesUtil')) { + \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true); } } -add_action( 'woocommerce_blocks_loaded', 'woocommerce_paygate_woocommerce_blocks_support' ); +add_action('woocommerce_blocks_loaded', 'woocommerce_paygate_woocommerce_blocks_support'); -function woocommerce_paygate_woocommerce_blocks_support() { - if ( class_exists( 'Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType' ) ) { +function woocommerce_paygate_woocommerce_blocks_support() +{ + if (class_exists('Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType')) { require_once dirname(__FILE__) . '/classes/WC_Gateway_PayGate_Blocks_Support.php'; add_action( 'woocommerce_blocks_payment_method_type_registration', - function( Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry $payment_method_registry ) { - $payment_method_registry->register( new WC_Gateway_PayGate_Blocks_Support ); + function (Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry $payment_method_registry) { + $payment_method_registry->register(new WC_Gateway_PayGate_Blocks_Support); } ); } diff --git a/readme.txt b/readme.txt index 478a17f..84c360b 100644 --- a/readme.txt +++ b/readme.txt @@ -2,9 +2,9 @@ Contributors: appinlet Tags: ecommerce, e-commerce, woocommerce, automattic, payment, paygate, app inlet, credit card, payment request Requires at least: 5.6 -Tested up to: 6.4.1 +Tested up to: 6.5.3 Requires PHP: 8.0 -Stable tag: 1.4.7 +Stable tag: 1.4.8 License: GPLv3 License URI: https://www.gnu.org/licenses/gpl-3.0.html @@ -43,6 +43,10 @@ Need help to configure this plugin? Feel free to connect with our Payfast Suppor 4. WooCommerce Admin Paygate Additional Settings continued == Changelog == += 1.4.8 - 2024-05-28 = + * Tested on WooCommerce 8.9.1, PHP 8.1 and WordPress 6.5.3. + * Fix payment types compatibility. + = 1.4.7 - 2023-11-22 = * Tested on WooCommerce 8.3.1, PHP 8.0 and WordPress 6.4.1. * Add support for HPOS and Blocks. @@ -54,16 +58,14 @@ Need help to configure this plugin? Feel free to connect with our Payfast Suppor * Fix multi-domain multisite network activation. * Fix invalid checksum message if order is already paid. -= 1.4.5 - 2022-01-04 = - * Tested on WooCommerce 6.0 and WordPress 5.8. - * Implement payment type filter hooks. - * Update Masterpass to Scan to Pay. - * Add transient in notify handler to curb duplicate transactions. - [See changelog for all versions](https://raw.githubusercontent.com/PayGate/PayWeb_WooCommerce/master/changelog.txt). == Upgrade Notice == += 1.4.8 - 2024-05-28 = + * Tested on WooCommerce 8.9.1, PHP 8.1 and WordPress 6.5.3. + * Fix payment types compatibility. + = 1.4.7 - 2023-11-22 = * Tested on WooCommerce 8.3.1, PHP 8.0 and WordPress 6.4.1. * Add support for HPOS and Blocks. @@ -74,9 +76,3 @@ Need help to configure this plugin? Feel free to connect with our Payfast Suppor * Tested on WooCommerce 6.7.0, PHP 8.0 and WordPress 6.0.1. * Fix multi-domain multisite network activation. * Fix invalid checksum message if order is already paid. - -= 1.4.5 - 2022-01-04 = - * Tested on WooCommerce 6.0 and WordPress 5.8. - * Implement payment type filter hooks. - * Update Masterpass to Scan to Pay. - * Add transient in notify handler to curb duplicate transactions.