Skip to content

Process Returns

⏱ 20 minutes intermediate
📜Corecommerce

Returns are inevitable in commerce. A smooth return process builds customer trust and encourages repeat purchases. A clunky process creates support tickets and negative reviews. Commerce provides the building blocks for return merchandise authorization (RMA), refund processing, and inventory restocking so you can handle returns consistently.

This guide covers creating RMA requests, authorizing returns, processing refunds through your payment gateway, and restocking returned items.

  1. Create an RMA request against a purchase order
  2. Authorize the return and validate line items
  3. Process a refund through the payment gateway
  4. Restock returned items in inventory

An RMA (Return Merchandise Authorization) tracks which items from an order a customer wants to return, the reason, and the requested resolution (refund, exchange, store credit).

Create a return request
csharp
using EPiServer.Commerce.Order;

public class ReturnService
{
    private readonly IOrderRepository _orderRepo;
    private readonly IReturnLineItemCalculator
        _returnCalc;

    public ReturnService(
        IOrderRepository orderRepo,
        IReturnLineItemCalculator returnCalc)
    {
        _orderRepo = orderRepo;
        _returnCalc = returnCalc;
    }

    public IReturnOrderForm CreateReturn(
        IPurchaseOrder order,
        IEnumerable<ReturnRequest> items)
    {
        var returnForm = order.ReturnForms
            .FirstOrDefault()
            ?? order.CreateReturnOrderForm();

        foreach (var item in items)
        {
            var shipment = returnForm.Shipments
                .FirstOrDefault()
                ?? returnForm.CreateShipment();

            var returnItem = shipment.CreateLineItem();
            returnItem.Code = item.VariantCode;
            returnItem.Quantity = item.Quantity;
            returnItem.ReturnReason = item.Reason;
            shipment.LineItems.Add(returnItem);
        }

        returnForm.ReturnType =
            ReturnFormType.Refund.ToString();
        returnForm.Status =
            ReturnFormStatus.AwaitingApproval
                .ToString();

        _orderRepo.Save(order);
        return returnForm;
    }
}

public class ReturnRequest
{
    public string VariantCode { get; set; }
    public decimal Quantity { get; set; }
    public string Reason { get; set; }
}

Review the RMA and approve or reject it based on your return policy.

Authorize or reject a return
csharp
public class ReturnAuthorizationService
{
    private readonly IOrderRepository _orderRepo;

    public ReturnAuthorizationService(
        IOrderRepository orderRepo)
    {
        _orderRepo = orderRepo;
    }

    public void Authorize(IPurchaseOrder order,
        IReturnOrderForm returnForm)
    {
        returnForm.Status =
            ReturnFormStatus.AwaitingCompletion
                .ToString();

        order.Properties["ReturnAuthorizedDate"]
            = DateTime.UtcNow;

        _orderRepo.Save(order);
    }

    public void Reject(IPurchaseOrder order,
        IReturnOrderForm returnForm, string reason)
    {
        returnForm.Status =
            ReturnFormStatus.Canceled.ToString();
        returnForm.Properties["RejectionReason"]
            = reason;

        _orderRepo.Save(order);
    }
}

After receiving the returned items, issue a refund through your payment gateway. Commerce creates a credit payment against the original transaction.

Process a refund
csharp
public class RefundService
{
    private readonly IOrderRepository _orderRepo;
    private readonly IPaymentProcessor _paymentProc;

    public RefundService(
        IOrderRepository orderRepo,
        IPaymentProcessor paymentProc)
    {
        _orderRepo = orderRepo;
        _paymentProc = paymentProc;
    }

    public bool ProcessRefund(IPurchaseOrder order,
        IReturnOrderForm returnForm)
    {
        var refundAmount = returnForm.Shipments
            .SelectMany(s => s.LineItems)
            .Sum(li => li.PlacedPrice * li.Quantity);

        var originalPayment = order.Forms
            .SelectMany(f => f.Payments)
            .First(p => p.Status ==
                PaymentStatus.Processed.ToString());

        var refundPayment = order.CreatePayment();
        refundPayment.Amount = refundAmount;
        refundPayment.TransactionType =
            TransactionType.Credit.ToString();
        refundPayment.TransactionID =
            originalPayment.TransactionID;
        refundPayment.PaymentMethodId =
            originalPayment.PaymentMethodId;

        var message = string.Empty;
        var result = _paymentProc
            .ProcessPayment(order, refundPayment,
                ref message);

        if (result)
        {
            returnForm.Status =
                ReturnFormStatus.Complete.ToString();
        }

        _orderRepo.Save(order);
        return result;
    }
}

After inspecting returned items, add them back to inventory if they are in sellable condition.

Restock returned items
csharp
using Mediachase.Commerce.InventoryService;

public void RestockItems(
    IInventoryService inventoryService,
    IReturnOrderForm returnForm,
    string warehouseCode)
{
    foreach (var shipment in returnForm.Shipments)
    {
        foreach (var item in shipment.LineItems)
        {
            var existing = inventoryService
                .QueryByEntry(new[] { item.Code })
                .FirstOrDefault(r =>
                    r.WarehouseCode == warehouseCode);

            if (existing != null)
            {
                var updated = new InventoryRecord
                {
                    CatalogEntryCode = item.Code,
                    WarehouseCode = warehouseCode,
                    PurchaseAvailableQuantity =
                        existing.PurchaseAvailableQuantity
                        + item.Quantity,
                    PurchaseAvailableUtc = DateTime.UtcNow,
                    IsTracked = existing.IsTracked
                };

                inventoryService.Save(
                    new[] { updated });
            }
        }
    }
}
IssueCauseFix
Refund fails with “transaction not found”Original payment transaction ID missing or expiredVerify the original transaction ID exists in the payment provider
Return quantity exceeds ordered quantityValidation not checking against original orderCompare return quantities against the original order line items
Inventory not updated after restockRestock step skipped or warehouse code incorrectVerify the warehouse code matches and the restock method runs after inspection
Customer sees “return not available”Return window has expired based on your policyCheck the return eligibility window in your return policy configuration