Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save greathmaster/5d8adedd2a955fddc20eef95459501fb to your computer and use it in GitHub Desktop.
Save greathmaster/5d8adedd2a955fddc20eef95459501fb to your computer and use it in GitHub Desktop.
Change PMPro membership cancellation to set expiration date for next payment instead of cancelling immediately.
/*
Change cancellation to set expiration date for next payment instead of cancelling immediately.
Assumes orders are generated for each payment (i.e. your webhooks/etc are setup correctly).
Since 2015-09-21 and PMPro v1.8.5.6 contains code to look up next payment dates via Stripe and PayPal Express APIs.
*/
//before cancelling, save the next_payment_timestamp to a global for later use. (Requires PMPro 1.8.5.6 or higher.)
function my_pmpro_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;
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', 'my_pmpro_before_change_membership_level', 10, 2);
//give users their level back with an expiration
function my_pmpro_after_change_membership_level($level_id, $user_id)
{
//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("SELECT * FROM $wpdb->pmpro_memberships_users WHERE membership_id = '" . $order->membership_id . "' AND user_id = '" . $user_id . "' ORDER BY id DESC LIMIT 1");
//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("Y-m-d", $order->timestamp);
/*
next payment date
*/
//if stripe or PayPal, try to use the API
if(!empty($pmpro_next_payment_timestamp))
{
$nextdate = $pmpro_next_payment_timestamp;
}
else
{
$nextdate = $wpdb->get_var("SELECT UNIX_TIMESTAMP('" . $lastdate . "' + INTERVAL " . $level->cycle_number . " " . $level->cycle_period . ")");
}
//if the date in the future?
if($nextdate - time() > 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", "my_pmpro_after_change_membership_level", 10, 2);
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", "my_pmpro_after_change_membership_level", 10, 2);
add_filter('pmpro_cancel_previous_subscriptions', 'my_pmpro_cancel_previous_subscriptions');
//change message shown on cancel page
add_filter("gettext", "my_gettext_cancel_text", 10, 3);
}
}
}
add_action("pmpro_after_change_membership_level", "my_pmpro_after_change_membership_level", 10, 2);
//this replaces the cancellation text so people know they'll still have access for a certain amount of time
function my_gettext_cancel_text($translated_text, $text, $domain)
{
if($domain == "pmpro" && $text == "Your membership has been cancelled.")
{
global $current_user;
$translated_text = "Your recurring subscription has been cancelled. Your active membership will expire on " . date(get_option("date_format"), pmpro_next_payment($current_user->ID, "cancelled")) . ".";
}
return $translated_text;
}
//want to update the cancellation email as well
function my_pmpro_email_body($body, $email)
{
if($email->template == "cancel")
{
global $wpdb;
$user_id = $wpdb->get_var("SELECT ID FROM $wpdb->users WHERE user_email = '" . esc_sql($email->email) . "' LIMIT 1");
if(!empty($user_id))
{
$expiration_date = pmpro_next_payment($user_id);
//if the date in the future?
if($expiration_date - time() > 0)
{
$body .= "<p>Your access will expire on " . date(get_option("date_format"), $expiration_date) . ".</p>";
}
}
}
return $body;
}
add_filter("pmpro_email_body", "my_pmpro_email_body", 10, 2);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment