Stripe Appsで、OAuthを利用してGoogleなどの外部サービスとStripeを連携させる方法



この記事は、[Stripe Apps を25日間紹介し続ける Advent Calendar 2022](https://qiita.com/advent-calendar/2022/stripe-apps) 19日目の記事です。

![スクリーンショット 2022-11-24 17.56.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/e0261865-0418-daa2-423a-5f4d70558b1b.png)

## 「Stripeと、外部のSaaSを連携する」

StripeのApp Marketplaceを見ると、[Mailchimp](https://mailchimp.com/)や[Intercom](https://www.intercom.com/)・[DocuSign](https://www.docusign.jp/products/electronic-signature)など、さまざまなサービスと連携するアプリが公開されています。

![スクリーンショット 2022-12-19 11.35.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/e61beb53-c416-1ced-414b-b92465f31581.png)

Stripeが持つ決済やサブスクリプション・顧客の「情報」やプランの変更や請求書の期限超過などの「イベント」を、外部のサービスと連携させることで、よりワークフローを効率的にすることができます。

## Stripeとの連携には、OAuthを利用する

Stripe Apps上で外部のサービスにアクセスする場合、OAuthによる認証を推奨しています。

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

OAuthを利用する場合、[Proof Key for Code Exchange(PKCE)フロー](https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-proof-key-for-code-exchange-pkce)に対応する必要がありますので、事前にご確認ください。

## Google Cloud APIとStripe Appsを連携させる方法

ここからは、連携の例としてGoogle Cloud APIとの連携方法を紹介します。

:::note info
Google CloudおよびStripeダッシュボードのスクリーンショットは、2022/12時点のものです。
記事作成後、それぞれのサービスがUI・デザインを変更している場合もありますので、ご了承ください。
:::

### Step0: Google Cloudで、Gmail APIを有効化する

今回はGmail APIを利用します。

Google CloudのAPIライブラリから、Gmailを検索して有効化しましょう。

https://console.cloud.google.com/apis/library

[gmail]で検索し、[Gmail API]を見つけたら、[有効にする]ボタンをクリックします。

![スクリーンショット 2022-12-19 12.08.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/bd0967be-4813-22bf-d0c3-8c44f49b6213.png)

Google Cloud管理画面の[有効なAPIとサービス]に[Gmail API]が表示されていればOKです。

![スクリーンショット 2022-12-19 12.10.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/f094ff12-b1da-8cff-af1c-185046352d60.png)


### Step1: Google Cloudで、OAuth同意画面を作成する

OAuthで認証するための設定をGoogle Cloud側で行います。

Google Cloudの管理画面から、[APIとサービス > OAuth同意画面]へ移動します。

![スクリーンショット 2022-12-19 12.03.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/19d56ae8-4d20-a2e6-22d4-07d48c44be70.png)

[外部]を選び、[作成]をクリックしましょう。

![スクリーンショット 2022-12-19 12.03.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/a3da65b4-1ba5-5d5d-8982-57330368f6bc.png)

公開アプリの場合は、サポートサイトやプライバシーポリシーのURLなどを整備する必要があります。

作成に成功すると、設定画面が開きます。

![スクリーンショット 2022-12-19 13.58.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/1163b924-b521-f865-404d-62477c08594d.png)

#### テストユーザーを追加しよう

テストモードで認証をパスさせるために、事前にテストで利用するアカウントを登録します。

[テストユーザー]にて、[+ ADD USERS]をクリックしましょう。

![スクリーンショット 2022-12-19 14.04.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/47d05bc1-4b7e-5a46-c5a1-81e491197153.png)

メールアドレスを１００件まで登録できます。テストに利用したいGoogleアカウントのメールアドレスを追加しましょう。

![スクリーンショット 2022-12-19 14.04.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/a5e3ce78-8b67-7131-ffd4-4686d7d518b8.png)


### Step2: OAuthの接続情報を発行・取得する

同意画面の用意ができましたので、接続情報を取得します。

Google Cloud管理画面の[接続情報]から[認証情報を作成]を選択し、[OAuthクライアントID]を選びましょう。

![スクリーンショット 2022-12-19 13.11.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/08fe5a33-d1ab-0556-b8fb-719c33479ea6.png)

[アプリケーションの種類]を訊かれますので、[ウェブアプリケーション]を選びましょう。

![スクリーンショット 2022-12-19 13.12.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/218d9dd6-2467-ff99-20d8-6fcb9e30036f.png)

### Stripe DashboardのURLを登録する

OAuthでの認証が完了した後に遷移するページなどを登録します。

![スクリーンショット 2022-12-19 13.15.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/d408c853-6f26-c53e-7d8e-cbfa64106c3e.png)

[承認済みのJavaScript]生成元には`https://dashboard.stripe.com`を登録します。

また、[承認済みのリダイレクトURI]には、`https://dashboard.stripe.com/test/apps-oauth/{Stripe AppsのID}`を登録しましょう。

[作成]をクリックすると、OAuthクライアントが追加されます。

[JSONをダウンロード]をクリックすると、次のようなJSONがDLできます。

```json
{
    "web": {
      "client_id": "xxxxxx.apps.googleusercontent.com",
      "project_id": "numeric-citron-123456789",
      "auth_uri": "https://accounts.google.com/o/oauth2/auth",
      "token_uri": "https://oauth2.googleapis.com/token",
      "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
      "client_secret": "GOCSPX-xxxxxxxxxxx",
      "redirect_uris": [
        "https://dashboard.stripe.com/test/apps-oauth/com.example.first-stripe-app"
      ],
      "javascript_origins": [
        "https://dashboard.stripe.com"
      ]
    }
  }
```

## Stripe AppsからOAuthの認証画面に遷移する

Google側の設定が終わりましたので、Stripe AppsからGoogleに接続を進めます。

まずはGoogleアカウントでログインするためのUIを用意しましょう。

`src/views/App.tsx`を次のように変更します。

```tsx
import { SignInView } from "@stripe/ui-extension-sdk/ui"
import { useEffect, useState } from 'react'
import { ExtensionContextValue } from "@stripe/ui-extension-sdk/context"
import { createOAuthState } from "@stripe/ui-extension-sdk/utils"

// ダウンロードしたクライアントID
const GOOGLE_API_CLIENT_ID = "xxxxxxx.apps.googleusercontent.com"


const App = ({ environment }: ExtensionContextValue) => {  
  const { mode } = environment
  const redirectUrl = [
    "https://dashboard.stripe.com",
    mode === "live" ? null : "test",
    "apps-oauth",
    "{Stripe AppsのID}"
  ].filter(Boolean).join("/")

  const [authURL, setAuthURL] = useState("")
  useEffect(() => {
    createOAuthState()
    .then(({state, challenge}) => {
      const url = new URL("https://accounts.google.com/o/oauth2/auth")
      const params = new URLSearchParams({
        response_type: "code",
        client_id: GOOGLE_API_CLIENT_ID,
        redirect_uri: redirectUrl,
        state,
        code_challenge: challenge,
        scope: "https://www.googleapis.com/auth/gmail.readonly",
        code_challenge_method: "S256"
      })
      setAuthURL(`${url}?${params}`)
    })
  }, [redirectUrl])

  return (
    <SignInView
      description="Googleにログインします。"
      primaryAction={authURL ? {
        label: "ログイン",
        href: authURL
      } : undefined}
    />
  )
}

export default App

```

変更を保存すると、アプリのUIが次のように変わります。

![スクリーンショット 2022-12-19 14.09.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/2a5dd518-e390-3b5a-afe1-74fe292a7e7d.png)

[ログイン]をクリックすると、Googleの認証画面に移動します。

なお、テストモードのため、警告メッセージが表示されることがあります。

![スクリーンショット 2022-12-19 14.06.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/0e3ba70b-de64-27c4-5ab8-208b9d4e098a.png)

[続行]をクリックすることで、処理を継続できます。

![スクリーンショット 2022-12-19 14.06.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/cb1a7dfc-781a-fed3-8fd0-1c525a15a2e7.png)

権限の確認画面で、[続行]をクリックすると、Stripeダッシュボードにリダイレクトされます。

## 認証成功時の処理を追加する

認証画面への移動は実装できましたので、続いてトークンの取得や保存処理を追加しましょう。

### Google APIへのアクセス権を設定する

アクセストークンなどを取得するために、Google APIを呼び出す必要があります。

Stripe CLIで、APIを許可リストに追加しましょう。

```bash
$ stripe apps grant url "https://oauth2.googleapis.com/token" "Integrate with Google"
````

**関連記事**

https://qiita.com/hideokamoto/items/612d7006f5eb13699511

### Google APIを呼び出して、アクセストークンを取得する

続いてGoogle APIからアクセストークンを取得しましょう。

`src/views/App.tsx`を次のように変更します。

```diff_typescript
// ダウンロードしたクライアントID
const GOOGLE_API_CLIENT_ID = "xxxxxxx.apps.googleusercontent.com"
// ダウンロードしたクライアントシークレット
const GOOGLE_API_CLIENT_SECRET = "GOCSPXーxxxxx"

-const App = ({ environment }: ExtensionContextValue) => {
+const App = ({ environment, oauthContext }: ExtensionContextValue) => {   
+  const { code, verifier } = oauthContext || {} 
  const { mode } = environment
  const redirectUrl = [
    "https://dashboard.stripe.com",
    mode === "live" ? null : "test",
    "apps-oauth",
    "{Stripe AppsのID}"
  ].filter(Boolean).join("/")

+  useEffect(() => {
+    if (!code || !verifier) return
+    const tokenRequestURL = new URL("https://oauth2.googleapis.com/token")
+    const params = {
+      code,
+      grant_type: "authorization_code",
+      code_verifier: verifier,
+      client_id: GOOGLE_API_CLIENT_ID,
+      client_secret: GOOGLE_API_CLIENT_SECRET,
+      redirect_uri: redirectUrl
+    }
+    fetch(`${tokenRequestURL}?${new URLSearchParams(params)}`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/x-www-form-urlencoded"
+      },
+    }).then(response => {
+      if (response.ok) {
+        return response.json()
+      }
+      return response.json().then(error => {
+        throw new Error(`${error.error}: ${error.error_description}`)
+      })
+    }).then(data => {
+      console.log(data)
+    }).catch(console.log) 
+  }, [code, verifier, redirectUrl])
```

この状態で、再度[ログインボタン]をクリックしてみましょう。

ダッシュボードにリダイレクトされた際、開発コンソールのログにアクセストークンなどが表示されています。

![スクリーンショット 2022-12-19 15.27.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/2f916731-79bd-34d0-6585-3128782a0f08.png)

:::note warn
*画像のトークンはダミーです。
実際に発行されたトークンをブログやGitHubなどで公開しないようにご注意ください。
:::


### 取得したアクセストークンを、Stripe Secret Store APIで保存する

取得したアクセストークンを利用できるようにするため、Secret Store APIを使って保存しましょう。

**関連記事**

https://qiita.com/hideokamoto/items/a825dc8a9de820c19ff3

まず、Stripe CLIでアクセス権限を設定します。

```bash
$ stripe apps grant permission "secret_write" "Store accesst token"
```

続いて`src/views/App.tsx`を次のように変更します。


```diff_typescript
import { createOAuthState } from "@stripe/ui-extension-sdk/utils"
+import Stripe from 'stripe'
+import {createHttpClient, STRIPE_API_KEY} from '@stripe/ui-extension-sdk/http_client'

+const stripe = new Stripe(STRIPE_API_KEY, {
+  httpClient: createHttpClient(),
+  apiVersion: '2022-11-15',
+});

-const App = ({ environment, oauthContext }: ExtensionContextValue) => {
+const App = ({ environment, oauthContext, userContext }: ExtensionContextValue) => {
+  const userId = userContext.id

...

    }).then(response => {
      if (response.ok) {
        return response.json()
      }
      response.text().then(text => {
        throw new Error(text)
      })
    }).then(data => {
-      console.log(data)
+      return stripe.apps.secrets.create({
+        scope: {
+          type: 'user',
+          user: userId,
+        },
+        name: "oauth_token",
+        payload: JSON.stringify(data)
+      })
    }).catch(console.log)
-  }, [code, verifier, redirectUrl])
+  }, [code, verifier, redirectUrl, userId])
```

これでブラウザをリロードしたり、別のデバイスやブラウザからStripeダッシュボードにログインした場合でも、Google APIにアクセスするためのトークンを利用できます。

## Gmail APIを呼び出してみよう

最後に取得・保存したアクセストークンを利用して、Gmail APIを呼び出してみましょう。

Stripe CLIで、APIを許可リストに追加しましょう。

```bash
$ stripe apps grant url "https://www.googleapis.com/gmail" "Integrate with Google"
````

続いてGmail APIを呼び出すための処理を、`src/views/App.tsx`に追加します。

```diff_typescript

  }, [code, verifier, redirectUrl, userId])

+  useEffect(() => {
+    if (code && verifier) return
+    if (!userId) return 
+    stripe.apps.secrets.find({
+      scope: {
+        type: "user",
+        user: userId,
+      },
+      name: "oauth_token",
+      expand: ["payload"]
+    }).then(({payload}) => {
+      if (!payload) return
+      const secrets = JSON.parse(payload)
+      if (!secrets.access_token) return
+      return fetch(`https://www.googleapis.com/gmail/v1/users/{連携させたGmailアドレス}/messages`, {
+        method: "GET",
+        headers: {
+          "Authorization": `${secrets.token_type} ${secrets.access_token}`
+        }
+      }).then(data => data.json())
+    }).then(console.log)
+    .catch(e => {
+      if (e.code === "resource_missing") {
+        return
+      }
+      console.log(`${e.code}: ${e.message}`)
+    })
+  }, [code, verifier, userId])

  const [authURL, setAuthURL] = useState("")
```

接続に成功していれば、Gmailのメールスレッド情報が開発コンソールに表示されます。

![スクリーンショット 2022-12-19 16.00.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/8f47d457-7d44-2dbe-313f-09bb52fa8568.png)

## 本格的な開発に向けて

この記事では、連携させるところまでを紹介しました。

実際の開発では、次のような要件も発生します。

- アクセストークンの有効期限が切れた場合のトークンの更新と保存
- 連携済み or 未連携でアプリのUIを出し分ける
- 設定画面から、連携の解除や変更ができる機能

決済やサブスクリプションの運用業務を効率化させるための開発や機能提案の１つとして、ぜひ2023年はStripe Appsをご検討ください。

## [Tips]一度認証したGoogleアカウントで、再度認証フローに入りたい場合

一度認証に成功し、アクセストークンを取得すると、アクセス権の確認などのページに遷移しなくなります。

認証フローをもう一度試したい場合は、[Googleアカウントの[セキュリティ > アカウントにアクセスできるサードパーティアプリ]](https://myaccount.google.com/permissions?continue=https%3A%2F%2Fmyaccount.google.com%2Fsecurity)から登録したアプリを削除しましょう。

![スクリーンショット 2022-12-19 15.33.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/0492323e-5b75-6ff8-22b7-0b621a187b1b.png)

## Documents

https://developers.google.com/identity/protocols/oauth2

https://developers.google.com/gmail/api

https://github.com/stripe/stripe-apps/tree/main/examples/dropbox-oauth-pkce

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

## Stripe Appsひとりアドベントカレンダー 2022

今年ベータリリースされたばかりのStripe Appsは、まだ日本語の情報が多くありません。

そこでQiita Advent Calendar 2022にて、毎日Stripe Appsについての情報を投稿します。

https://qiita.com/advent-calendar/2022/stripe-apps

ノーコードで利用する方法や、開発するためのTipsなども紹介予定ですので、ぜひ購読をお願いします。