Case Study: Stereo's WordPress E-Commerce Example

WordPress, WordPress, WordPress...

It is the most widely used CMS in the world, and at the same time seems to be the most controversial among the Jamstack's community.

What if I told you that you could craft a beautiful e-commerce website with WordPress, using the least amount of plugins possible? Would you be keen to renew your love for this age-old CMS?

Well, our friends at Stereo web agency successfully built a beautiful and efficient e-commerce site on WordPress, and we were able to ask them some questions about the project.

And how nice is it to connect with people from our home province, Québec? It's even better when those people come from the home town (Trois-Rivières) of one of our co-founders & product owner, Charles.

Let's look into this WordPress e-commerce website example. How did they build it using Snipcart, and what impact did it have on the digital transformation of a local shoe shop?

For this case study, Jonathan Grenier was kind enough to answer our questions in detail. PHP and JavaScript developer first, he has been working at Stereo for over 4 years and a developer for more than 12 years.

Let's jump in!

Who's Stereo?

Stereo is a web agency based in Trois-Rivières, QC, Canada. Since their launch, they've been creating numerous web projects ranging from e-commerce to government agency websites.

The quality of their work has allowed them to gain a long list of awards that would even make Beyoncé jealous.

Who's Caron Chaussures?

Caron Chaussures is a Québec-based quality footwear shop. Since 1936, the family-owned business has been devoted to offering high-quality shoes to local feet.

They are specialized in offering personalized service for adjusted footwear.

15 years ago, they used homemade PHP and an in-house CMS. Before switching to their new Stereo/Snipcart-powered website, online sales represented only 1% of overall sales.

When they implemented the new website using Snipcart in 2019, they saw an increase of 3750% in online sales!

Selling quality footwear online with Snipcart: Interview with Jonathan Grenier

What's the stack?

For this project, NPM & Grunt were used to manage the compiling of JavaScript and Sass. Several NPM modules are used to make the user experience better, like BarbaJS, a SmoothScroll, and a Swiper.

We have now switched to WebPack for commodity. Our stack is constantly evolving and adapting to stay up to date with the latest technologies.

As for WordPress, we are using Bedrock from Roots.io for theme development. It enables us to work in GitHub to collaborate more easily and have a better development structure.

How was Caron Chaussures operating prior to your development efforts?

Caron Chaussures was using an in-house CMS connected to Retail Point, a POS they are still using to this day.

What is your experience with e-commerce?

We have more and more clients asking for an e-commerce website. The main tools we have been working with are Snipcart, WooCommerce, and Shopify. Snipcart is the solution we like the most because of its flexibility in the development process. Webhooks are great. The speed at which we can implement it makes it a go-to for us. As the vast majority of our projects are built on WordPress and that we want to use the least amount of plugins possible, Snipcart helps us create our custom e-commerce solution easily. It's also easier for us to keep control over code.

What were your e-commerce needs for this project?

First of all, we wanted to share the shopping cart between multiple domains. Caron Chaussures have more than one e-commerce store but wanted to keep the shopping experience uniform and centralize the data.

We also needed to have multiple inventories synchronized together.

Because of the nature of the product (footwear), we needed to support multiple product variants (size, width, color).

It needed to be built on WordPress (most Stereo's projects are built on WordPress).

We needed to have flexibility in the frontend and not be limited visually and graphically to customize the UX.

Why use Snipcart and not another solution?

We've used Snipcart in the past for other projects, so using it and implementing it was easy for us. The fact that Snipcart is from Québec was also important for our client. It was easier for him to get support and could communicate in French.

We could have used another solution like WooCommerce, but it would have meant some heaviness to our code, more complicated upgrades, and overall development process. Snipcart enables us to build our own solution and to maintain our focus on the website rather than the shopping cart.

Tell us about your experience with Snipcart's v3?

We started this project with Snipcart's v2. The v3 was something we implemented in 2020 because we needed to use a custom payments gateway. The look and the feel of the cart are awesome. Customizability is also great; our client often asks us for upgrades and new features, which Snipcart enables.

Have you used Snipcart's API & webhooks for this integration?

Yes, we use many webhooks to offer custom shipping solutions and create JSON files when an order is made so their 3rd party inventory management system can be updated. We also used them to enable payment with gift cards via the custom payment gateways.

Jonathan was kind enough to share a code sample of the different custom feature he implemented for this project:

<?php

class Product
{
    public function __construct()
    {
        add_action( 'rest_api_init', [$this, 'register_api'] );
    }

    public function register_api()
    {
        /**
         * Webhook Shipping
         */
        register_rest_route( 'caron/'.$this->version, '/shipping', array(
            'methods' => 'POST',
            'callback' => [$this, 'shipping']
        ));

        /**
         * Webhook Order
         */
        register_rest_route( 'caron/'.$this->version, '/order', array(
            'methods' => 'POST',
            'callback' => [$this, 'order']
        ));

        /**
         * Webhook Custom payment gateway
         */
        register_rest_route( 'caron/'.$this->version, '/payment-methods', array(
            'methods' => 'POST',
            'callback' => [$this, 'payment_methods']
        ));
    }

    public function payment_methods()
    {
        $this->authenticateRequest();

        $json = file_get_contents('php://input');
        $body = json_decode($json, true);

        $publicToken = $body['publicToken'];
        $mode = $body['mode'];

        return [
            [
                'id' => 'snipcart_custom_gatway_1',
                'name' => 'Carte-Cadeau',
                'checkoutUrl' => get_home_url() . '/paiement-carte-cadeau'
            ]
        ];
    }

    public function order()
    {
        $this->authenticateRequest();

        $json = file_get_contents('php://input');
        $body = json_decode($json, true);

        $output = [
            'ordered' => []
        ];

        if (is_null($body) or !isset($body['eventName'])) {
            header('HTTP/1.1 400 Bad Request');
            return;
        }

        switch ($body['eventName']) {
            case 'order.completed':

                // We export the full order to the client invoice system (Retail Point)
                $this->exportJSON($body);

                $items = $body['content']['items'];
                $discounts = $body['content']['discounts'];

                // For every products, we update quantity available based on our inventory management, as we get full inventory once a day
                foreach ($items as $key => $item) {
                    $id = intval($item['id']);
                    $sold_today = get_field('sold_today', $id);
                    if ($sold_today === null) $sold_today = 0;
                    $out = [
                        'id' => $id,
                        'before' => $sold_today
                    ];
                    $sold_today += $item['quantity'];
                    $out['after'] = $sold_today;
                    update_field('sold_today', $sold_today, $id);
                    $output['ordered'][] = $out;
                }

                // We update gift cards amount based on discount codes
                $giftcard = new Giftcard;
                foreach ($discounts as $key => $discount) {
                    $card = $giftcard->find($discount['code']);
                    if ($card !== false) {
                        $amount = $discount['amount'];
                        $itemsTotal = $body['content']['itemsTotal'];
                        $updatedAmount = $amount - $itemsTotal;
                        if ($updatedAmount<0) $updatedAmount=0;
                        $giftcard->update($card, $updatedAmount);
                    }
                }

                break;
        }
        return $output;
    }

    public function shipping()
    {
        $this->authenticateRequest();

        $snipcart = json_decode(file_get_contents('php://input'), true);

        $rates = [];
        $free = false;

        // Give free shipping on order's total amount requirements and province
        if (isset($snipcart["content"]) && $snipcart["content"]['shippingAddressProvince']=='QC' && $snipcart["content"]['subtotal'] >= 74) {
            $rates[] = [
                'cost' => 0,
                'description' => 'Livraison gratuite au Québec en haut de 74$'
            ];
            $free = true;
        }else if (isset($snipcart["content"]) && $snipcart["content"]['shippingAddressProvince']!='QC' && $snipcart["content"]['subtotal'] > 199) {
            $rates[] = [
                'cost' => 0,
                'description' => 'Livraison gratuite en haut de 199$'
            ];
            $free = true;
        }

        $total = 0;
        foreach ($snipcart["content"]['items'] as $item) {
            $total += $item['quantity'];
        }

        // If no free shipping available, give Purolator options based on fixed amount by the client
        if (!$free && isset($snipcart["content"]) && $snipcart["content"]['shippingAddressProvince']=='QC') {
            if ($total < 3) {
                $rates[] = [
                    'cost' => 9,
                    'description' => 'Purolator'
                ];
            } else if ($total < 5) {
                $rates[] = [
                    'cost' => 12,
                    'description' => 'Purolator'
                ];
            } else {
                $rates[] = [
                    'cost' => 15,
                    'description' => 'Purolator'
                ];
            }
        } else if (!$free) {
            $rates[] = [
                'cost' => 25,
                'description' => 'Purolator'
            ];
        }

        // Add local pickup to shipping options
        $rates[] = [
                'cost' => 0,
                'description' => 'Ramassage en magasin chez Caron Chaussures - Trois-Rivières'
        ];
        $rates[] = [
                'cost' => 0,
                'description' => 'Ramassage en magasin chez Caron Chaussures - Sherbrooke'
        ];
        $rates[] = [
                'cost' => 0,
                'description' => 'Ramassage en magasin chez Chaussures Gaïa – Cap-de-la-Madeleine'
        ];
        $rates[] = [
                'cost' => 0,
                'description' => 'Ramassage en magasin chez Chaussures Gaïa - Trois-Rivières-Ouest'
        ];

        return [
            'rates' => $rates
        ];
    }
}

new Product;

Were our documentation & support helpful?

I love your documentation. It's clear and easy to navigate. It answers almost every need we have. We also used your online support. Communications were quick and efficient. I have nothing but good words about these.

Did you use advanced Snipcart features (inventory management, abandoned carts, email templates customization, multi-currency, etc.) for this project?

I think our client is using the abandoned carts feature to recover clients. As for other features, we didn't use them for this project.

What feature could make Snipcart an even better e-commerce solution for developers & merchants?

I would say that a feature we get asked a lot about is payments via gift cards. We successfully created a custom payment gateway for this, but it's not perfect. We often have a gift card that doesn't cover the whole amount of the order, and we need to develop another credit card payment to complete the order. That's far from ideal, considering Snipcart already manages payments. If you could add a gift card section in your dashboard (as you do for promo codes), we could create via an API our own codes and associate them with an amount so Snipcart could automatically manage the balance on those gift cards once an order is processed would be awesome. It's the main feature that more and more clients ask for.

Bonus question: your thoughts about the Jamstack?

We never used the Jamstack in our projects (headless CMS, SSG, etc.). It's something we see more and more. We try to stay up to date with this approach. We're subscribed to your newsletter et read every blog post. We'll certainly try it in the near future. We find it really interesting.

Enough tech talk

We've talked about the technical aspect of this project, but what about the operational side of it?

Snipcart offers built-in features to manage and run your online business, but Caron Chaussures isn't even using most of them! On the merchant side, Snipcart's dashboard is almost not used. They rely heavily on email notifications. At the moment, shipping and fulfilling are made manually using Purolator.

For the inventory, it's managed directly in their existing retail system. Online inventory and store inventory are updated once daily using the JSON file generated by orders.

Closing thoughts

So what now?

The web and e-commerce world is in constant evolution, especially now, with this pandemic.

Going from 1% of overall sales online to more than 40% in less than 2 years is impressive. Yet, it's what Caron Chaussures achieved since their switch to Snipcart back in 2019. Is it because our online solution is amazing or because of the massive shift toward e-commerce caused by the pandemic? Well, it's a little bit of both.

Even if e-commerce has become an important aspect of our consumption habits nowadays, and new solutions are appearing every other day, keeping your identity and brand essence online can still be difficult. Of course, you could go for an all-in-one, easy, just fill-in-the-blank solution, but it would still be bland and probably out of sync with your (or your client's) identity.

Caron Chaussures built its brand around its service and proximity with the clients. They needed to keep this kind of proximity while selling online. And from what we heard in our interview with folks at Stereo, the flexibility offered by Snipcart enabled that.

Of course, using an external inventory management system is not optimal, but Snipcart's inventory features would mean changing the whole system they already have in stores. Expensive and time-consuming.

Building an e-commerce store for a company shouldn't mean throwing everything into the garbage, and starting from scratch, at least not in most cases. Being able to adapt your e-commerce solution to your need and to personalize it is what we offer, and in some cases, it might be what you need.

And that's the key idea we retain from this interview. We offer flexibility and customizability.

Let us know in the comments section below what makes your e-commerce journey a success story!


We want to thank the folks at Stereo and especially Jonathan for taking their time to answer our questions. They have been Snipcart users for a long time, and we are proud to feature them in our case study!

Follow them on social and check out their latest creations.


If you've enjoyed this post, please take a second to share it on Twitter. Got comments, questions? Hit the section below!

About the author

Ludovic Armand
Digital Marketing Specialist

Ludovic has a long-term love for everything technological, making him the perfect fit to become the next web development content expert.

Top 5 Best Online Shopping Cart for 2022

Read next from Ludovic
View more

36 000+ geeks are getting our monthly newsletter: join them!