Skip to content

Commit

Permalink
Fix creation of recurrent with invalid charge_at date
Browse files Browse the repository at this point in the history
If `subscription_types.fixed_end` is set, next `charge_at` is always
set to value `subscription_types.fixed_end - recurrent_charge_before`
which can be (when current date is close to or after `fixed_end`)
before `subscriptions.start_time`.

`RecurrentPaymentsRepository->calculateChargeAt()` now throws exception
if calculated next `charge_at` is invalid (recurrent payment's next charge
shouldn't be before subscriptions start date or before current datetime).

And if `RecurrentPaymentsRepository->createFromPayment()` catches this
exception, recurrent payment is stopped (marked as stopped by system).

remp/crm#1662
  • Loading branch information
markoph committed Jul 29, 2021
1 parent 1b1ab79 commit a2edddb
Showing 1 changed file with 31 additions and 4 deletions.
35 changes: 31 additions & 4 deletions src/model/Repositories/RecurrentPaymentsRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ final public function createFromPayment(
?float $customChargeAmount = null
): ?IRow {
if (!in_array($payment->status, [PaymentsRepository::STATUS_PAID, PaymentsRepository::STATUS_PREPAID], true)) {
Debugger::log("Could not create recurrent payment from payment [{$payment->id}], invalid payment status: [{$payment->status}]");
Debugger::log(
"Could not create recurrent payment from payment [{$payment->id}], invalid payment status: [{$payment->status}]",
Debugger::ERROR
);
return null;
}

Expand All @@ -92,7 +95,12 @@ final public function createFromPayment(
$retries = count((array)$retries);

if (!$chargeAt) {
$chargeAt = $this->calculateChargeAt($payment);
try {
$chargeAt = $this->calculateChargeAt($payment);
} catch (\Exception $e) {
Debugger::log($e, Debugger::ERROR);
return null;
}
}

$recurrentPayment = $this->add(
Expand Down Expand Up @@ -346,13 +354,23 @@ final public function getDuplicate()
->fetchAll();
}

/**
* @throws Exception If calculated next charge at date is invalid (before subscription's start date / in past)
*/
final public function calculateChargeAt($payment)
{
$subscriptionType = $payment->subscription_type;
$subscription = $payment->subscription;

$endTime = clone $subscription->end_time;

if ($endTime <= new DateTime()) {
throw new Exception(
"Calculated next charge of recurrent payment would be in the past." .
" Check payment [{$payment->id}] and subscription [{$subscription->id}]."
);
}

$chargeBefore = null;
if (!$chargeBefore) {
$chargeBefore = $subscriptionType->recurrent_charge_before;
Expand All @@ -371,7 +389,16 @@ final public function calculateChargeAt($payment)
if ($chargeBefore) {
$newEndTime = (clone $endTime)->sub(new \DateInterval("PT{$chargeBefore}H"));
if ($newEndTime < $subscription->start_time) {
Debugger::log("Calculated next charge of recurrent payment would be sooner than subscription start time. Check subscription: " . $subscription->id, Debugger::WARNING);
throw new Exception(
"Calculated next charge of recurrent payment would be before subscription's start time." .
" Check payment [{$payment->id}] and subscription [{$subscription->id}]."
);
}
if ($newEndTime <= new DateTime()) {
throw new Exception(
"Calculated next charge of recurrent payment would be in the past." .
" Check payment [{$payment->id}] and subscription [{$subscription->id}]."
);
}
$endTime = $newEndTime;
}
Expand Down Expand Up @@ -412,7 +439,7 @@ final public function latestSuccessfulRecurrentPayment($recurrentPayment)
if (!$previousRecurrentCharge) {
return null;
}

return $this->latestSuccessfulRecurrentPayment($previousRecurrentCharge);
}
}

0 comments on commit a2edddb

Please sign in to comment.