npm package: @frontend-sdk/recharge
Description: ReCharge integration for Shogun Frontend.
Author: Shogun
Latest version: 0.27.0

# 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.

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

<br />

[Official website →](https://rechargepayments.com/)

## 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 <a href="https://www.shopify.com/partners/blog/shopify-subscription-apis" target="_blank">Shopify Subscription API</a>.

## 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`](https://www.npmjs.com/package/frontend-checkout/v/3.0.3) 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](https://user-images.githubusercontent.com/1743568/132682948-67c2eecd-7c06-42ac-953d-9a7ec9e07593.gif?raw=true)
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](https://user-images.githubusercontent.com/1743568/132683007-d09cef5c-71d3-41f1-81ee-faf4b24cca65.gif?raw=true)
4. Initialize recharge by importing the hook and and adding boilerplate code as shown below
   ![Use ReCharge](https://user-images.githubusercontent.com/1743568/132683040-e97c9626-c567-4c1f-b7d6-566b531ca6de.gif?raw=true)

```jsx
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.

```jsx
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.

```typescript jsx
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`.

```typescript jsx
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

```jsx
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

```typescript jsx
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
```