Last active
March 10, 2023 01:40
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.