Skip to content

Instantly share code, notes, and snippets.

@labsecrets
Forked from strangerstudios/pmpro-custom-prorating.php
Last active August 29, 2015 14:12
Show Gist options
  • Save labsecrets/43a1918ce28990a20ef5 to your computer and use it in GitHub Desktop.
Save labsecrets/43a1918ce28990a20ef5 to your computer and use it in GitHub Desktop.
<?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');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment