Refund Reasons, Now for Returns and Individual Lines
Structured reasons now extend to returns and replacements, and you can attach a reason to each individual line of a refund or return.
Last year we introduced structured refund reasons: instead of free-text notes, you could attach a reference to a Model (formerly Page) representing a predefined reason, making refunds easy to aggregate, translate, and report on.
That first release covered manual refunds and granted refunds at the order level. With Saleor 3.23 we're extending the same idea in two directions that merchants kept asking for: returns and replacements, and per-line granularity.
Per-line reasons for granted refunds
Previously a granted refund carried a single reasonReference for the whole order. But a refund often spans several items returned for different reasons — one damaged, one the wrong size. Now each line can carry its own reason.
orderGrantRefundCreate and orderGrantRefundUpdate accept a reasonReference on every line, stored on the individual OrderGrantedRefundLine:
A few rules worth knowing:
- The order-level
reasonReferenceis required for staff when a refund reason type is configured, and always optional for apps. - Per-line
reasonReferenceis always optional for everyone. There's no inheritance from the order level — lines without one are stored asnull. - Any reference you provide must point to a
Pageof the configuredPageType, or you'll get aValidationError.
Reasons for returns and replacements
Refunds aren't the only place customers tell you why. Returns and replacements are now first-class citizens too, through the orderFulfillmentReturnProducts mutation.
Reasons work at two levels here: a global reason / reasonReference describing the whole return (stored on the resulting Fulfillment), and a per fulfillment line reason / reasonReference (stored on each FulfillmentLine).
Importantly, the return reason (why the items are coming back) is independent of the refund reason (why money was returned). The refund flag has no effect on reason validation — the same rules apply whether or not you're issuing a refund alongside the return.
A separate setting for returns
Because returns and refunds are conceptually different events, they're governed by separate settings. Returns use a dedicated returnReasonReferenceType, independent of the refundReasonReferenceType you already configured for refunds. Configuring one does not configure the other.
You set it the same way — create a Model Type for your return reasons, then point the setting at it:
Once configured, staff users must supply a global reasonReference when returning products; apps may omit it. To opt back out, run returnReasonReferenceClear.
Supported mutations at a glance
| Mutation | Order-level | Per-line | Since |
|---|---|---|---|
transactionRequestAction |
refundReasonReference |
— | 3.22 |
orderGrantRefundCreate |
reasonReference |
lines[].reasonReference |
3.22 / 3.23 |
orderGrantRefundUpdate |
reasonReference |
addLines[].reasonReference |
3.22 / 3.23 |
orderFulfillmentReturnProducts |
reasonReference |
fulfillmentLines[].reasonReference |
3.23 |
The refund mutations are gated by refundReasonReferenceType; the return mutation by returnReasonReferenceType.
Full details, validation rules, and complete examples are in the refunds documentation.