Marketplaces / Big Commerce Technical Scope / Big Commerce Order Management / Big Commerce Order Refund

Big Commerce Order Refund

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
09.08.2023 v1.0 Bogomil Pavlov First Publish
01.12.2023 v1.1 Bogomil Pavlov Gift Wrapping Handle
11.01.2024 v1.2 Bogomil Pavlov Mapping Changes
29.01.2024 v1.3 Beatris Bunova Dev Notes added (processing refunds for order 1 by 1)
05.02.2024 v1.4 Hristiyan Georgiev Added logic for the error handling
15.02.2024 v1.5 Bogomil Pavlov Pre and Post Shipment Refund flow
03.04.2024 v1.6 Bogomil Pavlov VAT Rounding logic
06.08.2024 v1.7 Bogomil Pavlov Introduce Refund Quotes
22.08.2024 v1.8 Bogomil Pavov Refund Tax Control
25.11.2024 v1.9 Bogomil Pavov Online/Offline Refund Control

<v1.7>

Order Refund Quote

Before each refund we are making we want to obtain the provider_id and validate the amount and in order to do so we are doing additional request. If everything is fine with details we will proceed and make the refund otherwise we will return an error on this step first. We will be sending all the relevant details for the refund and we are following the same logic as for the actual order refund request and basically we are validating the whole refund we already have for the order. The trigger is to have a “pending” refund and we want both request to work together - obtain the provider_id, validate the refund amounts and do the refund. The first step is the refund quote:

API call: POST [https://api.bigcommerce.com/stores/{hash}/v3/orders/{order_marketplace_id}/payment_actions/refund_quotes](https://api.bigcommerce.com/stores/i3kt5xjesl/v3/orders/16438/payment_actions/refund_quotes) API doc: https://developer.bigcommerce.com/docs/rest-management/transactions/payment-actions#create-a-refund-quote Body:

{
  "items": [
    {
      "item_type": "PRODUCT",
      "item_id": 8292,
      "amount": 27.5000,
      "quantity": 1,
      "reason": "Customer requested refund"
    },
     {
       "item_type":"GIFT_WRAPPING",
        "item_id":93,
        "quantity":4,
        "amount":null,
        "reason":""
    },
     {
                "item_type": "SHIPPING",
                "item_id": 7,
                "quantity": null,
                "amount": 10,
                "reason": "Customer requested refund"
            }
  ]
}

Sample Request: POST [https://api.bigcommerce.com/stores/i3kt5xjesl/v3/orders/16438/payment_actions/refund_quotes](https://api.bigcommerce.com/stores/i3kt5xjesl/v3/orders/16438/payment_actions/refund_quotes)

Body:

{
  "items": [
    {
      "item_type": "PRODUCT",
      "item_id": 8292,
      "amount": 27.5000,
      "quantity": 1,
      "reason": "Customer requested refund"
    }
  ]
}

Mapping: In the actual url we want to use the store hash and the Order > Marketplace Order Id

Big Commerce Field Hemi Field Required Comment
items
item_type Yes Possible values GIFT_WRAPPING , PRODUCT, SHIPPING
Based what we have for the order in the refund.
item_id Product In Order > Item Order Line ID

OR Order > Shipping Address ID | Yes | If we have "item_type": "GIFT_WRAPPING" or “PRODUCT” we need to use the Product In Order > Item Order Line ID if we have “SHIPPING” we want to use the Order > Shipping Address ID | | | amount | Order Refund Row > Amount | Yes/No | This is required only if we have "item_type": "SHIPPING"

<v1.8>Based on the Account BigCommerce > Refund with Tax we will decide if we have to refund the shipping amount with or without VAT. </1.8> Please note we are allowed to do only full shipping refunds. | | | quantity | | Yes/No | Need to calculate this based on the Amount in Order refund Row and get the correct quantity and not required for "item_type":“SHIPPING” | | | reason | N/A | No | |

Sample Response:

{
    "data": {
        "total_refund_amount": 27.5,
        "total_refund_tax_amount": 4.58,
        "rounding": 0,
        "adjustment": 0,
        "is_tax_included": true,
        "order_level_refund_amount": 0,
        "refund_methods": [
            [
                {
                    "provider_id": "paypalcommercecreditcards",
                    "provider_description": "PayPal (PayPal Credit Cards)",
                    "amount": 27.5,
                    "offline": false,
                    "offline_provider": false,
                    "offline_reason": ""
                }
            ],
            [
                {
                    "provider_id": "custom",
                    "provider_description": "Custom",
                    "amount": 27.5,
                    "offline": true,
                    "offline_provider": true,
                    "offline_reason": "This is an offline payment provider."
                }
            ]
        ]
    },
    "meta": {}
}

Response Mapping:

Big Commerce Field Hemi Field Required Comment
data
total_refund_amount N/A No
total_refund_tax_amount N/A No
rounding N/A No
adjustment N/A No
is_tax_included N/A No
order_level_refund_amount N/A No
refund_methods Please note we may have more than one refund methods and we want to use always the first one received.
provider_id Yes Used for the next request as provider_id in order to make the actual refund.
provider_description N/A No
amount No Used to validate the refund total amount Order Payment > Total Amount. If there are discrepancies between the total amount of the quote and the refund we want to return an error stating “The amount you are trying to refund is incorrect. The available total amount for refund is {amount}”
This way the use will know what is available for refund.
offline N/A No
offline_provider N/A No
offline_reason N/A No

</v1.7>

There are a couple types of refunds and depends what we are refunding different payloads will be generated however the refund requests are per order. <v1.5>The refunds are available for all order statuses and we need to handle both Pre and Post shipment orders.</v1.5>

We want all triggers, status, error handling, etc. to be as per the Refunds send general logic

API Call: POST https://api.bigcommerce.com/stores/{store_hash}/v3/orders/{order_id}/payment_actions/refunds

Docs: https://developer.bigcommerce.com/docs/rest-management/transactions/payment-actions#create-a-refund

Shipping Refunds

Big Commerce support only full shipping cost refund

Sample Shipping Refund Request:

{
  "items": [
        {
      "item_type": "SHIPPING",
      "item_id": 3,
      "amount": 10,
      "reason": "Customer requested refund"
    }
  ],
  "payments": [
    {
      "provider_id": "cod",
      "amount": 12,
      "offline": true
    }
  ]
}

Mapping:

Big Commerce Field Hemi Field Required Comment
items
item_type SHIPPING Yes Hard coded as SHIPPING
item_id Order > Shipping Address ID Yes
amount Order Refund Row > Amount Yes <v1.8>Based on the Account BigCommerce > Refund with Tax we will decide if we have to refund the shipping amount with or without VAT.

</1.8> Please note we are allowed to do only full shipping refunds. | | | reason | Order Payment > Reason | No | | | payments | | | | | | | provider_id | | Yes | <v1.7>Obtained from Get Refund Quotes. Before each refund we will be checking what is the provider id because different payment providers have different ids and we need to be flexible</v1.7> | | | amount | Order Payment > Total Amount | Yes | This is the shipping cost amount with VAT as it is taken from the Hemi Refund Row | | | offline | <v1.9>Account Big Commerce > Offline Refund | Yes | If we have Account Big Commerce > Offline Refund = Yes we push this as “true” if we have Account Big Commerce > Offline Refund = No we push this as “false” By default we want to have Account Big Commerce > Offline Refund = Yes</v1.9> |

Sample Response:Status 200 OK

{
    "data": {
        "id": 15,
        "order_id": 114,
        "user_id": 0,
        "created": "2023-08-23T20:52:04+00:00",
        "reason": "",
        "total_amount": 210.35,
        "total_tax": 35.058,
        "uses_merchant_override_values": false,
        "payments": [
            {
                "id": null,
                "provider_id": "cod",
                "amount": 210.35,
                "offline": true,
                "is_declined": false,
                "declined_message": ""
            }
        ],
        "items": [
            {
                "item_type": "SHIPPING",
                "item_id": 18,
                "amount": 12,
                "requested_amount": null,
                "reason": "Customer requested refund"
            }
        ]
    },
    "meta": {}
}

From the success response we want to store the id as an Order Payment > Transaction Id


Product Full Refund

Big Commerce supports partial and full product refunds however we will be supporting only FULL LINE refunds and if we try to refund partial line we want to return internal error. This means if we have an order with single product with x2 quantity we allow to refund x1 quantity but we want to stop all partial line refunds and be able to refund partial amount of the quanitty. For full product or products refund we must have the following structure

Sample Product Full Refund Request:

{
  "items": [
    {
      "item_type": "PRODUCT",
      "item_id": 3,
      "quantity": 1,
      "reason": "Customer requested refund"
    },
    {
      "item_type": "PRODUCT",
      "item_id": 4,
      "quantity": 2,
      "reason": "Customer requested refund"
    }
  ],
  "payments": [
    {
      "provider_id": "cod",
      "amount": 49.50,
      "offline": true
    }
  ]
}

Mapping:

Big Commerce Field Hemi Field Required Comment
items
item_type PRODUCT Yes Hard coded as PRODUCT
item_id Product In Order > Item Order Line ID Yes
quantity Order Refund Row > Amount Yes Need to calculate this based on the Amount in Order refund Row
reason Order Payment > Reason No
payments
provider_id Yes <v1.7>Obtained from Get Refund Quotes.
Before each refund we will be checking what is the provider id because different payment providers have different ids and we need to be flexible</v1.7>
amount Order Payment > Total Amount Yes This is the product total amount with tax as it is taken from the Hemi Refund Row
offline <v1.9>Account Big Commerce > Offline Refund Yes If we have Account Big Commerce > Offline Refund = Yes we push this as “true” if we have Account Big Commerce > Offline Refund = No we push this as “false
By default we want to have Account Big Commerce > Online Offline = Yes </v1.9>

Sample Response: Status 200 OK

{
    "data": {
        "id": 16,
        "order_id": 114,
        "user_id": 0,
        "created": "2023-08-23T20:52:04+00:00",
        "reason": "",
        "total_amount": 210.35,
        "total_tax": 35.058,
        "uses_merchant_override_values": false,
        "payments": [
            {
                "id": null,
                "provider_id": "cod",
                "amount": 210.35,
                "offline": true,
                "is_declined": false,
                "declined_message": ""
            }
        ],
        "items": [
            {
                "item_type": "PRODUCT",
                "item_id": 18,
                "quantity": 1,
                "requested_amount": null,
                "reason": "Customer requested refund"
            }
        ]
    },
    "meta": {}
}

From the success response we want to store the id as a Order Payment > Transaction Id


Product and Shipping Cost Refund

We can combine different types of refunds in single request which allows us to make product or products and shipping cost refund in the same time

Sample Order Full + Shipping Cost Request:

{
  "items": [
    {
      "item_type": "PRODUCT",
      "item_id": 3,
      "quantity": 1,
      "reason": "Customer requested refund"
    },
    {
      "item_type": "SHIPPING",
      "item_id": 3,
      "amount": 10,
      "reason": "Customer requested refund"
    }
  ],
  "payments": [
    {
      "provider_id": "cod",
      "amount": 59.50,
      "offline": true
    }
  ]
}

Mapping:

Big Commerce Field Hemi Field Required Comment
items
item_type Yes Possible values PRODUCT, SHIPPING, GIFT_WRAPPING
item_id Product In Order > Item Order Line ID

OR Order > Shipping Address ID | Yes | If we have "item_type": "PRODUCT" we need to use the Product In Order > Item Order Line ID If we have "item_type": "SHIPPING" we need to use the Order > Shipping Address ID | | | amount | Order Refund Row > Amount | Yes/No | Please note depends on what type of refund we are making this field is not required. NOT required only for "item_type": "PRODUCT" <v1.8>Based on the Account BigCommerce > Refund with Tax we will decide if we have to refund the shipping amount with or without VAT. </1.8> Please note this amount must be without the value in Hemi which is stored as the full amount with Tax. For this we will have to use Orders > Total Shipping Marketplace VAT to calculate the correct amount.Please note we are allowed to do only full shipping refunds. | | | quantity | | Yes/No | Please note depends on what type of refund we are making this field is not required. NOT required for "item_type": "SHIPPING" Need to calculate this based on the Amount in Order refund Row | | | reason | Order Payment > Reason | No | | | payments | | | | | | | provider_id | | Yes | <v1.7>Obtained from Get Refund Quotes. Before each refund we will be checking what is the provider id because different payment providers have different ids and we need to be flexible</v1.7> | | | amount | Order Payment > Total Amount | Yes | This is the amount including tax/vat from the refund | | | offline | “true“ | Yes | Hardcoded as “true“ |

Sample Response: Status 200 OK

{
    "data": {
        "id": 6,
        "order_id": 106,
        "user_id": 0,
        "created": "2023-08-09T11:26:08+00:00",
        "reason": "",
        "total_amount": 71.4,
        "total_tax": 11.9,
        "uses_merchant_override_values": false,
        "payments": [
            {
                "id": null,
                "provider_id": "cod",
                "amount": 71.4,
                "offline": true,
                "is_declined": false,
                "declined_message": ""
            }
        ],
        "items": [
            {
                "item_type": "PRODUCT",
                "item_id": 8,
                "quantity": 1,
                "requested_amount": null,
                "reason": "Customer requested refund"
            },
            {
                "item_type": "SHIPPING",
                "item_id": 7,
                "quantity": null,
                "requested_amount": 10,
                "reason": "Customer requested refund"
            }
        ]
    },
    "meta": {}
}

From the success response we want to store the id as a Order Payment > Transaction Id

<v1.1>Gift Wrap Cost Refund

We will have to track if we have a Gift Wrap Costs in the refund and send it as follows

Sample Gift Wrap Cost Request:

{
  "items": [
    {
       "item_type":"GIFT_WRAPPING",
        "item_id":93,
        "quantity":4,
        "requested_amount":null,
        "reason":""
    }
  "payments": [
    {
      "provider_id": "cod",
      "amount": 59.50,
      "offline": true
    }
  ]
}

Mapping:

Big Commerce Field Hemi Field Required Comment
items
item_type Yes Possible values GIFT_WRAPPING
item_id Product In Order > Item Order Line ID Yes If we have "item_type": "GIFT_WRAPPING" we need to use the Product In Order > Item Order Line ID
requested_amount N/A No
quantity Yes Need to calculate this based on the Amount in Order refund Row and get the correct quantity
reason Order Payment > Reason No
payments
provider_id Yes <v1.7>Obtained from Get Refund Quotes.
Before each refund we will be checking what is the provider id because different payment providers have different ids and we need to be flexible</v1.7>
amount Order Payment > Total Amount Yes This is the amount including tax/vat from the refund
offline true Yes Hardcoded as “true

Sample Response: Status 200 OK

{
    "data": {
        "id": 6,
        "order_id": 139,
        "user_id": 0,
        "created": "2023-08-09T11:26:08+00:00",
        "reason": "",
        "total_amount": 71.4,
        "total_tax": 11.9,
        "uses_merchant_override_values": false,
        "payments": [
            {
                "id": null,
                "provider_id": "cod",
                "amount": 71.4,
                "offline": true,
                "is_declined": false,
                "declined_message": ""
            }
        ],
        "items": [
            {
                "item_type": "GIFT_WRAPPING",
                "item_id": 93,
                "quantity": 1,
                "requested_amount": null,
                "reason": "Customer requested refund"
            }
        ]
    },
    "meta": {}
}

From the success response we want to store the id as a Order Payment > Transaction Id

</v1.1>

General Error Responses

Sample Error Response #1:

{
    "status": 422,
    "title": "Order with ID 106 can not be refunded.",
    "type": "https://developer.bigcommerce.com/api-docs/getting-started/api-status-codes"
}

Sample Error Response #2:

{
    "status": 400,
    "title": "Input is invalid",
    "type": "https://developer.bigcommerce.com/api-docs/getting-started/api-status-codes",
    "detail": "Syntax error"
}

Sample Error Response #3:

{
    "status": 422,
    "title": "Invalid field(s): items",
    "type": "https://developer.bigcommerce.com/api-docs/getting-started/api-status-codes",
    "errors": {
        "items": "item_type must be in `{ \"PRODUCT\", \"GIFT_WRAPPING\", \"SHIPPING\", \"HANDLING\", \"ORDER\" }`"
    }
}

Sample Error Response #4:

{
    "status": 422,
    "title": "Order with ID 106 can not be refunded.",
    "type": "https://developer.bigcommerce.com/api-docs/getting-started/api-status-codes"
}

We want to store the title as error message in Order Error Table.<v1.4> Since there are some errors which will not show in the title, we also want to make a check and if we have a node errors to store the error from there, if there is no errors node we store the title as error message.<v1.4>

<v1.3> Dev notes: Because of the way we are calculating refunded VAT for the order in order to have the correct amount to deduct from the last shipping refund (please see the table above for more info on this part), we decided to load only one refund for a given order on a cron run. This is to ensure the calculations are correct and also to save some time for development and avoid code complications. This means that if an order has two refunds on “Pending” status, it will take two runs to process them. </v1.3> <v1.6> Please note the way the roundings need to be calculated is:

</v1.6>

Is this article helpful?
0 0 0