nopSolutions/nopCommerce · warning · NopException
The refund was cancelled
Error message
The refund was cancelled
What it means
Thrown by RefundAsync after a successful PayPal refund API call when the returned refund.Status equals CANCELLED. This is PayPal's own status indicating the refund was cancelled (e.g. due to a dispute reversal or PayPal action), not a local config problem. It surfaces through HandleFunctionAsync as the Error string, so the refund id is not persisted.
Source
Thrown at src/Plugins/Nop.Plugin.Payments.PayPalCommerce/Services/PayPalCommerceServiceManager.cs:2576
if (!IsConfigured(settings))
throw new NopException("Plugin not configured");
var currencyCode = (await _currencyService.GetCurrencyByIdAsync(_currencySettings.PrimaryStoreCurrencyId))?.CurrencyCode;
if (string.IsNullOrEmpty(currencyCode))
throw new NopException("Primary store currency not set");
if (string.IsNullOrEmpty(nopOrder.CaptureTransactionId))
throw new NopException("Capture ID not set");
var request = new CreateRefundRequest
{
CaptureId = nopOrder.CaptureTransactionId,
Amount = amount.HasValue ? PrepareMoney(amount.Value, currencyCode) : null
};
var refund = await _httpClient.RequestAsync<CreateRefundRequest, CreateRefundResponse>(request, settings);
if (refund.Status?.ToUpper() == RefundStatusType.CANCELLED.ToString())
throw new NopException("The refund was cancelled");
if (refund.Status?.ToUpper() == RefundStatusType.FAILED.ToString())
throw new NopException("The refund could not be processed");
if (refund.Status?.ToUpper() == RefundStatusType.PENDING.ToString())
throw new NopException($"Capture is in {refund.Status} status due to {refund.StatusDetails?.Reason}");
//save id to avoid double refund
var refundIds = await _genericAttributeService
.GetAttributeAsync<List<string>>(nopOrder, PayPalCommerceDefaults.RefundIdAttributeName)
?? new();
if (!refundIds.Contains(refund.Id))
refundIds.Add(refund.Id);
await _genericAttributeService.SaveAttributeAsync(nopOrder, PayPalCommerceDefaults.RefundIdAttributeName, refundIds);
return refund;
});
}View on GitHub (pinned to 64bdf2ff08)
Solutions
- Treat the (null refund, error) result as 'not refunded' and inform the merchant the refund was cancelled.
- Check the PayPal dashboard / dispute state for the capture to understand why it was cancelled.
- Retry the refund only after confirming the underlying cancellation/dispute is resolved.
Example fix
// before
var (refund, err) = await _serviceManager.RefundAsync(settings, order, amount);
// after
var (refund, err) = await _serviceManager.RefundAsync(settings, order, amount);
if (!string.IsNullOrEmpty(err))
{
_logger.Warning($"Refund for order {order.Id} did not complete: {err}");
// surface to admin; do NOT mark the order as refunded
return;
}
if (refund.Status?.ToUpper() == RefundStatusType.CANCELLED.ToString())
NotifyAdmin("PayPal cancelled the refund; review the dispute/capture"); Defensive patterns
Strategy: fallback
Try / catch
var (refund, error) = await mgr.RefundAsync(settings, order, amount);
if (!string.IsNullOrEmpty(error))
{
_logger.Warning($"Refund not completed for order {order.Id}: {error}");
// leave order un-refunded; let staff review the dispute/capture
return;
} Prevention
- Never auto-mark an order refunded when the Error string is non-empty.
- Monitor PayPal dispute events that can cancel refunds.
- Provide staff a way to inspect the capture/dispute state before retrying.
When it happens
Trigger: PayPal returns a refund resource with status CANCELLED; the refund is initiated but then cancelled by PayPal or through a dispute process before completing.
Common situations: Buyer dispute causes PayPal to cancel the refund; PayPal manually reverses a refund; rare intermediary status on bank-funded refunds.
Related errors
- The refund could not be processed
- Capture is in {refund.Status} status due to {refund.StatusDe
- Capture ID not set
- Shopping cart has no recurring items
- Setup token not set
AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13).
Data as JSON: /api/errors/fe8040b17c167e20.
Report an issue: GitHub.