Skip to content

Instantly share code, notes, and snippets.

@strangerstudios
Last active December 21, 2015 03:28
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save strangerstudios/6242052 to your computer and use it in GitHub Desktop.
Save strangerstudios/6242052 to your computer and use it in GitHub Desktop.
PMPro prorated payments that can handle different billing cycle periods and numbers.
/*
!!!Important!!! The latest version of our prorating code can be found here:
https://gist.github.com/strangerstudios/ff83cec20a285cfcdc52
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.
Add this code to your active theme's functions.php or include this in a custom plugin.
*/
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;
//get their payment date
$morder = new MemberOrder();
$morder->getLastMemberOrder($current_user->ID, array('success', '', 'cancelled'));
//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);
//if no days in period (next payment should have happened already) return level with no change to avoid divide by 0
if($days_in_period <= 0)
return $level;
//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 = min($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 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;
}
//add/remove hooks in init to make sure it runs after PMPro loads
function my_pmpro_init()
{
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
}
add_action('init', 'my_pmpro_init');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment