labsecrets
12/23/2014 - 11:26 PM

my_isDowngrade.php

<?php
/*
Plugin Name: PMPro Customizations
Plugin URI: http://www.paidmembershipspro.com/wp/pmpro-customizations/
Description: Custom Prorating Code and Other Code for PMPro
Version: .1
Author: Stranger Studios
Author URI: http://www.strangerstudios.com
*/

/*
  Custom Prorated payments. When a member chooses to upgrade, 
  he should be charged a pro-rated amount for the new membership level immediately, 
  and the payment date should stay the same. 
  Assumes initial payments are equal to billing amount.
  
  When downgrading, the user is not charged and keeps their level until the next payment date.
*/
/*
	Function to check if a level change is a downgrade.	
*/
function my_isDowngrade($old, $new)
{
	$monthly_levels = array(1,2,3);	//in order from low to high
	$annual_levels = array(4,5,6);
	$free_levels = array(7);
	
	if($old == $new)
	{
		//same level, not a downgrade
		return false;
	}
	elseif(in_array($old, $free_levels))
	{
		//Old level is free. Can't downgrade from that.
		return false;
	}
	elseif(in_array($new, $free_levels))
	{
		//New level is free, old one isn't. Must be a downgrade.
		return true;
	}
	else
	{
		//figure out which comes later in the arrays
		if(in_array($new, $monthly_levels))
			$new_pos = array_search($new, $monthly_levels);
		else
			$new_pos = array_search($new, $annual_levels);
		
		if(in_array($old, $monthly_levels))
			$old_pos = array_search($old, $monthly_levels);
		else
			$old_pos = array_search($old, $annual_levels);	
		
		if($new_pos < $old_pos)
			return true;
		else
			return false;
	}	
}

//filter level at checkout to prorate
function my_pmpro_checkout_level($level)
{
	//does the user have a level already?
	if(pmpro_hasMembershipLevel())
	{
		//get current level
		global $current_user;
		$clevel = $current_user->membership_level;
		
		//downgrading?		
		if(my_isDowngrade($clevel->id, $level->id))
		{					
			//downgrade, just $0 initial payment
			$level->initial_payment = 0;
			
			//remember the old level for later
			global $pmpro_checkout_old_level;
			$pmpro_checkout_old_level = $clevel;
			
			//return now
			return $level;
		}
											
		//get their payment date
		$morder = new MemberOrder();
		$morder->getLastMemberOrder();
		
		//no order?
		if(empty($morder->timestamp))
			return $level;
				
		$payment_date = strtotime(date("Y-m-d", $morder->timestamp));			
		$payment_day = intval(date("j", $morder->timestamp));
					
		//when would the next payment be			
		$next_payment_date = strtotime(date("Y-m-d", $payment_date) . " + " . $clevel->cycle_number . " " . $clevel->cycle_period);
				
		//today
		$today = current_time("timestamp");
				
		//how many days in this period
		$days_in_period = ceil(($next_payment_date - $payment_date)/3600/24);
						
		//how many days have passed		
		$days_passed = ceil(($today - $payment_date)/3600/24);
									
		//what percentage
		$per_passed = $days_passed / $days_in_period;		//as a % (decimal)
		$per_left = 1 - $per_passed;
		
		/*
			Now figure out how to adjust the price.
			(a) What they should pay for new level = $level->billing_amount * $per_left.
			(b) What they should have paid for current level = $clevel->billing_amount * $per_passed.
			What they need to pay = (a) + (b) - (what they already paid)
		*/
		$new_level_cost = $level->billing_amount * $per_left;
		$old_level_cost = $clevel->billing_amount * $per_passed;
				
		$level->initial_payment = round($new_level_cost + $old_level_cost - $morder->total, 2);
		
		//just in case we have a negative payment
		if($level->initial_payment < 0)
			$level->initial_payment = 0;								
	}
		
	return $level;
}
add_filter("pmpro_checkout_level", "my_pmpro_checkout_level");

/*
	If downgrading, keep the same billing date
*/
function my_pmpro_profile_start_date($date, $order)
{
	global $current_user, $pmpro_checkout_old_level;
		
	if(!empty($pmpro_checkout_old_level) && my_isDowngrade($pmpro_checkout_old_level->id, $order->membership_id))
	{
		//set profile date to next billing date
		$next_payment = pmpro_next_payment($current_user->ID);
		if(!empty($next_payment))
		{
			$date = date("Y-m-d", pmpro_next_payment($current_user->ID));
			
			//remember for later
			$pmpro_checkout_old_level->next_payment = date("Y-m-d", $next_payment);
		}
	}
		
	return $date;
}
add_filter('pmpro_profile_start_date', 'my_pmpro_profile_start_date', 10, 2);

/*
	If checking out for the same level, keep your old startdate.
  Updated from what's in paid-memberships-pro/includes/filters.php to run if the user has ANY level
*/
function my_pmpro_checkout_start_date_keep_startdate($startdate, $user_id, $level)
{				
	if(pmpro_hasMembershipLevel())  //<-- the line that was changed
	{
		global $wpdb;
		$sqlQuery = "SELECT startdate FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . $wpdb->escape($user_id) . "' AND membership_id = '" . $wpdb->escape($level->id) . "' AND status = 'active' ORDER BY id DESC LIMIT 1";		
		$old_startdate = $wpdb->get_var($sqlQuery);
		
		if(!empty($old_startdate))
			$startdate = "'" . $old_startdate . "'";		
	}
	
	return $startdate;
}
remove_filter("pmpro_checkout_start_date", "pmpro_checkout_start_date_keep_startdate", 10, 3);	//remove the default PMPro filter
add_filter("pmpro_checkout_start_date", "my_pmpro_checkout_start_date_keep_startdate", 10, 3);	//our filter works with ANY level

/*
	After checkout, if the user downgraded, then revert to the old level and remember to change them to the new level later.
*/
function my_pmpro_after_checkout($user_id)
{
	global $pmpro_checkout_old_level, $wpdb;
	if(!empty($pmpro_checkout_old_level) && !empty($pmpro_checkout_old_level->next_payment))
	{
		$new_level = pmpro_getMembershipLevelForUser($user_id);
		
		//remember to update to this level later
		update_user_meta($user_id, "pmpro_change_to_level", array("date"=>$pmpro_checkout_old_level->next_payment, "level"=>$new_level->id));
		
		//change their membership level
		$wpdb->query("UPDATE $wpdb->pmpro_memberships_users SET membership_id = '" . $pmpro_checkout_old_level->id . "' WHERE membership_id = '" . $new_level->id . "' AND user_id = '" . $user_id . "' AND status = 'active'");
	}
	else
		delete_user_meta($user_id, "pmpro_change_to_level");
}
add_filter('pmpro_after_checkout', 'my_pmpro_after_checkout');

/*
	Update confirmation message.
*/
function my_pmpro_confirmation_message($message, $invoice)
{	
	if(!empty($invoice) && !empty($invoice->user_id))
	{	
		$downgrading = get_user_meta($invoice->user_id, "pmpro_change_to_level", true);
			
		if(!empty($downgrading))
		{
			$dlevel = pmpro_getLevel($downgrading['level']);
		
			$message .= "<p>You will be downgraded to " . $dlevel->name . " on " . date(get_option("date_format"), strtotime($downgrading['date'], current_time('timestamp'))) . ".";
		}
	}
	
	return $message;
}
add_filter("pmpro_confirmation_message", "my_pmpro_confirmation_message", 10, 2);

/*
	Update account page.
*/
function my_the_content($content)
{
	global $current_user, $pmpro_pages;
		
	if(is_user_logged_in() && is_page($pmpro_pages['account']))
	{
		$downgrading = get_user_meta($current_user->ID, "pmpro_change_to_level", true);
				
		if(!empty($downgrading))
		{
			$downgrade_message = "<p><strong>Important Note:</strong> You will be downgraded to " . $downgrading['level']->name . " on " . date(get_option("date_format"), strtotime($downgrading['date'], current_time('timestamp'))) . ".";
			
			$content = $downgrade_message . $content;
		}
	}

	return $content;
}
add_filter("the_content", "my_the_content");

/*
	Check for level changes daily.
*/
function daily_check_for_membership_changes()
{
	global $wpdb;
	
	//make sure we only run once a day
	$today = date("Y-m-d", current_time('timestamp'));
		
	//get all users with scheduled level changes
	$level_changes = $wpdb->get_col("SELECT user_id FROM $wpdb->usermeta WHERE meta_key = 'pmpro_change_to_level'");
	
	if(empty($level_changes))
		return;
		
	foreach($level_changes as $user_id)
	{
		//today?
		$change = get_user_meta($user_id, 'pmpro_change_to_level', true);
				
		if(!empty($change) && !empty($change['date']) && !empty($change['level']) && $change['date'] <= $today)
		{
			//get user's current level
			$clevel = pmpro_getMembershipLevelForUser($user_id);
		
			//change back
			if(!empty($clevel))
				$wpdb->query("UPDATE $wpdb->pmpro_memberships_users SET membership_id = '" . $change['level'] . "' WHERE membership_id = '" . $clevel->id . "' AND user_id = '" . $user_id . "' AND status = 'active'");
				
			//delete user meta
			delete_user_meta($user_id, 'pmpro_change_to_level');
		}
	}
}
//hook to run when pmpro_cron_expire_memberships does
add_action('pmpro_cron_expire_memberships', 'daily_check_for_membership_changes');