@frontend-sdk/recharge
v0.27.0
Published
ReCharge integration for Shogun Frontend.
Downloads
97
Keywords
Readme
ReCharge
ReCharge integration for Shogun Frontend.
ReCharge is a subscription payments platform designed for merchants to set up and manage dynamic recurring billing across web and mobile.
Overview
Users of Shogun Frontend with Shopify stores can leverage this package to add subscription and/or selling plans to their products.
📘
@frontend-sdk/recharge
supports Shopify Subscription API.
Installation
yarn add @frontend-sdk/recharge
npm install @frontend-sdk/recharge
Requirements
- The Shogun Frontend store must use Shopify's GraphQL API (not Shopify's REST API).
- Version
3.0.3
or higher offrontend-checkout
must installed on the Shogun Frontend store.
Usage
- Create a new Section, for this example we will name the new section
ReChargeProduct
- In the IDE, add a new variable to your section. This variable needs to be a reference type
- Name the variable
product
and select theproducts
CMS Group from the reference field dropdown - Initialize recharge by importing the hook and and adding boilerplate code as shown below
import { useReCharge } from '@frontend-sdk/recharge'
export const ReChargeProduct = (props) => {
/**
* The product data is added to your section in Shogun Frontend's IDE
* by creating a reference variable to the Products CMS Group
* */
const { product } = props
const { variants } = product
const firstOrDefaultVariant = React.useMemo(() => {
if (!variants.length) {
return null
}
return variants.find((variant) => variant.position === 1) || product.variants[0]
}, [variants])
const recharge = useReCharge({
product,
storeUrl: '…', // the store’s domain in Shopify
currentVariantId: firstOrDefaultVariant?.externalId,
})
return <div>...</div>
}
useReCharge
returns the following properties
- isRechargeable
- getCartItem
- shippingIntervalFrequency
- shippingIntervalUnitType
Now let's take a look at each of these properties
and how they might be applied in code
isRechargeable
A boolean
value that will return true
when a product has subscription options.
import React from 'react'
import { useReCharge } from '@frontend-sdk/recharge'
export const ReChargeProduct = (props) => {
const { product } = props
const { variants } = product
const firstOrDefaultVariant = React.useMemo(() => {...}, [variants])
const recharge = useReCharge({...})
// Here we check if the current product has subscriptions
if (recharge.isRechargeable) {
return <div>Subscription product, show the subscription options</div>
}
return <div>Regular product, no subscription</div>
}
shippingIntervalFrequency
An array
of numbers for the interval at which delivery can be arranged for.
import React from 'react'
import { useReCharge } from '@frontend-sdk/recharge'
export const ReChargeProduct = (props) => {
const { product } = props
const { variants } = product
const [currentShippingInterval, setCurrentShippingInterval] = React.useState()
const firstOrDefaultVariant = React.useMemo(() => {...}, [variants])
const recharge = useReCharge({...})
const handleShippingIntervalChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
setShippingInterval(Number(event.target.value))
}
return (
<select
value={String(currentShippingInterval)}
onChange={handleShippingIntervalChange}
onBlur={handleShippingIntervalChange}>
{shippingIntervalFrequency?.map((frequency, idx) => (
<option value={`${frequency}`} key={`${frequency}-${idx}`}>
Deliver every {frequency} {shippingIntervalUnitType}
</option>
))}
</select>
)
}
shippingIntervalUnitType
A string
that contains the unit type that describes the shippingIntervalFrequency
. Eg. Days
, Weeks
, Months
.
import React from 'react'
import { useReCharge } from '@frontend-sdk/recharge'
export const ReChargeProduct = (props) => {
const { product } = props
const { variants } = product
const [currentShippingInterval, setCurrentShippingInterval] = React.useState()
const firstOrDefaultVariant = React.useMemo(() => {...}, [variants])
const recharge = useReCharge({...})
const handleShippingIntervalChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
setShippingInterval(Number(event.target.value))
}
return (
<select
value={String(currentShippingInterval)}
onChange={handleShippingIntervalChange}
onBlur={handleShippingIntervalChange}>
{shippingIntervalFrequency?.map((frequency, idx) => (
<option value={`${frequency}`} key={`${frequency}-${idx}`}>
Deliver every {frequency} {shippingIntervalUnitType}
</option>
))}
</select>
)
}
getCartItem
A function
that will return
an object that can be passed to frontend-checkout
's addItems
action
import React from 'react'
import { useReCharge } from '@frontend-sdk/recharge'
import { useCartActions } from 'frontend-checkout'
export const ReChargeProduct = (props) => {
const { product } = props
const { variants } = product
// Set current variant and shipping interval
const [currentVariant, setCurrentVariant] = React.useState({ id: 0, quantity: 1 })
const [currentShippingInterval, setCurrentShippingInterval] = React.useState(2)
const firstOrDefaultVariant = React.useMemo(() => {...}, [variants])
const recharge = useReCharge({...})
const { addItems } = useCartActions()
const handleAddToCart = React.useCallback(async () => {
const defaultVariantCartItem = {
id: currentVariant.externalId,
quantity: 1
}
const cartItem = recharge.getCartItem(defaultVariantCartItem, currentShippingInterval)
await addItems(cartItem)
}, [currentVariant])
return <button type="button" onClick={handleAddToCart}>Add To Cart</button>
}
Example
import React, { useState, useCallback, useMemo, useEffect } from 'react'
import { useReCharge } from '@frontend-sdk/recharge'
import { useCartActions, useCartState } from 'frontend-checkout'
function getFirstOrDefaultVariant(product) {
if (!product.variants.length) return null
return product.variants.find((variant) => variant.position === 1) || product.variants[0]
}
const Button = (props) => {
const { onClick, children, status } = props
const isLoading = status === 'loading'
return (
<button disabled={isLoading} type="button" onClick={onClick}>
{isLoading ? 'loading...' : children}
</button>
)
}
const ReCharge = ({ product }: ReChargeProps) => {
const { addItems } = useCartActions()
const cart = useCartState()
const [requestStatus, setRequestStatus] = useState<Status>('ready')
const [defaultCartItem, setDefaultCartItem] = useState<CartItem>({ id: 0, quantity: 1 })
const [productType, setProductType] = useState<RadioValues>('subscription')
const [shippingInterval, setShippingInterval] = useState<number>()
const [currentVariantId, setCurrentVariantId] = useState<number>(0)
const recharge = useReCharge({
product,
storeUrl: '…', // the store’s domain in Shopify
currentVariantId,
})
const { getCartItem, shippingIntervalFrequency, shippingIntervalUnitType, isRechargeable } = recharge
/**
* Default shipping interval
*
* We set a default for shippingIntervalFrequency selector
*/
useEffect(() => {
if (shippingIntervalFrequency && shippingIntervalFrequency.length > 0) {
setShippingInterval(shippingIntervalFrequency[0])
} else {
setShippingInterval(undefined)
}
}, [shippingIntervalFrequency])
/**
* Currently selected variant.
*
* We keep track of the currently selected variant. Products could have many variants.
* The selected variant determines which data is displayed
*/
const selectedVariant = useMemo(() => {
if (!product) return
const currentVariant = getFirstOrDefaultVariant(product)
if (currentVariant) {
setDefaultCartItem({ id: currentVariant.externalId, quantity: 1 })
setCurrentVariantId(currentVariant.externalId)
}
return currentVariant
}, [product])
const cartItem = useMemo(() => {
if (!selectedVariant) return defaultCartItem
if (productType === 'onetime') return defaultCartItem
return getCartItem(defaultCartItem, Number(shippingInterval))
}, [getCartItem, shippingInterval, selectedVariant, defaultCartItem, productType])
/**
* Based on selected variant and subscription option,
* we build the cartItem that is passed to addItems when adding an item to cart.
*/
const handleAddToCart = useCallback(async () => {
setRequestStatus('loading')
try {
if (productType === 'onetime') {
await addItems(defaultCartItem)
}
if (productType === 'subscription') {
await addItems(cartItem)
}
} catch (error) {
throw new Error(error)
} finally {
setRequestStatus('ready')
}
}, [cartItem, addItems, productType, defaultCartItem])
const handleRadioChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value === 'onetime' ? 'onetime' : 'subscription'
setProductType(value)
}
const handleShippingIntervalChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
setShippingInterval(Number(event.target.value))
}
return (
<div className={css.recharge}>
<div className={css.rechargeDemoContainer}>
<div className={css.rechargeForm}>
<h2>Subscription Widget</h2>
<h3>{product?.name}</h3>
{isRechargeable && (
<>
<ReChargeRadioButton label="Onetime" value="onetime" checked={productType} onChange={handleRadioChange} />
<ReChargeRadioButton
label="Subscription"
value="subscription"
checked={productType}
onChange={handleRadioChange}
disabled={!isRechargeable}
/>
{productType === 'subscription' && (
<select
value={String(shippingInterval)}
onChange={handleShippingIntervalChange}
onBlur={handleShippingIntervalChange}>
{shippingIntervalFrequency?.map((frequency, idx) => (
<option value={`${frequency}`} key={`${frequency}-${idx}`}>
{frequency} {shippingIntervalUnitType}
</option>
))}
</select>
)}
</>
)}
<Button onClick={handleAddToCart} status={requestStatus}>
Add to Cart
</Button>
</div>
</div>
</div>
)
}
ReCharge.displayName = 'ReCharge Form and Data'
export default ReCharge