Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eighty20results/59410465209dde525c9d to your computer and use it in GitHub Desktop.
Save eighty20results/59410465209dde525c9d to your computer and use it in GitHub Desktop.
Change PMPro membership cancellation to set expiration date to be the end of the payment period, instead of cancelling immediately (except when payment gateway is causing cancellation)
<?php
/*
Change cancellation to set expiration date et expiration date to be the end of the current month, instead of cancelling immediately.
Assumes orders are generated for each payment (i.e. your webhooks/etc are setup correctly).
*/
function e20r_stripe_delete_action( $user_id ) {
global $subscription_deleted;
$ubscription_deleted = $user_id;
}
add_action( 'pmpro_stripe_subscription_deleted', 'e20r_stripe_delete_action', 10, 1 );
add_action( 'pmpro_subscription_payment_failed', 'e20r_stripe_delete_action', 10, 1 );
function e20r_before_change_membership_level( $level_id, $user_id ) {
//are we on the cancel page?
global $pmpro_pages, $wpdb, $pmpro_stripe_event, $pmpro_next_payment_timestamp;
global $subscription_deleted;
// Running while processing a Stripe/PayPal payment failure webhook/IPN message, don't mess with the expiration date.
if ( isset( $subscription_deleted ) && ( ! empty( $subscription_deleted ) || is_object( $subscription_deleted ) ) ) {
return;
}
if( $level_id == 0 && ( is_page( $pmpro_pages['cancel'] ) || ( is_admin() && ( empty($_REQUEST['from'] ) || $_REQUEST['from'] != 'profile' ) ) ) ) {
//get last order
$order = new MemberOrder();
$order->getLastMemberOrder($user_id, "success");
//if stripe or PayPal, try to use the API
if( ! empty( $order ) && $order->gateway == "stripe" ) {
if ( ! empty( $pmpro_stripe_event ) ) {
//cancel initiated from Stripe webhook
if ( ! empty( $pmpro_stripe_event->data->object->current_period_end ) ) {
$pmpro_next_payment_timestamp = $pmpro_stripe_event->data->object->current_period_end;
}
} else {
//cancel initiated from PMPro
$pmpro_next_payment_timestamp = PMProGateway_stripe::pmpro_next_payment("", $user_id, "success");
}
}
elseif ( ! empty( $order ) && $order->gateway == "paypalexpress" ) {
if( ! empty( $_POST[ 'next_payment_date' ] ) && $_POST[ 'next_payment_date' ] != 'N/A' ) {
//cancel initiated from IPN
$pmpro_next_payment_timestamp = strtotime($_POST['next_payment_date'], current_time('timestamp'));
} else {
//cancel initiated from PMPro
$pmpro_next_payment_timestamp = PMProGateway_paypalexpress::pmpro_next_payment("", $user_id, "success");
}
}
}
}
add_action('pmpro_before_change_membership_level', 'e20r_before_change_membership_level', 10, 2);
//give users their level back with an expiration
function e20r_pmpro_after_change_ml($level_id, $user_id)
{
global $subscription_deleted;
// Running while processing a Stripe/PayPal payment failure webhook/IPN message, don't reset the expiration date.
if ( isset( $subscription_deleted ) && ( ! empty( $subscription_deleted ) || is_object( $subscription_deleted ) ) ) {
return;
}
//are we on the cancel page?
global $pmpro_pages, $wpdb, $pmpro_next_payment_timestamp;
if( $level_id == 0 && ( is_page( $pmpro_pages[ 'cancel' ] ) || ( is_admin() && ( empty( $_REQUEST[ 'from' ] ) || $_REQUEST[ 'from' ] != 'profile' ) ) ) ) {
/*
okay, let's give the user his old level back with an expiration based on his subscription date
*/
//get last order
$order = new MemberOrder();
$order->getLastMemberOrder( $user_id, "cancelled" );
//can't do this if we can't find the order
if( empty( $order->id ) ) {
return false;
}
//get the last level they had
$level = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->pmpro_memberships_users} WHERE membership_id = %d AND user_id = %d ORDER BY id DESC LIMIT 1", $order->membership_id, $user_id ) );
//can't do this if the level isn't recurring
if( empty( $level->cycle_number ) ) {
return false;
}
//can't do if we can't find an old level
if( empty( $level ) ) {
return false;
}
//last payment date
$lastdate = date_i18n("Y-m-d", $order->timestamp);
/*
next payment date
*/
if ( ! empty( $pmpro_next_payment_timestamp ) ) {
$nextdate = $pmpro_next_payment_timestamp;
} else {
$nextdate = $wpdb->get_var( $wpdb->prepare( "SELECT UNIX_TIMESTAMP( %s + INTERVAL %d {$level->cycle_period})", $lastdate, $level->cycle_number ) );
}
//if the date in the future?
if($nextdate - current_time('timestamp') > 0) {
//give them their level back with the expiration date set
$old_level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_memberships_users WHERE membership_id = '" . $order->membership_id . "' AND user_id = '" . $user_id . "' ORDER BY id DESC LIMIT 1", ARRAY_A);
$old_level['enddate'] = date("Y-m-d H:i:s", $nextdate);
//disable this hook so we don't loop
remove_action("pmpro_after_change_membership_level", "e20r_pmpro_after_change_ml", 10 );
remove_filter('pmpro_cancel_previous_subscriptions', 'my_pmpro_cancel_previous_subscriptions');
//change level
pmpro_changeMembershipLevel($old_level, $user_id);
//add the action back just in case
add_action("pmpro_after_change_membership_level", "e20r_pmpro_after_change_ml", 10, 2);
add_filter('pmpro_cancel_previous_subscriptions', 'my_pmpro_cancel_previous_subscriptions');
//change message shown on cancel page
add_filter("gettext", "e20r_cancel_text", 10, 3);
}
}
}
add_action("pmpro_after_change_membership_level", "e20r_pmpro_after_change_ml", 10, 2);
//this replaces the cancellation text so people know they'll still have access for a certain amount of time
function e20r_cancel_text($translated_text, $text, $domain) {
if( ( "pmpro" == $domain || "paid-memberships-pro" == $domain ) && "Your membership has been cancelled." == $text) {
global $current_user;
$translated_text = sprintf( "Your recurring subscription has been cancelled. Your active membership will expire on %s.", date_i18n( get_option( "date_format" ), pmpro_next_payment( $current_user->ID, "cancelled" ) ) );
}
return $translated_text;
}
//want to update the cancellation email as well
function e20r_email_body($body, $email) {
if($email->template == "cancel") {
global $wpdb;
$user_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->users} WHERE user_email = %s LIMIT 1", $email->email ) );
if( ! empty( $user_id ) ) {
$expiration_date = pmpro_next_payment( $user_id );
//if the date in the future?
if( $expiration_date - current_time( 'timestamp' ) > 0 ) {
$body .= "<p>" . sprintf( "Your access will expire on %s.", date_i18n( get_option( "date_format" ), $expiration_date ) ) . "</p>";
}
}
}
return $body;
}
add_filter("pmpro_email_body", "e20r_email_body", 10, 2);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment