Shopify Auto Refund Tool

Hi everyone!

I wanted to share a flow I built last week using Shopify’s REST API. This flow automatically processes refunds daily.

The Challenge

Our logistics partner provided us with a CSV file logging daily returns at our warehouse. This file contained key information including Order Numbers and product SKUs. I manually refunded orders by searching them in Shopify’s Admin and issuing a refund for each line item that was returned.

Oftentimes, this process took over an hour to complete, especially after seasonal product launches or occasional promotions. That’s when I discovered Parabola and built a flow that would automate this process.

What I Needed

To issue an automatic refund, I needed these additional pieces of information when making a final POST request.

  1. Order ID
  2. Line Item ID
  3. Transaction ID/Payment Details
  4. Total Refund Calculated

GET Order ID and Line Item ID

After uploading the original CSV and reformatting it using Column Filters, Row Filters, and Column Renames, I was ready to make my initial GET request using an API Enrichment step. Here’s what the CSV looked like:

To get a list of Order IDs via Order Numbers, I used this endpoint:

GET /admin/api/2020-04/orders.json?name={order_number}&status=any

A Formatting Hiccup

A line_items object was also returned with an array nested inside. Using a JSON Flattener, I was able to break up the array into separate columns. However, this caused a formatting issue:

For each request, all line items and their respective IDs and SKUs were returned. This caused 2 issues:

  1. The line_items.id and line_items.sku were mismatched from the sku column.
  2. Rows were duplicated since flattening the line_items object contained a nested array.

I had to find a way to match the line_items.id and the line_items.sku to the sku column from the original CSV. Enter the If/Else step. The logic is set up like this:

If the value of the line_items.sku column matches the reference value of the sku column, create a new column and fill it with the line_items.id value.

The Line Item IDs are now correctly linked to the SKUs. To remove the duplicate rows, I used a Row Filter to remove rows that contained blank values in the GET Line Item ID by SKU column.

Next, I needed to get the Transaction ID and payment details linked to each order by using Shopify’s Transaction Resource.

GET Transaction ID and Payment Details

To retrieve a list of transactions I used an API Enrichment step with this endpoint:

GET /admin/api/2020-04/orders/{order_id}/transactions.json

This returned a transaction object with a nested array. After flattening the array, I filtered these properties to be passed along to the final refund request:

  1. transaction.id
  2. transaction.status
  3. transaction.kind
  4. trasnaction.gateway

I used a Column Filter to keep the columns containing the transaction.id and trasnaction.gateway. The gateway stores data on the type of payment method that was used. PayPal, Amazon Pay, and Shopify Payments are common payment methods we accept on our store.

I also used another Row Filter step to returns rows that had a transaction.kind of sale and a transaction.status of success. This removed any rows that had failed or voided payment attempts.

Here’s what the data table looks like at this point. I renamed the transaction.id column to parent_id.

Now, I have all the necessary payment details for each order. I just need to calculate the refund total before issuing a refund.

Calculating a Refund Total

To retrieve an amount available for a refundable line item, I used this endpoint:

POST /admin/api/2020-04/orders/{order_id}/refunds/calculate.json

In the body of the request, I passed along the {line_items.id} column as a variable:

{
  "refund": {
    "shipping": {
      "full_refund": false
    },
    "refund_line_items": [
      {
        "line_item_id": {line_items.id},
        "quantity": 1,
        "restock_type": "no_restock"
      }
    ]
  }
}

This returned a refund_line_items object that was flattened out to display 3 properties which I renamed below:

  1. refund_price
  2. refund_tax
  3. refund_discount_amount

I used a Math step to calculate the refund total using this simple equation:

{refund_price} + {refund_tax} - {refund_discount_amount}

The value of this equation was added to a new column that I named refund_total. Now that we have the refund total, the last thing we need to do is send a POST request to issue the refunds!

Issuing a Refund

To issue a refund, I sent a POST request to this endpoint:

POST /admin/api/2020-04/orders/{order_id}/refunds.json

Since my data table has all the necessary columns, I was able to pass those as variables in my final POST request. Here’s what I sent in the body of the request:

{
  "refund": {
    "currency": "USD",
    "notify": true,
    "note": "Auto Refund",
    "shipping": {
      "full_refund": false
    },
    "refund_line_items": [
      {
        "line_item_id": {line_items.id},
        "quantity": 1,
        "restock_type": "restock"
      }
    ],
    "transactions": [
      {
        "parent_id": {parent_id},
        "amount": {refund_total},
        "kind": "refund",
        "gateway": {gateway}
      }
    ]
  }
}

Just like that, the flow was finished, and orders were automatically being refunded!

Feature Request

Having the ability for error handling would be great. I set up a Row Filter step before issuing a refund to remove any rows that were processed prior to any errors occurring. It works for now, but I’d love to have that as a feature in the future.

Room for Improvement

If anybody has suggestions with ways I can improve this flow, please let me know! I would love any feedback or pointers. At some point, I’ll rebuild this flow using the Shopify Import and Export steps that were soft-launched before the weekend.

Check out this thread for more info:

Using Parabola has opened up a new world of possibilities for me. If this post can help others create an equally helpful tool, that would be awesome.

Please don’t hesitate to reply to this thread or reach out to me directly if you have any questions.

Always happy to help!
Daniel

This is so awesome, @Daniel_V!! Very impressed with what you’ve built. Agree better error handling would be great, by the way. And hopefully a lot of this will be made much easier with our new integration. :slight_smile:

3 Likes

Thanks for the kind words, @andrew! The Shopify integration tool looks great—excited to see what other flows I can build with it.

Cheers!
Daniel

3 Likes

Thanks for sharing, Daniel! This is so cool. I love hearing about the time savings you were able to get from building this flow. Can’t wait to see what other cool workflows you come up with using our new Shopify steps!

1 Like

Thank you, Sachi! Your advice and insight were so valuable while I was setting this up.

Much appreciated. :slightly_smiling_face:
Daniel

4 Likes