Skip to content

Instantly share code, notes, and snippets.

@mircobabini
Last active July 17, 2024 15:52
Show Gist options
  • Save mircobabini/950523ae1efc594a34bf931445ecdde5 to your computer and use it in GitHub Desktop.
Save mircobabini/950523ae1efc594a34bf931445ecdde5 to your computer and use it in GitHub Desktop.
<?php
/**
* Cancel subscriptions when a recurring payment fails.
* THIS IS A REPLACEMENT FOR Failed Payment Limit Add On, with MAX=1 forced.
*
* https://gist.github.com/mircobabini/950523ae1efc594a34bf931445ecdde5
*
*
* === Changelog ===
*
* Version: 1.1 (20240717)
* - PMPro 3+ compat. CONPD in core, ecc.
*
* Version: 1.0 (20221118)
* - Ensure we are logging something for the major gateways
*
* Version: 0.9 (20221118)
* - Ensure we are working on the correct membership_id.
*
* Version: 0.8
* - switch from ->Gateway->cancelSubscriptionAtGateway() (which is not standard over gateways) to $order->cancel().
*
* Version: 0.7
* - Restore the manual call to the cancelSubscriptionAtGateway(); can't rely on the pmpro_cancelMembershipLevel
* which is not always doing the ->cancel().
*
* Version: 0.6
* - Delegate the cancelSubscriptionAtGateway() call to the cancel() call made by pmpro_cancelMembershipLevel()
* under certain conditions, depending on the current gateway.
*
* Version: 0.5
* - Rollback pmpro_cancelMembershipLevel w/o third parameter (dont want to set the last order as error)
* on renewal failed, because there is no new order created, then we would affect the previous order
* which was legit.
*
* Version 0.4
* - Disable SCA emails, so the user just receive a cancellation email
* then he will check out again completing the SCA verification at checkout.
*
* Version 0.3
* - Use pmpro_cancelMembershipLevel( $order->membership_id, $order->user_id, 'error' );
* third param is to skip CONPD (NB: this introduced a bug. check 0.5 rollback.
*
* Version 0.2
* - Replace pmpro_changeMembershipLevel with pmpro_cancelMembershipLevel, because we are cancelling.
*
* Version 0.1
* - First commit
*
*/
add_action( 'pmpro_subscription_payment_failed', function ( $order ) {
// avoid "PHP Notice: Trying to get property 'ID' of non-object"
if ( ! is_object( $order ) || ! $order->membership_id ) {
return;
}
// this is probably not needed anymore, because of the billing limit set up for new subscriptions
// still useful for the old subscriptions though.
pmpro_handle_subscription_cancellation_at_gateway( $order->subscription_transaction_id, $order->gateway, $order->gateway_environment );
$order->cancel(); // Ensure cancellation at gateway too (if applicable)
if ( pmpro_doing_webhook( 'paypal' ) ) {
if ( function_exists( 'ipnlog' ) ) {
ipnlog( "Subscription deleted for user ID #$order->user_id ($order->subscription_transaction_id) - Addon PMPro Billing Failure." );
}
// exit so we don't send failed payment email/etc
exit;
}
if ( pmpro_doing_webhook( 'stripe' ) ) {
if ( function_exists( 'pmpro_stripeWebhookExit' ) ) {
global $logstr;
$logstr .= "Subscription deleted for user ID #$order->user_id ($order->subscription_transaction_id) - Addon PMPro Billing Failure.";
// exit so we don't send failed payment email/etc
pmpro_stripeWebhookExit();
}
}
// ensure to exit the process here.
exit;
} );
/**
* Disable SCA emails, so the user just receive a cancellation email
* then he will check out again completing the SCA verification at checkout.
*/
add_filter( 'pmpro_email_filter', function ( $temail ) {
if ( empty( $temail ) ) {
return $temail;
}
if ( in_array( $temail->template, [ 'payment_action', 'payment_action_admin' ] ) ) {
return null;
}
return $temail;
} );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment