Introduction
Our team at Studio recently built and beta launched Refer, a tool to create and distribute shoppable videos. During the development process, we dove deep into Shopify’s Sales Channel App framework to learn and develop a Shopify app. Following a complex attempt at using Shopify app syncing between client products and our own shop with orders forwarded to client, we discovered this Channel App type which solved our blockers. But not so fast. Our backends are typically built in Python with Django, as was the case for Refer. Unfortunately, Shopify doesn’t currently have an app template for Django (just Node PHP and Ruby) so we were left to build ourselves. Below we’ve outlined the steps any developer would need to implement a Sales Channel app backend in Python/Django.
The sales channel app type
Channel app allows merchants to reach new customers on platforms outside their online store to make a sale. In more concrete terms, as an app developer, a sales channel app allows you to access products from partner merchants (who installed your app) and sell them on your own platform to reach new customers. The checkout still happens on the merchant store though, and as such, the merchant still has control over the sale and any post-purchase communications - like transactional emails.
Our stack
As mentioned earlier, this backend will be implemented in python (3.9) with Django (3.2, yes, I know we need to update to the next LTS). On top of that, we use the Django Rest Framework (DRF) as we are a pure API and the framework simplifies a lot. Side services used are PostgreSQL for the database and Redis for caching.
Application setup
Because we do not need npm or anything related, we decided not to use the Shopify CLI to create the app and instead went through the web procedure on the partner UI. You can still use the Shopify CLI and end up in the same spot. Once the application is created, you need to do a bit of configuration in order for it to become a Sales Channel App:
- Go to the App’s page on your partner page
- Hit “Distribution”
- Select “Public”
- Don't worry, it won't actually publish your app to the store or for review, it will stay there in draft until you actually create the listing for review. But this is a necessary step as public applications can only be turned into a sales channel.
- Head to “App Setup”
- Scroll down to “Sales Channel”
Congrats! Your app is now a sales channel, and you will have access to the dedicated APIs (namely Checkout API and product publishing API). Now that our application is correctly setup, we can move on to the actual development.
App installation process
For a general introduction to Shopify application installation process have a look at the documentation; it is pretty comprehensive. Here we will instead focus on the specificities of our Django backend.
OAuth token
The first step in installing an application (be it a sales channel or not) is to go through the OAuth negotiation process. Because this is sensitive, we wanted to avoid any hand rolling and deferred this to the Shopify implementation. Because Shopify doesn't provide a Django template, we decided to keep the OAuth negotiation part on the Frontend using the libraries that Shopify made. The backend then provides an endpoint to register a shop from the frontend which contains the OAuth token allowing us to make API calls.
Webhook installation
Once we get the shop registered along with its API token, we can start doing the bookkeeping needed. The first order is registering to webhooks from Shopify that may be of interest to us. Registering webhooks is a REST API call away, but as we are using the Shopify API Python library it's all hidden behind an ORM interface which makes it even simpler:
import Shopify
webhook = shopify.Webhook()
webhook.topic = "orders/paid"
webhook.address = "<https://foobared.null/your_webhook_endpoint>"
webhook.format = "json"
webhook.save()
The endpoint handler is then a simple ViewSet (in DRF parlance) with a custom authentication class doing the Shopify webhook signature verification documented here
As a sales channel application here are a few of the webhooks that you might be interested in :
- "orders/*": Classic order related product. We use those to do bookkeeping on our side and analytics.
- "product_listings/*": See more in the product handling section, but those allow you to keep up to date with the products that are available to your sales channel (like inventory and new product types)
This wraps up the webhook installation, and we are now left with syncing (or not syncing depending on your needs) the shop's products.
Product syncing and triaging
The last step of our application installation process is the syncing of the products that the shop sells. It can be a real sync or a fake sync depending on your needs, but you do need to complete validation.
If you do need to sync the product to a local database for later usage, then this is the moment to do exactly that by using the Product Listing resource. Be aware that, as of the time of writing, this resources are <u>not</u> available over the GraphQL API.
Even if you do not need to save them to a local database, you still need to go over the products and check for invariants. For example, you might require that every product has a description to be usable on your own application. Shopify gives you a way to flag products with missing data by using the Resource Feedback resource. Using those, you can flag any missing data to the shop owner, and he will get an UI cue on the products.
Initially, we did not intend to put hard restrictions on products, but during the review process they asked us to use the ResourceFeedback APIs, so we ended up doing this even though it's not mentioned anywhere in the documentation as being mandatory.
Product handling
Product handling is where the sales channel application type shines, for it brings a dedicated UI experience to the shop owner and a dedicated API to the application developer.
For the shop owner
On the shop owner side, you have a dedicated UI to indicate missing data on specific products required to be usable on the sales channel application. The second part is the actual channel part, where the application you install appears as a dedicated sale channel (obviously!) and this gives the shop owner the freedom to select which products should be available to the aforementioned application. On the UI side, the app should display a recap of products failing to sync and the count of products you made available on its home page. More details about this will be covered in the companion FE article.
For the developer
As we briefly mentioned earlier, the channel application type gives you access to a new set of APIs to handle products, namely the Product Listing and a dedicated set of corresponding webhooks. The product list resource is a near perfect drop-in replacement for the Product resource (a few fields are different), so you should feel right at home, but the biggest difference is in the set of products it returns. In effect, this API will only return the set of products selected as being sold on your sales channel by the shop owner using the UI mentioned in the previous section. However, that there are no counterparts to this in the GraphQL version of the API. So if you ever wanted to use GraphQL, use the Storefront API for Products which will naturally behave like ProductListing.
To keep your local database up to date, you can now use the product_lists/* set of webhooks which provide the expected add/update/remove notifications when products are either made available, updated, or removed from your sales channel.
There are two things to be aware when using the admin API as a sales channel:
- The Product API is still accessible, but it will return all the products in the shop even those that are not marked as available to the sales channel. Based on that, we wouldn’t recommend using it for a full search without specifying products that are authorized for use.
- The ProductListing API <u>will</u> return the products marked as missing data with the Product ResourceFeedback API, so the admin must take care of excluding those when creating listings to be used later on. Put more clearly, the ResourceFeedback is merely a way to inform the shop owner but doesn't affect product selection.
Checkout handling
As a sales channel application, checkout and order handling are simple; don't do them! The checkout happens on the merchant's shop directly. Your application frontend will handle the creation of the cart, and you can support multi-shop carts quite simply, but when it comes to the actual checkout it's all deferred to the merchant's store. Which means they keep track of all the sales, and sales going through your application are annotated as such in the shop's admin. This also means that promo code still work transparently as does transactional comms and returns.
Nevertheless, you might still want to keep track of the sales happening via your application for bookkeeping or other reasons. To do so, you can use the "orders/*" family of webhooks which will get triggered whenever a sale happens through your application.
Subscription fee
The last piece of data you might need, is to be able to bill the merchant for your usage, be it a fee or a percentage on each sale. In order to do so, Shopify comes with a bunch of endpoints, which allow you to first create the subscription plan using SubscriptionCreate. This plan will need to be accepted by the shop owner upon installation. Once the plan is accepted, you can use UsageRecordCreateto bill a per usage fee to the merchant. This is where the "orders/*" webhooks mentioned earlier comes handy, because they let you calculate a fee on the sale and charge it back to the merchant using the aforementioned API.
Conclusion
Shopify typically produces detailed documentation but can sometimes lack the birds eye view, especially when it comes to Sales Channel. This walkthrough serves as a high-level guide to get the Sales Channel backend app built and approved in our framework - Python/Django.