ASOS - Create Refund/Cancellation
Summary of Changes: (The purpose of this table is to keep traceability and Product team to highlight the things that were changed into the scope, based on comments or discussions)
Date | Version | Name | Applied changes |
---|---|---|---|
18/09/2022 | v1.0 | Initial Version | |
20/10/2022 | v1.1 | Bogomil Pavlov | Added additional request OR30 for cancellations and extended the reason list |
30/11/2022 | v1.2 | Bogomil Pavlov | Reasons prefix updated |
13.02.2024 | v1.3 | Bogomil Pavlov | Incorporate Cancellation on not debited orders. |
The purpose of this page is to describe in details the refund flow on MIRAKL.
The refund/cancellation request on MIRAKL is per item line id and we can include each separate item as additional “refund“ or “cancellation“ node. To process the refund/cancellation , we have to specify a reason which need to be added in the Refund UI for ASOS Marketplace. In order to obtain the reasons, we should use GET RE01 - List reasons call and then, to make a refund/cancellation , we should use PUT OR28 - Request a refund and OR30 - Request cancelations on order lines
- GET RE01 - List reasons
API Call:/api/reasons
https://asosuk-dev.mirakl.net/help/api-doc/seller/mmp.html#RE01
Please note we need to store only the reasons which are with type = “REFUND“ and type = “CANCELATION“
Example response:
{
"reasons": [
{
"code": "1",
"is_shop_right": false,
"label": "Item not received",
"type": "INCIDENT_OPEN"
},
{
"code": "2",
"is_shop_right": false,
"label": "Item not as ordered",
"type": "INCIDENT_OPEN"
},
{
"code": "3",
"is_shop_right": false,
"label": "Defective item",
"type": "INCIDENT_OPEN"
},
{
"code": "4",
"is_shop_right": false,
"label": "Right to retract order",
"type": "INCIDENT_OPEN"
},
{
"code": "5",
"is_shop_right": false,
"label": "Counterfeit item",
"type": "INCIDENT_OPEN"
},
{
"code": "6",
"is_shop_right": false,
"label": "Delivery took longer than expected",
"type": "INCIDENT_OPEN"
},
{
"code": "7",
"is_shop_right": false,
"label": "Client did not respond",
"type": "INCIDENT_CLOSE"
},
{
"code": "8",
"is_shop_right": true,
"label": "I finally received my order",
"type": "INCIDENT_CLOSE"
},
{
"code": "9",
"is_shop_right": false,
"label": "Item fit the description after all",
"type": "INCIDENT_CLOSE"
},
{
"code": "10",
"is_shop_right": true,
"label": "Replacement item received",
"type": "INCIDENT_CLOSE"
},
{
"code": "11",
"is_shop_right": false,
"label": "Agreement made with the vendor",
"type": "INCIDENT_CLOSE"
},
{
"code": "12",
"is_shop_right": false,
"label": "Solution found by the operator",
"type": "INCIDENT_CLOSE"
},
{
"code": "13",
"is_shop_right": true,
"label": "Customer Service handled by the vendor",
"type": "INCIDENT_CLOSE"
},
{
"code": "14",
"is_shop_right": false,
"label": "No response from the shop",
"type": "REFUND"
},
{
"code": "15",
"is_shop_right": true,
"label": "Out of stock",
"type": "REFUND"
},
{
"code": "16",
"is_shop_right": true,
"label": "Cancelled by the client prior to shipping",
"type": "REFUND"
},
{
"code": "17",
"is_shop_right": true,
"label": "Item returned",
"type": "REFUND"
},
{
"code": "18",
"is_shop_right": true,
"label": "Item not received",
"type": "REFUND"
},
{
"code": "19",
"is_shop_right": true,
"label": "Agreement found with the vendor",
"type": "REFUND"
},
{
"code": "20",
"is_shop_right": true,
"label": "Question about delivery",
"type": "MESSAGING"
},
{
"code": "21",
"is_shop_right": true,
"label": "Cancel the order",
"type": "MESSAGING"
},
{
"code": "22",
"is_shop_right": true,
"label": "Issue with item received",
"type": "MESSAGING"
},
{
"code": "23",
"is_shop_right": true,
"label": "Item not received",
"type": "MESSAGING"
},
{
"code": "24",
"is_shop_right": true,
"label": "Request an invoice",
"type": "MESSAGING"
},
{
"code": "25",
"is_shop_right": true,
"label": "Information on the product",
"type": "MESSAGING"
},
{
"code": "26",
"is_shop_right": true,
"label": "Other question",
"type": "MESSAGING"
},
{
"code": "27",
"is_shop_right": true,
"label": "Information about the product",
"type": "MESSAGING"
},
{
"code": "28",
"is_shop_right": true,
"label": "Delivery conditions",
"type": "MESSAGING"
},
{
"code": "29",
"is_shop_right": true,
"label": "Cancelation, return and refund conditions",
"type": "MESSAGING"
},
{
"code": "30",
"is_shop_right": true,
"label": "Pricing",
"type": "MESSAGING"
},
{
"code": "31",
"is_shop_right": true,
"label": "Other",
"type": "MESSAGING"
},
{
"code": "20",
"is_shop_right": true,
"label": "Question about delivery",
"type": "ORDER_MESSAGING"
},
{
"code": "21",
"is_shop_right": true,
"label": "Cancel the order",
"type": "ORDER_MESSAGING"
},
{
"code": "22",
"is_shop_right": true,
"label": "Issue with item received",
"type": "ORDER_MESSAGING"
},
{
"code": "23",
"is_shop_right": true,
"label": "Item not received",
"type": "ORDER_MESSAGING"
},
{
"code": "24",
"is_shop_right": true,
"label": "Request an invoice",
"type": "ORDER_MESSAGING"
},
{
"code": "25",
"is_shop_right": true,
"label": "Information on the product",
"type": "ORDER_MESSAGING"
},
{
"code": "26",
"is_shop_right": true,
"label": "Other question",
"type": "ORDER_MESSAGING"
},
{
"code": "27",
"is_shop_right": true,
"label": "Information about the product",
"type": "OFFER_MESSAGING"
},
{
"code": "28",
"is_shop_right": true,
"label": "Delivery conditions",
"type": "OFFER_MESSAGING"
},
{
"code": "29",
"is_shop_right": true,
"label": "Cancelation, return and refund conditions",
"type": "OFFER_MESSAGING"
},
{
"code": "30",
"is_shop_right": true,
"label": "Pricing",
"type": "OFFER_MESSAGING"
},
{
"code": "31",
"is_shop_right": true,
"label": "Other",
"type": "OFFER_MESSAGING"
},
{
"code": "34",
"is_shop_right": true,
"label": "Cancelled by the client prior to shipping",
"type": "CANCELATION"
},
{
"code": "CANCELATION_UTS",
"is_shop_right": true,
"label": "Unable to Ship - Out of stock",
"type": "CANCELATION"
},
{
"code": "SYSTEM_LATE_SHIPMENT_CANCELATION",
"is_shop_right": false,
"label": "Canceled due to late shipment",
"type": "CANCELATION"
},
{
"code": "CANCELATION_SELLERCUSTOMER",
"is_shop_right": true,
"label": "Customer Cancelled Via Seller",
"type": "CANCELATION"
}
],
"total_count": 47
}
Bear in mind we need to display the label
into the UI but to send the code
.
Because in our UI we cannot distinguish the refund from the cancellation we need to add in the label what type is the reason is for the refund or for cancellation.
E.G. [REFUND] - {label} or [CANCELATION] - {label}
- PUT OR28 - Request a refund
API Call: /api/orders/refund
API Docs: https://asosuk-dev.mirakl.net/help/api-doc/seller/mmp.html#OR28
*Note:** Each payment row (type=refund) will need to be send as a separate request.
Example call: (one item in order)
{
"refunds": [
{
"amount": 10.00,
"currency_iso_code": "GBP",
"order_line_id": "Order_25082022-6-A-1",
"quantity": 1,
"reason_code": "15",
"excluded_from_shipment": false,
"shipping_amount": 2.00
}
]
}
Example call: (two items in order)
{
"refunds": [
{
"amount": 10.00,
"currency_iso_code": "GBP",
"order_line_id": "Order_25082022-6-A-1",
"quantity": 1,
"reason_code": "15",
"excluded_from_shipment": false,
"shipping_amount": 0
},
{
"amount": 10.00,
"currency_iso_code": "GBP",
"order_line_id": "Order_25082022-6-A-2",
"quantity": 1,
"reason_code": "15",
"excluded_from_shipment": false,
"shipping_amount": 0
}
]
}
MIRAKL allows us to refund:
- Partially items;
- Shipping
- Orders
So the only validation we will need is to make sure we are not refunding more than we can for particular order item line!
Mapping:
Integration Field | Integration Notes | Integration required | Hemi Mapping | Hemi Notes | |
---|---|---|---|---|---|
refunds |
|||||
amount |
Refund Row > Amount | Refund rows with type “item“ | |||
currency_iso_code |
Order > Order currency | ||||
order_line_id |
Product In Order >Item Order Line ID | ||||
quantity |
Product In Order > Quantity | Based on the Refund Row > Amount we need to calculate the quantity! |
• If we are doing partial line refund we specify the quantity as 0.
• If we are doing full line refund (the full quantity)
we specify the actual quantity we have for the item e.g. 1, 2, 3 etc. |
| | reason_code
| | | Order Payment > Reason | We need to use the reason code. |
| | excluded_from_shipment
| | | N/A | |
| | shipping_amount
| | | Refund Row > Amount | Refund rows with type “shipping “ |
Example Response: (one item in order)
{
"order_tax_mode": "TAX_INCLUDED",
"refunds": [
{
"amount": 10.00,
"amount_breakdown": {
"parts": [
{
"amount": 10.00,
"commissionable": true,
"debitable_from_customer": true,
"payable_to_shop": true
}
]
},
"currency_iso_code": "GBP",
"excluded_from_shipment": false,
"order_line_id": "Order_25082022-6-A-1",
"quantity": 1,
"reason_code": "15",
"refund_id": "1109",
"shipping_amount": 2.00,
"shipping_amount_breakdown": {
"parts": [
{
"amount": 2.00,
"commissionable": true,
"debitable_from_customer": true,
"payable_to_shop": true
}
]
},
"shipping_taxes": [],
"taxes": []
}
]
}
Response Mapping:
Integration Field | Integration Notes | Integration required | Hemi Mapping | Hemi Notes | ||
---|---|---|---|---|---|---|
order_tax_mode |
||||||
refunds |
||||||
amount |
N/A | |||||
amount_breakdown |
N/A | |||||
currency_iso_code |
N/A | |||||
excluded_from_shipment |
N/A | |||||
order_line_id |
N/A | This will be used for the mapping | ||||
quantity |
N/A | |||||
reason_code |
N/A | |||||
refund_id |
Order Payment Details > Transaction ID | Because each order item is pushed in a separate transaction we will receive more than one refund_id thus we will have to concatenate them with “-“ | ||||
shipping_amount |
||||||
shipping_amount_breakdown |
||||||
shipping_taxes |
||||||
taxes |
- PUT OR30 - Request cancelations on order lines
API Call: /api/orders/cancel
API Docs: https://asosuk-dev.mirakl.net/help/api-doc/seller/mmp.html#OR28
*Note:** Each payment row (type=refund) will need to be send as a separate request.
Example call: (one item in order)
{
"cancelations": [
{
"amount": 1996,
"currency_iso_code": "GBP",
"order_line_id": "419244321-PUM-A-1",
"quantity": 0,
"reason_code": 34,
"shipping_amount": 0
}
]
}
Example call: (two items in order)
{
"cancelations": [
{
"amount": 1996,
"currency_iso_code": "GBP",
"order_line_id": "419244321-PUM-A-1",
"quantity": 0,
"reason_code": 34,
"shipping_amount": 0
},
{
"amount": 1996,
"currency_iso_code": "GBP",
"order_line_id": "419244321-PUM-A-2",
"quantity": 0,
"reason_code": 34,
"shipping_amount": 0
}
]
}
MIRAKL allows us to refund:
- Partially items;
- Shipping
- Orders
So the only validation we will need is to make sure we are not refunding more than we can for particular order item line!
Mapping:
Integration Field | Integration Notes | Integration required | Hemi Mapping | Hemi Notes | |
---|---|---|---|---|---|
cancellations |
|||||
amount |
Refund Row > Amount | Refund rows with type “item“ | |||
currency_iso_code |
Order > Order currency | ||||
order_line_id |
Product In Order >Item Order Line ID | ||||
quantity |
Product In Order > Quantity | Based on the Refund Row > Amount we need to calculate the quantity! |
• If we are doing partial line refund we specify the quantity as 0.
• If we are doing full line refund (the full quantity)
we specify the actual quantity we have for the item e.g. 1, 2, 3 etc. |
| | reason_code
| | | Order Payment > Reason | We need to use the reason code. |
| | shipping_amount
| | | Refund Row > Amount | Refund rows with type “shipping “ |
Example Response: (one item in order)
{
"cancelations": [
{
"amount": 2.00,
"amount_breakdown": {
"parts": [
{
"amount": 2.00,
"commissionable": true,
"debitable_from_customer": true,
"payable_to_shop": true
}
]
},
"cancelation_id": "1146",
"currency_iso_code": "GBP",
"order_line_id": "419244321-PUM-A-2",
"quantity": 0,
"reason_code": "34",
"shipping_amount": 0.00,
"shipping_amount_breakdown": {
"parts": [
{
"amount": 0.00,
"commissionable": true,
"debitable_from_customer": true,
"payable_to_shop": true
}
]
},
"shipping_taxes": [
],
"taxes": [
]
}
],
"order_tax_mode": "TAX_INCLUDED"
}
Response Mapping:
Integration Field | Integration Notes | Integration required | Hemi Mapping | Hemi Notes | |||
---|---|---|---|---|---|---|---|
cancelations |
|||||||
amount |
N/A | ||||||
amount_breakdown |
N/A | ||||||
parts |
|||||||
amount |
N/A | ||||||
commissionable |
N/A | ||||||
debitable_from_customer |
N/A | ||||||
payable_to_shop |
N/A | ||||||
cancellation_id |
Order Payment Details > Transaction ID | Because each order item is pushed in a separate transaction we will receive more than one refund_id thus we will have to concatenate them with “-“ | |||||
currency_iso_code |
N/A | ||||||
excluded_from_shipment |
N/A | ||||||
order_line_id |
N/A | This will be used for the mapping | |||||
quantity |
N/A | ||||||
reason_code |
N/A | ||||||
shipping_amount |
N/A | ||||||
shipping_amount_breakdown |
N/A | ||||||
parts |
|||||||
amount |
N/A | ||||||
commissionable |
N/A | ||||||
debitable_from_customer |
N/A | ||||||
payable_to_shop |
N/A | ||||||
shipping_taxes |
N/A | ||||||
taxes |
N/A | ||||||
order_tax_mode |
N/A |
<v1.3>
PUT OR29 - Cancel an order not yet debited from the customer
API Call: /api/orders/{order_id}/cancel
API Docs: https://help.mirakl.net/help/api-doc/seller/mmp.html#OR29
We have to specify in the URL our Orders
> Marketplace Order Id
Cancel an order not yet debited from the customer. All accepted but NOT shipped orders are treated as “Not Yet Debited” which means that if a cancellation has to happen, we need to use the OR29 call instead of OR30. Please note only full order cancellations for non debited orders are allowed and we would like to add internal validation and return internal error if not full cancellation is done.
Example call:
PUT https://mirakl.marketplace.laredoute.com/api/orders/12341231-A/cancel
Example Response: 204: No Content
Please note since there is no content in the response we wont have a transaction id for the refund so we need to make a OR11 - List Orders call for the specific order that we have cancelled and store the transaction ID.
This needs to be done after each 204: No Content response for the specific order that we are cancelling.
We have developed all possible scenarios:
- If we have
can_cancel
=true
andcustomer_debited_date
=null
andcan_refund
=false
We need to use the OR29 to cancel the order - If we have
can_cancel
=true
andcustomer_debited_date
=date
(in a2023-12-04T12:26:07.043Z
format) andcan_refund
=false
we need to use the OR30 to cancel - If we have for some reason
can_refund
=true
andcan_cancel
=true
we need to use the OR30 to cancel (this is not an expected behavior anyways, if for some reason it happens for whatever reason we want to have it covered) - If we have
can_cancel
=false
andcustomer_debited_date
=null
andcan_refund
=true
We need to use the OR28 to refund the order (this is not an expected behavior anyways, if for some reason it happens for whatever reason we want to have it covered) -
If we have
can_cancel
=false
andcustomer_debited_date
=date
(in a2023-12-04T12:26:07.043Z
format) andcan_refund
=true
We need to use the OR28 to refund the order</v1.3>
Specifics:
We will have couple of cases here because we will have one Order Payment row with type = refund but in MIRAKL there might be couple of transaction:
- If all transaction are successfully and we receive the
refund_id
or thecancellation_id
,we need to mark the Order Payment > Status = "Completed" and Refund Row > Status = "Completed" - If not all transactions are completed but we have one or more completed we mark the Order Payment > Status = “Partially Completed“, Refund Row > Status = “Completed“ for the successful line and Refund Row > Status = “Error“.
Note: The error message stating which Order Item Line failed is stored in Order Error table.
- **If all transactions failed we mark Order Payment > Status = “Error“, Refund Row > Status = “Error“ and store the error in Order Error table.
How to decide when to make a cancellation or refund is stated on the order. There are two flags which we can rely on in the order payload called "can_cancel" and “can_refund“ based on them we will know which is the correct request.