@frontend-sdk/recharge
    TypeScript icon, indicating that this package has built-in type declarations

    0.22.0 • Public • Published

    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.

    ⚠️ This package runs on Shogun Frontend and is in customer Beta. It might not currently support all ecommerce platforms or cover all use cases. It also may require additional setup by a Shogun developer to work properly!

    Official website →

    Overview

    Users of Shogun Frontend with Shopify stores can leverage this package to add subscription and/or selling plans to their products

    Installation

    yarn add @frontend-sdk/recharge

    npm install @frontend-sdk/recharge

    Usage

    1. Create a new Section, for this example we will name the new section ReChargeProduct Create New Section
    2. In the IDE, add a new variable to your section. This variable needs to be a reference type
    3. Name the variable product and select the products CMS Group from the reference field dropdown Create Reference Variable
    4. Initialize recharge by importing the hook and and adding boilerplate code as shown below Use ReCharge
    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: String(process.env.PLATFORM_PUBLIC_DOMAIN),
        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: String(process.env.PLATFORM_PUBLIC_DOMAIN),
        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

    Development

    Setting up .env

    Create a new file /.env in the repo root with the following content:

    PLATFORM_PUBLIC_DOMAIN= // Insert store domain here. E.g. myfancywidgets.myshopify.com
    

    Keywords

    none

    Install

    npm i @frontend-sdk/recharge

    DownloadsWeekly Downloads

    114

    Version

    0.22.0

    License

    MIT

    Unpacked Size

    45.6 kB

    Total Files

    23

    Last publish

    Collaborators

    • shogun-engineering