Commit 94c827cc authored by ananelson's avatar ananelson
Browse files

Add more flexible recurring billing cycle day behavior.

parent f0ea01f5
......@@ -12,6 +12,8 @@
use CRM_Stripe_ExtensionUtil as E;
use Civi\Payment\PropertyBag;
const ONE_DAY = 60*60*24;
/**
* Class CRM_Core_Payment_Stripe
*/
......@@ -705,6 +707,10 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
if ($billingCycleAnchor) {
$subscriptionParams['billing_cycle_anchor'] = $billingCycleAnchor;
}
if ($params['backdate_start_date']) {
$subscriptionParams['backdate_start_date'] = $params['backdate_start_date'];
$subscriptionParams['proration_behavior'] = 'create_prorations';
}
// Create the stripe subscription for the customer
$stripeSubscription = $this->stripeClient->subscriptions->create($subscriptionParams);
......@@ -782,16 +788,37 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
*
* @return int|null
*/
private function getRecurBillingCycleDay($params) {
private function getRecurBillingCycleDay(&$params) {
if (isset($params['receive_date'])) {
$receiveDateTimestamp = strtotime($params['receive_date']);
// If `receive_date` was set to "now" it will be in the past (by a few seconds) by the time we actually send it to Stripe.
$receiveDateTimeAgo = strtotime('now') - $receiveDateTimestamp;
// Adjustments to Receive Date to ensure is valid for Stripe and is what
// we intended.
if ($receiveDateTimestamp > strtotime('now')) {
// We've specified a receive_date in the future, use it!
return $receiveDateTimestamp;
} else if ($receiveDateTimeAgo > ONE_DAY AND $receiveDateTimeAgo < 32*ONE_DAY) {
// Recur date is at least 1 day in the past and within the past month.
// Backdate subscription start to this date
$params['backdate_start_date'] = $receiveDateTimestamp;
// Determine next payment date - this same day next month
$nextMonthDate = strtotime("first day of +1 month");
$y = (int) date('Y', $nextMonthDate);
$m = (int) date('n', $nextMonthDate);
$d = (int) date('d', $receiveDateTimestamp);
$firstRecurDate = new DateTime();
$firstRecurDate->setDate($y, $m, $d);
$firstRecurDate->setTime(23, 59, 59);
return $firstRecurDate->getTimestamp();
}
}
// Either we had no receive_date or receive_date was in the past (or "now" when form was submitted).
// Either we had no receive_date or receive_date was in the past (or "now"
// when form was submitted, which is by now a few moments in the past).
return NULL;
}
......@@ -1039,7 +1066,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
$numberOfUnits = $params['installments'] * $params['recurFrequencyInterval'];
$endDate = new DateTime($params['receive_date']);
$endDate->add(new DateInterval("P{$numberOfUnits}{$frequencyUnit}"));
return $endDate->format('Ymd') . '235959';
return $endDate->format('YmdHis');
}
/**
......
......@@ -27,4 +27,20 @@ class CRM_Stripe_Hook {
CRM_Utils_Hook::$_nullObject, CRM_Utils_Hook::$_nullObject, 'civicrm_stripe_updateRecurringContribution');
}
/**
* This hook allows modifying possible recurring contribution start dates.
* Start dates in the past will cause backdate_start_date to be passed to
* Stripe API and will generate an initial charge, then begin regular billing
* on the specified date next month.
*
*
* @param array $recurStartDates Array of dates with keys in YmdHis, values formatted dates (or other text) to show user
*
* @return mixed
*/
public static function updateRecurStartDates(&$recurStartDates) {
return CRM_Utils_Hook::singleton()
->invoke(['customizeRecurStartDates'], $recurStartDates, CRM_Utils_Hook::$_nullObject, CRM_Utils_Hook::$_nullObject, CRM_Utils_Hook::$_nullObject,
CRM_Utils_Hook::$_nullObject, CRM_Utils_Hook::$_nullObject, 'civicrm_stripe_updateRecurStartDates');
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment