npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@frontend-sdk/recharge

v0.27.0

Published

ReCharge integration for Shogun Frontend.

Downloads

97

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.

Official website →

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 of frontend-checkout must installed on the Shogun Frontend store.

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: '…', // 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