Stripeでサブスクリプション情報からCheckout / Payment Linksの申し込み情報を取得する方法

Stripe Checkout(ローコード)やPayment Links(ノーコード)でサブスクリプション申し込みを受け付けると、住所や名前などの情報はCustomerに保存されます。

![スクリーンショット 2023-08-22 11.49.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/e8418ad4-0bf6-490a-4673-e1fdc7368f0a.png)

もしCustomerやSubscriptionに表示されない情報（カスタムフィールドなど）を取得・表示したい場合は、Stripe APIを利用しましょう。

## SubscriptionのIDから、Checkout / Payment Linksの決済セッション情報を取得する方法

Checkout / Payment Linksの決済セッションは、`stripe.checkout.sessions.list`　APIから取得できます。

https://stripe.com/docs/api/checkout/sessions/list

`subscription`引数に、該当するsubscriptionのidを渡しましょう。

```js
    const { data: sessions } = await stripe.checkout.sessions.list({
      subscription: subscriptionId
    })
    if (!sessions || sessions.length < 1) return;
    console.log(sessions[0])

    // カスタムフィールド情報を取得する
    if (sessions[0].custom_fields) {
      console.log(sessions[0].custom_fields)
    }
```

どのようなデータが取得できるかは、APIドキュメントをご確認ください。

https://stripe.com/docs/api/checkout/sessions/object

## Stripe Appsで、ダッシュボード上にカスタムフィールドの値を表示する

バックオフィス担当者や顧客サポートチームが情報を見れるようにするには、Stripe Appsでプライベートアプリを使います。

ドキュメントのガイドに従って、アプリをセットアップしましょう。

https://stripe.com/docs/stripe-apps/create-app

その後、Checkout / Payment Linksのセッション情報を取得する処理を実装します。

### Stripe CLIでAPIアクセスに必要な権限を設定する

まずはアプリからStripe APIを呼び出す権限を設定します。

```bash
$ stripe apps grant permission checkout_session_read "retrieve checkout session data"
```

`stripe-app.json`の`permissions`に追加されれば、OKです。

```diff_json
{
  "id": "com.example.checkout-custom-field",
  "version": "0.0.1",
  "name": "Checkout Custom Field",
  "icon": "",
  "permissions": [
+    {
+      "permission": "checkout_session_read",
+      "purpose": "Load the checkout session to get the custom field data"
+    }
  ],
```

更新されたアプリの設定を読み込むため、`stripe apps start`実行中画面に表示されるURLにアクセスして、承認処理を行いましょう。

```bash
To reflect recent changes from stripe-app.json, click "Continue" on https://dashboard.stripe.com/apps/enable-preview?cliaccount=acct_1xxxxxx
```

### Stripe CLIで、アプリのViewを追加する

サブスクリプション詳細ページで情報を確認するため、Viewを追加しましょう。

```bash
% stripe apps add view --viewport stripe.dashboard.subscription.detail
```

`src/views`に`SubscriptionDetailView.tsx`が生成されればOKです。

更新されたアプリの設定を読み込むため、`stripe apps start`実行中画面に表示されるURLにアクセスして、承認処理を行いましょう。

```bash
To reflect recent changes from stripe-app.json, click "Continue" on https://dashboard.stripe.com/apps/enable-preview?cliaccount=acct_1xxxxxx
```

### Stripe Appsで、Checkoutのセッション情報を取得する

設定が終われば、あとはReactのSPA(Single Page Application)を作るだけです。

#### Stripe SDKをセットアップする

Stripe SDKに設定するAPIキーなどは、UI Extension SDKからimportします。

```tsx
import { createHttpClient, STRIPE_API_KEY } from '@stripe/ui-extension-sdk/http_client'
import Stripe from 'stripe'

const stripe = new Stripe(STRIPE_API_KEY, {
  httpClient: createHttpClient(),
  apiVersion: '2023-08-16',
})
```

#### Propsから、サブスクリプションのIDを取得する

ページごとのコンポーネントには、`ExtensionContextValue`が渡されます。

この中に、「どのViewにいるか」や「そのページのリソースID（`sub_xxx`や`cus_xxx`など）」などが含まれていますので、取得しましょう。

```tsx
const SubscriptionDetailView = ({environment}: ExtensionContextValue) => {
  const { objectContext } = environment
  const subscriptionId = useMemo(() => {
    if (!objectContext) return null
    if (objectContext.object !== 'subscription') return null
    return objectContext.id
  }, [objectContext])

```

`if (objectContext.object !== 'subscription') return null`で、念の為サブスクリプション以外のリソースIDを取得しないようにしています、

#### `useEffect`でStripe APIを呼び出す

Stripe APIを呼び出すための値が手に入ったので、Stripe APIを呼び出します。

```tsx
  useEffect(() => {
    if (!subscriptionId) return
    stripe.checkout.sessions.list({
      subscription: subscriptionId
    }).then(({data}) => {
      if (!data) return;
      const session = data[0]
      console.log(session)
    })

  }, [subscriptionId])
```

#### `useState`で取得した情報をStateにいれる

取得した情報をUIで処理するために、`useState`を追加します。

```diff_tsx
+  const [customFields, setCustomFields] = useState<Array<Stripe.Checkout.Session.CustomField> | null>(null)
  useEffect(() => {
    if (!subscriptionId) return
    stripe.checkout.sessions.list({
      subscription: subscriptionId
    }).then(({data}) => {
      if (!data) return;
      const session = data[0]
+      if (session.custom_fields) {
+        setCustomFields(session.custom_fields)
+      }
    })

  }, [subscriptionId])
```

#### [全コード]　取得したカスタムフィールドを表示する

最後に取得した情報をUIに出力しましょう。

```tsx
import { Box, ContextView, List, ListItem } from "@stripe/ui-extension-sdk/ui"
import type { ExtensionContextValue } from "@stripe/ui-extension-sdk/context"
import { useState, useMemo, useEffect } from 'react'
import { createHttpClient, STRIPE_API_KEY } from '@stripe/ui-extension-sdk/http_client'
import { clipboardWriteText, showToast } from '@stripe/ui-extension-sdk/utils'

import Stripe from 'stripe'
const stripe = new Stripe(STRIPE_API_KEY, {
  httpClient: createHttpClient(),
  apiVersion: '2023-08-16',
})

function getFieldValue(field?: Stripe.Checkout.Session.CustomField): string | null  {
  if (!field) return null
  switch (field.type) {
    case 'dropdown':
      return field.dropdown?.value || null
    case 'text':
      return field.text?.value || null
    case 'numeric':
      return field.numeric?.value || null
  }
}

const SubscriptionDetailView = ({environment}: ExtensionContextValue) => {
  const [customFields, setCustomFields] = useState<Array<Stripe.Checkout.Session.CustomField> | null>(null)
  const { objectContext } = environment
  const subscriptionId = useMemo(() => {
    if (!objectContext) return null
    if (objectContext.object !== 'subscription') return null
    return objectContext.id
  }, [objectContext])
  useEffect(() => {
    if (!subscriptionId) return
    stripe.checkout.sessions.list({
      subscription: subscriptionId
    }).then(({data}) => {
      if (!data) return;
      const session = data[0]
      if (session.custom_fields) {
        setCustomFields(session.custom_fields)
      }
    })

  }, [subscriptionId])
  return (
    <ContextView
      title="Custom Field View"
    >
      {customFields ? (
        <List
          onAction={async id => {
            const selectedField = customFields.find(field => field.key === id)
            const fieldValue = getFieldValue(selectedField);
            if(fieldValue) {
              await clipboardWriteText(fieldValue)
              await showToast("Copied", { type: "success" })
            }
          }}
        >
          {customFields.map(field => {
            const fieldValue = getFieldValue(field);
            return (
              <ListItem
                key={field.key}
                id={field.key}
                title={<Box>{field.label.custom}</Box>}
                value={fieldValue}
              />
            )
          })}
        </List>
      ): (
        <Box>
          This subscription was not created from Checkout session.
        </Box>
      )} 
    </ContextView>
  );
};

export default SubscriptionDetailView;

```

## アプリをテスト環境で起動する

実装が終われば、`stripe apps start`でアプリを起動しましょう。

「Checkout / Payment Linksで、カスタムフィールドに情報を入力したサブスクリプション」ページに移動してみます。

すると下の画像のように、カスタムフィールドの内容が表示されます。

![スクリーンショット 2023-08-22 13.48.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/35bb2068-b48d-a2e3-7b6e-bffb184b9048.png)

## アプリをStripeアカウント内で利用・共有できるようにする方法

特定のStripeアカウントでのみアプリを利用できるようにするには、「プライベートアプリ」として公開します。

公開方法は、次の記事をご覧ください。


https://qiita.com/hideokamoto/items/14bc44a0c5c7204893c6

## Stripeで、決済や請求・サブスクリプションの「バックオフィス業務」も効率化・自動化しよう

このようにStripeでは、決済やサブスクリプションの組み込みだけでなく、バックオフィス業務の効率化やカスタマイズも可能です。

また、レポーティングや分析・データウェアハウスまたは外部システムへの連携などもシームレスに行えるため、「決済・注文・サブスクリプション申し込みの受け付けを**終えた後**」についても効率化や改善・自動化できます。

![スクリーンショット 2023-08-22 13.51.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/859fcf9f-fd2e-d2bc-bcb7-f86a30042686.png)
https://stripe.com/jp/use-cases/finance-automation

「バックオフィス業務の自動化・効率化」に興味がある方は、以下のドキュメントから詳細を確認してみましょう。

https://stripe.com/jp/use-cases/finance-automation

https://stripe.com/docs/finance-automation

https://marketplace.stripe.com/