[Stripe Updates] Stripe Checkoutにて、配送方法と送料を複数から選択できるようになりました

Stripe Checkout APIと、Stripe Shipping Rates APIの２種類がアップデートされました。
これにより、Stripe Checkoutでユーザーが複数の配送方法を選択できるようになります。

# Checkoutでの配送方法選択画面

配送方法の設定を行なった場合、以下の画像のように、複数の配送方法・料金をユーザーが選べるようになります。

![スクリーンショット 0003-12-18 3.14.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/1ee2b47e-964f-4b07-597e-380dc495cb0f.png)

# 配送方法・料金の設定方法

Dashboardの[配送料金](https://dashboard.stripe.com/test/shipping-rates)から、新規追加で登録することができます。

![スクリーンショット 0003-12-18 3.15.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/fb7f3f32-f730-77ea-183d-fd4086111c59.png)

配送料金だけでなく、配送予定日（X営業日からY営業日まで）についても設定ができます。また、Stripe Taxを有効化している場合、送料に対する税金の計算も可能です。

## Node.js SDKで設定する

Stripe APIからも設定することができます。Node.jsの場合、以下のような実装です。

```js
    await stripe.shippingRates.create({
      fixed_amount: {
        currency: 'jpy',
        amount: 500
      },
      display_name: '配送料金',
      delivery_estimate: {
        minimum: {
          unit: 'business_day',
          value: 1
        },
        maximum: {
          unit: 'business_day',
          value: 5
        }
      },
    })
```

配送日時の設定方法については、ドキュメントの[「配送に関する見積もり」](https://stripe.com/docs/payments/checkout/shipping#delivery-estimates?utm_source=qiita&utm_medium=stripeUpdates)をご確認ください。

# CheckoutのURL生成時に、配送料金を設定する

事前に配送料金を登録している場合、Stripe CheckoutのURL作成時に配送方法の選択肢を指定することができます。

```js
    const session = await stripe.checkout.sessions.create({
      line_items: [{
        price: 'price_xxxxx',
        quantity: 1
      }],
      cancel_url: 'http://localhost:3000',
      success_url: 'http://localhost:3000',
      shipping_options: [{
        shipping_rate: 'shr_yyy'
      }, {
        shipping_rate: 'shr_xxx'
      }],
      mode: 'payment'
    })
```

もし登録している配送料金全てを表示したい場合は、以下のように`shippingRates.list` APIの実行結果を利用しましょう。

```js
    const {data: rates} = await stripe.shippingRates.list({
      active: true,
      currency: 'jpy'
    })
    const session = await stripe.checkout.sessions.create({
      line_items: [{
        price: 'price_xxxx',
        quantity: 1
      }],
      cancel_url: 'http://localhost:3000',
      success_url: 'http://localhost:3000',
      shipping_options: rates ? rates.map(rate => {
        return {
          shipping_rate: rate.id
        }
      }): undefined,
      mode: 'payment',
    })
  })
```

## CheckoutのURL作成時に、配送料金も登録する場合

`shipping_options.shipping_rate_data`を利用することで、CheckoutのURL(セッション)作成時に配送料金を新しく作成することもできます。

```js
    const session = await stripe.checkout.sessions.create({
      line_items: [{
        price: 'price_xxxx',
        quantity: 1
      }],
      cancel_url: 'http://localhost:3000',
      success_url: 'http://localhost:3000',
      shipping_options: [{
        shipping_rate_data: {
          display_name: '動的な配送料金設定',
          type: 'fixed_amount',
          fixed_amount: {
            amount: 500,
            currency: 'jpy'
          }
        }
      }],
      mode: 'payment',
    })
```

# Webhookで、選択された配送方法を取得する

バックエンドのシステムは、Webhookを利用してこの情報を取得します。

Stripe Checkoutで注文処理が完了した場合、`checkout.session.completed`イベントが送信されます。

```js
/**
 * コンビニ決済などの、その場で決済が完了しない支払い方法にも対応する
 **/
if (
  event.type === 'checkout.session.completed' ||
  event.type === 'checkout.session.async_payment_succeeded'
) {
  const session = event.data.object
  const shippingRateId = session.shipping_rate
  /**
   * 決済が完了している場合のみ処理する
   **/
  if (session.payment_status === 'paid') {

   /**
　　   * 未指定の場合、`session.shipping_rate`は`null`になります
 　　  **/
   if (shippingRateId) {
     const amountShipping = session.total_details.amount_shipping
      const selectedShippingRate = await stripe.shippingRates.retrieve(shippingRateId)
　
     // 外部システムに配送情報をPOSTする処理などを記述しましょう
    }
  }
}
```

コード内のコメントにもありますが、`shipping_rate`が`null`になる可能性がある点に注意しましょう。

## 購入した商品データを取得する方法

カートの中身を取得する場合は、以下のようにAPIを追加で１つ呼び出します。

```diff_javascript
/**
 * コンビニ決済などの、その場で決済が完了しない支払い方法にも対応する
 **/
if (
  event.type === 'checkout.session.completed' ||
  event.type === 'checkout.session.async_payment_succeeded'
) {
  const session = event.data.object
  const shippingRateId = session.shipping_rate
  /**
   * 決済が完了している場合のみ処理する
   **/
  if (session.payment_status === 'paid') {

+    const { items } = await stripe.checkout.sessions.listLineItems(session.id);

   /**
　　   * 未指定の場合、`session.shipping_rate`は`null`になります
 　　  **/
   if (shippingRateId) {
     const amountShipping = session.total_details.amount_shipping
      const selectedShippingRate = await stripe.shippingRates.retrieve(shippingRateId)
　
     // 外部システムに配送情報をPOSTする処理などを記述しましょう
    }
  }
}
```

https://qiita.com/hideokamoto/items/565e3fbba33885be726e

# Stripe CLIでイベントのテストを行う方法

`checkout.session.completed`イベントを発火した場合の動作確認は、Stripe CLIからも行うことができます。

```bash
% stripe trigger checkout.session.completed 
Setting up fixture for: checkout_session
Running fixture for: checkout_session
Setting up fixture for: payment_page
Running fixture for: payment_page
Setting up fixture for: payment_method
Running fixture for: payment_method
Setting up fixture for: payment_page_confirm
Running fixture for: payment_page_confirm
Trigger succeeded! Check dashboard for event details.
```

こちらでは配送方法の設定はできません。
ですがブラウザでの動作確認前に実行しておくと、より効率的にテストを行うことができます。

# 現時点（2021/12/21）での制限について

現在、Checkoutでの配送料金指定は支払いモード`mode: 'payment'`でのみ利用することができます。

```
shipping_rates` cannot be used in setup or subscription mode.
```

定期課金や請求書・Payment Linksなどでは利用できませんのでご注意ください。


# 参考URL
具体的な設定方法や、Node.js以外での実装方法などについては、ドキュメントをご確認ください。

- [Docs - Stripe Checkout クイックスタート](https://stripe.com/docs/checkout/quickstart?utm_source=qiita&utm_medium=stripeUpdates)
- [API Docs - 配送を追加](https://stripe.com/docs/payments/checkout/shipping?utm_source=qiita&utm_medium=stripeUpdates)

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

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

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

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