Shopify Webhook API Tutorial: How To Delete Data on Uninstall


When you develop a Shopify app, you are required to store merchants’ data such as access tokens and shop URLs. These two data are one of the most important data for using the API.

The take away with this is that- you are responsible for deleting their data as soon as they uninstall the app.

So, how can you do that?

It’s simple. Just use webhooks.


What is Webhook?

Webhook is a method of sending notifications about particular events that have happened in a shop. For example, when a merchant uninstalls an app, Shopify will send a notification to the developer that a merchant has uninstalled his/her app. This notification will have a set of data that the developer can use to determine which store was the one who uninstalled the app.

Webhooks are also available for the following events/topics:

  • Cart
  • Collection
  • Checkout
  • Collection Publication
  • Customer
  • Customer Saved Search
  • Draft Order
  • Fulfillment
  • Fulfillment Event
  • Inventory Item
  • Inventory Level
  • Location
  • Order
  • Order Transaction
  • Product
  • Product Listing
  • Refund
  • Shop
  • Shop Alternate Locale
  • Tender Transaction
  • Theme

If you want to learn more about each of the events listed above, you may visit Shopify webhook documentation.

For this tutorial, we’ll be focusing on how to implement webhook API for the event/topic: app/uninstalled.

We’ll assume once again that you have already set up your Shopify app and connected it with the database because we’re going to delete the stored data as soon as we uninstall the app.

Read more: How to connect Shopify app to MySQL database

We’ll also assume that you are using the same script that we’re using for this tutorial. So if you’re not sure what we’re talking about here, we suggest you follow the tutorial where we teach you how to create a Shopify app from scratch.

We’re going to be editing generate_token.php or token_generator.php for this tutorial. So make sure you have that file ready.


Implementing Webhooks

Before you can make a post request to webhooks API, you will need to create a payload that contains the topic and the target URL that will load once the event is fired.

Of course, for this tutorial, we’ll be using PHP to implement webhook API although using a different programming language will also have the same process.

Now, let’s open our token generator script.

For the moment, we have the following inside our code.

<?php // Get our helper functions require_once("inc/functions.php"); require_once("inc/connect.php"); // Set variables for our request $api_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; //Replace with your API KEY $shared_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; //Replace with your Shared secret key $params = $_GET; // Retrieve all request parameters $hmac = $_GET['hmac']; // Retrieve HMAC request parameter $shop_url = $params['shop']; $params = array_diff_key($params, array('hmac' => '')); // Remove hmac from params ksort($params); // Sort params lexographically $computed_hmac = hash_hmac('sha256', http_build_query($params), $shared_secret); // Use hmac data to check that the response is from Shopify or not if (hash_equals($hmac, $computed_hmac)) { // Set variables for our request $query = array( "client_id" => $api_key, // Your API key "client_secret" => $shared_secret, // Your app credentials (secret key) "code" => $params['code'] // Grab the access key from the URL ); // Generate access token URL $access_token_url = "https://" . $params['shop'] . "/admin/oauth/access_token"; // Configure curl client and execute request $ch = curl_init(); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_URL, $access_token_url); curl_setopt($ch, CURLOPT_POST, count($query)); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($query)); $result = curl_exec($ch); curl_close($ch); // Store the access token $result = json_decode($result, true); $access_token = $result['access_token']; // Show the access token (don't do this in production!) //echo $access_token; $sql = "INSERT INTO example_table (store_url, access_token, install_date) VALUE ('" . $shop_url . "', '" . $access_token . "', NOW())"; if( mysqli_query( $conn, $sql ) ) { header('Location: https://' . $shop_url . '/admin/apps'); exit(); } else { echo "Error installation: " . mysqli_error( $conn ); } } else { // Someone is trying to be shady! die('This request is NOT from Shopify!'); }

As you can see in the code above, we have already implemented MySQL to save the merchant’s data.

However, we’re not just going to save the merchant’s data but we’re also going to register webhook on app uninstall.

Also, make sure you reference your functions.php file since we’re going to do some API calls.

Now let’s talk about the payload. Let’s assume we have the following:

$array = array( 'webhook' => array( 'topic' => 'app/uninstalled', 'address' => 'THE URL OF YOUR SCRIPT THAT YOU WANT TO LOAD WHEN THE EVENT IS FIRED', 'format' => 'json' ) );

Copy the code above just before the header() function.

Since we’re going to delete the merchant’s data when the app/uninstalled event is fired, that means we’re going to create a PHP file that uses MySQL functions.

In your project folder, create a new folder and name it webhooks.

Open the webhooks folder and inside, create a new file and name it delete.php.

Now, let’s update our payload.

$array = array( 'webhook' => array( 'topic' => 'app/uninstalled', 'address' => '' . $shop_url, 'format' => 'json' ) );

Next, let’s get the subdomain of the store because we need that for the API function.

Copy the following code just underneath the code above.

$parsedUrl = parse_url('https://' . $shop_url ); $host = explode('.', $parsedUrl['host']); $subdomain = $host[0]; $shop = $subdomain;

Next, let’s create the POST request for webhook API.

Again, copy the following code just underneath the code above.

$webhook = shopify_call($access_token, $shop, "/admin/api/2019-10/webhooks.json", $array, 'POST'); $webhook = json_decode($webhook['response'], JSON_PRETTY_PRINT);

Save your file and let’s open our store.

This time, we’re not going to open our app but uninstall the app.

Deleting or Uninstalling Shopify app

Keep in mind though that the webhook is not yet registered which means we have to reinstall our app to register it.

Installing unlisted private shopify app

Install your app.


Checking Applied Webhooks

If you are wondering how you can check the list of webhooks applied to your store, you have to do it by sending a GET request instead of just browsing the webhooks.json file.

Let’s open our index.php file and update our code to the following.

<?php require_once("inc/functions.php"); require_once("inc/connect.php"); $requests = $_GET; $hmac = $_GET['hmac']; $serializeArray = serialize($requests); $requests = array_diff_key($requests, array( 'hmac' => '' )); ksort($requests); $sql = "SELECT * FROM example_table WHERE store_url='" . $requests['shop'] . "' LIMIT 1"; $result = mysqli_query( $conn, $sql ); $row = mysqli_fetch_assoc($result); $token = $row['access_token']; $url = parse_url( 'https://' . $row['store_url'] ); $host = explode('.', $url['host'] ); $shop = $host[0]; $webhook = shopify_call($token, $shop, "/admin/api/2019-10/webhooks.json", array(), 'GET'); $webhook = json_decode($webhook['response'], JSON_PRETTY_PRINT); echo print_r( $webhook ); ?> <!DOCTYPE html> <html> <head> <title>Shopify Example App</title> </head> <body> <div> </div> </body> </html>

Save your file and open your app.

You should see the following JSON response.

Displaying list of webhooks in Shopify API PHP

There you have it! You have successfully applied a webhook for the event app/uninstalled!

Now, the next thing we need to do is to actually create the delete.php file and use MySQL functions to delete the store data from the database.

So let’s open our delete.php file and add the following code:

<?php require_once("../inc/connect.php"); require_once("../inc/functions.php"); define('SHOPIFY_APP_SECRET', 'xxxxxxxxxxxxxx'); // Replace with your SECRET KEY function verify_webhook($data, $hmac_header) { $calculated_hmac = base64_encode(hash_hmac('sha256', $data, SHOPIFY_APP_SECRET, true)); return hash_equals($hmac_header, $calculated_hmac); } $res = ''; $hmac_header = $_SERVER['HTTP_X_SHOPIFY_HMAC_SHA256']; $topic_header = $_SERVER['HTTP_X_SHOPIFY_TOPIC']; $shop_header = $_SERVER['HTTP_X_SHOPIFY_SHOP_DOMAIN']; $data = file_get_contents('php://input'); $decoded_data = json_decode($data, true); $verified = verify_webhook($data, $hmac_header); if( $verified == true ) { if( $topic_header == 'app/uninstalled' || $topic_header == 'shop/update') { if( $topic_header == 'app/uninstalled' ) { $sql = "DELETE FROM example_table WHERE store_url='".$shop_header."' LIMIT 1"; $result = mysqli_query($conn, $sql); $response->shop_domain = $decoded_data['shop_domain']; $res = $decoded_data['shop_domain'] . ' is successfully deleted from the database'; } else { $res = $data; } } } else { $res = 'The request is not from Shopify'; } error_log('Response: '. $res); //check error.log to see the result ?>

Save the file and open your store.

As soon as you uninstall your app. Proceed to your webhooks folder and you should see a file called error_log. This file was generated by the delete.php file.

Open error_log file with your code editor. You should see the following if the process is successful.

Getting response from Webhook API in Shopify PHP

If you see the same response then that means your store data is also deleted from the database.


Why do we add the webhook in the token generator? Instead of the index?

The reason why we have to register the webhook in token generator is because, merchants can always change their minds and uninstall the app without opening your app.


Webhooks in Shopify is very important, in fact, Shopify is encouraging you to implement this before you plan to publish your app to the app store.

If you have questions, don’t hesitate to comment down below. Let us know what you think by sending us your reaction!

Wow! I can’t believe 2019 is about to end. We’ve been working for this blog for almost 2 years now.

Now that 2020 is about to come, we’ll do our best to provide you more lessons that you can use for your projects. We’re so excited! Are you excited?

React to this topic
Notify of
Newest Most Voted
Inline Feedbacks
View all comments