Skip to content

Instantly share code, notes, and snippets.

@cballou
Last active March 10, 2023 01:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save cballou/774c5a15f9771314f0d1 to your computer and use it in GitHub Desktop.
Save cballou/774c5a15f9771314f0d1 to your computer and use it in GitHub Desktop.
Calculate a proration percentage given the current timestamp and a current billing period
<?php
/**
* Handle calculating a percentage/fraction (proration) we should charge the
* user for based on the current day of the month before their next bill cycle.
* To use yourself, implement a getSubscription method which returns an object
* containing current_period_start and current_period_end DateTime objects.
*
* @access public
* @return float
*/
function prorateUpcomingBillingCycle()
{
$now = new DateTime('now', new DateTimeZone('UTC'));
// determine the next billing cycle
$subscription = getSubscription();
$currentPeriodStart = $subscription->current_period_start;
$currentPeriodEnd = $subscription->current_period_end;
if (is_string($currentPeriodStart)) {
$currentPeriodStart = new DateTime($currentPeriodStart, new DateTimeZone('UTC'));
}
if (is_string($currentPeriodEnd)) {
$currentPeriodEnd = new DateTime($currentPeriodEnd, new DateTimeZone('UTC'));
}
// get the number of second difference between the cycle start and end date
$currentPeriodStartEpoch = (int) $currentPeriodStartEpoch->format('U');
$currentPeriodEndEpoch = (int) $currentPeriodEndEpoch->format('U');
$nowEpoch = (int) $now->format('U');
// if we aren't between the start and end of the subscription period, we have a problem
if ($nowEpoch < $currentPeriodStartEpoch || $nowEpoch > $currentPeriodEndEpoch) {
throw new Exception('The current timestamp does not fall within the current subscription period.');
}
// get the difference of the start and end time in seconds
$epochDifference = $currentPeriodEndEpoch - $currentPeriodStartEpoch;
// get the prorated number of seconds till the end of the subscription period
$remainingSecondsInPeriod = $currentPeriodEndEpoch - $nowEpoch;
// return fraction of the total seconds in the current billing period
return $remainingSecondsInPeriod / $epochDifference;
}
/**
* Returns an example subscription object with a 30 day subscription period.
*
* This is only for reference.
*
* @return object
*/
function getSubScription()
{
$now = new DateTime('now', new DateTimeZone('UTC'));
$obj = new stdClass();
$obj->current_period_start = clone $now;
$obj->current_period_start->sub(new DateInterval('P15D');
$obj->current_period_end = clone $now;
$obj->current_period_end->add(new DateInterval('P15D')));
return $obj;
}
@cballou
Copy link
Author

cballou commented Jun 19, 2014

Utilize this method if you don't want to tie yourself down to a third parties proration handler in all cases and chose to do so on your own accord. It can handle any billing cycle lengths (monthly, bi-monthly, quarterly, yearly) as it's based on seconds since the epoch and the start and end dates of the billing cycle.

@rmdhfz
Copy link

rmdhfz commented Oct 7, 2021

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment