Streamlining Customer Support: Integrating Shopify with Zendesk

Thushara Sampath
Choreo Tech Blog
Published in
12 min readSep 4, 2023

--

In today’s fast-paced business landscape, efficient customer support is paramount for maintaining a competitive edge. Integrating different platforms to streamline operations has become a game-changer, and when it comes to handling Shopify customer inquiries and support requests, integrating Shopify with Zendesk can revolutionize the way businesses manage their customer interactions. In this article, you’ll explore the power of integration, the benefits it brings, and how Ballerina packages for Shopify and Zendesk make this process accessible and efficient. Then, you’ll delve into how Choreo provides a robust platform to deploy, manage, test, and observe APIs, giving businesses a comprehensive solution for their integration needs.

Scenario Overview

This article focuses on a scenario where a customer requests a refund for a Shopify order. To effectively manage this process, a seamless integration between Shopify and Zendesk can be established. This sample integration demonstrates how you can automate order validation, refund request creation, Zendesk ticket creation, and communication with the customer via email. Let’s break down the steps involved in this scenario.

Integration Flow

  • Customer refund request submission: Initiate a refund request via an API, providing the order ID, reason for refund, and customer email.
  • Fetch and validate order details: Retrieve order details based on the provided order ID and validate the order.
  • Initiate order Refund: Submit a full refund request for the given order.
  • Create a Zendesk Ticket: Create a support ticket with customer information, order details, and the reason for the refund.
  • Send Email: Send an email to the customer providing details of the Shopify refund request details and Zendesk ticket.

Prerequisites

Step 1: Setting up Shopify

To start off, you’ll need to acquire an API Access key from Shopify. The following steps will guide you through the process:

1.1 Create a Development Store

To Create a development store using your Shopify partner account, Navigate to Stores > Add Store > Create development store.

Figure 1: Create a development store

Add the following details when setting up the development store.

Note that the Store URL will be used later when defining the Ballerina Shopify client.

1.2 Creating a Developer App

To get access to Shopify APIs, you need a Shopify App. Follow the steps below to create a Shopify App.

1. In your development store, go to Settings > Apps and sales channels > Develop Apps.

2. Before creating an app, ensure you’ve allowed custom app development by clicking Allow custom app development.

3. Click Create an app, enter a preferred name, and click Create app.

Figure 3: Create an app

1.3 Obtain an Access Token

To utilize the Ballerina Shopify Admin package, you need an access token from Shopify. Follow the steps below to obtain an access token:

1. Go to the Shopify app you created and click Configure Admin API Scopes to set up permissions.

2. Choose Write and Read permissions for Orders, then click Save. These permissions are sufficient for this scenario.

Figure 4: Choose permissions

3. After saving the configurations, click Install app. This will install the app in your store.

4. In the Access token pane, click Reveal token once. Make sure to securely copy and store this access token, as you’ll need it in the subsequent steps of this guide.

Step 2: Setting up Zendesk

You need to obtain your Zendesk subdomain. Sign in to your Zendesk account, and the URL of your browser should be in the format of https://<your subdomain>.zendesk.com. Copy the subdomain.

In addition to the subdomain, the Ballerina Zendesk Support package requires the email and password associated with your Zendesk account.

Step 3: Implementing the Integration in Ballerina

Now that you’ve configured your Shopify and Zendesk accounts, let’s dive into implementing the integration using Ballerina.

3.1 Create a Ballerina Project

Start by creating a Ballerina project. Open your command line and navigate to a preferred directory. run the following command. This will create a ballerina project for you.

bal new shopify-zendesk-integration

In Ballerina projects, the main.bal file will house your integration logic, while the Ballerina.toml file contains the project metadata. Visit the Ballerina Getting Started guide for more information. Open the newly created Ballerina project in an IDE of your choice.

3.2 Define the Shopify Client

To interact with Shopify’s functionalities, you need to set up the Shopify client in your Ballerina code.

Import the Shopify admin package as shopifyAdmin.

import ballerinax/shopify.admin as shopifyAdmin;

In Ballerina, you can declare variables as configurables, allowing you to provide their values at runtime. Introduce two configurable variables: the Shopify access token and the store name which is the auto-filled Store URL in step 1.1.

configurable string shopifyAccessToken = ?;
configurable string shopifyStoreName = ?;

​​Now, define the shopifyConfig to hold the access token and create the shopifyClient using these configurations. This client will serve as your gateway to call Shopify APIs in later steps.

shopifyAdmin:ApiKeysConfig shopifyConfig = {
xShopifyAccessToken: shopifyAccessToken
};
shopifyAdmin:Client shopifyClient = check new (shopifyConfig, "https://" + shopifyStoreName + ".myshopify.com");

3.3 Define the Zendesk Client

Import the Zendesk support package as zenSupport.

import ballerinax/zendesk.support as zenSupport;

Introduce your Zendesk username (email), password, and subdomain as configurable variables. Define the zendeskConfig with those configurable variables. Next, define the zendeskClient with those configurations.

configurable string zendeskUsername = ?;
configurable string zendeskPassword = ?;
configurable string zendeskSubDomain = ?;
zenSupport:ConnectionConfig zendeskConfig = {
auth: {
username: zendeskUsername,
password: zendeskPassword
}
};
zenSupport:Client zendeskClient = check new (zendeskConfig, serviceUrl = "https://" + zendeskSubDomain + ".zendesk.com");

3.4 Define the Email Client

For sending email notifications to customers, let’s set up the email client.

import wso2/choreo.sendemail;
sendemail:Client emailClient = check new ();

3.5 Implement the Integration Logic

Now that we’ve laid the foundation, let’s delve into the core of the integration logic.

We can now expose the integration as an HTTP service in Ballerina. The following code defines a service that listens on port 8090 and handles HTTP POST requests to the /refund endpoint. Consumers can use this service by sending the required data: user email and order ID as query parameters. The integration logic resides within the post function.

import ballerina/http;
service /refund on new http:Listener(8090) {
resource function post .(string userEmail, string orderId, http:Request reason) returns string|error|http:Response {
// integration logic
}
}

Figure 5 shows the overview of the integration logic which involves the Shopify order validation, order creation, Zendesk ticket creation, sending an email notification to the customer, and returning results to the API consumer.

Figure 5: Overview of the integration logic

Following is the ballerina implementation of the integration logic.

// validate order
shopifyAdmin:OrderObject|error orderDetail = shopifyClient->getOrder(orderId);
if (orderDetail is error) {
log:printError("Failed to get order details", err = orderDetail.toString());
return createResponse(http:STATUS_NOT_FOUND, "Failed to get order details");
} else if (orderDetail["order"]["financial_status"] !== "paid") {
return createResponse(http:STATUS_BAD_REQUEST, "Order is not paid");
} else {

// initiate a refund
shopifyAdmin:RefundLineItemObject[] refundLineItems = [];
shopifyAdmin:LineItem[] orderLineItems = orderDetail["order"]["line_items"] ?: [];
foreach shopifyAdmin:LineItem lineItem in orderLineItems {
shopifyAdmin:RefundLineItemObject refundLineItem = {
line_item_id: lineItem["id"],
quantity: lineItem["quantity"]
};
refundLineItems.push(refundLineItem);
}
shopifyAdmin:CreateRefund createRefund = {
refund: {
note: check reason.getTextPayload(),
refund_line_items: refundLineItems
}
};
shopifyAdmin:RefundObject|error refund = shopifyClient->createRefundForOrder(orderId, createRefund);
if (refund is error) {
log:printError("Failed to create refund request", err = refund.toString());
return createResponse(http:STATUS_INTERNAL_SERVER_ERROR, "Failed to create refund request");
} else {

// create Zendesk ticket
zenSupport:TicketResponse|error ticket = zendeskClient->createTicket({
ticket: {
subject: "Shopify Refund Request",
comment: {
body: "Refund request for Shopify order:" + orderId + " by user:" + userEmail + ". Reason: "
+ check reason.getTextPayload() + ". Refund Id: " + refund["refund"]["id"].toString()
}
}
});
if (ticket is error) {
log:printError("Failed to create zendesk ticket", err = ticket.toString());
return createResponse(http:STATUS_INTERNAL_SERVER_ERROR, "Failed to create zendesk ticket");
} else {

string emailBody = "Your refund request has been received" + "\nOrder ID: " + orderId + "\nTicket ID: " + ticket["ticket"]["id"].toString() +
"\nRefund ID: " + refund["refund"]["id"].toString();

string _ = check emailClient->sendEmail(userEmail, "Refund Requested", emailBody);

// respond with reference IDs
json payload = {
orderId: orderId,
ticketId: ticket["ticket"]["id"],
refundId: refund["refund"]["id"]
};
return createResponse(http:STATUS_OK, payload);
}
}
}

You also need to define the following helper function to prepare the HTTP responses.

function createResponse(int statusCode, string|json payload) returns http:Response {
http:Response response = new;
response.statusCode = statusCode;
response.setPayload(payload);
return response;
}

Your final code will look like this.

import ballerinax/shopify.admin as shopifyAdmin;
import ballerinax/zendesk.support as zenSupport;
import ballerina/http;
import wso2/choreo.sendemail;
import ballerina/log;

configurable string shopifyAccessToken = ?;
configurable string shopifyStoreName = ?;
configurable string zendeskUsername = ?;
configurable string zendeskPassword = ?;
configurable string zendeskSubDomain = ?;

// shopify client
shopifyAdmin:ApiKeysConfig shopifyConfig = {
xShopifyAccessToken: shopifyAccessToken
};
shopifyAdmin:Client shopifyClient = check new (shopifyConfig, "https://" + shopifyStoreName + ".myshopify.com");

// zendesk client
zenSupport:ConnectionConfig zendeskConfig = {
auth: {
username: zendeskUsername,
password: zendeskPassword
}
};
zenSupport:Client zendeskClient = check new (zendeskConfig, serviceUrl = "https://" + zendeskSubDomain + ".zendesk.com");

// email client
sendemail:Client emailClient = check new ();

// helper function to prepare HTTP response
function createResponse(int statusCode, string|json payload) returns http:Response {
http:Response response = new;
response.statusCode = statusCode;
response.setPayload(payload);
return response;
}

service /refund on new http:Listener(8090) {
resource function post .(string userEmail, string orderId, http:Request reason) returns string|error|http:Response {

// validate order
shopifyAdmin:OrderObject|error orderDetail = shopifyClient->getOrder(orderId);
if (orderDetail is error) {
log:printError("Failed to get order details", err = orderDetail.toString());
return createResponse(http:STATUS_NOT_FOUND, "Failed to get order details");
} else if (orderDetail["order"]["financial_status"] !== "paid") {
return createResponse(http:STATUS_BAD_REQUEST, "Order is not paid");
} else {

// initiate a refund
shopifyAdmin:RefundLineItemObject[] refundLineItems = [];
shopifyAdmin:LineItem[] orderLineItems = orderDetail["order"]["line_items"] ?: [];
foreach shopifyAdmin:LineItem lineItem in orderLineItems {
shopifyAdmin:RefundLineItemObject refundLineItem = {
line_item_id: lineItem["id"],
quantity: lineItem["quantity"]
};
refundLineItems.push(refundLineItem);
}
shopifyAdmin:CreateRefund createRefund = {
refund: {
note: check reason.getTextPayload(),
refund_line_items: refundLineItems
}
};
shopifyAdmin:RefundObject|error refund = shopifyClient->createRefundForOrder(orderId, createRefund);
if (refund is error) {
log:printError("Failed to create refund request", err = refund.toString());
return createResponse(http:STATUS_INTERNAL_SERVER_ERROR, "Failed to create refund request");
} else {

// create Zendesk ticket
zenSupport:TicketResponse|error ticket = zendeskClient->createTicket({
ticket: {
subject: "Shopify Refund Request",
comment: {
body: "Refund request for Shopify order:" + orderId + " by user:" + userEmail + ". Reason: "
+ check reason.getTextPayload() + ". Refund Id: " + refund["refund"]["id"].toString()
}
}
});
if (ticket is error) {
log:printError("Failed to create zendesk ticket", err = ticket.toString());
return createResponse(http:STATUS_INTERNAL_SERVER_ERROR, "Failed to create zendesk ticket");
} else {

string emailBody = "Your refund request has been received" + "\nOrder ID: " + orderId + "\nTicket ID: " + ticket["ticket"]["id"].toString() +
"\nRefund ID: " + refund["refund"]["id"].toString();

string _ = check emailClient->sendEmail(userEmail, "Refund Requested", emailBody);

// respond with reference IDs
json payload = {
orderId: orderId,
ticketId: ticket["ticket"]["id"],
refundId: refund["refund"]["id"]
};
return createResponse(http:STATUS_OK, payload);
}
}
}
}
}

Step 4: Deploy into Choreo

Deploying your integration into Choreo brings your carefully crafted logic to life. Follow these steps to set the wheels in motion.

4.1 Create the source code repository

For a seamless experience in Choreo, your integration code needs to reside in a GitHub or Bitbucket repository. You can use either of the following options:

1. Commit and Push Code: If you’ve been following along, commit and push the Ballerina project you created in the previous steps to your GitHub or Bitbucket repository. Your code is now ready for deployment.

2. Use Choreo Examples Repository: To simplify things, you can utilize the Choreo Examples GitHub repository, which contains the Ballerina project for this guide. You can follow the upcoming steps using a fork of this repository.

4.2 Create a component

Let’s create a Ballerina service component in Choreo by following the steps below:

1. Sign in to Choreo-https://console.choreo.dev/. This takes you to the project home page.

2. Create a new project to house your component or use an existing project.

3. Click Create on the Integration as an API card on the Components page.

4. Enter a unique name and description for the service. For this guide, you can use the following.

5. Click Next.

6. To allow Choreo to connect to your GitHub account, click Authorize with GitHub.If you still need to connect your GitHub repository to Choreo, enter your GitHub credentials, and select the fork of the Choreo Examples GitHub repository to install the Choreo GitHub App.

7. In the Connect Repository pane, enter the following information:

8. Click Create. Once the component creation is complete, you will see the component overview page.

You have successfully created an Integration as an API component.

4.3 Build and Deploy

Now that we have connected the source repository, it’s time to build and deploy the integration. Follow the steps below:

1. In the left navigation, click Deploy and navigate to the Deploy page.

2. On the Deploy page, click Configure and Deploy. This will open the Configuration pane.

Figure 6: Build and Deploy

3. Provide the necessary values you obtained in previous steps for the relevant configurations and Click Next.

4. In the Endpoints pane, click the edit icon to edit the Endpoint. Under Network Visibility, select Public and click Update.

5. Click Deploy.

After the deployment is successful, your integration service is now up and running. Let’s test the integration.

Step 5: Test in Choreo

Now comes the exciting part — testing your integration within Choreo which ensures everything is functioning as expected.

5.1 Obtain Shopify Order Id

To test the integration, you’ll need to create a sample order. Follow these steps to generate an order and obtain its ID for testing:

1. In your development store, navigate to the Orders section. Click Create order to initiate the process.

2. Within the Create order page, browse and add a few products to the order.

Figure 7: Select products

3. Click Collect Payment and select Mark as Paid. Click Create Order in the confirmation dialog.

Figure 8: Create an order

You’ll be redirected to the newly created order page. While on this page, take note of the URL in your browser. It should resemble this format: https://admin.shopify.com/store/<your-store-name>/orders/<order-id>. Hence, You can obtain the order Id from this URL.

5.2 Test the Service

1. In the Choreo Console, navigate to the component you created. Click Test in the left navigation menu and then click Console. This opens the OpenAPI Console pane.

2. Click Get Test Key to generate a test API key.

3. Expand the POST resource and Click Try it out.

4. Enter your email address in the userEmail field. Enter the order id you obtained in step 5.1 in the orderId field.

5. In the Request body field, provide the reason for the refund, such as “This product size is too large.”

6. Click Execute. The response in the Response body will contain the following JSON object

{
"orderId": <Order id you sent>
"ticketId": <Id of created Zendesk ticket>,
"refundId": <Id of created Shopify refund request>
}

7. Refresh the order page in Shopify. You’ll notice that the items in the order have been successfully removed.

Figure 9: Items were removed from the order

8. Verify your Zendesk dashboard to confirm that the ticket was created as anticipated.

Figure 10: Zendesk ticket is created

9. Finally, navigate to the inbox of the email address you provided. Here, you should find an email containing details regarding the refund.

Figure 11: Email with the refund details

Step 6: Observe, Manage, and More

In addition to deploying and testing your integration, Choreo provides advanced capabilities for observing and managing services.

If you want to view Kubernetes-level insights for a more detailed diagnosis of this Ballerina service, see Choreo’s DevOps capabilities.

Conclusion

The fusion of Shopify and Zendesk through seamless integration offers a dynamic approach to enhancing customer support operations. Leveraging Ballerina packages for Shopify and Zendesk provides a straightforward development process, while Choreo’s capabilities for deployment, monitoring, and testing usher in a new era of efficient API management. While this article has presented a simplified use case, it underscores the broader implications of such integrations in revolutionizing customer interaction, ultimately leading to enhanced user satisfaction and optimized workflows.

--

--

Thushara Sampath
Choreo Tech Blog

Undergraduate | Electronic and Telecommunication Engineering at University of Moratuwa , Sri Lanka