{"record":{"id":"d0a476c636333719","repo":"nopSolutions/nopCommerce","slug":"cannot-do-partial-refund-for-order","errorCode":null,"errorMessage":"Cannot do partial refund for order.","messagePattern":"Cannot do partial refund for order\\.","errorType":"exception","errorClass":"NopException","httpStatus":null,"severity":"error","filePath":"src/Libraries/Nop.Services/Orders/OrderProcessingService.cs","lineNumber":2832,"sourceCode":"\r\n        return false;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Partially refunds an order (from admin panel)\r\n    /// </summary>\r\n    /// <param name=\"order\">Order</param>\r\n    /// <param name=\"amountToRefund\">Amount to refund</param>\r\n    /// <returns>\r\n    /// A task that represents the asynchronous operation\r\n    /// The task result contains a list of errors; empty list if no errors\r\n    /// </returns>\r\n    public virtual async Task<IList<string>> PartiallyRefundAsync(Order order, decimal amountToRefund)\r\n    {\r\n        ArgumentNullException.ThrowIfNull(order);\r\n\r\n        if (!await CanPartiallyRefundAsync(order, amountToRefund))\r\n            throw new NopException(\"Cannot do partial refund for order.\");\r\n\r\n        var request = new RefundPaymentRequest();\r\n        RefundPaymentResult result = null;\r\n        try\r\n        {\r\n            request.Order = order;\r\n            request.AmountToRefund = amountToRefund;\r\n            request.IsPartialRefund = true;\r\n\r\n            result = await _paymentService.RefundAsync(request);\r\n\r\n            if (result.Success)\r\n            {\r\n                //total amount refunded\r\n                var totalAmountRefunded = order.RefundedAmount + amountToRefund;\r\n\r\n                //update order info\r\n                order.RefundedAmount = totalAmountRefunded;\r","sourceCodeStart":2814,"sourceCodeEnd":2850,"githubUrl":"https://github.com/nopSolutions/nopCommerce/blob/64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2/src/Libraries/Nop.Services/Orders/OrderProcessingService.cs#L2814-L2850","documentation":"Thrown by OrderProcessingService.PartiallyRefundAsync when CanPartiallyRefundAsync(order, amountToRefund) returns false. A partial online refund is allowed only when OrderTotal > 0, there is a refundable remainder (OrderTotal - RefundedAmount > 0), amountToRefund does not exceed that remainder, PaymentStatus is Paid or PartiallyRefunded, and the payment method supports partial refund. It guards against over-refunding and against refunding via a gateway that cannot do partials.","triggerScenarios":"Calling PartiallyRefundAsync when: amountToRefund > OrderTotal - RefundedAmount; OrderTotal == 0; remainder <= 0 (fully refunded); PaymentStatus not Paid/PartiallyRefunded; or the payment method does not support partial refund.","commonSituations":"Passing an amount that exceeds the remaining refundable total; refunding more than once such that nothing is left; payment gateway plugin lacking partial-refund support; order not Paid; rounding/currency mismatches making amountToRefund marginally exceed the remainder.","solutions":["Guard with: if (!await _orderProcessingService.CanPartiallyRefundAsync(order, amountToRefund)) return; and clamp/validate the amount first.","Compute the remainder and clamp amountToRefund = Math.Min(amountToRefund, order.OrderTotal - order.RefundedAmount).","Verify _paymentService.SupportPartiallyRefundAsync(order.PaymentMethodSystemName); use PartiallyRefundOfflineAsync if the gateway cannot do it.","Confirm PaymentStatus is Paid or PartiallyRefunded before offering the action."],"exampleFix":"// before\nvar errors = await _orderProcessingService.PartiallyRefundAsync(order, amount);\n\n// after\nvar remaining = order.OrderTotal - order.RefundedAmount;\namount = Math.Min(amount, remaining);\nif (!await _orderProcessingService.CanPartiallyRefundAsync(order, amount))\n    return new[] { \"Partial refund not available.\" };\n\nvar errors = await _orderProcessingService.PartiallyRefundAsync(order, amount);","handlingStrategy":"validation","validationCode":"var remaining = order.OrderTotal - order.RefundedAmount;\namountToRefund = Math.Min(amountToRefund, remaining);\nif (!await _orderProcessingService.CanPartiallyRefundAsync(order, amountToRefund))\n    return Array.Empty<string>();\n\nreturn await _orderProcessingService.PartiallyRefundAsync(order, amountToRefund);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Clamp amountToRefund to OrderTotal - RefundedAmount before calling.","Confirm PaymentStatus is Paid or PartiallyRefunded.","Verify the gateway supports partial refund; otherwise use the offline variant."],"tags":["refund","partial","payment","order-processing"],"backgroundTag":null,"analyzedSha":"64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2","analyzedAt":"2026-08-13T21:19:38.062Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}