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.
- Order ID
- Line Item ID
- Transaction ID/Payment Details
- 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:
- The
line_items.id
andline_items.sku
were mismatched from thesku
column. - 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:
transaction.id
transaction.status
transaction.kind
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:
refund_price
refund_tax
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