In a recent deep dive into WooCommerce Subscriptions, we found ourselves grappling with the intricacies of failed payment retry rules, seeking to optimize their functionality for a valued client.
These rules, pivotal within the WooCommerce Subscriptions plugin, dictate the course of action when a subscription renewal payment encounters a stumbling block. The specific challenge on our hands was devising a mechanism to automatically cancel subscriptions after a specified period, following the exhaustion of all retry rules without a successful payment.
Unfortunately, the existing retry rules lacked this particular functionality, as the subscription status is left on hold, the customer is sent an email notifying them of the failed payment, and the order payment is flagged as failed.
Our solution involved interfacing with the retry rule process. Once all retry rules were exhausted, we implemented a scheduled task to update the subscription after a predefined duration, contingent upon specific conditions being met.
Initially, we employed a hook within WooCommerce Subscriptions triggered after a payment retry is processed. Within this function, we ensured the order still required payment before proceeding. Using the given order, we traversed through all attached subscriptions, verified the absence of scheduled retry rules, appended an order note for clarity, and scheduled an event to run in 4 weeks, passing in the subscription ID and order ID:
/**
* After a payment retry is processed, if the payment failed and there are no more retry
* rules, add a scheduled event to transition the associated subscriptions to cancelled status after 4 weeks.
*
* @param WCS_Retry $retry Details of the retry just processed
* @param WC_Order $order The order on which the failed payment retry attempt was processed
*/
function pie_schedule_sub_cancel_after_payment_retry( $retry, $order ) {
// If payment completed, nothing to do
if ( ! $order->needs_payment() ) {
return;
}
$subscriptions = wcs_get_subscriptions_for_renewal_order( $order );
foreach ( $subscriptions as $subscription ) {
// The subscription will only have retry date in future if this isn't the last retry
if ( $subscription->get_time( 'payment_retry' ) <= gmdate( 'U' ) ) {
$subscription->add_order_note( __( 'All payment retry attempts failed: Cancellation scheduled in 4 weeks', 'pie-subs' ) );
wp_schedule_single_event( time() + ( 4 * WEEK_IN_SECONDS ), 'pie_schedule_cancel_failed_subscription', array( $subscription->get_id(), $order->get_id() ) );
}
}
}
add_filter( 'woocommerce_subscriptions_after_payment_retry', 'pie_schedule_sub_cancel_after_payment_retry', 10, 2 );
When the scheduled event executes, the subsequent function is activated. We initiate a check on the order to determine if it had been marked as “failed” (a status set by the final retry rule), enabling us to exit the process early if necessary. A couple of checks on the subscription ensure its continued existence in the system and that it had been marked as “on-hold” (set by the last retry rule). We then update the subscription status to “cancelled” if required, appending a note for clarity:
/**
* Scheduled action to cancel the given subscription if the subscription status is on-hold
*
* @param int $subscription_id
* @param int $order_id
* @return void
*/
function pie_cancel_failed_subscription( $subscription_id, $order_id ) {
$order = wc_get_order( $order_id );
// If order hasn't been marked as failed, bail
if ( ! $order->has_status( 'failed' ) ) {
return;
}
$subscription = wcs_get_subscription( $subscription_id );
// If subscription is still on hold then cancel it
if ( $subscription && $subscription->has_status( 'on-hold' ) ) {
$subscription->update_status( 'cancelled', __( 'Payment failed after all retry attempts: ', 'pie-subs' ) );
}
}
add_action( 'pie_schedule_cancel_failed_subscription', 'pie_cancel_failed_subscription', 10, 2 );
This approach ensures a seamless and automated process for handling unsuccessful subscription payments, offering a more efficient and user-friendly experience for WooCommerce store owners.