# サーバー側のロジックを追加する アプリでバックエンドコードを使用して、ユーザーのアクションとデータを検証し、処理します。 Stripe Apps を使用すると、サーバー側のロジックにセルフホスト型のバックエンドを追加できます。セルフホスト型のバックエンドサービスでは、次の実行が可能です。 - サーバー側の組み込みを必要とするサードパーティーのシステムと安全に連携します。 - Stripe からの *Webhook* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) イベントに登録し、Stripe を他のシステムと同期します。 - ユーザーがブラウザーを閉じたときに実行する、存続期間の長いアプリロジックを使用します。 - cron ジョブのような機能を提供するアプリを構築して、特定の操作をスケジュールします。 ![アプリのバックエンドのフローチャート](https://b.stripecdn.com/docs-statics-srv/assets/app-backend.46a6d040d44872962a59728aaa65ee06.png) セルフホスト型のバックエンドとアプリの関連性 ## UI からアプリのバックエンドに対してユーザーを認証する ダッシュボードからユーザーを認証するには、バックエンドに、共有シークレット付きのシグネチャーと、現在サインインしているダッシュボードユーザーのアカウントとユーザー ID が必要です。ユーザーに API を呼び出す権限がない場合は、[権限エラー](https://docs.stripe.com/error-handling.md?lang=node#permission-errors)が返されます。 ## Before you begin 1. バックエンドサービスが、HTTP リクエストを送受信できることを確認します。これまでに API サーバーを構築したことがない場合は、[インタラクティブな Webhook エンドポイントビルダー](https://docs.stripe.com/webhooks/quickstart.md)を使用してみることを検討してください。 1. [アプリをアップロード](https://docs.stripe.com/stripe-apps/upload-install-app.md)して共有シークレットを作成します。 ```bash stripe apps upload ``` アプリの最新バージョンの開発を完了していなくても問題ありません。アップロードしても、本番環境のアプリは更新されません。 1. バックエンドで署名を確認するため、アプリのシークレットを取得します。 a. [アプリ](https://dashboard.stripe.com/apps)からアプリを選択し、*Stripe アプリ* (An app that you can build on top of Stripe to customize the functionality of the Stripe Dashboard UI, leverage Stripe user data, store data on Stripe, and more)の詳細ページに移動します。 b. アプリケーション ID にあるオーバーフローメニュー (⋯) をクリックし、**署名シークレット**をクリックして署名シークレットダイアログを開きます。 c. クリップボード :clipboard: をクリックし、署名シークレットダイアログからアプリのシークレットをコピーします。 ### 署名付きのリクエストを送信する ![署名付きのリクエストを送信する](https://b.stripecdn.com/docs-statics-srv/assets/authenticate-ui-extension.9e45231756741ade2e1a73f56585864c.png) 署名付きのリクエストをアプリのバックエンドに送信するには、次のようにします。 1. [fetchStripeSignature](https://docs.stripe.com/stripe-apps/reference/extensions-sdk-api.md#fetchStripeSignature) 非同期関数を使用して、現在のシグネチャーを取得します。 1. `Stripe-Signature` ヘッダーに署名を追加します。 1. リクエストに `user_id` オブジェクトと `account_id` オブジェクトを含めます。 1. アプリのバックエンドで、リクエストに署名、アプリのシークレット、`user_id`、および `account_id` が含まれていることを確認します。 [追加データを指定して署名付きのリクエストを送信する例](https://docs.stripe.com/stripe-apps/build-backend.md#send-a-signed-request-with-additional-data)をご覧ください。 `Stripe-Signature` ヘッダーがある Stripe アプリからのリクエストの例: ```js 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 }` ```js // 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` を使用して署名文字列を生成します。 追加ペイロードを使用してシークレットを生成する例: ```js // 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, }; (async () => { 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', }), }); })(); ``` 追加ペイロードを使用して生成された署名を検証するサンプルバックエンド ```js // 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](https://docs.stripe.com/stripe-apps/reference/extensions-sdk-api.md#roledefinition) のリストが返されます。ペイロードの役割が指定された `user_id` に割り当てられていない場合、`fetchStripeSignature` が無効なリクエストエラー (400) を返します。 ```js // 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 つ生成します。 アプリのシークレットを期限切れにして作成するには、以下を手順に従います。 1. [アプリ](https://dashboard.stripe.com/apps)でアプリを選択し、*Stripe アプリ* (An app that you can build on top of Stripe to customize the functionality of the Stripe Dashboard UI, leverage Stripe user data, store data on Stripe, and more)の詳細ページに移動します。 1. ページヘッダーで、オーバーフローメニュー (⋯) をクリックし、**署名シークレット**をクリックして署名シークレットのダイアログを開きます。 1. 署名シークレットダイアログから**シークレットを期限切れにする**をクリックして、「シークレットを期限切れにする」ダイアログを開きます。 1. 現在のアプリのシークレットの有効期限を選択します。 1. **シークレットを期限切れにする**をクリックします。 ### クロスオリジンリソース共有 (CORS) を処理する [クロスオリジンリソース共有 (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) は、[クロスサイトスクリプティング攻撃(XSS)](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting)からアプリを安全に保つための重要なメカニズムです。Stripe App UI 拡張機能は、必然的にオリジン間および[サンドボックス化された iframe](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox) 内で使用されるため、クロスオリジンリクエストヘッダーの処理には特定のアプローチを採用する必要があります。 UI 拡張機能でバックエンドサービスのデータを取得するには、以下が行われるようにバックエンドサービスを設定する必要があります。 - [Options メソッド](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS)を使用するリクエストを許可する。 - `null` オリジンからのリクエストを許可するには、`Access-Control-Allow-Origin` を `*` に設定します。 > セキュリティ上の理由により、UI 拡張機能は[安全にサンドボックス化された iframe](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox) 内で実行されるため、オリジンが null になります。 多くのバックエンドフレームワークには、CORS の処理に役立つライブラリとガイダンスが用意されています。より詳細なガイダンスについては、フレームワークのドキュメントをご確認ください。 特定のユーザーまたはアカウントの代わりに Stripe からのリクエストを認証するには、[UI からバックエンドに対してユーザーを認証する](https://docs.stripe.com/stripe-apps/build-backend.md#authenticate-ui-to-backend)を参照してください。 > 認証済みのエンドポイントと UI 拡張機能が通信するエンドポイントでのみ `Access-Control-Allow-Origin: *` を使用するように設定します。その他の対策が取られていない場合、未認証のエンドポイントは、[CSRF](https://developer.mozilla.org/en-US/docs/Glossary/CSRF) 攻撃に対して脆弱になります。 ## Stripe API を使用する Stripe と対話するために、Stripe API を使用して、リクエストを認証できます。 ### リクエストを認証する リクエストを認証するには、既存の加盟店アカウント API キーを使用して Stripe と対話し、ユーザーの `stripeAccountId` を指定します。 サーバー側での API コールでは、プラットフォームユーザーの Stripe アカウント ID (プレフィックスは `acct_`) とともに特殊なヘッダー `Stripe-Account` を使用することで、連結アカウントとしてリクエストを作成できます。下記は、プラットフォームの [API シークレットキー](https://docs.stripe.com/keys.md)とユーザーの[アカウント](https://docs.stripe.com/api/accounts.md) ID を使用して [PaymentIntent を作成](https://docs.stripe.com/api/payment_intents/create.md)する方法を示す例です。 ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -H "Stripe-Account: {{CONNECTEDACCOUNT_ID}}" \ -d amount=1000 \ -d currency=usd \ -d "payment_method_types[]"=card ``` URL に Stripe アカウント ID を含む API リクエストのすべてで、`Stripe-Account` ヘッダーによる方法が必要になります。下記は、URL 内のユーザーの[アカウント](https://docs.stripe.com/api/accounts.md) ID を使用して[アカウントを取得](https://docs.stripe.com/api/accounts/retrieve.md)する方法を示す例です。 ```curl curl https://api.stripe.com/v1/accounts/{{CONNECTEDACCOUNT_ID}} \ -u "<>:" ``` また、Stripe のすべてのサーバー側ライブラリは、リクエストごとにこの方法に対応します。次に例を示します。 #### Ruby ```ruby Stripe.api_key = "{{PLATFORM_SECRET_KEY}}" Stripe::Customer.create( {email: 'person@example.edu'}, {stripe_account: '{{CONNECTED_STRIPE_ACCOUNT_ID}}'} ) # Fetching an account just needs the ID as a parameter Stripe::Account.retrieve('{{CONNECTED_STRIPE_ACCOUNT_ID}}') ``` ### UI 拡張機能からセルフホスト型のバックエンドを呼び出す UI 拡張機能からバックエンドへのリクエストを実行するときは、[リクエストとともに署名を送信](https://docs.stripe.com/stripe-apps/build-backend.md#authenticate-ui-to-backend)して、リクエストの正当性を証明します。UI 拡張機能から、現在のユーザーの `stripeAccountId` を渡して、ユーザーの代わりにバックエンドリクエストを実行できるようにします。 ```javascript // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. // Find your keys at 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!")); ``` ## 他の API を呼び出す セルフホスト型のバックエンドから、独自の API や他の開発者または会社が作成した API など、すべての API を呼び出すことができます。 詳細については、[アプリにシークレット認証情報やトークンを保存する](https://docs.stripe.com/stripe-apps/store-secrets.md)方法をご覧ください。 Stripe から別のサービスにユーザー情報を渡す必要がある場合は、UI 拡張機能から渡された `stripeAccountId` を使用します。 ```javascript 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 を呼び出す](https://docs.stripe.com/stripe-apps/build-ui.md#use-third-party-apis)こともできます。 ## アプリに関するイベント通知を受け取る *Webhook* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) を使用して Stripe アプリのイベント (ユーザーによるインストールやアンインストールなど) をリッスンし、次のリアクションを組み込みのバックエンドで自動的にトリガーできるようにします。 - ユーザーアカウントの作成 - 権限の更新 - ユーザーアカウントの無効化およびデータの削除 ### イベントを受信 アカウントのみに提供されている非公開アプリ、または *App Marketplace* (A marketplace to browse, search, and install Stripe apps) に掲載されているアプリに関するイベントを Stripe から受け取ることができます。 #### App Marketplace での公開 *App Marketplace* (A marketplace to browse, search, and install Stripe apps) で公開されているアプリに関するイベントを受け取るには、以下のようにします。 1. [アプリのバックエンドで Webhook イベントを処理](https://docs.stripe.com/webhooks.md#webhook-endpoint-def)します。 1. Stripe ダッシュボードで [Webhook エンドポイントを登録](https://docs.stripe.com/webhooks.md#webhooks-summary)し、その際、**連結アカウントでイベントをリッスンする**を選択します。 1. アプリに `event_read` 権限を追加します。 ```bash stripe apps grant permission "event_read" "Allows reading event data from users who have installed the app" ``` 1. Webhook エンドポイントがリッスンするイベントごとに、対応する権限を追加します。 ```bash stripe apps grant permission "PERMISSION_NAME" "EXPLANATION" ``` 以下を置き換えます。 - [イベントの権限の名前](https://docs.stripe.com/stripe-apps/reference/permissions.md)を含む `PERMISSION_NAME`。 - アクセス有効化についての説明を示す `EXPLANATION`。この説明はアプリのインストール時にユーザーに表示されます。例: 「アプリをインストールしたユーザーからのイベントデータの読み取りを許可する」。 #### お客様のアカウントのみに提供 (非公開) アカウントのユーザーのみに提供されている非公開アプリのイベントを受け取るには、以下のようにします。 1. アプリのバックエンドで [Webhook イベント](https://docs.stripe.com/webhooks.md#webhook-endpoint-def)を処理します。 1. Stripe ダッシュボードで [Webhook エンドポイントを登録](https://docs.stripe.com/webhooks.md#webhooks-summary)します。 事業者がイベントをトリガーすると、Stripe によって以下の [Event](https://docs.stripe.com/api/events/object.md) オブジェクトが提供されます。このイベントには、このイベントをトリガーした事業者のアカウント ID を特定する `account` プロパティーが含まれます。 ```json { "id": "evt_orWziM4j7CiRL8", "livemode": true, "object": "event", "type": "app.install.created", "account": "acct_orWziM4j7CiRL8", "pending_webhooks": 2, "created": 1349654313, "data": {...} } ``` `account` 属性を使用すると、次の操作が可能になります。 - 貴社のアプリをインストールおよびアンインストールした事業者の数を監視します。 - [Stripe Connect を使用してユーザーの代わりに API コールを実行します](https://docs.stripe.com/connect/authentication.md)。 ### Stripe アプリのイベント Stripe Apps では、[Stripe がサポートするイベントタイプ](https://docs.stripe.com/api/events/types.md)に加えて次のイベントもサポートされます。 | 加盟店のアクション | 結果としてアプリのバックエンドに送信される Webhook イベント | | --------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | アプリを接続またはインストールする | [account.application.authorized](https://docs.stripe.com/api/events/types.md#event_types-account.application.authorized) | | アプリを接続解除またはアンインストールする | [account.application.deauthorized](https://docs.stripe.com/api/events/types.md#event_types-account.application.deauthorized) | アプリのインストールイベントのリスニングと返されるデータの詳細については、[Listen to events](https://docs.stripe.com/stripe-apps/events.md) を参照してください。 ## イベントの動作はインストールの環境によって異なります ユーザーは、本番環境、テスト環境、その両方、または*サンドボックス* (A sandbox is an isolated test environment that allows you to test Stripe functionality in your account without affecting your live integration. Use sandboxes to safely experiment with new features and changes)環境でアプリをインストールできます。以下のガイドラインに従って Webhook を設定します。 - アプリがサンドボックス環境にインストールされている場合、イベントはサンドボックス環境にのみ送信されます。 - アプリが本番環境でのみインストールされている場合は、本番環境のイベントが本番環境のエンドポイントに送信されます。 - アプリがテスト環境でのみインストールされている場合、テスト環境のイベントがテスト環境のエンドポイントに送信されます。 - アプリが両方の環境でインストールされている場合、テスト環境のイベントはテスト環境と本番環境の両方のエンドポイントに送信され、本番環境のイベントは本番環境のエンドポイントに送信されます。 本番環境とテスト環境で Connect `/webhook` を設定してから、アプリのどちらのモードでも以下のスニペットを使用します。エンドポイント全体の例については、[Webhook に関するドキュメント](https://docs.stripe.com/webhooks.md#example-endpoint)をご覧ください。 #### Ruby ```ruby require 'sinatra' require 'json' post '/webhook' do event = JSON.parse(request.body.read) if event['livemode'] puts "Handling live event: #{event}" # Handle live events handle_live_event(event) else puts "Handling test event: #{event}" # Handle test events handle_test_event(event) end status 200 body 'Event received' end ``` ### トラブルシューティング 予期したイベントの受信がない場合は、構成を見直して以下の一般的な見落としがないか確認してください。 - 本番環境の Webhook には本番環境のキーが使用され、テスト環境の Webhook にはテスト環境のキーが使用されていることを確認してください。 - 本番環境のイベントの場合は、インストールするアカウントが有効化されていることを確認してください。 - アプリが本番環境とテスト環境の両方のイベントを処理できることを確認します。 - テストイベントをトリガーしても、アプリ構成で明示的に設定されていない限り、ライブイベントの動作はレプリケートされません。 ### Webhook をローカルでテストする 次を対象に、Webhook をローカルでテストできます。 - 貴社のアカウントのすべてのユーザーのみが入手でき、そのアカウント自体のイベントをリッスンするアプリ - Stripe App Marketplace で入手でき、貴社のアプリをインストールしたアカウントのイベントをリッスンするアプリ Webhook をローカルでテストするには次のようにします。 1. [Stripe CLI をインストール](https://docs.stripe.com/stripe-cli.md)します。 1. 次のようにアカウントを認証します。 ```bash stripe login ``` 1. 端末ウィンドウを 2 つ開きます。 - ある端末ウィンドウで、[イベント転送を設定](https://docs.stripe.com/webhooks.md#local-listener)します。 #### 貴社のアカウントのみの非公開アプリ ```bash stripe listen --forward-to localhost:{{PORT}}/webhook ``` #### App Marketplace でのリスト公開 ```bash stripe listen --forward-connect-to localhost:{{PORT}}/webhook ``` - 別の端末ウィンドウで、[イベントをトリガーして Webhook との連携をテスト](https://docs.stripe.com/webhooks.md#trigger-test-events)します。 #### 貴社のアカウントのみの非公開アプリ ```bash stripe trigger {{EVENT_NAME}} ``` #### App Marketplace でのリスト公開 ```bash stripe trigger --stripe-account {{EVENT_NAME}} ``` 詳細については、[Webhook エンドポイントのテスト](https://docs.stripe.com/webhooks.md#local-listener)に関する Stripe ドキュメントをご覧ください。 ## See also - [UI を構築する](https://docs.stripe.com/stripe-apps/build-ui.md) - [アプリをアップロードおよびインストールする](https://docs.stripe.com/stripe-apps/upload-install-app.md) - [アプリを公開する](https://docs.stripe.com/stripe-apps/publish-app.md)