Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e41ef6a
Add remediation for canceled authorization fees
mgascam Nov 18, 2025
e28efb3
Merge branch 'develop' into cancel-auth-fee-remediation
mgascam Nov 26, 2025
dba00e7
feat: add HPOS support for affected orders remediation
mgascam Nov 26, 2025
1c5aad0
Merge branch 'develop' into cancel-auth-fee-remediation
mgascam Nov 26, 2025
ac87be1
Address copilot comments
mgascam Nov 26, 2025
67b4ebb
feat: enhance remediation logic for canceled authorization fees and i…
mgascam Nov 26, 2025
72b6a74
feat: implement admin notice and remediation tool for canceled author…
mgascam Nov 26, 2025
a917c0b
refactor: remove unused plugin update method and clean up test imports
mgascam Nov 26, 2025
b111c29
feat: enhance remediation tool for canceled authorization fees with d…
mgascam Nov 26, 2025
3a83f23
feat: update remediation logic to change order status from 'refunded'…
mgascam Nov 26, 2025
ea90207
feat: add refund stats cleanup logic and corresponding unit tests for…
mgascam Nov 26, 2025
f8d44ec
Merge branch 'develop' into cancel-auth-fee-remediation
mgascam Nov 27, 2025
1aca52c
feat: add confirmation message for canceled authorization remediation…
mgascam Nov 27, 2025
63ce0c2
feat: remove deprecated admin notice for canceled authorization fee r…
mgascam Nov 27, 2025
f93e62e
feat: remove action for plugin update on upgrader process completion
mgascam Nov 27, 2025
3f7e799
fix: update SQL queries to use IN clause for transaction fee meta key…
mgascam Nov 27, 2025
978cad5
feat: enhance refund deletion message to include deleted refund IDs
mgascam Nov 27, 2025
bbf7c43
Add changelog
mgascam Nov 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 155 additions & 2 deletions includes/class-wc-payments-status.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function debug_tools( $tools ) {
return array_merge(
$tools,
[
'clear_wcpay_account_cache' => [
'clear_wcpay_account_cache' => [
'name' => sprintf(
/* translators: %s: WooPayments */
__( 'Clear %s account cache', 'woocommerce-payments' ),
Expand All @@ -80,7 +80,7 @@ public function debug_tools( $tools ) {
),
'callback' => [ $this->account, 'refresh_account_data' ],
],
'delete_wcpay_test_orders' => [
'delete_wcpay_test_orders' => [
'name' => sprintf(
/* translators: %s: WooPayments */
__( 'Delete %s test orders', 'woocommerce-payments' ),
Expand All @@ -94,6 +94,14 @@ public function debug_tools( $tools ) {
),
'callback' => [ $this, 'delete_test_orders' ],
],
'remediate_canceled_auth_fees' => [
'name' => __( 'Fix canceled authorization analytics', 'woocommerce-payments' ),
'button' => $this->get_remediation_button_text(),
'desc' => $this->get_remediation_description(),
'confirm' => __( 'This will update order metadata and delete incorrect refund records for affected orders. Make sure you have a recent backup before proceeding. Continue?', 'woocommerce-payments' ),
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The confirmation message should include a warning about the potential impact on analytics reporting. While the message mentions updating metadata and deleting refund records, it doesn't explicitly warn that this will affect historical WooCommerce Analytics data, which merchants should be aware of before proceeding.

Suggested change
'confirm' => __( 'This will update order metadata and delete incorrect refund records for affected orders. Make sure you have a recent backup before proceeding. Continue?', 'woocommerce-payments' ),
'confirm' => __( 'This will update order metadata and delete incorrect refund records for affected orders. This will affect historical WooCommerce Analytics data. Make sure you have a recent backup before proceeding. Continue?', 'woocommerce-payments' ),

Copilot uses AI. Check for mistakes.
'callback' => [ $this, 'schedule_canceled_auth_remediation' ],
'disabled' => $this->is_remediation_running_or_complete(),
],
]
);
}
Expand Down Expand Up @@ -155,6 +163,151 @@ public function delete_test_orders() {
}
}

/**
* Schedules the canceled authorization fee remediation.
*
* This tool fixes incorrect refund records and fee data from orders where
* payment authorization was canceled but never captured.
*
* @return string Success or error message.
*/
public function schedule_canceled_auth_remediation() {
// Add explicit capability check.
if ( ! current_user_can( 'manage_woocommerce' ) ) {
return __( 'You do not have permission to run this tool.', 'woocommerce-payments' );
}

try {
include_once WCPAY_ABSPATH . 'includes/migrations/class-wc-payments-remediate-canceled-auth-fees.php';
$remediation = new WC_Payments_Remediate_Canceled_Auth_Fees();

// Check if already complete.
if ( $remediation->is_complete() ) {
return __( 'Remediation has already been completed.', 'woocommerce-payments' );
}

// Check if already running.
if ( function_exists( 'as_has_scheduled_action' ) && as_has_scheduled_action( WC_Payments_Remediate_Canceled_Auth_Fees::ACTION_HOOK ) ) {
return __( 'Remediation is already in progress. Check the Action Scheduler for status.', 'woocommerce-payments' );
}

// Schedule the remediation.
$remediation->schedule_remediation();

return __( 'Remediation has been scheduled and will run in the background. You can monitor progress in the Action Scheduler.', 'woocommerce-payments' );

} catch ( Exception $e ) {
return sprintf(
/* translators: %s: error message */
__( 'Error scheduling remediation: %s', 'woocommerce-payments' ),
$e->getMessage()
);
}
}

/**
* Get the button text for the remediation tool based on current status.
*
* @return string Button text.
*/
private function get_remediation_button_text(): string {
$status = get_option( 'wcpay_fee_remediation_status', '' );

if ( 'completed' === $status ) {
return __( 'Completed', 'woocommerce-payments' );
}

if ( 'running' === $status || $this->is_remediation_action_scheduled() ) {
return __( 'Running...', 'woocommerce-payments' );
}

return __( 'Run', 'woocommerce-payments' );
}

/**
* Get the description for the remediation tool including current status.
*
* @return string Tool description with status.
*/
private function get_remediation_description(): string {
$base_desc = __( 'This tool removes incorrect refund records and fee data from orders where payment authorization was canceled (not captured). This fixes negative values appearing in WooCommerce Analytics for stores using manual capture.', 'woocommerce-payments' );

$status = get_option( 'wcpay_fee_remediation_status', '' );

if ( 'completed' === $status ) {
$stats = get_option( 'wcpay_fee_remediation_stats', [] );
$processed = isset( $stats['processed'] ) ? (int) $stats['processed'] : 0;
$remediated = isset( $stats['remediated'] ) ? (int) $stats['remediated'] : 0;

if ( $processed > 0 ) {
return sprintf(
/* translators: 1: base description, 2: number of orders processed, 3: number of orders remediated */
__( '%1$s <strong>Status: Completed.</strong> Processed %2$d orders, remediated %3$d.', 'woocommerce-payments' ),
$base_desc,
$processed,
$remediated
);
}

return sprintf(
/* translators: %s: base description */
__( '%s <strong>Status: Completed.</strong> No affected orders found.', 'woocommerce-payments' ),
$base_desc
);
}

if ( 'running' === $status || $this->is_remediation_action_scheduled() ) {
$stats = get_option( 'wcpay_fee_remediation_stats', [] );
$processed = isset( $stats['processed'] ) ? (int) $stats['processed'] : 0;

if ( $processed > 0 ) {
return sprintf(
/* translators: 1: base description, 2: number of orders processed so far */
__( '%1$s <strong>Status: Running...</strong> Processed %2$d orders so far. Check the Action Scheduler for details.', 'woocommerce-payments' ),
$base_desc,
$processed
);
}

return sprintf(
/* translators: %s: base description */
__( '%s <strong>Status: Running...</strong> Check the Action Scheduler for details.', 'woocommerce-payments' ),
$base_desc
);
}

return $base_desc;
}

/**
* Check if the remediation is currently running or already complete.
*
* @return bool True if running or complete.
*/
private function is_remediation_running_or_complete(): bool {
$status = get_option( 'wcpay_fee_remediation_status', '' );

if ( 'completed' === $status || 'running' === $status ) {
return true;
}

return $this->is_remediation_action_scheduled();
}

/**
* Check if the remediation action is scheduled in Action Scheduler.
*
* @return bool True if action is scheduled.
*/
private function is_remediation_action_scheduled(): bool {
if ( ! function_exists( 'as_has_scheduled_action' ) ) {
return false;
}

include_once WCPAY_ABSPATH . 'includes/migrations/class-wc-payments-remediate-canceled-auth-fees.php';
return as_has_scheduled_action( WC_Payments_Remediate_Canceled_Auth_Fees::ACTION_HOOK );
}

/**
* Renders WCPay information on the status page.
*/
Expand Down
16 changes: 16 additions & 0 deletions includes/class-wc-payments.php
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,13 @@ class WC_Payments {
*/
private static $payment_method_service;

/**
* Instance of WC_Payments_Remediate_Canceled_Auth_Fees, created in init function
*
* @var WC_Payments_Remediate_Canceled_Auth_Fees
*/
private static $fee_remediation;

/**
* Entry point to the initialization logic.
*/
Expand Down Expand Up @@ -492,6 +499,7 @@ public static function init() {
include_once __DIR__ . '/class-wc-payments-order-service.php';
include_once __DIR__ . '/class-wc-payments-order-success-page.php';
include_once __DIR__ . '/class-wc-payments-file-service.php';
include_once __DIR__ . '/migrations/class-wc-payments-remediate-canceled-auth-fees.php';
include_once __DIR__ . '/class-wc-payments-webhook-processing-service.php';
include_once __DIR__ . '/class-wc-payments-webhook-reliability-service.php';
include_once __DIR__ . '/fraud-prevention/class-fraud-prevention-service.php';
Expand Down Expand Up @@ -552,6 +560,7 @@ public static function init() {
self::$incentives_service = new WC_Payments_Incentives_Service( self::$database_cache );
self::$duplicate_payment_prevention_service = new Duplicate_Payment_Prevention_Service();
self::$duplicates_detection_service = new Duplicates_Detection_Service();
self::$fee_remediation = new WC_Payments_Remediate_Canceled_Auth_Fees();

( new WooPay_Scheduler( self::$api_client ) )->init();

Expand All @@ -564,6 +573,7 @@ public static function init() {
self::$compatibility_service->init_hooks();
self::$customer_service->init_hooks();
self::$token_service->init_hooks();
self::$fee_remediation->init();

/**
* FLAG: PAYMENT_METHODS_LIST
Expand Down Expand Up @@ -1537,6 +1547,9 @@ public static function add_woo_admin_notes() {

require_once WCPAY_ABSPATH . 'includes/notes/class-wc-payments-notes-stripe-billing-deprecation.php';
WC_Payments_Notes_Stripe_Billing_Deprecation::possibly_add_note();

require_once WCPAY_ABSPATH . 'includes/notes/class-wc-payments-notes-canceled-auth-remediation.php';
WC_Payments_Notes_Canceled_Auth_Remediation::possibly_add_note();
}

if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '7.5', '<' ) && get_woocommerce_currency() === 'NOK' ) {
Expand Down Expand Up @@ -1603,6 +1616,9 @@ public static function remove_woo_admin_notes() {

require_once WCPAY_ABSPATH . 'includes/notes/class-wc-payments-notes-stripe-billing-deprecation.php';
WC_Payments_Notes_Stripe_Billing_Deprecation::possibly_delete_note();

require_once WCPAY_ABSPATH . 'includes/notes/class-wc-payments-notes-canceled-auth-remediation.php';
WC_Payments_Notes_Canceled_Auth_Remediation::possibly_delete_note();
}
}

Expand Down
Loading
Loading