nopSolutions/nopCommerce · error · NopException

Cannot do refund for order.

Error message

Cannot do refund for order.

What it means

Thrown by OrderProcessingService.RefundAsync when CanRefundAsync(order) returns false. A full online refund is allowed only when OrderTotal > 0, RefundedAmount == 0 (no prior partial refunds), PaymentStatus == Paid, and the payment method supports refund (SupportRefundAsync). It blocks refunding zero-total orders, orders already partially refunded, or orders whose gateway cannot refund.

Source

Thrown at src/Libraries/Nop.Services/Orders/OrderProcessingService.cs:2648

            return true;

        return false;
    }

    /// <summary>
    /// Refunds an order (from admin panel)
    /// </summary>
    /// <param name="order">Order</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains a list of errors; empty list if no errors
    /// </returns>
    public virtual async Task<IList<string>> RefundAsync(Order order)
    {
        ArgumentNullException.ThrowIfNull(order);

        if (!await CanRefundAsync(order))
            throw new NopException("Cannot do refund for order.");

        var request = new RefundPaymentRequest();
        RefundPaymentResult result = null;
        try
        {
            request.Order = order;
            request.AmountToRefund = order.OrderTotal;
            request.IsPartialRefund = false;
            result = await _paymentService.RefundAsync(request);
            if (result.Success)
            {
                //total amount refunded
                var totalAmountRefunded = order.RefundedAmount + request.AmountToRefund;

                //update order info
                order.RefundedAmount = totalAmountRefunded;
                order.PaymentStatus = result.NewPaymentStatus;
                await _orderService.UpdateOrderAsync(order);

View on GitHub (pinned to 64bdf2ff08)

Solutions

  1. Guard with: if (!await _orderProcessingService.CanRefundAsync(order)) return; and inspect why it is ineligible.
  2. If RefundedAmount > 0, use PartiallyRefundAsync(order, remainingAmount) instead of RefundAsync.
  3. Verify _paymentService.SupportRefundAsync(order.PaymentMethodSystemName); install a payment plugin that supports refund, or use the offline refund flow.
  4. Confirm PaymentStatus == Paid before offering the refund action.

Example fix

// before
var errors = await _orderProcessingService.RefundAsync(order);

// after
if (!await _orderProcessingService.CanRefundAsync(order))
{
    if (order.RefundedAmount > decimal.Zero)
        return await _orderProcessingService.PartiallyRefundAsync(order, order.OrderTotal - order.RefundedAmount);
    return new[] { "Refund is not available for this order." };
}
var errors = await _orderProcessingService.RefundAsync(order);
Defensive patterns

Strategy: validation

Validate before calling

if (!await _orderProcessingService.CanRefundAsync(order))
{
    if (order.RefundedAmount > decimal.Zero)
        return await _orderProcessingService.PartiallyRefundAsync(order, order.OrderTotal - order.RefundedAmount);
    return Array.Empty<string>();
}

return await _orderProcessingService.RefundAsync(order);

Prevention

When it happens

Trigger: Calling RefundAsync when: OrderTotal == 0; RefundedAmount > 0 (use partial refund instead); PaymentStatus != Paid; or the payment method does not support refund. Typical from admin 'Refund' on an ineligible order.

Common situations: Trying a full refund after a partial refund was already issued (must use PartiallyRefundAsync); payment gateway plugin not supporting refund; order not actually Paid (still Pending/Authorized); free order with zero total.

Related errors


AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13). Data as JSON: /api/errors/888c7df1a34f25fb. Report an issue: GitHub.