Stripe Apps入門 - Stripe Dashboardに独自のカスタムアプリウィジェットを追加しよう


Stripe Sessions 2022で発表されたStripe Apps、その開発方法を簡単に紹介します。

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

## Stripe Appsとは？

Stripe Appsは、「**Stripe管理画面に、独自または3rd partyのアプリケーションを追加するシステム**」です。

Dashboard上にウィジェットを追加し、独自のワークフローを実行したり、外部サービスとのデータ連携を簡単に行うことができます。

![スクリーンショット 2022-05-24 15.18.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/0f84d6f9-b888-17f1-fcb1-d27997fbdabd.png)

## Stripe Appsのユースケース

### ワークフローの自動化・効率化

請求業務や顧客サポートで、Dashboardの複数ページを移動するタスクがある場合に、Appsを活用できます。

Appsでアプリを開発することで、1つのページ・１つのクリックで複雑なワークフローを完了させることが可能です。

グラフやフォーム機能も用意されていますので、よく確認するデータの簡易チェックウィジェットや、顧客ごとのTODOリストなども作成できます。

### 外部サービスとの連携

Appsから外部のAPIを呼び出すことも可能です。
また、APIキーの保存やOAuthでの連携にも対応していますので、オンラインビジネスに欠かせない様々なSaaSとStripeを連携できます。

#### 連携の例:

- HubSpotと連携し、注文情報の連携や取引履歴の確認を行う
- 未払いや請求書の送付をZendesk経由で行う
- SendGridやLINEで顧客にマーケティングメールなどを送付する
- 要対応の顧客・支払いについて、Backlogでチケットを立てる
- microCMSやAlgoliaに登録した商品・料金データを同期させる
- etc...

## Stripe Appsを開発する

Stripe Appsは、React / TypeScriptとStripe CLIを利用して開発します。

### Stripe CLIでセットアップ

アプリのセットアップはStripe CLIを利用します。

まず、`stripe login`で設定したいStripeアカウントとCLIを連携させましょう。

```bash
% stripe login
```

もしCLIのバージョンが`1.8.11`よりも古い場合は、[CLIの更新](https://stripe.com/docs/stripe-cli/upgrade)が必要です。

```bash
% stripe version
# 1.8.11 or later
```

ログインと更新が終われば、Stripe Appsを利用するためのプラグインを追加しましょう。

```bash
% stripe plugin install apps
```

最後に、`stripe apps create [アプリ名]`でアプリをセットアップします。

```bash
% stripe apps create helloworld
```

「アプリのID」と「表示名」の入力が求められます。
ここでは「App ID」は`[Enterキー]`をクリックしてデフォルト名を利用しましょう。

`Display name`には`Hello World`を入れましょう。

```bash
🖊️ Enter details of your new Stripe app (these can be changed later)
✔ App ID: █om.example.nonpertinent-unkindredly

✗ Display name: █
```

入力が終わると、npm/yarnでのライブラリインストールが始まります。
以下のメッセージが表示されればセットアップ完了です。

```bash
🙌 Created  Hello World  at  /Users/stripe/examples/helloworld
Inside that directory, you can run several commands:

$ stripe apps start
Start a development server locally for viewing your app in the Stripe dashboard

$ stripe apps add view
Add a new view to your app

$ stripe apps upload
Upload your app to be submitted for review

To get started, try starting the development server from the app directory:

$ cd helloworld
$ stripe apps start
```

#### アプリを起動させてみる

入力したアプリ名でディレクトリができますので、その中で`stripe apps start`を実行しましょう。

```bash
% cd helloworld
% stripe apps start
```

以下のように、「`[Enterキー]`を押してダッシュボードに移動しよう」とメッセージが表示されます。

```bash
[16:13:01] compiled successfully
💻 Running development server on http://localhost:4242 (^C to quit)
✨ You can now preview Hello World in your Stripe account.

🌐 Press Enter to open the Stripe dashboard
```

Stripe　Dashboardが開き、有効化の確認画面が表示されます。

![スクリーンショット 2022-05-24 16.15.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/369bb88b-4af7-a4a4-8864-68c0cb0fe82f.png)

`[続行]`をクリックすると、Dashboardのトップページに移動し、画面右側にウィジェットが追加されます。

![スクリーンショット 2022-05-24 16.17.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/b8d410de-7b64-c951-0192-1559340c8474.png)

デフォルトでは顧客詳細ページに画面が用意されています。
顧客を作成し、詳細ページに移動すると、画面が切り替わります。

![スクリーンショット 2022-05-24 16.19.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/92790933-e203-a3c0-fdfa-4f9e6e573cb3.png)

### React / TypeScriptでUIをカスタマイズ

アプリの開発は、ReactとTypeScriptを利用します。

`src/views/Apps.tsx`を開くと、顧客詳細ページで表示しているコードが確認できます。

```tsx
import {
  Box,
  ContextView,
  Link,
} from '@stripe/ui-extension-sdk/ui';
import type { ExtensionContextValue } from '@stripe/ui-extension-sdk/context';

const App = ({userContext, environment}: ExtensionContextValue) => {
  return (
	<ContextView title="Create your first Stripe app">
      <Box
        css={{
          padding: 'large',
          backgroundColor: 'container',
          fontFamily: 'monospace',
          borderRadius: 'small',
        }}
      >
        <Box css={{paddingY: 'xxlarge', marginY: 'xxlarge'}} />
        <Box css={{paddingTop: 'large', marginBottom: 'large'}}>
          Edit src/views/App.tsx and save to reload this view
        </Box>
        <Box css={{paddingBottom: 'xlarge'}}>
          <Link
            href="https://stripe.com/docs/stripe-apps/ui-toolkit/components"
            target="stripe-ui-docs"
          >
            Go to UI documentation
          </Link>
        </Box>
        <Box css={{paddingY: 'xxlarge', marginY: 'xxlarge'}} />
      </Box>
	</ContextView>
  );
};

export default App;

```

`src/views/App.tsx`を以下のコードに変更してみましょう。

```tsx
import {
  Box,
  ContextView,
  Spinner,
} from '@stripe/ui-extension-sdk/ui';
import type { ExtensionContextValue } from '@stripe/ui-extension-sdk/context';

const App = ({userContext, environment}: ExtensionContextValue) => {
  return (
    <ContextView title="Create your first Stripe app">
      <Box
        css={{
          alignX: 'center',
          layout: 'column',
        }}
      >
        <Spinner />
      </Box>
    </ContextView>
  );
};

export default App;
```

保存するとアプリの表示が以下のように変わります。

![スクリーンショット 2022-05-24 16.35.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/bb15c704-c565-0530-c860-5bf6f82836b1.png)

このように、Stripe Appsでは、`@stripe/ui-extension-sdk/ui`に含まれるコンポーネントを利用して、UIを実装します。

https://stripe.com/docs/stripe-apps/components

#### 注意: `p`や`div`などのHTMLタグは利用できません

Reactアプリのように開発が可能ですが、ReactのようにHTMLタグが利用できるわけではないことに注意です。

以下のように、「`@stripe/ui-extension-sdk/ui`にないコンポーネント」を利用すると、エラーが発生します。

```tsx

    <ContextView title="Create your first Stripe app">
      <div>
        <h1>Hello</h1>
      </div>
    </ContextView>
```

![スクリーンショット 2022-05-24 16.39.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/ebb9488b-7cd1-7f7e-0a5e-6e21a3b708a1.png)

かならず[UIコンポーネント集](https://stripe.com/docs/stripe-apps/components)に含まれるコンポーネントを使うようにしましょう。


### Stripe APIを呼び出してみる

Appsで作成したアプリは、Stripe APIを利用することができます。

### CLIでAPIの呼び出し権限を設定する

APIを呼び出すには、事前に権限の設定が必要です。

`stripe apps grant [権限名] [利用する理由]`のようにCLIを実行しましょう。

```bash
% stripe apps grant permission customer_read "Get customer data"
✔ Granted permission customer_read
```

-> [設定可能な権限の一覧](https://stripe.com/docs/stripe-apps/reference/permissions#event)

### 変更した権限の許可

権限を変更した場合、アプリのプレビューを再実行する必要があります。

[https://dashboard.stripe.com/apps/enable-preview](https://dashboard.stripe.com/apps/enable-preview)にアクセスして、再度プレビューを有効化しましょう。

### Stripe SDKのセットアップ

Stripe APIを呼び出すためのSDKやAPIキーは、`@stripe/ui-extension-sdk`から取得できます。
`src/views/App.tsx`に以下のコードを追加しましょう。

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

export const stripeClient = new Stripe(STRIPE_API_KEY, {
  httpClient: createHttpClient() as Stripe.HttpClient,
  apiVersion: '2020-08-27',
});
```

### 顧客情報を取得・表示する

あとはReactでREST APIを呼び出す時のように実装するだけです。

`src/views/App.tsx`を以下のように変更しましょう。

```tsx
import {
  Box,
  ContextView,
  Table,
  TableBody,
  TableCell,
  TableRow,
} from '@stripe/ui-extension-sdk/ui';
import type { ExtensionContextValue } from '@stripe/ui-extension-sdk/context';
import Stripe from 'stripe';
import { createHttpClient, STRIPE_API_KEY } from '@stripe/ui-extension-sdk/http_client';
import { useEffect, useState } from 'react';

export const stripeClient = new Stripe(STRIPE_API_KEY, {
  httpClient: createHttpClient() as Stripe.HttpClient,
  apiVersion: '2020-08-27',
});

const App = ({environment}: ExtensionContextValue) => {
  const customerId = environment.objectContext?.id
  const [customer, setCustomer] = useState<Stripe.Customer | null>(null)
  useEffect(() => {
    if (!customerId) return;
    stripeClient.customers.retrieve(customerId)
    .then(response => {
      if (response.deleted) return;
      setCustomer(response)
    })
  }, [setCustomer, customerId])
  return (
    <ContextView title="Create your first Stripe app">
      {customer ? (
        <Table>
          <TableBody>
            <TableRow>
              <TableCell>名前</TableCell>
              <TableCell>{customer.name}</TableCell>
            </TableRow>
            <TableRow>
              <TableCell>残高</TableCell>
              <TableCell>{customer.balance} {customer.currency}</TableCell>
            </TableRow>
          </TableBody>
        </Table>
      ): (
        <Box>No customer data</Box>
      )}
    </ContextView>
  );
};

export default App;

```

アプリのウィジェット内でも、顧客データを取得・表示できるようになりました。

![スクリーンショット 2022-05-24 16.58.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/5b0bfb55-1b93-ebdb-feeb-7538857b525f.png)


## Stripe Appのカスタマイズ

アプリはDashboardの複数ページに追加できます。

`stripe apps add views [ページ名]`を実行すると、指定したページに画面を追加します。

```bash
% stripe apps add view Products --viewport stripe.dashboard.product.list
✔ stripe.dashboard.product.list
✔ Added view Products
```

-> [利用可能なViewport](https://stripe.com/docs/stripe-apps/reference/viewports)

`src/views/[ページ名].tsx`と`src/views/[ページ名].test.tsx`が生成されます。

ページを追加・変更した場合も、[https://dashboard.stripe.com/apps/enable-preview](https://dashboard.stripe.com/apps/enable-preview)にアクセスして、再度プレビューを有効化しましょう。

有効化すると、設定したページにもウィジェットが表示されます。

![スクリーンショット 2022-05-24 17.10.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/3721c32d-f487-a41c-aadd-ff8feea3aad6.png)

## 注意点

開発にあたって注意が必要な点をいくつか紹介します。

### 環境変数を使わず、SDKが用意する値と関数を利用する

Stripe Appsで作成したアプリには、環境変数が設定できません。

環境・ユーザーに関する情報をUI Extension SDKから取得できますので、この値を利用しましょう。

https://stripe.com/docs/stripe-apps/reference/extensions-sdk-api#props

### 外部APIはフルパス指定で許可しないとダメ

Stripe Appsアプリから直接外部のAPIを呼び出す場合、「APIのフルパス」に対してGrantする必要があります。

```bash
stripe apps grant connect-src "https://*.api.example.com/path/" "Send data to example service..."
```

https://stripe.com/docs/stripe-apps/build-ui#use-third-party-apis

動的なパスを含むAPIを利用したい場合、Expressなどで固定パスのProxy APIを用意して利用しましょう。

## 学習リソース

### UIや実装サンプル（GitHub / Figma）

https://stripe.com/docs/stripe-apps/sample-apps

### OAuthでの認証

https://stripe.com/docs/stripe-apps/oauth

### Secret store

https://stripe.com/docs/stripe-apps/store-secrets