nopSolutions/nopCommerce · warning · NopException
Next payment date could not be calculated
Error message
Next payment date could not be calculated
What it means
Thrown in ProcessNextRecurringPaymentAsync when GetNextPaymentDateAsync(recurringPayment) returns null. nopCommerce computes the next billing date from CycleLength/CyclePeriod/StartDate/CyclesRemaining; if that math yields no valid future date (e.g. all cycles exhausted, or the start date is invalid), it refuses to charge the customer.
Source
Thrown at src/Libraries/Nop.Services/Orders/OrderProcessingService.cs:1905
/// The task result contains the collection of errors
/// </returns>
public virtual async Task<IEnumerable<string>> ProcessNextRecurringPaymentAsync(RecurringPayment recurringPayment, ProcessPaymentResult paymentResult = null)
{
ArgumentNullException.ThrowIfNull(recurringPayment);
try
{
if (!recurringPayment.IsActive)
throw new NopException("Recurring payment is not active");
var initialOrder = await _orderService.GetOrderByIdAsync(recurringPayment.InitialOrderId)
?? throw new NopException("Initial order could not be loaded");
var customer = await _customerService.GetCustomerByIdAsync(initialOrder.CustomerId)
?? throw new NopException("Customer could not be loaded");
if (await GetNextPaymentDateAsync(recurringPayment) is null)
throw new NopException("Next payment date could not be calculated");
//payment info
var processPaymentRequest = new ProcessPaymentRequest
{
StoreId = initialOrder.StoreId,
CustomerId = customer.Id,
OrderGuid = Guid.NewGuid(),
InitialOrder = initialOrder,
RecurringCycleLength = recurringPayment.CycleLength,
RecurringCyclePeriod = recurringPayment.CyclePeriod,
RecurringTotalCycles = recurringPayment.TotalCycles
};
processPaymentRequest.CustomValues.FillByXml(initialOrder.CustomValuesXml);
//prepare order details
var details = await PrepareRecurringOrderDetailsAsync(processPaymentRequest);
View on GitHub (pinned to 64bdf2ff08)
Solutions
- Compare CyclesProcessed against TotalCycles - if equal, set IsActive=false (subscription complete).
- Correct the recurring payment's CycleLength/CyclePeriod/StartDateUtc if malformed.
- Guard the recurring task to skip payments whose next date is null.
Example fix
// before
var errors = await _orderProcessingService.ProcessNextRecurringPaymentAsync(recurringPayment);
// after
if (await _orderProcessingService.GetNextPaymentDateAsync(recurringPayment) is null)
{
recurringPayment.IsActive = false; // cycles exhausted
await _orderService.UpdateRecurringPaymentAsync(recurringPayment);
continue;
}
var errors = await _orderProcessingService.ProcessNextRecurringPaymentAsync(recurringPayment); Defensive patterns
Strategy: validation
Validate before calling
if (await _orderProcessingService.GetNextPaymentDateAsync(recurringPayment) is null)
{
// cycles exhausted or misconfigured - mark complete
recurringPayment.IsActive = false;
await _orderService.UpdateRecurringPaymentAsync(recurringPayment);
_logger.LogInformation("Recurring payment {Id} has no next payment date - deactivating.",
recurringPayment.Id);
return Array.Empty<string>();
}
return await _orderProcessingService.ProcessNextRecurringPaymentAsync(recurringPayment); Type guard
Task<bool> HasNextCycleAsync(RecurringPayment rp)
=> _orderProcessingService.GetNextPaymentDateAsync(rp).ContinueWith(t => t.Result is not null); Try / catch
try
{
var errors = await _orderProcessingService.ProcessNextRecurringPaymentAsync(recurringPayment);
}
catch (NopException ex) when (ex.Message == "Next payment date could not be calculated")
{
recurringPayment.IsActive = false;
await _orderService.UpdateRecurringPaymentAsync(recurringPayment);
_logger.LogWarning(ex, "Recurring payment {Id} has invalid cycle config", recurringPayment.Id);
} Prevention
- Pre-check GetNextPaymentDateAsync before processing each recurring payment.
- Validate CycleLength > 0 and CyclePeriod is set when configuring recurring products.
- Flip IsActive=false when CyclesProcessed reaches TotalCycles so exhausted subscriptions are not retried.
When it happens
Trigger: A recurring payment whose cycle configuration produces no next date: total cycles already completed, CycleLength <= 0, CyclePeriod unset, or StartDateUtc in an unparseable state. The recurring task picks it up but cannot determine when to bill.
Common situations: Subscription reached its total cycle count but IsActive was not flipped to false; misconfigured recurring product (zero cycle length); timezone/UTC date corruption in the order seed.
Related errors
- Recurring payment is not active
- Recurring payments are not supported by selected payment met
- Not supported recurring payment type
- Initial order could not be loaded
- Customer could not be loaded
AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13).
Data as JSON: /api/errors/362a210657965807.
Report an issue: GitHub.