In a membership model, a subscriber pays a flat fee monthly or annually to join a membership program similar to Amazon Prime. Each merchant can then customize membership perks based on their business model, including but not limited to benefits like monthly credit in their customer's accounts, credit back for additional purchases, exclusive discounts, early access to new releases, free shipping, and loyalty gifts and tie-in trial products.
This model does not require prior use of subscriptions to succeed. While all verticals can find success with a membership model, we’ve found that fashion, beauty, and wellness focused businesses find the most success because of the brand loyalty they build. When customers find brands they like, they tend to re-purchase from the same brand instead of finding similar items from other companies.
Before you start
- This feature will require advanced Shopify Liquid, API, HTML and JavaScript knowledge. This is not part of ReCharge's standard turnkey solution. If you are a merchant looking to implement a membership-model on your ReCharge store, we recommend reaching out to a ReCharge Expert.
- This is an example implementation using the Shopify Debut Theme and Smile.io. While many themes are similar, the exact file names and location of code blocks could differ depending on your chosen theme. ReCharge also does not currently support credit system tracking out of the box, so we recommend integrating with one of our third party loyalty solutions, such as Smile.io, LoyaltyLion, Swell.
- If you are using Yotpo Loyalty, please refer to the Yotpo Membership setup guide for an alternative membership program setup.
- This guide only handles the use case of using ReCharge for the recurring membership product while offering the rest of your products as physical one-time goods. If you offer additional subscription-based products outside of the membership itself, you will need to utilize our API to customize the ReCharge checkout experience. If this is your use case, we recommend reaching out to a ReCharge Expert to discuss further.
Membership examples
- You can read the ReCharge Freshly Picked case study and check out other examples of subscription models including LenoFX, Cocokind, and Eight Sleep.
- You can view the ReCharge sample store to see all implemented functionality in action.
- Contact your Partner Manager for more information or email partners@rechargeapps.com.
Technical implementation
This step-by-step guide walks through an example of a sample store and shows how you can convert your store to adopt a membership model.
The main focus of this implementation is the utilization of a StoreMember customer tag that is assigned to a Shopify customer once a ReCharge subscription product is purchased. First, you will setup a backend that listens for ReCharge webhooks to add/remove this tag. Then you will update your website to check for this tag and display member benefits if it exists, or default non-member functionality if it doesn’t. The steps to implement will be broken down into the following:
- Create middleware server/functions
- Install ReCharge/Create ReCharge API token
- Create Shopify private app
- Create function to add StoreMember tag
- Create function to remove StoreMember tag
- Register webhooks
- Update Shopify products/Liquid code
- Segment your products and collections
- Add member specific code snippets
- Update site navigation and routing
- Update add to cart button for Member-Only products
- Update product pricing
- Update search capabilities
- Offer free shipping to members
- Integrate credit tracking solution (Smile.io)
Step 1 - Create a backend or middleware server
The first step is to create functionality that allows segmentation of website users between being members and non-members. The can be accomplished using a Shopify customer tag and then conditionally showing/hiding member benefits depending on whether the customer has this tag.
To implement this, you will need to create two functions: one to add the customer tag (in this example, StoreMember) and one to remove it. You will need to use ReCharge’s API webhooks to trigger these functions when necessary.
To see a list of available webhooks, please refer to ReCharge API Documentation. In this example, we will setup these functions as serverless functions on Google Cloud Platform, but you can implement these in any backend server or serverless function format you see fit.
Install ReCharge on your Shopify store
The original purchase of the VIP Membership Model will be handled by ReCharge. The download and install is straightforward and can be set up in minutes. Simply add your membership products to a ruleset. For information on set-up, visit Getting started with subscriptions.
Create a ReCharge API token
Once ReCharge is installed, you will want to create an API token to be used when you make calls to the ReCharge API. Please refer to API Token Creation on how to create your ReCharge token.
When generating the token, you will need to set permissions to specific ReCharge objects. You will need Read access to the Customer object to retrieve the Shopify_Customer_ID, and you will need Read/Write access to the Subscriptions object to subscribe to the subscription webhooks. These are the bare minimum permissions needed for this tutorial, but any customizations or enhanced functionality added might require additional permissions.
Create Shopify private app
You will also need a Shopify API token to make calls to add/remove the StoreMember tag. To do this, you will need to create a Shopify private app to obtain an API token. Please refer to the Shopify documentation on how to Create a Shopify private app.
Create function to add StoreMember tag
Next you will use the API tokens from Shopify and ReCharge to create a function to add the StoreMember tag to the customer. The following code is written in JavaScript and uses the request-promise package to make HTTP requests. Since this function will be triggered from a ReCharge webhook, you are taking a ReCharge customer ID as the input to build the rest of the function off of.
First, make a call to the ReCharge Customer API endpoint so you can obtain the customer’s Shopify Customer ID. This Shopify Customer ID is what you will use to identify which customer in Shopify to append the tag to.
Next, make a call to the Shopify API with the Shopify Customer ID as a parameter. This allows you to obtain the customer’s current set of customer tags.
After this, parse the customer tags into a list. Shopify does not currently have a way to append a tag to the customer’s current set of tags, so the algorithm is to get the tags into list form. Once you have your tags list, you will check whether the StoreMember tag already exists. If it doesn’t you will add the StoreMember tag to the list and then write the tags back to a string form.
If you added the tag, the final step is to make an API call to Shopify to update the customer’s tags with the new string list you have created.
Finally, exit the function and return a 200 response to the webhook.
index.js
require('request')
var request = require('request-promise');
var rc = {
//ReCharge API Token
rechargeApiToken: ‘<ReCharge API Token>’,
//Shopify API Tokens
ShopifyApiUsername: ‘<Shopify API Token>,
ShopifyApiPassword: ‘<Shopify API Token Password>’,
ShopifyApiShop: '<Shopify Store Name>',
ShopifyApiVersion: '<Shopify API Version>’,
ShopifyCustomerID: null,
tagName: 'StoreMember',
buildShopifyURL: function(resource) {
return 'https://' + rc.ShopifyApiUsername + ':' + rc.ShopifyApiPassword + '@' + rc.ShopifyApiShop + '.myShopify.com/admin/api/' + rc.ShopifyApiVersion + '/' + resource;
},
getReChargeCustomer: function(customerID) {
return request({
"method": "GET",
"uri": 'https://api.rechargeapps.com/customers/' + customerID,
"headers": {
"X-ReCharge-Access-Token": rc.rechargeApiToken
}
});
},
getShopifyCustomer: function(rcCustomer) {
rc.ShopifyCustomerID = JSON.parse(rcCustomer).customer.shopify_customer_id;
return request({
"method": "GET",
"uri": rc.buildShopifyURL('customers/' + rc.ShopifyCustomerID + '.json'),
});
},
setCustomerTags: function(ShopifyCustomer) {
var tagsString = JSON.parse(ShopifyCustomer).customer.tags;
var tags = tagsString.split(",").map(function(item) {
return item.trim();
});
return tags;
},
hasTag: function( arr, value) {
return arr.indexOf(value) > -1;
},
updateShopifyCustomer: function(tagsArray) {
return request({
"method": "PUT",
"uri": rc.buildShopifyURL('customers/' + rc.ShopifyCustomerID + '.json'),
"json": true,
"body": {
"customer": {
"tags": tagsArray.join()
}
},
})
.then(function(response) {
return 'Shopify Customer Updated';
});
},
processRequest: function(tags) {
if(rc.hasTag(tags, rc.tagName)) {
return rc.tagName + ' Tag Already Present';
}
else {
tags.push(rc.tagName);
console.log(rc.tagName + ' Tag Added');
return rc.updateShopifyCustomer(tags);
}
}
}
exports.main = (req, res) => {
var customerID = req.body.subscription.customer_id || null;
if(customerID) {
var result = rc.getReChargeCustomer(customerID)
.then(rc.getShopifyCustomer)
.then(rc.setCustomerTags)
.then(rc.processRequest);
console.log(result);
}
else {
console.log("CustomerID not found. Could not process webhook.")
}
res.status(200).send("Webhook Received");
};
package.json
{ "name": "runner", "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { "request": "^2.88.0", "request-promise": "^4.2.5" } }
Create function to remove StoreMember tag
Similar to the function to add a StoreMember tag, the function to remove the tag uses the same code except for a slight tweak in the processRequest function. Instead of appending a new tag to the tags list, you would look for the index of the StoreMember tag and remove it from the list. From there, you can process the function in the same way and return a 200 response to the webhook.
index.js
require('request')
var request = require('request-promise');
var rc = {
//ReCharge API Token
rechargeApiToken: ‘<ReCharge API Token>’,
//Shopify API Tokens
ShopifyApiUsername: ‘<Shopify API Token>,
ShopifyApiPassword: ‘<Shopify API Token Password>’,
ShopifyApiShop: '<Shopify Store Name>',
ShopifyApiVersion: '<Shopify API Version>’,
//Shopify Customer ID
ShopifyCustomerID: null,
tagName: 'StoreMember',
buildShopifyURL: function(resource) {
return 'https://' + rc.ShopifyApiUsername + ':' + rc.ShopifyApiPassword + '@' + rc.ShopifyApiShop + '.myShopify.com/admin/api/' + rc.ShopifyApiVersion + '/' + resource;
},
getReChargeCustomer: function(customerID) {
return request({
"method": "GET",
"uri": 'https://api.rechargeapps.com/customers/' + customerID,
"headers": {
"X-ReCharge-Access-Token": rc.rechargeApiToken
}
});
},
getShopifyCustomer: function(rcCustomer) {
rc.ShopifyCustomerID = JSON.parse(rcCustomer).customer.shopify_customer_id;
return request({
"method": "GET",
"uri": rc.buildShopifyURL('customers/' + rc.ShopifyCustomerID + '.json'),
});
},
setCustomerTags: function(ShopifyCustomer) {
var tagsString = JSON.parse(ShopifyCustomer).customer.tags;
var tags = tagsString.split(",").map(function(item) {
return item.trim();
});
return tags;
},
hasTag: function( arr, value) {
return arr.indexOf(value) > -1;
},
updateShopifyCustomer: function(tagsArray) {
return request({
"method": "PUT",
"uri": rc.buildShopifyURL('customers/' + rc.ShopifyCustomerID + '.json'),
"json": true,
"body": {
"customer": {
"tags": tagsArray.join()
}
},
})
.then(function(response) {
return 'Shopify Customer Updated';
});
},
processRequest: function(tags) {
if(rc.hasTag(tags, rc.tagName)) {
var index = tags.indexOf(rc.tagName);
if(index > -1) {
tags.splice(index, 1);
}
console.log(rc.tagName + ' Tag Removed');
return rc.updateShopifyCustomer(tags);
}
else {
return rc.tagName + ' Tag Not Present';
}
}
}
exports.main = (req, res) => {
let customerID = req.body.subscription.customer_id || null;
if(customerID) {
let result = rc.getReChargeCustomer(customerID)
.then(rc.getShopifyCustomer)
.then(rc.setCustomerTags)
.then(rc.processRequest);
console.log(result);
}
else {
console.log("CustomerID not found. Could not process webhook.")
}
res.status(200).send("Webhook Received");
};
package.json
{
"name": "runner",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"request": "^2.88.0",
"request-promise": "^4.2.5"
}
}
Register webhooks
Now that you have your functions to add and remove the StoreMember tag, you will need to register the webhooks that will trigger these functions. For adding the tag, you have registered the subscription/created and subscription/activated webhooks. For removing the tag, you have registered the subscription/cancelled and subscription/deleted webhooks. Refer to the ReCharge API Documentation for a description of these webhooks, if needed.
You can use the provided JavaScript script below as a template to create these webhooks, but please use your own internal guidelines for registering webhooks.
Webhooks template
require('request')
var request = require('request-promise');
var rc = {
//ReCharge API Token
rechargeApiToken: ‘<ReCharge API Token>’,
createWebhook: function(address, topic) {
return request({
"method": "POST",
"uri": "https://api.rechargeapps.com/webhooks",
"headers": {
"X-ReCharge-Access-Token": rc.rechargeApiToken,
"Accept": "application/json",
"Content-Type": "application/json"
},
"body": JSON.stringify({
"address": address,
"topic": topic,
})
});
},
}
function main() {
var addTagAddress = '<Add Tag Function Address>';
var removeTagAddress = '<Remove Tag Function Address>';
rc.createWebhook(addTagAddress, 'subscription/created')
.then(rc.createWebhook(addTagAddress, 'subscription/activated'))
.then(rc.createWebhook(removeTagAddress, 'subscription/cancelled'))
.then(rc.createWebhook(removeTagAddress, 'subscription/deleted'))
}
main();
Step 2 - Update Shopify products/Liquid code
Now that you have a way to add/remove the StoreMember tag, you need to update your Shopify website to utilize this tag. First, you will update the products and collections in Shopify, then you will update the associated Liquid code to show/hide the member benefits.
Add tags to products
All functionality and coding updates that you add will rely on specific tags tied to each product. This will allow you to segment your products and create collections to put your users into a 'members' vs. 'non-members' tier.
You will need to navigate to your Products section within Shopify and add one of the following tags to each product based on the type of product it is:
Membership
Includes all ReCharge subscription products used for the purchase of the actual membership. This allows you to segment your ReCharge products into their own product page and prevent customers from redeeming their loyalty points on membership products.
Members-Only
Includes all products that only members should have access to. This allows you to segment specific products into their own Members Only product page that the general public will not have access to. This is also a good way to create a pre-sale page or other similar functionality that you would only want members to have access to.
All-Products
Includes all other products that do not belong to Membership or Members-Only. Due to limitations of Shopify, there is no way to create a Collection to give you all products NOT part of Membership or Members-Only. Because of this, you have to create another tag to query off of when creating the All Products collection.
Create collections for products
Next, you will navigate to the Collections section in Shopify and create three new collections for the tags you just created. Set the collections up as Automated collection with the following conditions for each:
- Membership: Product Tag is equal to Membership
- Members-Only: Product Tag is equal to Members-Only
- All-Products: Product Tag is equal to All-Products
Add member specific code snippets
The first step is to add a few preliminary code snippets that will be reused when you are updating the existing code. To make code changes, open the Shopify Theme Editor by navigating to Online Store > Themes > Actions > Edit Code.
Add a new snippet called membership-check.liquid. This snippet will create 2 boolean variables that you will use throughout the other portions of the code base. You will declare an isMember variable that will contain true if the user is a member, or false otherwise. The other variable, isNotMember, will contain true if the user is not a member, or false otherwise. To assign these variables, you will check whether there is an active customer logged in, and that they have the StoreMember tag.
membership-check.liquid
{% assign isMember = false %} {% assign isNotMember = true %} {% if customer and customer.tags contains "StoreMember" %} {% assign isMember = true %} {% assign isNotMember = false %} {% endif %}
Add a new snippet called membership-access.liquid. The code from this snippet will be displayed anytime a non-member needs to be notified that they do not have access to something (i.e. a non-member navigates to a members only url, a product is reserved exclusively for members). We have created a blanket snippet that just displays HTML directing the user to either login or sign up for a membership, but create as many of these snippets as needed for your specific use case.
membership-access.liquid
<h2 style="text-align:center;">Members Access Only</h2>
<p style="text-align:center;">
<a href="/account/login">Log in</a> or <a href="/collections/membership">Sign Up Today</a>!
</p>
Update site navigation and routing
The next step will be to update the site navigation to show different menus pending whether the user is a member or non-member, as well as update the site to redirect the user to a different page if they are a non-member trying to access member-only areas.
Site navigation
To update your site navigation, the first step is to create two new menu items: Members-Menu and Non-Members-Menu. You can create these menus by navigating to Online Store > Navigation within Shopify. When creating these menu items, include any links you would like members to see in the Members-Menu, and any links that non-members should see in the Non-Members-Menu. For this example, you can setup the Members-Menu to include links to Home, All Products collection, and Membership collection, while your Non-Members-Menu include links to Home, All Products collection, and Members-Only collection.
Next, open site-nav.liquid in the Shopify Theme Editor. At the top of the file, you will be adding a condition to check whether the user is a member, and storing that result in the menu_handle. Be sure to also include the membership-check.liquid file so you have access to the isMember variable. Once you have the correct value in menu_handle, update the for loop to use the menu_handle variable instead of section.settings.main_linklist inside the linklists object.
Before
<ul class="site-nav list--inline {{ nav_alignment }}" id="SiteNav">
{% for link in linklists[section.settings.main_linklist].links %}
{%- assign child_list_handle = link.title | handleize -%}
After
{% include 'membership-check' %}
{% assign menu_handle = "Non-Members-Menu" %}
{% if isMember %}
{% assign menu_handle = "Members-Menu" %}
{% endif %}
<ul class="site-nav list--inline {{ nav_alignment }}" id="SiteNav">
{% for link in linklists[menu_handle].links %}
{%- assign child_list_handle = link.title | handleize -%}
Routing
You have already setup a Members-Only collection and limited the navigation to only show that collection when the member is logged in, but there is nothing preventing a non-member from directly typing the URL of the Members-Only area into their browser. To do this, you can update your routing files to show the membership-access.liquid snippet instead of the member’s content when a non-member is trying to access it.
Open the theme.liquid template and navigate down to around line 131 where the {{ content_for_layout }}
object is located. You will add an if statement to check whether the user is not a member and that they are trying to access a members only area. If so, redirect them to the membership-access.liquid snippet, otherwise show them the regular content designed for the page they are accessing.
Before
<div class="page-container" id="PageContainer">
<main class="main-content js-focus-hidden" id="MainContent" role="main" tabindex="-1">
{{ content_for_layout }}
</main>
{% section 'footer' %}
After
<div class="page-container" id="PageContainer">
<main class="main-content js-focus-hidden" id="MainContent" role="main" tabindex="-1">
{% include 'membership-check' %}
{% if isNotMember and handle contains "members-only" %}
{% include 'membership-access' %}
{% else %}
{{ content_for_layout }}
{% endif %}
</main>
{% section 'footer' %}
Update add to cart button for Member-Only products
If you have general products that aren’t in the Members-Only collection, but require the user to be a member to purchase, you can update the Add to Cart button to conditionally show based on the user being a member or not.
The first step is to add a tag to all products that fit this criteria that you can use to condition off on the product page. In this example, you can add a tag called “Member-Product”.
Next, open the product-template.liquid template and navigate down to around line 180 near the product form section where the Add to Cart button is added. Add an if statement to check whether the user is not a member and the product contains the Member-Product tag you just created. If so, display the membership-access snippet, otherwise display the Add to Cart button as normal.
Before
<div class="product-form__error-message-wrapper product-form__error-message-wrapper--hidden{% if section.settings.enable_payment_button %} product-form__error-message-wrapper--has-payment-button{% endif %}" data-error-message-wrapper role="alert">
<span class="visually-hidden">{{ 'general.accessibility.error' | t }} </span>
{% include 'icon-error' %}
<span class="product-form__error-message" data-error-message>{{ 'products.product.quantity_minimum_message' | t }}</span>
</div>
After
{% include "membership-check" %}
{% if isNotMember and product.tags contains "Member-Product" %}
{% include "membership-access" %}
{% else %}
<div class="product-form__error-message-wrapper product-form__error-message-wrapper--hidden{% if section.settings.enable_payment_button %} product-form__error-message-wrapper--has-payment-button{% endif %}" data-error-message-wrapper role="alert">
<span class="visually-hidden">{{ 'general.accessibility.error' | t }} </span>
{% include 'icon-error' %}
<span class="product-form__error-message" data-error-message>{{ 'products.product.quantity_minimum_message' | t }}</span>
</div>
Update product pricing
You can set up your store to give discounted pricing site-wide to all products based on the user being a member. First, you need to update the product templates that display the price on all product pages, then you will update the cart functionality to make sure the correct price is passed along through to the cart. In this example, you will change your pricing to offer a 10% discount to all members, but this number can be changed to whatever best suits the store needs.
Update product pages
All of the product pricing displayed on the site outside of the cart is controlled from one snippet that you will manipulate. You will be updating the pricing to show a normal full price if the user is not a member, or to show a highlighted discounted price with a strikethrough full price next to it if the user is a member.
Open the product-price.liquid snippet. First, you will include the membership-check.liquid to gain access to your isMember variable. Then you will replace the if statement for the price--on-sale class in the dl tag to check the isMember. You do this to give the strikethrough pricing on the normal price and the highlighted color on the discounted pricing.
You also update the money_price variable to be called normal_price and add a membership_price variable. This membership price variable will contain the new price with 10% off.
Before
<!-- snippet/product-price.liquid -->
{% if variant.title %}
{%- assign compare_at_price = variant.compare_at_price -%}
{%- assign price = variant.price -%}
{%- assign available = variant.available -%}
{% else %}
{%- assign compare_at_price = 1999 -%}
{%- assign price = 1999 -%}
{%- assign available = true -%}
{% endif %}
{%- assign money_price = price | money -%}
<dl class="price{% if available and compare_at_price > price %} price--on-sale{% endif %}{% if available and variant.unit_price_measurement %} price--unit-available{% endif %}" data-price>
After
<!-- snippet/product-price.liquid -->
{% include 'membership-check' %}
{% if variant.title %}
{%- assign compare_at_price = variant.compare_at_price -%}
{%- assign price = variant.price -%}
{%- assign available = variant.available -%}
{% else %}
{%- assign compare_at_price = 1999 -%}
{%- assign price = 1999 -%}
{%- assign available = true -%}
{% endif %}
{%- assign normal_price = price | money -%}
{%- assign membership_price = price | times: 0.9 | money -%}
<dl class="price{% if isMember %} price--on-sale{% endif %}{% if available and variant.unit_price_measurement %} price--unit-available{% endif %}" data-price>
You also need to update the code a few lines lower to use your new normal_price and membership price variables when you output prices. Look for the divs with price__regular and price__sale classes as the places to add these new prices to.
Before
<div class="price__regular">
<dt>
<span class="visually-hidden visually-hidden--inline">{{ 'products.product.regular_price' | t }}</span>
</dt>
<dd>
{% if available %}
{% if compare_at_price > price %}
<s class="price-item price-item--regular" data-regular-price>
{{ compare_at_price | money }}
</s>
{% else %}
<span class="price-item price-item--regular" data-regular-price>
{{ money_price }}
</span>
{% endif %}
{% else %}
<span class="price-item price-item--regular" data-regular-price>
{{ 'products.product.sold_out' | t }}
</span>
{% endif %}
</dd>
</div>
<div class="price__sale">
<dt>
<span class="visually-hidden visually-hidden--inline">{{ 'products.product.sale_price' | t }}</span>
</dt>
<dd>
<span class="price-item price-item--sale" data-sale-price>
{{ money_price }}
</span>
<span class="price-item__label" aria-hidden="true">{{ 'products.product.on_sale' | t }}</span>
</dd>
</div>
After
<div class="price__regular">
<dt>
<span class="visually-hidden visually-hidden--inline">{{ 'products.product.regular_price' | t }}</span>
</dt>
<dd>
{% if available %}
{% if compare_at_price > price %}
<s class="price-item price-item--regular" data-regular-price>
{{ compare_at_price | money }}
</s>
{% else %}
<span class="price-item price-item--regular" data-regular-price>
{{ normal_price }}
</span>
{% endif %}
{% else %}
<span class="price-item price-item--regular" data-regular-price>
{{ 'products.product.sold_out' | t }}
</span>
Create cart script
The cart acts independently of the pricing pages and you will need to create a new script to handle this. This script will dynamically update the cart pricing as the user takes different actions (i.e. if the user adds all items to cart while logged out, the script will update the cart item’s prices once the member logs in).
You first need to install the Script Editor from the Shopify Apps Store. Refer to the Shopify Script Editor documentation for more info on installation and configuration.
Next, click Create Script > Line Items > Percentage (%) Off a Product, and name it 10% off Member Pricing. This script uses Ruby and will emulate similar functionality as what you did with the product-price.liquid snippet. First, look to make sure the customer has a StoreMember tag, then loop through each line item in the cart and lower the price to 10% off as long as it is not one of your membership products.
Example:
customer = Input.cart.customer
if !customer.nil? and customer.tags.include? "StoreMember"
Input.cart.line_items.each do |line_item|
product = line_item.variant.product
next if product.tags.include? "Membership"
line_item.change_line_price(line_item.line_price * 0.90, message: "Member Pricing")
end
end
Output.cart = Input.cart
Update search capabilities
You will also want to update the search capabilities of your site. If a non-member is searching for products, you do not want your Member-Only items to show up in the search.
Open search.liquid and navigate down to around line 58 where the code loops through the search.results object. In this for loop, you will add an unless statement to skip all search results where the product contains the Membership tag. This will remove your ReCharge subscription products from the search. You will also remove any products that are tagged as Members-Only when the user is not logged in as a member. This prevents non-members from seeing the Members-Only products when searching.
Before
<ul class="page-width list-view-items">
{% for item in search.results %}
<li class="list-view-item">
{% if item.object_type == 'product' %}
{% include 'product-card-list', product: item %}
{% else %}
After
<ul class="page-width list-view-items">
{% include 'membership-check' %}
{% for item in search.results %}
{% unless item.tags contains "Membership" or isNotMember and item.tags contains "Members-Only" %}
<li class="list-view-item">
{% if item.object_type == 'product' %}
{% include 'product-card-list', product: item %}
{% else %}
Offer free shipping to members
One nice benefit to reward your members is free shipping on purchases. You can set up your member pricing to offer free pricing on all shipping rates, or you can segment the shipping rates into different groups. For example, free standard shipping for being a member, but regular shipping pricing for expedited).
Since shipping is calculated during the checkout process, you will need to create a new script to handle this. This script will loop through all of your eligible member shipping rates and lower them to be free. Like the cart pricing discounts section above, you will need the Script Editor from the Shopify Apps Market for this as well. Refer to the Shopify Script Editor documentation for more info on installing and configuring this if you haven’t already.
Next, click Create Script > Shipping Rates > Modify Shipping Rate Price, and name it Free Standard Shipping for Members. The ruby code will emulate similar functionality as what you did with the product-price.liquid snippet. First, you will look to make sure the customer has a StoreMember tag, then loop through each shipping rate. If the shipping rate is in your ELIGIBLE_SERVICES array, lower that shipping rate to $0.00. In this case, you are offering free shipping for members on all Standard shipping, but normal pricing on all other shipping rates.
Example:
customer = Input.cart.customer
ELIGIBLE_SERVICES = ['Standard']
if !customer.nil? and customer.tags.include? "StoreMember"
Input.shipping_rates.each do |shipping_rate|
if ELIGIBLE_SERVICES.include? shipping_rate.name
shipping_rate.apply_discount(shipping_rate.price, message: "Free shipping for Members!")
end
end
end
Output.shipping_rates = Input.shipping_rates
Step 3 - Integrate credit tracking solution
The final step is to integrate your credit system solution. For this example, the credit system used is Smile.io. Please refer to this step-by-step guide to setup your out-of-the-box integration with Smile.io. This will add a Smile.io widget to your store that will allow customers to redeem points for discounts on future purchases. To make sure only members are receiving your loyalty benefits, you can setup your Smile actions to only take effect if the customer has a StoreMember tag.
Update theme portal to show customer points balance
A typical use case for many rewards programs is to show the customer's points balance once they have logged in. Smile provides an easy-to-use span tag to display this info for us.
Smile code block
{% include "membership-check" %}
{% if customer and isMember %}
<div>{{ customer.first_name }} (Credits: <span class="sweettooth-points-balance"></span>)</div>
{% endif %}
We can leverage our theme portal to create a customized membership experience for our customers, including displaying their points balance.
Note: The theme engine is only available to ReCharge Pro Plan merchants or ReCharge Partners. If you are looking to upgrade to this feature, reach out to our Support Staff.
Create function to add points on renewal
Another popular membership retention strategy is to give rewards points to members for resubscribing each month. Since Smile does not have a direct action to credit points on a recurring basis, this will require using the Smile.io API.
This will also require setting up a backend to listen for a ReCharge webhook. In this example, you will setup a serverless function similar to the functions you created for adding/removing the StoreMember tag, but utilize whatever backend you see fit.
First, make a call to the Smile API to get the Smile customer. Pass the customer email to obtain this info. Then use the Smile Customer ID from this call and use it to credit the customer a certain amount of points.
Once implemented, register this function to trigger off of the order/processed webhook. This way, every time the subscription is renewed for the customer, this function will trigger and credit the customer with more points.
index.js
require('request')
var request = require('request-promise');
var rc = {
smileApiToken: '<Smile API Token>’,
pointsIncrease: 10,
pointsDescription: "Membership Renewal Bonus!",
pointsInternalNote: "Membership Renewal Bonus!",
getSmileCustomer: function(email) {
return request({
"method": "GET",
"uri": 'https://api.smile.io/v1/customers/',
"headers": {
"Authorization": rc.smileApiToken,
"Content-Type": "application/json"
},
"body": JSON.stringify({
"email": email
})
});
},
updateSmilePointsBalance: function(response) {
var customers = JSON.parse(response).customers;
var customerid = null;
for(var i=0; i<customers.length; i++) {
customerid = customers[i].id;
}
return request({
"method": "POST",
"uri": 'https://api.smile.io/v1/points_transactions',
"headers": {
"Authorization": rc.smileApiToken,
"Content-Type": "application/json"
},
"body": JSON.stringify({
"points_transaction": {
"customer_id": customerid,
"points_change": rc.pointsIncrease,
"description": rc.pointsDescription,
"internal_note": rc.pointsInternalNote
}
})
});
},
}
exports.main = (req, res) => {
var email = req.body.order.email || null;
if(email) {
var result = rc.getSmileCustomer(email)
.then(rc.updateSmilePointsBalance)
.then(function(response) {
return "Points Balance Updated for " + email;
});
console.log(result);
}
else {
console.log("Email not found. Could not process webhook.")
}
res.status(200).send("Webhook Received");
};
package.json
{
"name": "runner",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"request": "^2.88.0",
"request-promise": "^4.2.5"
}
}
Create points slider on checkout
In the out-of-the-box solution, the Smile integration provides a widget which allows the redemption of points that will provide a discount code that the customer has to copy/paste into the checkout page. An upgraded alternative is to provide a Points Slider directly on the checkout page. Smile provides a step-by-step tutorial on how to create a points slider on the checkout page. Alternatively, they also have a step-by-step tutorial on how to implement a points dropdown on the checkout page.
Summary
If you have followed the guide step-by-step, you will have created:
- A backend that can add and remove membership tags on subscription.
- Member perks such as store credit, free shipping, special product pricing and collections/products only accessible to members.
- A credit system to store credits.
If you have any questions about this guide, reach out to the Partner Support team.