Creating a Shopify Proxy App with Django: A Step-by-Step Guide

Creating a Shopify Proxy App with Django: A Step-by-Step Guide

, 8 min reading time

In this blog post we'll go over how to add custom functionality to your Shopify store using Django and proxy apps. Shopify proxy apps give developers the ability to add dynamic content by redirecting users to external links while still appearing under your Shopify domain. 

Audience

I'll be focusing on developers that want to extend their store functionality vs. developers that want to make a custom app to sell to multiple shop owners. However, some of the information here might be relevant for both. This guide will focus on the backend server side. If you are interested in creating an app that integrates with the Shopify Admin interface, take a look at App Bridge.

Use Cases

Here are a few potential uses case why you might need a proxy app:

  • A web portal for paying/logged in customers
  • Running a license server for users of your digital software
  • Custom analytics or backend automation for your business
  • Affiliate program

Overview

This is the basic flow for a proxy app. All of this is transparent to the user.

  1. The user navigates to example.myshopify.com/apps/my_custom_app
  2. Shopify calls your proxy server with a signed HMAC payload containing the unix timestamp and logged in user ID
  3. The proxy app validates the signature, processes the requests and returns an HTML page
  4. Shopify returns the HTML page to the user.

Guide

Shopify has a sample application for Django, however it hasn't been updated in years and doesn't implement all of the functionality described above. In fact, there is no HMAC verification meaning anyone can navigate directly to your proxy server and spoof requests. We've added this functionality in our forked version.

Download the code and setup the environment

git clone git@github.com:ScalpRadar/sample-django-app.git
cd sample-django-app/
python -m venv .venv
 .venv/bin/activate

Register your app

Go to https://partners.shopify.com and login with your shopify account.

 Navigate to the "Apps" section and click "Create App".

Select "Create Manually"

Select "Custom distribution"

In the Configuration section,set your proxy url and callback. Replace example.com with your server domain name.

If you are doing local development, get a service like https://ngrok.com so your local web server will be accessible on the web.

Next, select the URL prefix and path Shopify will forward requests from. In the case, example.myshopify.com/apps/p/ will forward to your proxy. The sample-django-app handles proxy requests at the /shopify/ route.

Finally, take note of your Shopify key and secret from the Overview page.

Setup the server

Go back to the git repo and setup the environment variables. Use the Shopify key and secret from the previous step. APP_URL is the domain of your proxy. ShOP is the myshopify domain of your store.
cp env.example .env
Next, setup the Django database. By default the project uses sqlite.
python manage.py migrate
Install your app into Shopify by navigating to https://APP_URL/login/. Enter the Shopify domain of your store.

You will be redirected to Shopify then redirected back to your proxy. At this point the app is installed, but you will see a page that says "Invalid Request". This is due to the new HMAC verification that was added and will try to fix in future commits. Navigate back to https://admin.shopify.com manually for now.

Open the application from the Apps menu.

The application admin page has buttons to download order, customer and products to the server. There is also a form to add additional webhooks. The uninstall webhook is created during installation. It deletes store information from the database.

Let's add a new webhook. For the topic enter 'orders/fulfilled' and for the URL enter https://APP_URL/webhook/ (Replace APP_URL with your proxy domain). Shopify will call the /webhook/ endpoint every time an order is fulfilled. The server downloads all recent orders when this happens.

Demo

Now that we've seen the admin page, let's take a look at the page visible by customers. Login to your store as a customer. Then navigate to APP_URL.myshopify.com/apps/p/ (or whatever your app suffix is). You should see a basic welcome page with the logged in customers ID and a form that echos content back to the user.

This page can contain anything you wish your logged in users to see. In Scalp Radar's case, this is where customers manager their software licenses.

Code Review

Let's take a look at how everything works. These are the following Django apps

sample_django_app: Main Django app. Contains the URL routes and settings.py
home: Contains application views and models for proxy and admin page.
api: APIs for javascript to query info about the store. This came with the original sample app and is currently unsued.
shopify_app: Contains all of the logic for HMAC verification and app installation.

Let's take a look at the view code in home/views.py


This view handles the Shopify proxy requests at /shopify/. Most of the work is done by the decorators, which are found in shopify_app/decorators.py

shopify_hmac_verification: Verifies the signature to make sure Shopify is sending the request.
known_shop_required: Makes sure the shop that sent the request matches the shop this app was installed in.
latest_access_scopes_required: Checks if the app needs to be reinstalled if the access permissions have changed
csrf_exempt: Disable Djangos cross-site request forgery protection because it relies on cookies which are stripped by Shopify. HMAC verification makes up for this.

The rest of the GET and POST request code is pretty standard.

 Let's take a look at the signature verification in shopify_app/decorators.py


Shopify signs the HTTP parameters it sends us with the apps secret key. This code verifies the signature and checks that the timestamp is not too far in the past. The latter is to prevent replay attacks.

Here is what the request URL from Shopify looks like.

/shopify/?shop=example.myshopify.com&logged_in_customer_id=1234&path_prefix=%2Fapps%2Fp&timestamp=1720561758&signature=xxxxxx

The only complication is Shopify appears to calculate the signature differently for requests coming from the admin interface and from the external proxy page so we handle that here.

POST requests from the admin page, like 'sync orders' button, don't have parameters. Instead we need to look at the HTTP referer. That contains the URL and parameters that were used to load the admin page, similar to the URL above. The problem here is the timestamp may be old depending on how long the admin page has been open before the POST request is sent. I provide some alternative solutions in the code comment.

Errata

While we've tried to improve the Shopiyf Django app, there are still a few things that need work.

  1. There needs to be a better way to authenticate POST requests in the admin interface.
  2. Installing the App redirects to a page that fails due to the new HMAC verification.
  3. Process webhooks outside of the view handler context.
  4. Disable app instillation if app is already registered to a store.

Conclusion

Shopify can be a great platform to build dynamic web apps without having to implement all of the e-commerce logic. I hope this guide and sample code helps speed up your development process.

 

Leave a comment

Leave a comment


Blog posts

Login

Forgot your password?

Don't have an account yet?
Create account