@dotdev/reactive-rewards
v2.3.6
Published
AP21 rewards react library
Downloads
6
Readme
Reactive Apparel21 Rewards
The front end react library for the AP21 rewards integration.
Install
To install simply run
npm install @dotdev/reactive-rewards
Setup Shopify store
There are a few requirements for this integration. This integration requires Shopify scripts therefore it is only available to Shopify Plus partners.
- Install the Shopify Scripts and create a new line item script with [this script][c67a5b66]. The Shopify script will apply any rewards to cart and reduce benefits price down to 0.
- Create a new private app with storefront access to
unauthenticated_read_product_listings
,unauthenticated_read_product_tags
,unauthenticated_read_product_inventory
. This will be used to fetch reward and gift products asynchronously. [c67a5b66]: #shopify-script "Shopify Script" - Create a reward product. This product will need to have a price of 0.
Usage
Once installed the Loyalty components can be imported like this.
import * as Loyalty from "@dotdev/reactive-rewards"
Provider
All loyalty components should be wrapped within the Provider
component. The provider component takes the Shopify customer information and uses it to request the Apparel21 customer. The Apparel21 customer is then passed into the child components. It also takes other store specific configurations.
<Loyalty.Provider
customer={account.customer}
rewardId={32843578376280}
url={'https://connector-staging.cable-integration.io'}
storefront={{
key: '41e72449d9ae377baa2363774f504fd2',
url: window.theme.permanentDomain,
}}
theme={{
typography: {
headings: {
h3: {
fontFamily: 'Chronicle, Helvetica Neue, Arial, sans-serif',
fontSize: '2rem'
},
h4: {
fontSize: '1.25rem'
}
}
},
colors: {
primary: '#000',
secondary: '#fff',
},
spacing: {
vertical: 5,
horizontal: 5
},
container: {
width: 1440,
},
image: {
borderStyle: 'solid',
borderWidth: 1,
},
button: {
borderStyle: 'solid',
borderWidth: 1,
textTransform: 'uppercase',
letterSpacing: '0.075rem',
fontSize: '1rem',
fontWeight: 600,
},
}}
>
<Loyalty.Benefits
title={"Gifts & Rewards"}
redeemable
/>
</Loyalty.Provider>
prop | description | required
-------------|------------------------------------------|-------------------
customer | An object representing Shopify customer information containing first_name
, last_name
, token
, id
. Read below about how to generate a token. | yes
rewardId | A variant id representing a reward placeholder product in Shopify. | yes
url | An optional url prefix for using a staging integration or changing the proxy url | no
storefront | An object that takes the storefront api key and the store url. | yes
theme | An object representing the styles that you would like to pass to the components. Any of the keys you provide will overwrite the default styling but you do not need to provide all of them. | no
Generating a Token
For security reasons Liquid should be used to generate a token so that the secret is not exposed to the front end. Each customer will have a unique id and each store will have a different secret by combining the 2 with hmac_sha256 we can create a secret token for fetching the Apparel21 user.
{{ customer.metafields.ap21.person_id | hmac_sha256: settings.loyalty_secret | json }}
You may want to store the secret in the theme settings, especially if the store has multiple regions.
{
"name": "Loyalty",
"settings": [
{
"type": "text",
"id": "loyalty_secret",
"label": "Loyalty secret"
}
]
},
Basic Components
There are a two base components (Account and Benefits) which are designed to work "out of the box" and reduce the amount of time it takes to get started. These components can be customised simply by using the theme
prop on the Provider
component. If you need to customise the layout of the component you can use the render
prop.
Account
Displays basic account information such as current tier, the spend to the next tier, the date they achieved the current tier. It also has an optional render
prop so that you can fully customise the structure and layout of this component.
Benefits
Displays the current rewards and benefits to the customer. When the redeemable
prop is used it will change the layout to display buttons which will allow the gifts to be redeemed. The redeemable prop can technically be used anywhere on the site but would typically be used on the cart page.
Advanced components
If you want to have complete control over the rendering of the components there are two options. Either use the Consumer
component or hooks. If you would like to use hooks read below.
Consumer
Example
<Loyalty.Provider
customer={customer}
rewardId={32843578376280}
url={'https://connector-staging.cable-integration.io'}
storefront={{
key: '41e72449d9ae377baa2363774f504fd2',
url: window.theme.permanentDomain,
}}
>
<Loyalty.Consumer>
{({ account }) => {
if (!account) {
return <React.Fragment />
}
const name = account.tier_name as 'Cotton' | 'Merino' | 'Cashmere'
const tier = tiers[name]
return (
<div className="w-1/3 p-1">
<div className="bg-grey-100">
<div className="p-4">
<p className="font-heading text-2xl tracking-widest mt-1">
<span className="uppercase">{name}</span> tier
</p>
<ul className="mb-2 list-disc uppercase text-xs">
{tier.features.split("\n").map((item) => (
<li key={item}>
{item}
</li>
))}
</ul>
<h3 className="font-heading italic">
Annual Spend<br/>
{tier.spend}
</h3>
</div>
</div>
</div>
)
}}
</Loyalty.Consumer>
</Loyalty.Provider>
The consumer is provided with the context as a render prop.
Hooks
useAccount
Get the Apparel21 account
useBenefits
Get the Available rewards
useCart
Get the Shopify cart
Shopify Script
### Begin loyalty ###
# Check if cart contains ordinary product
cartContainsOrdinaryProduct = false
Input.cart.line_items.each do |line_item|
if (
!line_item.properties["_gift_id"]
) then
cartContainsOrdinaryProduct = true
break
end
end
# Calculate subtotal for reward eligibility (inc discount codes)
rewardSubtotal = Input.cart.subtotal_price
if Input.cart.discount_code && !Input.cart.discount_code.rejected?
if Input.cart.discount_code.is_a?(CartDiscount::Percentage)
rewardSubtotal -= (rewardSubtotal * (Input.cart.discount_code.percentage/ 100))
elsif Input.cart.discount_code.is_a?(CartDiscount::FixedAmount)
rewardSubtotal -= Input.cart.discount_code.amount
end
end
# Gifts only allowed for carts with an ordinary product
appliedGiftIds = []
Input.cart.line_items.each do |line_item|
next unless line_item.properties["_gift_id"]
if !cartContainsOrdinaryProduct || appliedGiftIds.include?(line_item.properties["_gift_id"])
line_item.change_properties({ "remove" => true }, message: "Remove")
end
appliedGiftIds.push(line_item.properties["_gift_id"])
end
Input.cart.line_items.delete_if { |item| item.properties["remove"] }
rewardAmount = Money.zero()
Input.cart.line_items.each do |line_item|
product = line_item.variant.product
if product.product_type == "Loyalty Reward" then
rewardAmount += Money.new(cents: line_item.properties["reward_amount"].to_i)
end
if line_item.properties["_gift_id"]
line_item.change_line_price(line_item.line_price - line_item.variant.price, message: "Gift Redemption")
end
end
Input.cart.line_items.each do |line_item|
product = line_item.variant.product
if (product.product_type != "Loyalty Reward" && product.product_type != "Gift Card") then
if (line_item.line_price.cents < rewardAmount.cents)
rewardAmount -= line_item.line_price
line_item.change_line_price(Money.zero(), message: "Reward Redemption")
else
line_item.change_line_price(line_item.line_price - rewardAmount, message: "Reward Redemption")
rewardAmount = Money.zero()
end
end
end
### End loyalty ###
Output.cart = Input.cart