Google Shopping Actions with Shopify

At Google Marketing Live on May 14th, Google announced a radical revamp of their Google Express shopping service. Even before this announcement, I was working with Google to get Threddies approved and setup using Google Shopping Actions. Google Shopping Actions (GSA) is a mechanism for exposing your products for purchase to the Google Assistant via Google Merchant Center. I had some very specific needs in doing this that required some additional work beyond the basic setup. In this article, I’ll walk through what I did and my reasons for doing so.

Hey Google, Buy some Threddies Scrunchies

Threddies has used Google Shopping Ads to promote many of our products across Google’s ecosystem since we started selling online. We currently use the Google Shopping Marketing campaign capability that is built into Shopify to do this. This setup exposes a product feed to Google Merchant Center using Google’s Content API. This allows us to keep our advertised products data (price, image, description, shipping, etc.) in sync with our website. The Content API is great! It gives prospective customers the best experience by providing up to date information. This eliminates any surprises when they ultimately click through to our website to make a purchase. A simple way to setup GSA would be to just expose this feed directly and call it a day.

Unfortunately, doing this isn’t what I would consider the best case scenario for a few reasons:

  • What if I want to sell products that I don’t advertise or only a subset of those that I do?
  • What if the new Google Shopping imposes constraints on data that Google Shopping Ads does not? (spoiler: it does, and I don’t like the idea of modifying my website content in order to meet these restrictions)
  • Ideally, I’d like to keep promoting our website products completely separated from selling the products on our new ‘Google channel’.

In addition, selling on Google’s platform was going to be more costly than selling directly to our customers via our website. Google charges a ‘commission’ for each product sold. Google Shopping also prefers a straight forward shipping policy to make its ‘universal cart’ more appealing to consumers. Participating in the new Google Shopping also requires us to support a more liberal return policy than we currently allow. Based on this, the ‘feed’ used to drive GSA would need to allow us to provide a different pricing model. One that directly matched our website would not work.

Feeding the Beast

Google Merchant Center does support ‘supplementing’ feeds and I initially planned to implement GSA using a supplemental feed. Ultimately, I decided to setup a completely separate feed for GSA. I did this mainly out of my desire to have no impact or dependency on the Content API feed used for Ads.

I setup a brand new GSA specific feed using Google Sheets. In this feed, I added all of the products that I initially wanted to sell in the new Google Shopping experience. My initial product list pretty closely matches the products that we are selling via Amazon. We did this in order to provide an alternative to dropshippers of our products who were already on Google Express. This is a problem that we are familiar with from our other sales channels. People dropshipping our products from Amazon tend to provide a less than ideal customer experience for many reasons. We try to discourage this practice wherever possible.

Taming the Beast

The first attempt at getting this to work was a mess. Initially I had multiple versions of the same products showing up (at different price points) in both Google Shopping and Ads. This would cause us to pay to advertise products that would be sold via Google Shopping (not desirable!). It also surfaced products that I was advertising from my website in my Google Shopping store (at a deep discount because they were using the website pricing!). I fixed both issues by creating a supplemental feed for Google Ads. In this feed, I use the ‘excluded_destination’ attribute to prevent products from ever showing up in Google Shopping.

Things were starting to look better, but I noticed an issue with products that were in both the Google Shopping Actions and the Google Ads feed. Both Google Shopping and Ads would prefer the data that was in the Google Shopping Actions feed. This resulted in some of my highest converting products being advertised with the Google Shopping data. These ads also brought users to my Google Shopping store rather than my website. I made two tweaks to my Google Shopping Actions feed to correct this. First, I created distinct ‘ids’ for every product in the feed to prevent overlap with the ids that were provided via the Content API feed. Second, I used the ‘included_destination’ attribute to specify that the products in this feed should only be surfaced in Google Shopping Actions.

Getting things in Ship(ping) Shape

One final note if you’re using a similar setup (Shopify with the Google Campaign Marketing app). I noticed that my Google Shopping Shipping policies (setup in Google Merchant Center) appeared to get blown away every few hours

This was maddening and it took me a bit to figure out what was going on. It turns out that Shopify’s Google Shopping marketing integration was doing it! This was easy to fix after I understood what was happening. You need to navigate in your Shopify admin to Apps > Google Shopping > Merchant Center Account and in the Shipping settings section select to manually manage them in the Import method drop down. To be extra safe, modify the ones that are imported from Shopify to never be used by Google Shopping Actions in Google Merchant Center first and then create new Google Shopping Actions only shipping policies after saving your import method settings in Shopify.

Unless you want to be an early adopter, I would recommend waiting a bit… a better Shopify Integration is coming. If you have been accepted into the Google Shopping Actions program and have a similar setup, hopefully this helps! Try it out and let me know what you think! If you want to use the Google Assistant to buy your next hair accessories: “Hey Google, I’d like to buy some Threddies Scrunchies.” or check out all of our products in Google Express.

Shopify Webhooks driving AWeber

This post is a solution to a problem I had with the AWeber Shopify integration. To get the most out of this post, check out the original problem here.

…the continuation…

Being that I was spending 30+ minutes every day manually solving this problem, it was important that I had a MVP solution quickly. I took a step back to think about my immediate needs and the future direction that I would like to take this solution and came up with the following constraints:

  • Need to get something basic up and running quickly that can be easily iterated upon
  • Everything needs to be deployable to the Google Cloud Platform (and not cost a fortune to run)
  • The solution should be something that I can eventually monetize. This means a clean, UI based integration in the Shopify ecosystem (i.e. support for Node, React, Next) and the need to be able to handle many Shopify stores and scale appropriately.
  • Anything built must be easy to fit into the multichannel lead generation vision of Threddies. Eventually this would need to become the way that all leads get added to my email service provider without using any direct integrations.

Shopify Webhooks

I did a little digging and realized that I could solve just about every variant of the core problem if I were notified any time a customer was created or updated in Shopify. Conveniently enough, Shopify provides webhooks for both of these cases (in addition to many more). Webhooks are great for creating quick integrations and very easy to handle using Google Cloud Functions.

I prototyped the ‘create customer’ webhook and had something up and running in no time for my test store. I also started to think more about how I can quickly iterate on webhook based integrations in the future. The most simplistic integration using webhooks doesn’t require authentication, but it does require verifying the data sent is actually from the expected Shopify store and not just anyone on the internet. This is done using the X-Shopify-Hmac-Sha256 header. When you receive the webhook data, you need to verify the data in the body by generating this value (using a private key) and comparing it with what Shopify sends. There are two different ways to do this and it depends on how you integrate with Shopify. The preferred approach is to develop a Shopify app which has it’s own key that you can use to verify every authorized store that is using your app. The drawback of this approach is that you need a full blown Shopify app that implements the store authorization flow and requires some UI work. Since I’m not a React expert, I opted to take the second approach and avoid the UI by having each Shopify store owner that was going to be using this integration provide their store’s key to me. You can get this key by going to your store’s settings page and registering a webhook in the ‘Notifications’ area. This key is used to verify the integrity of all webhook data sent. Things I learned from this step:

  • Cloud Functions would likely not be the final way of deploying this since it did not provide a way to surface a UI in a customer’s Shopify store.
  • The Cloud Function for each webhook is going to have a lot of repeated boilerplate for verifying the integrity of the data sent and handling responses/errors. I would also need a more centralized storage location for keeping Shopify Store specific data so that it wouldn’t need to be duplicated in every Cloud Endpoint
  • Shopify webhooks require a timely response, so putting any heavy lifting in the cloud function is not going to happen. Take too long to respond, and Shopify will deregister your interest in the webhook data. This started to get me thinking about how to recover from this scenario.

Hookup to AWeber

I was getting data from Shopify and verifying the integrity of the data, but at this point nothing was happening with it. In order to get the data into AWeber, I had to again obtain some information from each admin of the Shopify stores that I was integrating with. At the bare minimum, I needed an AWeber account id and a list id to add all subscribers to. I also needed my customer to authorize my app to interact with their AWeber account. More requirements for UI, but I still wanted to put that off and focus on solving the original problem. I also didn’t want to force my users to go into AWeber in order to add my integration. I found this great NodeJs wrapper library around AWeber’s API that did everything I wanted it to do. Using this, you can enter your AWeber integration information and use it to generate an auth URL that you can send to your customer. They can then use that URL to provide your integration with the necessary permissions to their account. They send back a verifier code after authorization that you can use to get all of the necessary tokens needed for your integration to access that account. This information doesn’t change unless the user removes your integration, so it works perfectly until I actually setup the full blown authorization path in my Shopify App.

There were two AWeber issues that I discovered at this point: there is no way to add the subscriber’s location without using an originating ip address for geolocation. This means the ability to send using the subscribers local time window is not something that will work with subscribers added this way. Also, AWeber integrations are confirmed opt-in by default; AWeber would not turn this off for everyone using my integration, so I had to turn it off for each account/list I wanted to use it with and tell my customers to do the same. This was necessary since my integration already leverages Shopify’s confirmed opt in and I didn’t want to confuse my subscribers by making them do it again. The Shopify webhook payload already includes a flag for ‘accepts_marketing’ and I verify that this flag is true before attempting to add any information to AWeber. Things learned at this point:

  • I really need that UI!
  • The create customer/update customer flows look very similar from a Cloud Function perspective, so there needs to be some consolidation.
  • The AWeber deauth path needs to be handled. This can’t be done in a cloud function since it’s so far removed from the user capable of fixing this problem. For now, just alert on the error when it occurs and add it to the list of issues to handle later. This is another item that indicates the need for a systems health check in the customer’s Shopify store (and a way to recover all subscribers added between the time when a failure occurs and the Shopify store owner resolves the problem)

Ready for Production

At this point, things were working well enough that I felt confident allowing this integration to start doing my job for me. Before moving everything to production, I refactored everything to eliminate the obvious problems that I saw at this point.

Instead of Cloud Functions being the primary entry point, I created a NodeJs app to do this instead. This allowed me to setup all of the webhook routing inside this app and put all of the webhook verification and ‘health check’ code into this app. If there was a problem, I could fail fast without fear of Shopify deregistering the webhook.

This also provided a place where I could add all of the UI code for the integration and the intelligence for recovering from failures. This app can also morph into a frontend that is capable of handling and routing any future webhook integration that I want to create.

I then deployed the NodeJS app to Google App Engine. At first it wasn’t working and the errors indicated that the Next.js build step wasn’t occurring on deployment. I solved this by adding a custom build step that is automatically run on deployment by Google Cloud Build. You can do this by adding the script ‘gcp-build’ to your package.json. All of this gets deployed to an App Engine standard environment using automatic scaling. So far with 4 Shopify stores using this integration, the entire platform stays under GCP’s daily usage quotas and costs nothing to run!

Next Steps

Obviously, if anyone else shows interest in this solution, the most immediate need is to make the UX better by rolling out a nice Shopify Admin UI, but there are a few other next steps that I’m currently working on.

  • There is still too much heavy lifting in the webhook handler. I’m working on just pulling enough out of the payload to know what ultimately needs to be done and then publishing this as a Shopify-agnostic event to Google Pub/Sub where it will ultimately be processed by a Google Cloud Function. This will allow me to let Shopify know that the webhook has been processed much more quickly and sets up the necessary infrastructure to start removing my other AWeber integrations in favor of directly publishing the necessary information as an event into this platform.
  • The event driven architecture opens up many additional possibilities… it allows me to do more analysis on the data before it gets into my email service provider. This allows me to better tag and identify the sources of this data. The AWeber Etsy integration for instance doesn’t provide any capability for tagging or otherwise identifying these subscribers. Eventually, this will be the place where I can plugin my ML project that I have been working on that correlates behavior and surfaces insights across sales channels
  • Turn this into a full blown marketing channel app for Shopify. I’ve always dreamt about a day where I can do my email marketing from Shopify the same way that I run Google Shopping or Facebook Ads campaigns. This platform provides the foundation for doing that. I’m really excited about the possibilities!
  • If enough interest exists, turn this into an actual product. Reach out if you’re a Shopify and AWeber customer that is already experiencing the original problem I solved, or have ideas for how this can become perfect for something that you’re trying to do.

Annoying Time Sink of the Month…

Every day there is an annoying thing that I MUST do.  It annoys me that I need to, but as part of running a professional business it must be done.  I discovered the issue about a month ago and have been doing this job manually every day since… all the while thinking about a solution to automating this task out of my life for good.

The Background

Threddies, being both an E-Commerce and Brick and Mortar business, has two distinct buyer’s journeys that start out in very different ways.  In one case, a prospective lead finds us online and is ultimately driven to our website (this journey becomes much more complicated when you consider that a user’s journey could start from Google, Amazon, Etsy, eBay, etc.).  In the other, someone stops by our shop in person… talks to us about the local happenings… and if we’re lucky, makes a purchase. Our end goal is to turn visitors into customers, and even better REPEAT customers. Most of the time, people do not become a customer on the first visit, so our goal is to make them a potential customer, by getting them to sign up for our email list, or at the very least, joining us on their preferred social media platform.  A lot has been written about this Omnichannel retail problem and it was something that we felt we had a fairly good handle on.

We recently have been consolidating our technology stack and introducing processes to make things consistent across all of the sales/leads ‘channels’ that we support.  One area of integration that occured (and ultimately became the root of my current problem) was our choice to move the brick and mortar store to use Shopify POS instead of Square’s offering.  This had many benefits: reduction in payment processing fees, combined CRM and inventory systems, a common user interface for our employees to use across all channels, etc.

The Vexing Problem

The problem that I didn’t see coming involves the collection of emails in our email marketing platform.  We use AWeber and AWeber has both a Square and Shopify integration. We were actively using the Shopify integration to collect emails from our website and the Square integration to collect emails from B&M purchases (We also use the Etsy integration, but the issues with that is a subject for another blog post).  In both cases, we use separate web based sign up forms or the AWeber Atom mobile app (appropriately tagged) to collect email addresses from those that don’t ultimately make a purchase. If you are a Shopify and AWeber user, you should be aware that AWeber’s integration does not support collecting emails that are entered using Shopify’s Newsletter functionality, this was something that I discovered early last year and have built an acceptable workaround for that (I can document this for anyone interested).

After removing Square from the store, I quickly noticed that no emails were being collected from B&M purchases.  Initially, I thought it was a sync issue and that they would eventually show up, but they never did. I did some digging and testing and it seems that the AWeber/Shopify integration was only collecting emails from customers who made a purchase from the website, no emails would ever be added from Shopify POS.  My daily chore of adding emails had begun… even worse, since I was constantly watching what was going on with email adds, I started to notice other issues.

We don’t require users to provide an email address in order to make a purchase, providing a valid phone number will allow you to make a purchase as well.  We do require an email address, however, in order to create a Threddies account which gives the customer access to some additional features that they wouldn’t get otherwise.  I noticed over time that there were many customers that made a purchase without an email and then ended up creating an account with an email at a later date. NONE OF THESE EMAILS WERE BEING CAPTURED!  This was a fairly large problem that required some explaining to our customers when I finally added them all manually to our email list. I did some additional testing and discovered that this same use case occurs when a customer updates their email address from their account.  AWeber never gets the updated email address. This was a particular issue since many of our customers originate from channels that obfuscate their real email addresses (Amazon, eBay, etc.), but then they ultimately warm to us and provide their real email address after making repeat purchases.


I immediately started looking for solutions since manually doing this  every day was a nightmare. The AWeber provided integration is free with an AWeber account, but there are several paid solutions in the Shopify app store.  All of the third party integrations suffered from various issues… either they used polling on a regular interval to collect email addresses rather than being reactive to events occurring in the Shopify ecosystem, or they came with heavy handed tag syncing.  None of them specifically guaranteed the Shopify POS or Newsletter functionality that I desired. Since this problem was/is on my mind every day, I started thinking about the ideal state that I would like to have… tailored email marketing automation that is triggered by the channel that the user originated from.  This is important because the Amazon/eBay/Etsy’s of the world tend to be very restrictive regarding the content of the email that you can send to their users. Due to this, many of the emails sent to customers originating from these channels tends to be manually sent (often through systems that don’t have guaranteed high deliverability like AWeber), rather than automated which takes a crazy amount of time.  I also want intelligence behind tag syncing between Shopify and AWeber. It was clear that none of the existing integrations could meet these needs. This is the problem without an automated solution… for now… Stay tuned!

If you made it this far, and are interested in the thrilling conclusion… I wrote about my solution here.

I’m retiring…

…from the rat race at least 😉  I’m extremely excited to announce that effective 11/09/2018 I will be a full time employee of Threddies!  Many of you are likely already aware that I’ve been working part time at my day job since April in preparation for this and I know, based on the questions that I’ve received, that it will save a lot of time for everyone if I document the basics .

A brief history lesson

We’ve been building Threddies for well over a decade and have learned a lot along the way.  The tech stack has evolved and been replaced several times.  We’ve overcome many of the difficulties of building a new technology based small business in the Upper Bucks and Montgomery County area of Pennsylvania.  We’ve made mistakes and come up with better ways of doing things, but in that time, we’ve grown what was once a tiny side project into a serious e-commerce and brick and mortar business.   In doing this, we’ve realized that we have a lot to offer others who want to do something similar in our region.

Threddies has never really been a major focus of mine since I’ve always had a full time ‘day’ job and a freelance consulting business throughout most of its lifetime.  I’ve been working on a transition plan for the last few months and laying out a plan for how to grow Threddies while also continuing to stay current with the latest advancements in technology.

So what exactly will you be doing?

As of COB on 11/09, I will manage the day to day operations of Threddies.  My consulting business will still exist, but will have a major change of focus.  We will be taking on fewer ‘implementation’ based contracts and instead focus on more ‘strategy’ based ones.  I want to make Upper Bucks and Montgomery counties a great place to start a small business, especially one that is technology based.  I want to help others succeed in building out awesome companies that make our local area a better place to live.  I want to help fill the void in relevant technology and entrepreneurship related education in our public school system.  If you’re passionate about joining me in this, check out the ‘How can I help?’ section below.

I’m currently in the process of taking things that have worked for Threddies and my consulting business and building solutions around them that others can use.  You’ll see more of me at local meetups discussing the latest initiatives and working to better understand pain points that we can offer additional solutions for.

I’m one of your existing clients, what does this mean for me?

First, I’m pretty confident that this is not news for you since I worked with every one of my existing clients to come up with a personalized plan for what happens after this change 😉 All existing contracts will be completed at the same level of quality as if this change never occurred.  For many, I won’t be taking on new work, but I hope to continue working with you on new strategic initiatives.

I was hoping to become a client, what now?

Reach out!  I’m interested in helping, especially if you’re local.  I might say ‘no’ to taking on your project directly, but I can refer you to someone who is more than capable.  I’m also really interested in hearing what you have planned and continuing to build my network.  I may have some expertise to help you and you might be doing something that’s too awesome for me to pass up being involved!

What about all of the fun, one-off experiments?

I believe that the worst thing you can do in business or technology is become stagnant and stop learning.  So the ‘experiments’ will definitely continue.  I fully believe that I will have even more time and resources to invest in conducting (and hopefully documenting) some really crazy experiments.  In fact, the majority of ‘implementation’ based consulting projects that will be continuing after 11/9 fall into this category!

How can I help?

Drop me a line and let’s chat.  I’m looking for people passionate about technology and small business in the Upper Bucks and Montgomery area.  Let’s bounce ideas off of each other and do great things!  If you are starting or growing a business in the area and want to chat or are looking for financing or technological expertise, I want to hear from you!

Using the Physical Web to Drive Subscriber Growth and Engagement

Threddies store front

NOTE: As of December 6, 2018, Google will be discontinuing Nearby Notifications on Android, making much of the information in this post no longer relevant.  Read more here.

Threddies recently made the transition from existing online only to having a B&M boutique shop.  The Threddies shop specializes in items that are noticeably distinct from the online offerings and this posed a challenge for email marketing efforts.  What was the best way to approach introducing the new store to existing customers?  How could we ease new customers of the boutique into the existing Threddies email campaign?  Would existing online customers (who are spread around the world) even care about the physical location?

Establishing the baseline

Many of the questions regarding how to actually structure the effort using our email marketing toolkit were quickly answered since AWeber was in the process of rolling out its segmenting on tags feature.  We knew we didn’t want to maintain separate lists for online customers and those who frequented the shop.  We also wanted to be able to easily keep all of our customers up to date about sales that were happening in the online store or new items from the shop that we intended to also sell in the online store.  What was very clear was that we did not want to bombard online customers with information that would only really be relevant to people who could actually visit our physical location.

We sent one email to all customers informing them of the plan to open the B&M storefront and directed anyone who was interested in more information to use an alternate signup form that we created with a tag that denoted their interest.  This would add new subscribers with the appropriate tag, but would also update existing subscribers to have the tag that we were going to key off of in order to send email with information specific to the boutique.  We now had the ability to send targeted emails to those who actually cared about the physical store.  That was great for existing customers, but it wasn’t really the best way to get visitors to the store signed up to our list.

Growing the local customer base

If someone made a purchase in the store, they would automatically be added to our marketing efforts, but we noticed in the early days, that a lot of people were stopping in and just browsing as we were tweaking the shop layout and products that were for sale.  We wanted to be sure that anyone who stopped in early on and wasn’t ‘converted’ would still have incentive to come back as the concept was evolving.  Setting up a device running AWeber’s Atom with the appropriate tag pre-filled was an option, but the shop is small and that would take up space that we could use for more merchandise and initial indications were that people weren’t going to be very proactive about signing up and would need to be instructed to do so.  Wouldn’t it be great if there was a way to inform any visitor to the shop that we have a way for them to register for more information without someone directing them to a tablet in the corner of the store?  Wouldn’t it be great if they could do all this from the device that they already have in their pocket?

Enter the Physical Web

I had been playing around with beacons and thought using them to solve this dilemma would be a good experiment.  A beacon is a Bluetooth Low Energy device, capable of broadcasting information.  Since it uses Bluetooth, the range of that broadcast is limited to a fairly small area and the beacon broadcast power can be tweaked to adjust that range.  There are two main competing beacon standards: iBeacon (favored by Apple) and Eddystone (an open standard developed by Google).  Android and iOS devices can use both standards, but iBeacon requires that an app be written to specifically interact with the beacon.  Eddystone has a URL format which has become the cornerstone of the Physical Web.  Android devices can interact natively with Eddystone-URL and iOS devices can do the same using the Chrome browser.  I already had some Estimote beacons which support both iBeacon and Eddystone, so I configured one to broadcast the Eddystone-URL format.

Setting up the beacon

Estimote provides an Android app and Web UI to configure their beacons but any beacon that supports Eddystone will have a similar configuration process that is manufacturer specific.  I’ll outline the basic steps without explicitly discussing the exact process required by my beacon manufacturer.

The first thing you’ll want to do is make sure the beacon is broadcasting Eddystone packets and more specifically the Eddystone-URL format.  This format requires a URL that holds the content of what you want to broadcast.  I used the URL for my web signup form that was configured to tag subscribers as being interested in the physical storefront.  You will need to configure the beacon to broadcast this URL as well.  The url cannot be bigger than 17 bytes, so you’ll likely need to use an URL shortener in order to accomplish this.  I like as it provides a nice dashboard with some metrics about your shortened URLs.  Once everything is configured, and your beacon is properly broadcasting the Eddystone-URL format, you should see a ‘nearby’ notification on any reasonably recent Android device.

When someone clicks on this notification, they will be directed to your signup form.

Now you can place your beacon in the location where you would like to broadcast your physical web location and adjust its broadcasting power accordingly.  Prepare to answer questions, as you’ll likely have someone who sees your ‘nearby’ notification for the first time and is curious about what exactly is happening.

Beacon of Hope

I’m still analyzing data regarding the effectiveness of this approach.  It’s not perfect because not every device supports it without additional configuration, but adopting a standards approach like this allows others using Eddystone beacons and the Physical Web to aid in familiarizing customers with this technology and help drive adoption.

Moving forward, I’ll likely use this in conjunction with a dedicated Threddies app allowing use of more beacon functionality while providing additional opportunities for engagement.  If you have any questions about using beacons or general discussion about the Physical Web, feel free to reach out, I’d love to hear from you.