From c1aaec8f5da0d972a1e13e92e466d78724bf065b Mon Sep 17 00:00:00 2001 From: Shahbaz Date: Wed, 1 Jan 2025 11:21:45 +0500 Subject: [PATCH 01/10] Consider order total instead of reservedAmount for capture --- classes/core/AltapaySettings.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/classes/core/AltapaySettings.php b/classes/core/AltapaySettings.php index 0e1d021d..85b29b3c 100755 --- a/classes/core/AltapaySettings.php +++ b/classes/core/AltapaySettings.php @@ -100,8 +100,9 @@ public function altapayOrderStatusCompleted( $orderID ) { if ( $pay->CapturedAmount > 0 ) { $this->saveCaptureWarning( 'Could not capture automatically. Manual capture is required for the order: ' . $orderID ); - } else { // Order wasn't captured and must be captured now. - $amount = $pay->ReservedAmount; // Amount to capture. + } else { + // Order wasn't captured and must be captured now. + $amount = $order->get_total(); try { if ( $subscription === true ) { From cdd46eec9934a97f6d47821cc2c53f15671fa0c5 Mon Sep 17 00:00:00 2001 From: Shahbaz Date: Wed, 1 Jan 2025 11:31:19 +0500 Subject: [PATCH 02/10] Update version and release notes --- CHANGELOG.md | 3 +++ altapay.php | 6 +++--- readme.txt | 7 +++++-- wiki.md | 4 ++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 009badfa..f62143bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog All notable changes to this project will be documented in this file. +## [3.7.6] +- Fix: Charge subscriptions using the renewal order total instead of the original reserved amount. + ## [3.7.5] - Display agreement information in the "AltaPay Payment Actions" grid. - Show the "pending payment" status if the subscription is awaiting a charge. diff --git a/altapay.php b/altapay.php index 48741bcd..ae64c446 100755 --- a/altapay.php +++ b/altapay.php @@ -7,10 +7,10 @@ * Author URI: https://altapay.com * Text Domain: altapay * Domain Path: /languages - * Version: 3.7.5 + * Version: 3.7.6 * Name: SDM_Altapay * WC requires at least: 3.9.0 - * WC tested up to: 9.4.3 + * WC tested up to: 9.5.1 * * @package Altapay */ @@ -41,7 +41,7 @@ } if ( ! defined( 'ALTAPAY_PLUGIN_VERSION' ) ) { - define( 'ALTAPAY_PLUGIN_VERSION', '3.7.5' ); + define( 'ALTAPAY_PLUGIN_VERSION', '3.7.6' ); } // Include the autoloader, so we can dynamically include the rest of the classes. diff --git a/readme.txt b/readme.txt index 79089ba2..ad07710b 100644 --- a/readme.txt +++ b/readme.txt @@ -4,10 +4,10 @@ Tags: AltaPay, Gateway, Payments, WooCommerce, Payment Card Industry Requires PHP: 7.4 Requires at least: 5.0 Tested up to: 6.7.1 -Stable tag: 3.7.5 +Stable tag: 3.7.6 License: MIT WC requires at least: 3.9.0 -WC tested up to: 9.4.3 +WC tested up to: 9.5.1 License URI: http://www.gnu.org/licenses/gpl-2.0.html A plugin that integrates your WooCommerce web shop to the AltaPay payments gateway. @@ -39,6 +39,9 @@ AltaPay's Payment Gateway for WooCommerce provides merchants with access to a fu == Changelog == += 3.7.6 = +* Fix: Charge subscriptions using the renewal order total instead of the original reserved amount. + = 3.7.5 = * Display agreement information in the "AltaPay Payment Actions" grid. * Show the "pending payment" status if the subscription is awaiting a charge. diff --git a/wiki.md b/wiki.md index 19cbd5fa..bef4db6a 100644 --- a/wiki.md +++ b/wiki.md @@ -275,13 +275,13 @@ In order to reconcile payments please follow the steps below: Minimum system requirements are: - WordPress min. 5.0 – max. 6.7.1 -- WooCommerce min. 3.9.0 – max. 9.4.3 +- WooCommerce min. 3.9.0 – max. 9.5.1 - PHP 7.4 and above - PHP-bcmath library installed. - PHP-curl MUST be enabled. The latest tested version is: -- WordPress 6.7.1, WooCommerce 9.4.3 and PHP 8.1 +- WordPress 6.7.1, WooCommerce 9.5.1 and PHP 8.1 ## Troubleshooting From 894c643e168c0cb0defbb82987efcc4e3dab0635 Mon Sep 17 00:00:00 2001 From: Shahbaz Date: Wed, 1 Jan 2025 11:41:30 +0500 Subject: [PATCH 03/10] Format code --- classes/core/AltapaySettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/core/AltapaySettings.php b/classes/core/AltapaySettings.php index 85b29b3c..48d7e0fe 100755 --- a/classes/core/AltapaySettings.php +++ b/classes/core/AltapaySettings.php @@ -101,7 +101,7 @@ public function altapayOrderStatusCompleted( $orderID ) { if ( $pay->CapturedAmount > 0 ) { $this->saveCaptureWarning( 'Could not capture automatically. Manual capture is required for the order: ' . $orderID ); } else { - // Order wasn't captured and must be captured now. + // Order wasn't captured and must be captured now. $amount = $order->get_total(); try { From a2725b02777e5eaa8ea2803abf0bd589cc5efd5d Mon Sep 17 00:00:00 2001 From: Shahbaz Date: Wed, 1 Jan 2025 18:08:06 +0500 Subject: [PATCH 04/10] Support export reconciliation data with WooCommerce High-Performance Order Storage (HPOS) --- CHANGELOG.md | 1 + classes/core/AltapayReconciliation.php | 110 ++++++++++--------------- 2 files changed, 46 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f62143bc..8437f43f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. ## [3.7.6] - Fix: Charge subscriptions using the renewal order total instead of the original reserved amount. +- Support export reconciliation data with WooCommerce High-Performance Order Storage (HPOS). ## [3.7.5] - Display agreement information in the "AltaPay Payment Actions" grid. diff --git a/classes/core/AltapayReconciliation.php b/classes/core/AltapayReconciliation.php index 966d602e..5825287f 100644 --- a/classes/core/AltapayReconciliation.php +++ b/classes/core/AltapayReconciliation.php @@ -18,6 +18,7 @@ class AltapayReconciliation { */ public function registerHooks() { add_action( 'manage_posts_extra_tablenav', array( $this, 'addReconciliationExportButton' ), 20, 1 ); + add_action( 'woocommerce_order_list_table_extra_tablenav', array( $this, 'addReconciliationExportButtonHpos' ), 10, 2 ); add_action( 'admin_init', array( $this, 'exportReconciliationCSV' ) ); } @@ -32,17 +33,37 @@ public function addReconciliationExportButton( $which ) { global $typenow; if ( 'shop_order' === $typenow && 'top' === $which ) { - ?> -
- - -
- btnExportReconciliationData(); } } + /** + * @param string $order_type The order type. + * @param string $which The location of the extra table nav markup: 'top' or 'bottom'. + * + * @return void + */ + public function addReconciliationExportButtonHpos( $order_type, $which ) { + + if ( 'shop_order' === $order_type && 'bottom' === $which ) { + $this->btnExportReconciliationData(); + } + } + + /** + * @return void + */ + public function btnExportReconciliationData() { + ?> +
+ + +
+ $perPage, - 'fields' => 'ids', - 'post_type' => 'shop_order', - 'post_status' => array_keys( wc_get_order_statuses() ), - 'paged' => $paged, + $ordersData = wc_get_orders( + array( + 'limit' => $perPage, + 'return' => 'ids', + 'type' => 'shop_order', + 'paginate' => true, + 'page' => $paged, + ) ); - if ( ! empty( $_GET['_customer_user'] ) ) { - $args['meta_query'] = array( - array( - 'key' => '_customer_user', - 'value' => (int) sanitize_text_field( wp_unslash( $_GET['_customer_user'] ) ), - 'compare' => '=', + if ( ! empty( $ordersData ) ) { + $orders = $ordersData->orders; + $orders_to_select = substr( str_repeat( ',%d', count( $orders ) ), 1 ); + $reconciliation_data = $wpdb->get_results( + $wpdb->prepare( + "SELECT orderId, identifier, transactionType FROM {$wpdb->prefix}altapayReconciliationIdentifiers WHERE orderId IN ($orders_to_select) ", + $orders ), + ARRAY_A ); - } - - if ( ! empty( $_GET['post_status'] ) ) { - $args['post_status'] = sanitize_text_field( wp_unslash( $_GET['post_status'] ) ); - } - - if ( ! empty( $_GET['orderby'] ) ) { - $args['orderby'] = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); - } - - if ( ! empty( $_GET['order'] ) ) { - $args['order'] = sanitize_text_field( wp_unslash( $_GET['order'] ) ); - } - - if ( ! empty( $_GET['m'] ) ) { - $yearMonth = sanitize_text_field( wp_unslash( $_GET['m'] ) ); - - if ( ! empty( $yearMonth ) && preg_match( '/^[0-9]{6}$/', $yearMonth ) ) { - - $year = (int) substr( $yearMonth, 0, 4 ); - $month = (int) substr( $yearMonth, 4, 2 ); - - $args['date_query'] = array( - array( - 'year' => $year, - 'month' => $month, - ), - ); - } - } - - $query = new \WP_Query( $args ); - - if ( $query->have_posts() ) { - - $PostToSelect = substr( str_repeat( ',%d', count( $query->posts ) ), 1 ); - - $reconciliation_data = - $wpdb->get_results( - $wpdb->prepare( - "SELECT orderId, identifier, transactionType FROM {$wpdb->prefix}altapayReconciliationIdentifiers WHERE orderId IN ($PostToSelect) ", - $query->posts - ), - ARRAY_A - ); $output = $output . 'Order ID,Date Created,Order Total,Currency,Transaction ID,Reconciliation Identifier,Type,Payment Method,Order Status'; $output .= "\n"; From e0a1a264f18f8816ff615c5135e16800818bc85b Mon Sep 17 00:00:00 2001 From: Shahbaz Date: Wed, 1 Jan 2025 18:23:50 +0500 Subject: [PATCH 05/10] Update release notes --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index ad07710b..75200eaf 100644 --- a/readme.txt +++ b/readme.txt @@ -41,6 +41,7 @@ AltaPay's Payment Gateway for WooCommerce provides merchants with access to a fu = 3.7.6 = * Fix: Charge subscriptions using the renewal order total instead of the original reserved amount. +* Support export reconciliation data with WooCommerce High-Performance Order Storage (HPOS). = 3.7.5 = * Display agreement information in the "AltaPay Payment Actions" grid. From d2e5ef0b348608a8bb4b2aa5facb0454092d17b2 Mon Sep 17 00:00:00 2001 From: Shahbaz Date: Thu, 2 Jan 2025 15:43:14 +0500 Subject: [PATCH 06/10] Filter export data based on status, customer and orderby --- classes/core/AltapayReconciliation.php | 37 ++++++++++++++++++++------ 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/classes/core/AltapayReconciliation.php b/classes/core/AltapayReconciliation.php index 5825287f..d56109d0 100644 --- a/classes/core/AltapayReconciliation.php +++ b/classes/core/AltapayReconciliation.php @@ -135,16 +135,37 @@ public function exportReconciliationCSV() { $paged = isset( $_REQUEST['orders_pagenum'] ) ? absint( $_REQUEST['orders_pagenum'] ) : 1; - $ordersData = wc_get_orders( - array( - 'limit' => $perPage, - 'return' => 'ids', - 'type' => 'shop_order', - 'paginate' => true, - 'page' => $paged, - ) + $args = array( + 'limit' => $perPage, + 'return' => 'ids', + 'type' => 'shop_order', + 'paginate' => true, + 'page' => $paged, ); + if ( ! empty( $_GET['_customer_user'] ) ) { + $customer_id = (int) sanitize_text_field( wp_unslash( $_GET['_customer_user'] ) ); + $args['customer'] = $customer_id; + } + + if ( ! empty( $_GET['post_status'] ) ) { + $args['post_status'] = sanitize_text_field( wp_unslash( $_GET['post_status'] ) ); + } + + if ( ! empty( $_GET['orderby'] ) ) { + $args['orderby'] = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); + } + + if ( ! empty( $_GET['order'] ) ) { + $args['order'] = sanitize_text_field( wp_unslash( $_GET['order'] ) ); + } + + if ( ! empty( $_GET['status'] ) ) { + $args['status'] = sanitize_text_field( wp_unslash( $_GET['status'] ) ); + } + + $ordersData = wc_get_orders( $args ); + if ( ! empty( $ordersData ) ) { $orders = $ordersData->orders; $orders_to_select = substr( str_repeat( ',%d', count( $orders ) ), 1 ); From ef6a6a279c635e26d52c5cd233041569315869ed Mon Sep 17 00:00:00 2001 From: Shahbaz Date: Thu, 2 Jan 2025 16:01:11 +0500 Subject: [PATCH 07/10] Include date filters for reconciliation data --- classes/core/AltapayReconciliation.php | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/classes/core/AltapayReconciliation.php b/classes/core/AltapayReconciliation.php index d56109d0..62201c90 100644 --- a/classes/core/AltapayReconciliation.php +++ b/classes/core/AltapayReconciliation.php @@ -144,20 +144,24 @@ public function exportReconciliationCSV() { ); if ( ! empty( $_GET['_customer_user'] ) ) { - $customer_id = (int) sanitize_text_field( wp_unslash( $_GET['_customer_user'] ) ); - $args['customer'] = $customer_id; + $args['customer'] = (int) sanitize_text_field( wp_unslash( $_GET['_customer_user'] ) ); } if ( ! empty( $_GET['post_status'] ) ) { $args['post_status'] = sanitize_text_field( wp_unslash( $_GET['post_status'] ) ); } - if ( ! empty( $_GET['orderby'] ) ) { - $args['orderby'] = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); - } + if ( ! empty( $_GET['m'] ) ) { + $yearMonth = sanitize_text_field( wp_unslash( $_GET['m'] ) ); + + if ( ! empty( $yearMonth ) && preg_match( '/^[0-9]{6}$/', $yearMonth ) ) { - if ( ! empty( $_GET['order'] ) ) { - $args['order'] = sanitize_text_field( wp_unslash( $_GET['order'] ) ); + $year = (int) substr( $yearMonth, 0, 4 ); + $month = (int) substr( $yearMonth, 4, 2 ); + + $last_day_of_month = date_create( "$year-$month" )->format( 'Y-m-t' ); + $args['date_created'] = "$year-$month-01..." . $last_day_of_month; + } } if ( ! empty( $_GET['status'] ) ) { @@ -168,7 +172,7 @@ public function exportReconciliationCSV() { if ( ! empty( $ordersData ) ) { $orders = $ordersData->orders; - $orders_to_select = substr( str_repeat( ',%d', count( $orders ) ), 1 ); + $orders_to_select = substr( str_repeat( ',%d', count( $orders ) ), 1 ); $reconciliation_data = $wpdb->get_results( $wpdb->prepare( "SELECT orderId, identifier, transactionType FROM {$wpdb->prefix}altapayReconciliationIdentifiers WHERE orderId IN ($orders_to_select) ", From 96ee94ba3f5c017da0a39f9303773c79d3693897 Mon Sep 17 00:00:00 2001 From: Shahbaz Date: Thu, 2 Jan 2025 16:08:15 +0500 Subject: [PATCH 08/10] Include orderby in reconciliation export data --- classes/core/AltapayReconciliation.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/classes/core/AltapayReconciliation.php b/classes/core/AltapayReconciliation.php index 62201c90..002ad22b 100644 --- a/classes/core/AltapayReconciliation.php +++ b/classes/core/AltapayReconciliation.php @@ -151,6 +151,18 @@ public function exportReconciliationCSV() { $args['post_status'] = sanitize_text_field( wp_unslash( $_GET['post_status'] ) ); } + if ( ! empty( $_GET['orderby'] ) ) { + $args['orderby'] = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); + } + + if ( ! empty( $_GET['order'] ) ) { + $args['order'] = sanitize_text_field( wp_unslash( $_GET['order'] ) ); + } + + if ( ! empty( $_GET['status'] ) ) { + $args['status'] = sanitize_text_field( wp_unslash( $_GET['status'] ) ); + } + if ( ! empty( $_GET['m'] ) ) { $yearMonth = sanitize_text_field( wp_unslash( $_GET['m'] ) ); @@ -164,10 +176,6 @@ public function exportReconciliationCSV() { } } - if ( ! empty( $_GET['status'] ) ) { - $args['status'] = sanitize_text_field( wp_unslash( $_GET['status'] ) ); - } - $ordersData = wc_get_orders( $args ); if ( ! empty( $ordersData ) ) { From 9660938e090282aca4bf660d10e35a95ea043322 Mon Sep 17 00:00:00 2001 From: TMansur5 Date: Thu, 2 Jan 2025 17:25:52 +0500 Subject: [PATCH 09/10] update integration test selectors --- .../integration-test/cypress/e2e/PageObjects/objects.cy.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/integration-test/cypress/e2e/PageObjects/objects.cy.js b/tests/integration-test/cypress/e2e/PageObjects/objects.cy.js index 18370a20..d85f48af 100644 --- a/tests/integration-test/cypress/e2e/PageObjects/objects.cy.js +++ b/tests/integration-test/cypress/e2e/PageObjects/objects.cy.js @@ -131,8 +131,9 @@ class Order { cy.get("#toplevel_page_woocommerce > ul > li:nth-child(3) > a").click() cy.get('tr').eq(1).click() cy.get('#openCaptureModal').click().wait(2000) - cy.get('.lh-copy > :nth-child(1) > :nth-child(7) > .form-control').click().clear().type('0').click() - cy.get('#altapay_capture').click() + cy.get('.lh-copy > :nth-child(1) > :nth-child(7) > .form-control').click().clear().type('0').wait(3000) + cy.get('#altapay_capture').click().wait(3000) + } refund() { @@ -162,7 +163,7 @@ class Order { cy.get("#toplevel_page_woocommerce > ul > li:nth-child(3) > a").click() cy.get('tr').eq(1).click() cy.get('#openRefundModal').click().wait(3000) - cy.get('#TB_ajaxContent > [style="overflow-x:auto;"] > .responsive-table > .w-100 > :nth-child(3) > :nth-child(1) > :nth-child(7) > .form-control').click().clear().type('0').click() + cy.get('#TB_ajaxContent > [style="overflow-x:auto;"] > .responsive-table > .w-100 > tbody > :nth-child(1) > :nth-child(7) > .form-control').click().clear().type('0') cy.get('#altapay_refund').click().wait(2000) From a191c8f6b91d1bfbd83ba588f836cf7be7b27018 Mon Sep 17 00:00:00 2001 From: TMansur5 Date: Fri, 3 Jan 2025 12:51:31 +0500 Subject: [PATCH 10/10] add amount assertions of partial capture and refund integration tests --- .../cypress/e2e/PageObjects/objects.cy.js | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/tests/integration-test/cypress/e2e/PageObjects/objects.cy.js b/tests/integration-test/cypress/e2e/PageObjects/objects.cy.js index d85f48af..c2b71ef4 100644 --- a/tests/integration-test/cypress/e2e/PageObjects/objects.cy.js +++ b/tests/integration-test/cypress/e2e/PageObjects/objects.cy.js @@ -131,14 +131,17 @@ class Order { cy.get("#toplevel_page_woocommerce > ul > li:nth-child(3) > a").click() cy.get('tr').eq(1).click() cy.get('#openCaptureModal').click().wait(2000) - cy.get('.lh-copy > :nth-child(1) > :nth-child(7) > .form-control').click().clear().type('0').wait(3000) + cy.get('.ap-order-capture-modify').first().click().clear().type('0') cy.get('#altapay_capture').click().wait(3000) + cy.get('.payment-captured').then(($span) => { + const captured_amount = parseFloat($span.text().trim()); // Extract the value and convert to a number + expect(captured_amount).to.be.greaterThan(0); // Assert that the amount is greater than 0 + }); + } refund() { - - cy.get('#openRefundModal').click().wait(3000) cy.get('#altapay_refund').click().wait(5000) cy.get('body').then(($a) => { @@ -152,19 +155,13 @@ class Order { } partial_refund() { - - cy.get('#toplevel_page_woocommerce > .wp-has-submenu > .wp-menu-name').click({force:true}).wait(3000) - cy.get('body').then(($a) => { - - if ($a.find('.components-modal__header > .components-button').length) { - cy.get('.components-modal__header > .components-button').click().wait(2000) - } - }) - cy.get("#toplevel_page_woocommerce > ul > li:nth-child(3) > a").click() - cy.get('tr').eq(1).click() - cy.get('#openRefundModal').click().wait(3000) - cy.get('#TB_ajaxContent > [style="overflow-x:auto;"] > .responsive-table > .w-100 > tbody > :nth-child(1) > :nth-child(7) > .form-control').click().clear().type('0') - cy.get('#altapay_refund').click().wait(2000) + cy.get('#openRefundModal').click().wait(2000) + cy.get('.ap-order-refund-modify').first().click().clear().type('0') + cy.get('#altapay_refund').click().wait(3000) + cy.get('.payment-refunded').then(($span) => { + const refunded_amount = parseFloat($span.text().trim()); // Extract the value and convert to a number + expect(refunded_amount).to.be.greaterThan(0); // Assert that the amount is greater than 0 + }); }