"use-shopping-cart"を使って、StripeとReactで簡単にECカート機能 + 決済機能を実装しよう


Stripeを利用することで、決済機能を簡単に実装することができます。
しかし通販機能を実装するためには、決済だけではなく買い物かご（カート）機能も実装する必要があります。

今回は、「use-shopping-cart」を利用して、Reactアプリで簡単にカート機能を実装する方法を紹介します。

# use-shopping-cartとは

![スクリーンショット 0004-01-13 10.31.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/c7374dc5-6046-9d16-4027-17a0449d8e49.png)


use-shopping-cartは、Reactでショッピングカート機能を実装するために必要な機能を備えたHookを提供するライブラリです。

https://useshoppingcart.com/

内部的にReduxを利用しており、「カート機能」と「Stripe Checkoutへの遷移」の２機能が利用できます。

## use-shopping-cartでできること（一部）

use-shopping-cartを利用することで、一般的なショッピングカートを実装するために必要なAPIを簡単に手に入れることができます。

- 商品をカートに追加する
- カートに追加した商品データや合計金額データを取得する
- 商品をカートから削除する
- カート内の商品の個数を変更する
- etc...

# use-shopping-cartを使ってカート機能を実装しよう
## サンプルアプリのセットアップ

Create React AppやViteを利用してアプリをセットアップしましょう。

```bash
// CRA
% npx create-react-app my-app

// Vite
% npm init vite
```

## ライブラリをインストールしよう

useShoppingCartライブラリをインストールします。
この際Stripe.jsも一緒にインストールします。
ただしReact　SDKは不要ですのでご注意ください。

```bash
% npm install @stripe/stripe-js use-shopping-cart
```

## CartProviderを設定しよう

use-shopping-cartは内部的にReduxを利用しています。
そのため、アプリ全体を`CardProvider`の子要素にする必要があります。


```jsx
import ReactDOM from 'react-dom'
import { CartProvider } from 'use-shopping-cart'

import App from './App'

ReactDOM.render(
  <CartProvider
    mode="payment"
    cartMode="client-only"
    stripe={YOUR_STRIPE_API_KEY_GOES_HERE}
    successUrl="http://localhost:3000"
    cancelUrl="http://localhost:3000"
    currency="JPY"
    allowedCountries={['US', 'GB', 'JP']}
    billingAddressCollection={true}
  >
    <App />
  </CartProvider>,
  document.getElementById('root')
)
```

`loadStripe`や`redirectToCheckout`などで設定するパラメータをここで設定します。

Providerで設定することで、`redirectToCheckout`を呼び出す際にリダイレクトURLなどの情報を省略することができます。

### cartModeでStripe Checkoutの起動方法が変更可能です

`cartMode`には、`checkout-session`と`client-only`の２種類が設定できます。

`client-only`を利用した場合は、`checkout.sessions.create`APIを利用せずに、クライアント側だけでStripe Checkoutへのリダイレクトを実施します。

APIでCheckoutのセッションを作成したい場合は、`checkout-session`を利用しましょう。


## 商品・料金データを設定しよう

商品データを作成します。簡単な方法はJSONまたはオブジェクトで設定することです。
`price_id`と`price`の２つが欠けていると、うまく動作しませんので注意しましょう。

```js
const productData = [
  {
    name: 'Bananas',
    price_id: 'price_xxxx',
    price: 400
  },
  {
    name: 'Tangerines',
    price_id: 'price_xxxx',
    price: 100
  }
]
```

続いて設定した商品データをReactで表示させましょう。

```jsx
import { formatCurrencyString } from 'use-shopping-cart'

...
<ul>
  {productData.map(product => (
    <li key={product.price_id}>
      {product.name} ({formatCurrencyString({ value: product.price, currency: 'JPY' })}) <br />
      <button>
        Add 1 to Cart 
      </button>
    </li>
  ))}
</ul>
```

`formatCurrencyString`を利用することで、金額を指定した通貨に合わせた表示に変換することができます。

![スクリーンショット 0004-01-12 18.19.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/b517ecc3-86c1-1b1e-a47a-9463b147f104.png)


## カート機能を実装する

いよいよカート機能の実装に入ります。
カートに関するメソッドなどは、`useShoppingCart`フックを利用して取得します。

```js
import { useShoppingCart } from 'use-shopping-cart'
...
const { totalPrice, cartCount, addItem, cartDetails, ...args } = useShoppingCart()
```

### カートに商品を追加する機能を実装しよう

まずは表示している商品をカートに追加するボタンを実装しましょう。

```jsx
import { formatCurrencyString, useShoppingCart } from 'use-shopping-cart'

const Shop = () => {

  const {
    addItem
  } = useShoppingCart()

  return (
   <div>
    <ul>
    {productData.map(product => (
      <li key={product.price_id}>
        {product.name} ({formatCurrencyString({ value: product.price, currency: 'JPY' })}) <br />
        <button onClick={() => addItem(product)}>
          Add 1 to Cart 
        </button>
      </li>
    ))}
   </ul>
  </div>
 )
}
```

`button`の`onClick`イベントに`addItem`を設定しました。
これで選択した商品をカートに追加完了です。

### カートの中身を確認しよう

追加に成功しましたが、実際にカートの中に何が登録されているかがわかりません。
そこでカートの中身についても表示させてみましょう。

```diff_jsx

const Shop = () => {

  const {
    addItem,
+   cartDetails,
  } = useShoppingCart()

  return (
   <div>
...
      <section>
        <h1>Cart</h1>
        <ul>
          {Object.entries(cartDetails).map(([priceId, cartItem]) => (
            <li key={`cart-${priceId}`}>
              {cartItem.name} ({cartItem.formattedPrice} * {cartItem.quantity} = {cartItem.formattedValue})<br/>
              <button>Increase</button>
              <button>Decrease</button>
              <button>Cancel</button>
            </li>
          ))}
        </ul>
      </section>
  </div>
 )
}
```

`cartDetails`を使用することで、カートに追加された商品データを取得・表示することができます。

![スクリーンショット 0004-01-12 18.35.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/e5a317c5-c106-a35d-52e9-ebbe9417a314.png)


##＃ カートの商品数を変更できるようにしよう

カートに追加した商品の数を増減させたり、削除することもできます。
以下のサンプルを参考に、コードを更新しましょう。

```diff_jsx

const Shop = () => {

  const {
    addItem,
   　cartDetails,
+　　　　　incrementItem, decrementItem, removeItem,
  } = useShoppingCart()

  return (
   <div>
...
      <section>
        <h1>Cart</h1>
        <ul>
          {Object.entries(cartDetails).map(([priceId, cartItem]) => (
            <li key={`cart-${priceId}`}>
              {cartItem.name} ({cartItem.formattedPrice} * {cartItem.quantity} = {cartItem.formattedValue})<br/>
+              <button onClick={() => incrementItem(priceId)}>１つ増やす</button>
+              <button onClick={() => decrementItem(priceId)}>１つ減らす</button>
+              <button onClick={() => removeItem(priceId)}>削除</button>
            </li>
          ))}
        </ul>
      </section>
  </div>
 )
}
```

これだけでカートの中身を増減や削除できるようになりました。

## Stripe Checkoutにリダイレクトさせて、注文できるようにしよう

最後にカートに登録した商品を注文できるようにしましょう。
これも`useShoppingCart`で取得できるメソッドを利用するだけです。


```diff_jsx

const Shop = () => {

  const {
    addItem,
   　cartDetails,
  　　　　incrementItem, decrementItem, removeItem,
+   redirectToCheckout,
  } = useShoppingCart()

  return (
   <div>
...
+      <button onClick={() => redirectToCheckout()}>
+        注文する
+      </button>
  </div>
 )
}
```

追加した「注文する」ボタンをクリックすると、カートに追加した商品・料金データと個数が反映されたCheckoutページが表示されます。

![スクリーンショット 0004-01-12 18.41.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/0d077b6e-4661-ff2e-ed25-6b724ba321cf.png)

# Appendix: カート情報はどこに保存されている？

実装を見る限り、カートの情報はlocalStorageに保存されています。

![スクリーンショット 0004-01-12 18.42.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/d689c414-4af6-1ef7-3520-8bba4c816097.png)

そのため、途中でユーザーがサイトから離脱した場合でも、同じブラウザであればカートの内容を維持することができます。
 
# 終わりに

use-shopping-cartの基本的な機能について紹介しました。
APIでCheckoutセッションを作成した場合など、ここでは紹介できていないユースケースもまだまだありますので、また改めて紹介したいと思います。


# [PR] Stripe開発者向け情報をQiitaにて配信中！
2021年12月よりQiitaにて、Stripe開発者のためのブログ記事更新を開始しました。

- [Stripe Updates]：開発者向けStripeアップデート紹介・解説
- ユースケース別のStripe製品や実装サンプルの紹介
- Stripeと外部サービス・OSSとの連携方法やTipsの紹介
- 初心者向けのチュートリアル(予定)

など、Stripeを利用してオンラインビジネスを始める方法について随時更新してまいります。

-> [Stripe Organizationsをフォローして最新情報をQiitaで受け取る](https://qiita.com/organizations/stripe)