サーバー側のロジックを追加する
Stripe Apps を使用すると、サーバー側のロジックにセルフホスト型のバックエンドを追加できます。セルフホスト型のバックエンドサービスでは、次の実行が可能です。
- サーバー側の組み込みを必要とするサードパーティーのシステムと安全に連携します。
- Stripe からの Webhook イベントに登録し、Stripe を他のシステムと同期します。
- ユーザーがブラウザーを閉じたときに実行する、存続期間の長いアプリロジックを使用します。
- cron ジョブのような機能を提供するアプリを構築して、特定の操作をスケジュールします。
セルフホスト型のバックエンドとアプリの関連性
UI からアプリのバックエンドに対してユーザーを認証する
ダッシュボードからユーザーを認証するには、バックエンドに、共有シークレット付きの署名と、現在サインインしているダッシュボードユーザーのアカウントとユーザー ID が必要です。ユーザーに API を呼び出す権限がない場合、Stripe は権限エラーを返します。
はじめに
バックエンドサービスが、HTTP リクエストを送受信できることを確認します。これまでに API サーバーを構築したことがない場合は、インタラクティブな Webhook エンドポイントビルダーを使用してみることを検討してください。
アプリをアップロードして共有シークレットを作成します。
Command Linestripe apps upload
アプリの最新バージョンの開発を完了していなくても問題ありません。アップロードしても、本番環境のアプリは更新されません。
バックエンドで署名を確認するため、アプリのシークレットを取得します。
a. アプリからアプリを選択し、Stripe アプリの詳細ページに移動します。
b. アプリケーション ID にあるオーバーフローメニュー () をクリックし、署名シークレットをクリックして署名シークレットダイアログを開きます。
c. クリップボード をクリックし、署名シークレットダイアログからアプリのシークレットをコピーします。
署名付きのリクエストを送信する
署名付きのリクエストをアプリのバックエンドに送信するには、次のようにします。
- fetchStripeSignature 非同期関数を使用して、現在の署名を取得します。
Stripe-Signature
ヘッダーに署名を追加します。- リクエストに
user_id
オブジェクトとaccount_id
オブジェクトを含めます。 - アプリのバックエンドで、リクエストに署名、アプリのシークレット、
user_id
、およびaccount_id
が含まれていることを確認します。
追加データを指定して署名付きのリクエストを送信する例をご覧ください。
Stripe-Signature
ヘッダーがある Stripe アプリからのリクエストの例:
import {fetchStripeSignature} from '@stripe/ui-extension-sdk/utils'; const App = ({ userContext, environment }: ExtensionContextValue) => { const makeRequestToMyBackend = async (endpoint, requestData) => { // By default the signature is signed with user id and account id. const signaturePayload = { user_id: userContext?.id, account_id: userContext?.account.id, }; return fetch(`https://example.com/${endpoint}/`, { method: 'POST', headers: { 'Stripe-Signature': await fetchStripeSignature(), 'Content-Type': 'application/json', }, // Include the account ID and user ID in the body to verify on backend. body: JSON.stringify({ ...requestData, ...signaturePayload, }), }); }; ... }
リクエストを確認するサンプルバックエンド:
署名確認を実行する際は、ペイロードのフィールドの順序と名前が重要であることに注意してください。user_id
は account_id
の前にあり、結果のオブジェクトは次のようになります。{ user_id, account_id }
// Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')(process.env.STRIPE_API_KEY); const express = require('express'); // Find your app's secret in your app settings page in the Developers Dashboard. const appSecret = 'absec_...'; // This example uses Express. const app = require('express')(); app.use(express.json()); // Match the raw body to content type application/json. app.post('/do_secret_stuff', (request, response) => { const sig = request.headers['stripe-signature']; // Retrieve user id and account id from the request body const payload = JSON.stringify({ user_id: request.body['user_id'], account_id: request.body['account_id'] }); try { // Verify the payload and signature from the request with the app secret. stripe.webhooks.signature.verifyHeader(payload, sig, appSecret); } catch (error) { response.status(400).send(error.message); } // Handle the request by returning a response // to acknowledge receipt of the event. response.json({ success: true }); }); app.listen(3000, () => console.log('Running on port 3000'));
追加データを指定して署名付きのリクエストを送信する
ユーザーを認証するには、ペイロード (追加データ) を指定して署名付きリクエストを送信します。追加のペイロードリクエストを指定して fetchStripeSignature
関数を呼び出すと、user_id
、account_id
、およびこの関数に渡した追加ペイロードを使用して署名が作成されます。デフォルトでは、Stripe アプリは user_id
と account_id
を使用して署名文字列を生成します。
追加ペイロードを使用してシークレットを生成する例:
// A valid payload object has keys of type string // and values of type string, number, or boolean. const payload = { "transaction_id": 'ipi_1KRmFUFRwUQjTSJEjRnCCPyV', "amount": 100, "livemode": false, }; fetch(`https://example.com/do_more_secret_stuff/`, { method: 'POST', headers: { 'Stripe-Signature': await fetchStripeSignature(payload), 'Content-Type': 'application/json', }, // Append the account ID and user ID in the body to verify on backend. body: JSON.stringify({ ...payload, user_id: 'usr_K6yd2CbXLO9A5G', account_id: 'acct_1JSkf6FRwUQjTSJE', }), });
追加ペイロードを使用して生成された署名を検証するサンプルバックエンド
// Match the raw body to content type application/json. app.post('/do_more_secret_stuff', (request, response) => { try { // Verify the signature from the header and the request body that // contains the additional data, user ID, and account ID with the app secret. stripe.webhooks.signature.verifyHeader(request.body, sig, appSecret); } catch (error) { response.status(400).send(error.message); } // Handle the request by returning a response // to acknowledge receipt of the event. response.json({ success: true }); });
ユーザーの役割を確認する (オプション)
特定の user_id
に割り当てられたユーザーの役割を確認するには、ペイロードに stripe_roles
キーを含めます。これを userContext?.roles
で指定すると、RoleDefinitions のリストが返されます。ペイロードの役割が指定された user_id
に割り当てられていない場合、fetchStripeSignature
が無効なリクエストエラー (400) を返します。
// Provide this special key in the same way you'd // provide any other key to the additional payload. const payload = { "stripe_roles": userContext?.roles, }; fetch(`https://example.com/do_more_secret_stuff/`, { method: 'POST', headers: { 'Stripe-Signature': await fetchStripeSignature(payload), 'Content-Type': 'application/json', }, // Append the account ID and user ID in the body to verify on backend. body: JSON.stringify({ ...payload, user_id: 'usr_K6yd2CbXLO9A5G', account_id: 'acct_1JSkf6FRwUQjTSJE', }), });
シークレットを期限切れにして作成する
シークレットが漏洩した場合、現在のアプリのシークレットは、すぐに失効させるか、最大 24 時間後に失効させることができます。後者の場合、バックエンドでアプリのシークレットを更新する時間が確保され、この間は、漏洩したシークレットと新たに生成されたシークレットの両方がエンドポイントで有効になります。Stripe は、有効期限が切れるまで、シークレットごとに署名を 1 つ生成します。
アプリのシークレットを期限切れにして作成するには、以下を手順に従います。
- アプリでアプリを選択し、Stripe アプリの詳細ページに移動します。
- ページヘッダーで、オーバーフローメニュー () をクリックし、署名シークレットをクリックして署名シークレットのダイアログを開きます。
- 署名シークレットダイアログからシークレットを期限切れにするをクリックして、「シークレットを期限切れにする」ダイアログを開きます。
- 現在のアプリのシークレットの有効期限を選択します。
- シークレットを期限切れにするをクリックします。
クロスオリジンリソース共有 (CORS) を処理する
クロスオリジンリソース共有 (CORS) は、アプリをクロスサイトスクリプティング攻撃 (XSS) から保護し続ける際に重要な役割を担います。Stripe アプリの UI 拡張機能は、当然ながら、クロスオリジンであり、サンドボックス化されているため、クロスオリジンのリクエストヘッダーを処理するには、特定のアプローチを採用する必要があります。
UI 拡張機能でバックエンドサービスのデータを取得するには、以下が行われるようにバックエンドサービスを設定する必要があります。
- Options メソッドを使用するリクエストを許可する。
null
オリジンからのリクエストを許可するには、Access-Control-Allow-Origin
を*
に設定します。
注
UI 拡張機能は、セキュリティ上の理由によりサンドボックスで実行されるため、null オリジンが設定されます。
多くのバックエンドフレームワークには、CORS の処理に役立つライブラリとガイダンスが用意されています。より詳細なガイダンスについては、フレームワークのドキュメントをご確認ください。
特定のユーザーまたはアカウントの代わりに Stripe からのリクエストを認証するには、UI からバックエンドに対してユーザーを認証するを参照してください。
注意
認証済みのエンドポイントと UI 拡張機能が通信するエンドポイントでのみ Access-Control-Allow-Origin: *
を使用するように設定します。その他の対策が取られていない場合、未認証のエンドポイントは、CSRF 攻撃に対して脆弱になります。
Stripe API を使用する
Stripe と対話するために、Stripe API を使用して、リクエストを認証できます。
リクエストを認証する
リクエストを認証するには、既存の加盟店アカウント API キーを使用して Stripe と対話し、ユーザーの stripeAccountId
を指定します。
サーバー側での API コールでは、プラットフォームユーザーの Stripe アカウント ID (プレフィックスは acct_
) とともに特殊なヘッダー Stripe-Account
を使用することで、連結アカウントとしてリクエストを作成できます。下記は、プラットフォームの API シークレットキーとユーザーのアカウント ID を使用して PaymentIntent を作成する方法を示す例です。
URL に Stripe アカウント ID を含む API リクエストのすべてで、Stripe-Account
ヘッダーによる方法が必要になります。下記は、URL 内のユーザーのアカウント ID を使用してアカウントを取得する方法を示す例です。
また、Stripe のすべてのサーバー側ライブラリは、リクエストごとにこの方法に対応します。次に例を示します。
UI 拡張機能からセルフホスト型のバックエンドを呼び出す
UI 拡張機能からバックエンドへのリクエストを実行するときは、リクエストとともに署名を送信して、リクエストの正当性を証明します。UI 拡張機能から、現在のユーザーの stripeAccountId
を渡して、ユーザーの代わりにバックエンドリクエストを実行できるようにします。
// Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')(
); const express = require("express"); const app = express(); app.use(express.static("public")); app.use(express.json()); app.post("/api/data", async (req, res) => { const { stripeAccountId } = req.body; const customer = await stripe.customers.create({ description: 'My First Test Customer (created for API docs)', }, { stripeAccount: stripeAccountId, }); res.send({ data: [] }); }); app.listen(3000, () => console.log("Node server listening on port 3000!"));'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
他の API の呼び出し
セルフホスト型のバックエンドから、独自の API や他の開発者または会社が作成した API など、すべての API を呼び出すことができます。
詳細については、アプリにシークレット認証情報やトークンを保存する方法をご覧ください。
Stripe から別のサービスにユーザー情報を渡す必要がある場合は、UI 拡張機能から渡された stripeAccountId
を使用します。
const express = require('express'); const fetch = require('isomorphic-fetch'); const app = express(); app.use(express.static('public')); app.use(express.json()); app.get('/api/time', async (req, res) => { fetch('http://worldclockapi.com/api/json/est/now') .then((response) => response.json()) .then((data) => { res.send({ data: data, }); }); }); app.listen(3000, () => console.log('Node server listening on port 3000!'));
UI 拡張機能からサードパーティーの API を呼び出すこともできます。
アプリに関するイベント通知の受信
Webhook を使用して Stripe アプリのイベント (ユーザーによるインストールやアンインストールなど) をリッスンし、次のリアクションを組み込みのバックエンドで自動的にトリガーできるようにします。
- ユーザーアカウントの作成
- 権限の更新
- ユーザーアカウントの無効化およびデータの削除
イベントを受信
アカウントのみに提供されている非公開アプリ、または App Marketplace に掲載されているアプリに関するイベントを Stripe から受け取ることができます。
加盟店がイベントをトリガーすると、Stripe によって以下のEvent (イベント) オブジェクトが提供されます。このイベントには、イベントをトリガーした加盟店のアカウント ID を指定する account
プロパティが含まれます。
{ "id": "evt_f5uDWBAhkHgF9Y", "livemode": true, "object": "event", "type": "account.application.authorized", "account": "acct_xAG9jWRPg3dNuQ", "pending_webhooks": 2, "created": 1349654313, "data": {...} }
account
属性を使用すると、次の操作が可能になります。
- アプリをインストールした加盟店とアンインストールした加盟店の数を監視する。
- Stripe Connect を使用してユーザーの代わりに API コールを実行します。
Stripe アプリのイベント
Stripe Apps では、Stripe がサポートするイベントタイプに加えて次のイベントもサポートします。
加盟店のアクション | 結果としてアプリのバックエンドに送信される Webhook イベント |
---|---|
アプリを接続またはインストールする | account.application.authorized |
アプリを接続解除またはアンインストールする | account.application.deauthorized |
Webhook をローカルでテストする
次を対象に、Webhook をローカルでテストできます。
- 貴社のアカウントのすべてのユーザーのみが入手でき、そのアカウント自体のイベントをリッスンするアプリ
- Stripe App Marketplace で入手でき、貴社のアプリをインストールしたアカウントのイベントをリッスンするアプリ
Webhook をローカルでテストするには次のようにします。
次のようにアカウントを認証します。
Command Linestripe login
端末ウィンドウを 2 つ開きます。
ある端末ウィンドウで、イベント転送を設定します。
別の端末ウィンドウで、イベントをトリガーして Webhook との連携をテストします。
詳細については、Webhook エンドポイントのテストに関する Stripe ドキュメントをご覧ください。