前提
この記事では以下を前提としています。
- Supabase CLI をインストール済みであり、使い方を把握していること
- Stripe アカウントを作成済みで Stripe 管理画面にアクセスできること
- Supabase アカウントを作成済みであること
参考サイト
主に以下の記事を参考にしました。
この記事のゴール
この記事では、以下を目標としています。
- Supabase の Edge Function で 作成した Webhook で Stripe のイベントを受け取る
動作確認方法として、Stripe の決済をcurlコマンドで実行し、実行した決済に関連するイベントが Supabase の Edge Function のログに出力されることを確認します。
ここでは、イベントを受け取るところまでとし、イベントを受け取ってイベントの内容に基づいて Supabase のデータベースのデータを更新したり、追加したりというところまでは扱いません。
作業内容
- Supabase Edge Function を作成する
- Stripe からのイベントを受け取るための内容に修正する
- Supabase Edge Function をデプロイする
- Stripe の管理画面で Webhook を登録する
- Edge Function 用の環境変数を設定する
- 動作確認する
Supabase Edge Function を作成する
Stripe のイベントを受け取って処理するための Webhook を Supabase Edge Function で作成します。
ここでは、my-projectというプロジェクト用のディレクトリがあり、その中に Edge Function を作ります。
なお、my-projectは例えば、Next.js や Nuxt.js のプロジェクトかもしれませんし、その他のフレームワークの中のプロジェクトかもしれません。
フレームワークの中でなくても、独立したディレクトリでももちろん OK です。
まず空の Edge Function を作成するために、以下のようにsupabase functions newコマンドを実行します。
ここでは、handle-stripe-eventという名前の Edge Function を作成します。
$ cd ~/my-project
$ supabase functions new handle-stripe-event
Created new Function at supabase/functions/handle-stripe-eventsupabase functions newによってfunctionsのhandle-stripe-eventが作成されます。
.branchesや.gitignore、.temp、migrationsは、supabase start時など、初めて Supabase CLI のコマンドを実行した際に作成されます。
.
├── .branches
│   └── _current_branch
├── .gitignore
├── .temp
│   └── import_maps
│       └── 392j9d39sofal2391f32308i42083j42083nsa40ak2d208td0283.json
├── config.toml
├── functions
│   ├── .vscode
│   │   ├── extensions.json
│   │   └── settings.json
│   └── handle-stripe-event
│       └── index.ts
├── migrations
└── seed.sqlfunctionsの中にあるhandle-stripe-eventのindex.tsが Edge Function の中身です。これはデフォルトですと以下のようになっています。
(ただし、Supabase は頻繁にアップデートされているため、デフォルトの中身も変わる可能性が高いです。)
// Follow this setup guide to integrate the Deno language server with your editor:
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
 
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
 
console.log('Hello from Functions!');
 
serve(async (req) => {
  const { name } = await req.json();
  const data = {
    message: `Hello ${name}!`,
  };
 
  return new Response(JSON.stringify(data), {
    headers: { 'Content-Type': 'application/json' },
  });
});
 
// To invoke:
// curl -i --location --request POST 'http://localhost:54321/functions/v1/' \
//   --header 'Authorization: Bearer sej9jmi923mpg-924mpsj39j9ahs8nd9a8caibhngnkjnwe' \
//   --header 'Content-Type: application/json' \
//   --data '{"name":"Functions"}'上記は、呼び出されるとHello from Functionsと出力して、reqに含まれるnameの値を使ってHello ${name}!というメッセージを返す処理になっています。
デフォルトの関数なのでこの中身を Stripe のイベントを受け取って処理できるように修正します。
Stripe からのイベントを受け取るための内容に修正する
デフォルトの状態の Edge Function を、Stripe からのイベント情報を受け取ってログに出力する内容に修正します。 参考にしたサンプルコードは以下の Supabase の公式リポジトリ内にあります。
ここでは、使用する Stripe のapiVersionやインポートする Stripe のパッケージ部分等を修正して、以下のようにしました。
// Follow this setup guide to integrate the Deno language server with your editor:
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
 
import { serve } from 'std/server'
import Stripe from 'https://esm.sh/stripe@11.16.0?target=deno';
 
const stripe = new Stripe(Deno.env.get('STRIPE_API_KEY') as string, {
  // This is needed to use the Fetch API rather than relying on the Node http
  // package.
  apiVersion: '2023-08-16',
  httpClient: Stripe.createFetchHttpClient(),
})
// This is needed in order to use the Web Crypto API in Deno.
const cryptoProvider = Stripe.createSubtleCryptoProvider()
 
console.log('Hello from Stripe Webhook!')
 
serve(async (request) => {
  const signature = request.headers.get('Stripe-Signature')
 
  // First step is to verify the event. The .text() method must be used as the
  // verification relies on the raw request body rather than the parsed JSON.
  const body = await request.text()
  let receivedEvent
  try {
    receivedEvent = await stripe.webhooks.constructEventAsync(
      body,
      signature!,
      Deno.env.get('STRIPE_WEBHOOK_SIGNING_SECRET')!,
      undefined,
      cryptoProvider
    )
  } catch (err) {
    return new Response(err.message, { status: 400 })
  }
  console.log(`🔔 Event received: ${receivedEvent.id}`)
  return new Response(JSON.stringify({ ok: true }), { status: 200 })
})apiVersionの最新の値は、以下の Stripe 公式ドキュメント内で確認できます。
なお、上記のコード内でDeno.env.get('STRIPE_API_KEY')とDeno.env.get('STRIPE_WEBHOOK_SIGNING_SECRET')の2つの環境変数を使用しています。
これらの値は Stripe の管理画面にて取得します。取得方法と設定方法は後述します。 なお、設定には
Supabase CLI を使用します。
次に Supabase CLI を使って Edge Function をデプロイします。
Supabase Edge Function をデプロイする
作成した Edge Function handle-stripe-eventを 以下のコマンドで Supabase にデプロイします。
$ supabase functions deploy --no-verify-jwt handle-stripe-eventここでオプションとして指定している--no-verify-jwtは、Stripe 側からこの Edge Function にアクセスできるようにするために、Supabase 側でアクセス元の認証をしないための指定になります。
これだけだとどこからでもこの Edge Function を呼び出せてしまいセキュリティリスクが高いです。
そのため、上記の Edge Function の中では、 Stripe 公式ドキュメントに記載されている通りにアクセス元が Stripe からであるか否かのチェックを 20 行目、29 行目部分で行っています。
もし外部から呼び出せる必要がない場合は、--no-verify-jwtを指定する必要はありません。
なお、--no-verify-jwtの使用は Supabase の公式リポジトリにあるサンプルコードにも含まれています。
デプロイが完了すると以下のように表示されます。
$ supabase functions deploy handle-stripe-event
You can find your project ref from the project's dashboard home page, e.g. https://app.supabase.com/project/<project-ref>.
Enter your project ref: iqjduehnshcuentoie
Version 1.30.3 is already installed
Bundling handle-stripe-event
Deploying handle-stripe-event (script size: 400.8kB)
Deployed Function handle-stripe-event on project iqjduehnshcuentoie
You can inspect your deployment in the Dashboard: https://app.supabase.com/project/iqjduehnshcuentoie/functions/handle-stripe-event/detailsEdge Function のデプロイに成功すると、Supabase の管理画面でも確認できます。
 
上記に表示されている URL を Stripe の Webhook として Stripe の管理画面から登録します。
Supabase プロジェクトの Reference ID の確認方法
もし以下のように表示された場合は、デプロイ先となるプロジェクトの Project Reference ID を入力して Enter を押下してください。
$ supabase functions deploy handle-stripe-event
You can find your project ref from the project's dashboard home page, e.g. https://app.supabase.com/project/<project-ref>.
Enter your project ref:Supabase の Project Reference ID は、以下のように Supabase の管理画面の中のサイドバー下部の歯車マークから「Settings」ページに遷移し、「General」の中にある「Reference ID」が該当します。
 
Stripe の管理画面で Webhook を登録する
以下のように、Stripe の管理画面の上部にある「開発者」ページを開き、その中の「Webhook」タブを開きます。まだ Webhook を作成したことがない場合は、以下のように表示されていると思います。 なお、まずはテスト環境で試したい場合は、右上にある「テスト環境」をオンにしてください。以下のような見た目になります。
 
上記で「エンドポイントを追加」ボタンをクリックします。 以下のように、エンドポイントの URL 入力フォームや、サンプルまで表示してくれます。
 
上記の「エンドポイント URL」には、デプロイした Edge Function の URL(https://iqjduehnshcuentoie.supabase.co/functions/v1/stripe-event-handlerのような URL) を入力します。
また、ここで受け取るイベントの種類を選択できます。
上記画像内にある「リッスンするイベントの選択」項目の「+イベントを選択」をクリックすると、エンドポイント(ここではデプロイした Edge Function )で受け取りたいイベントを選択する画面が表示されます。
 
イベントは項目毎に分かれており、試しに一番上にあるAccountをクリックして展開してみると、Accountに関連する4つのイベントを選択できることがわかります。
「全ての Account イベントを選択する」にチェックを入れると、以下のように右側に表示されているサンプルコードも自動で更新してくれます。
 
ここでは、決済関係の通知を受け取りたいため、CheckoutとPayment Intentの全てのイベントを選択します。(以下の画像ではCheckoutのみ選択しています。)
 
選択した状態で「イベントを追加」ボタンをクリックします。 以下のように選択したイベントが表示されるので、問題なければ再度「イベントを追加」ボタンをクリックします。
 
すると、Webhook が作成されて以下のように表示されます。
 
上記で、「署名シークレット」という項目に「表示」というリンクがあるので、これをクリックします。表示された値を Supabase Edge Function のSTRIPE_WEBHOOK_SIGNING_SECRETで使用します。
また、STRIPE_API_KEYは以下のように「開発者」ページの「API キー」タブの中にあるシークレットキーを使用します。公開可能キーではありませんので注意が必要です。
 
なお、上記の画像ではテスト環境の表示になっているため「テストキーを表示」というボタンがありますが、本番環境の場合は「本番キーを表示」というボタンが表示され、一度だけ表示できます。 後からダッシュボードで再確認ができないため、表示されたシークレットキーをどこかにメモしておく必要があります。 もし紛失した場合は、シークレットキーを削除して新たに作成できます。
Edge Function 用の環境変数を設定する
Edge Function で使用する 上記のコードの中では、以下の2つの環境変数を使用しています。
- STRIPE_API_KEY
- STRIPE_WEBHOOK_SIGNING_SECRET
Supabase の Edge Function の中で使用する環境変数は、Supabase CLI のsupabase secrets set コマンドを使用して設定します。
具体的には、以下を実行することで環境変数を設定できます。
$ supabase secrets set STRIPE_API_KEY=sk_test_skwie82l39mxnjskihxbfgh397dh37fh38dfhcfhvjyqg3474jcv8hdfjkjhchy2839ujks
$ supabase secrets set STRIPE_WEBHOOK_SIGNING_SECRET=whsec_22jd8jx38djdjdhwqoyr83i8ghq083hflhsbfoug082bouf8d上記を見てわかるとおり、以下のようにして使用できます。
$ supabase secrets set 環境変数名=値環境変数の設定が成功すると以下のようなレスポンスが返ってきます。
$ supabase secrets set STRIPE_API_KEY=sk_test_skwie82l39mxnjskihxbfgh397dh37fh38dfhcfhvjyqg3474jcv8hdfjkjhchy2839ujks
You can find your project ref from the project's dashboard home page, e.g. https://app.supabase.com/project/<project-ref>.
Enter your project ref: iqjduehnshcuentoie
Finished supabase secrets set.
 
$ supabase secrets set STRIPE_WEBHOOK_SIGNING_SECRET=whsec_22jd8jx38djdjdhwqoyr83i8ghq083hflhsbfoug082bouf8d
You can find your project ref from the project's dashboard home page, e.g. https://app.supabase.com/project/<project-ref>.
Enter your project ref: iqjduehnshcuentoie
Finished supabase secrets set.設定済みの環境変数一覧を確認するには、supabase secrets listを使用します。
$ supabase secrets listなお、supabase secrets setやsupabase secrets listを実行した時に以下のように表示される場合は、まず環境変数を設定したい Supabase のプロジェクトの Reference ID を指定する必要があります。(Reference ID の確認方法はこちら
)
$ supabase secrets list
You can find your project ref from the project's dashboard home page, e.g. https://app.supabase.com/project/<project-ref>.
Enter your project ref:具体的には、上記のように表示された時に、Enter your project ref:に続けてプロジェクトの ID を貼り付けて Enter を押下します。
すると、以下のように一覧が表示されます。
$ supabase secrets list
You can find your project ref from the project's dashboard home page, e.g. https://app.supabase.com/project/<project-ref>.
Enter your project ref: iqjduehnshcuentoie
 
 
               NAME              │              DIGEST
  ────────────────────────────────┼───────────────────────────────────
    STRIPE_API_KEY                │ 38dh588sk39hsfl38sf082j07jgay2o3
    STRIPE_WEBHOOK_SIGNING_SECRET │ 297dnlfjas97311nkfds97sanle98h3oなお、Edge Function をデプロイ後に環境変数を新しく設定したり変更したりした場合は、その後に再度デプロイしないと変更内容が反映されないので注意が必要です。 以下のように再度デプロイしてください。
$ supabase functions deploy --no-verify-jwt handle-stripe-event動作確認する
動作確認を行うためにcurlコマンドを使って Stripe に決済情報を送信します。
例えば、以下を実行すると、Stripe にクレジットカードを使った決済を実行できます。
以下は、クレジットカードで日本円 1,099 円の決済を実行する例です。
curl https://api.stripe.com/v1/payment_intents \
  -u "sk_test_skwie82l39mxnjskihxbfgh397dh37fh38dfhcfhvjyqg3474jcv8hdfjkjhchy2839ujks:" \
  -d amount=1099 \
  -d currency=jpy \
  -d "payment_method_types[]"=card上記が成功すると、以下のようなレスポンスが返ってきます。
{
  "id": "pi_3NozUVEnJYjxbzcQ06i9TNVH",
  "object": "payment_intent",
  "amount": 1099,
  "amount_capturable": 0,
  "amount_details": {
    "tip": {}
  },
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "client_secret": "pi_3skdj2082jifwsfd8024h08gaj08fj028j0a8efj0a8d",
  "confirmation_method": "automatic",
  "created": 1694397883,
  "currency": "jpy",
  "customer": null,
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "latest_charge": null,
  "livemode": false,
  "metadata": {},
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": null,
  "payment_method_options": {
    "card": {
      "installments": null,
      "mandate_options": null,
      "network": null,
      "request_three_d_secure": "automatic"
    }
  },
  "payment_method_types": [
    "card"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "requires_payment_method",
  "transfer_data": null,
  "transfer_group": null
}Stripe の Webhook のログを確認する
上記のログは、Stripe のダッシュボードからも確認できます。具体的には、以下のように開発者ページの中の「Webhook」タブの中にある、作成した Webhook の URL をクリックします。
 
すると、以下のように Stripe からのイベントのログを確認できます。
 
上記で確認したログと同じ内容が Supabase の Edge Function でも表示されていることを確認します。
Supabase Edge Function のログを確認する
Supabase にログインして、管理画面のサイドバーにある Edge Function メニューをクリックして Edge Function 一覧ページに遷移します。
 
そこで Stripe の Webhook として登録した Edge Function をクリックして Edge Function の詳細ページにアクセスします。そこにある「Logs」を開いてみると、 Edge Function のログとして、Stripe から受け取ったイベント情報が表示されるはずです。
すると、以下のように Stripe からのイベントのログを確認できます。
 
なお、上記で実行したcurlコマンドは、以下の Stripe 公式ドキュメントに記載のものです。以下のドキュメントには、他の処理についての実行方法や色々な言語での実行方法などが詳しく記載されています。
このドキュメントを見ながら色々と試してみると良いと思います。
まとめ
Stripe の決済情報に関連するイベントを Supabase の Edge Function で受け取る例をまとめました。この記事ではcurlコマンドによる動作確認を行っただけですが、実際にはブラウザ上で決済用ボタンを用意し、そこから Stripe の決済ページへ遷移して実際に決済するような形で実装することになると思います。