1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Expressで、Stripe Appsと連携するAPIサーバーを用意する方法

Posted at

この記事は、Stripe Apps を25日間紹介し続ける Advent Calendar 2022 20日目の記事です。

スクリーンショット 2022-11-24 17.56.45.png

さまざまなサービスとStripe Appsアプリを連携させる場合、「すでにあるAPIサーバーやシステムと接続させる」必要が発生します。

今回はExpressを例に、安全にStripe Appsアプリと外部のAPIサーバーを連携させる方法を紹介します。

ExpressでStripe Appsと連携するAPIを作成する

Step0: プロジェクトのセットアップ

まずは簡単なExpressアプリを用意しましょう。

ライブリロードなどの機能を手軽に手に入れるため、今回はNxで作成します。

便宜上、今回はプロジェクト名をexpress-stripeで統一して進めます。

$ npx create-nx-workspace --preset=express
Need to install the following packages:
  create-nx-workspace@15.3.3
Ok to proceed? (y) y

 >  NX   Let's create a new workspace [https://nx.dev/getting-started/intro]

✔ Repository name                       · express-stripe
✔ Application name                      · express-stripe
✔ Enable distributed caching to make your CI faster · No

セットアップ後、プロジェクトのディレクトリに移動してアプリを起動しましょう。

$ cd express-stripe
$ npm run start

chunk (runtime: main) main.js (main) 689 bytes [entry] [rendered]
webpack compiled successfully (24f80a260b1d420d)
Debugger listening on ws://localhost:9229/8faba312-c87d-47dc-b5e7-ef9d2d27b319
Debugger listening on ws://localhost:9229/8faba312-c87d-47dc-b5e7-ef9d2d27b319
For help, see: https://nodejs.org/en/docs/inspector
Type-checking in progress...
Listening at http://localhost:3333/api
No errors found.

APIの準備はできました。
ただし、CORSが未設定のため、ブラウザから呼び出すとエラーが発生します。

デモ用のAPIとして、apps/express-stripe/src/main.tsを次のように変更しましょう。

const app = express();
+app.use((request, response, next) => {
+  response.header('Access-Control-Allow-Origin', '*');
+  response.header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
+  response.header(
+    'Access-Control-Allow-Headers',
+      'Content-Type, Stripe-Signature'
+  );
+  if (request.method.toLocaleUpperCase() === 'OPTIONS') {
+    return response.send(200);
+  }
+  next();
+});
app.use('/assets', express.static(path.join(__dirname, 'assets')));

これでAPIが用意できました。

Step1: シンプルなAPIを連携させてみよう

さっそくStripe AppsのアプリとAPIを連携させてみましょう。

Stripe Appsアプリの、src/views/App.tsxを次のように実装します。

import { ContextView, Box } from "@stripe/ui-extension-sdk/ui";
import { useEffect, useState } from 'react';
import { ExtensionContextValue } from "@stripe/ui-extension-sdk/context";


const App = (props: ExtensionContextValue) => {
  const [message, setMessage] = useState("");
  useEffect(() => {
    fetch(new URL("http://localhost:3333/api"))
      .then(response => response.json())
      .then(data => {
        setMessage(data.message);
      })
      .catch(console.log);
  }, []);
  return (
    <ContextView
      title="API呼び出しサンプル"
    >
      <Box>{message}</Box>
    </ContextView>
  );
};

export default App;

アプリを更新すると、Express APIからのレスポンスが画面に表示されます。

スクリーンショット 2022-12-19 16.37.59.png

Step2: 署名検証を追加して、より安全に連携する

より安全にAPIサーバーを運用するために、Stripeが用意する署名検証の仕組みが利用できます。

開発中のアプリを、Stripeにアップロードする

署名シークレットを取得するには、Stripeアカウントにアプリを登録する必要があります。

そのため、Stripe CLIで、開発中のアプリを一度アップロードしましょう。

$ stripe apps upload
⬆ You are about to upload your Stripe App First Stripe App version "0.0.1" to テストアカウント(日本).
? Would you like to proceed? [Y/n] █

[y]を入力すると、ビルドとアップロードが始まります。

✔ Built files for production
✔ Packaged files for upload
✔ Uploaded First Stripe App

🌐 Press Enter to continue on the Stripe dashboard

と表示されますので、[Enterキー]を押してStripeダッシュボードに移動しましょう。

スクリーンショット 2022-12-19 16.48.09.png

アプリの画面に移動できれば、アップロード完了です。

アップロードしたアプリは、公開操作を行うまでは利用できません。
「必要な情報を取得するために、アプリを登録する」くらいの気軽さでアップロードしてみてください。

ビルドエラーにご注意
アップロード前に、ビルドが行われます。
このビルド時にエラーが発生すると、アップロード処理が中断されます。

Would you like to proceed: y
×  Failed to build files
✘ [ERROR] Expected identifier but found "{"

    .build/manifest.js:23:2:
      23 │   {
         ╵   ^

.build/のコードエラーが出た場合は、rm -rf ./.build/で中身を一度削除してみましょう。

アプリの署名シークレットを取得する

アプリを登録できたので、署名シークレットを取得しましょう。

アプリ詳細画面の[...]をクリックすると、[署名シークレット]が表示されます。

スクリーンショット 2022-12-19 16.52.28.png

[署名シークレット]をクリックすると、アプリ用の署名シークレットが発行されます。

スクリーンショット 2022-12-19 16.53.37.png

サーバー側で署名検証処理を実装する

署名シークレットを取得できたので、サーバー側で検証処理を組み込みましょう。

まずはStripe SDKをインストールします。

$ npm i stripe

続いて.envを作成して、StripeのAPIキーと署名シークレットを環境変数に登録します。

STRIPE_SECRET_API_KEY=sk_test_xxx
STRIPE_APP_SECRET=absec_xxxx

apps/express-stripe/src/main.tsを次のように変更しましょう。

import * as express from 'express';
import * as path from 'path';
+import Stripe from 'stripe';
+const stripe = new Stripe(process.env.STRIPE_SECRET_API_KEY as string, {
+  apiVersion: "2022-11-15"
+});

const app = express();
+app.use(express.json());

...
-app.get('/api', (request, response) => {
+app.post('/api', (request, response) => {
+  const sig = request.headers['stripe-signature'];
+  const payload = JSON.stringify(request.body);
+  try {
+    stripe.webhooks.signature.verifyHeader(payload, sig as string, process.env.STRIPE_APP_SECRET as string);
+  } catch (error) {
+    response.status(400).send(error.message);
+    return;
+  }
  response.send({ message: 'Welcome to express-stripe!' });
});

アプリを再読み込みすると、APIがエラーを返すようになります。

スクリーンショット 2022-12-19 17.06.01.png

アプリから、署名検証に対応したリクエストを送信する

サーバー側の準備ができましたので、アプリ側も署名検証に対応しましょう。

Stripe Appsアプリの、src/views/App.tsxを次のように変更します。

import { ExtensionContextValue } from "@stripe/ui-extension-sdk/context";
+import fetchStripeSignature from '@stripe/ui-extension-sdk/signature';

const App = (props: ExtensionContextValue) => {
  const [message, setMessage] = useState("");
+  const {
+    id: userId,
+    account: {
+      id: accountId,
+    }
+  } = props.userContext;
  useEffect(() => {
-    fetch(new URL("http://localhost:3333/api"))
+    if (!userId || !accountId) return;
+    fetchStripeSignature()
+    .then(signature => {
+      const signagurePayload = {
+        user_id: userId,
+        account_id: accountId,
+      }
+      return fetch(new URL("http://localhost:3333/api"), {
+        method: "POST",
+        headers: {
+          "Stripe-Signature": signature,
+          "Content-Type": "application/json"
+        },
+        body: JSON.stringify(signagurePayload)
+      })
+    })
      .then(response => response.json())
      .then(data => {
        setMessage(data.message);
      })
      .catch(console.log);
  }, [userId, accountId]);
  return (
    <ContextView
      title="API呼び出しサンプル"
    >
      <Box>{message}</Box>
    </ContextView>
  );
};

変更を保存すると、API呼び出しが再度成功します。

スクリーンショット 2022-12-19 17.30.00.png

終わりに

既存のAPIをStripe Appsから呼び出す場合にも、署名検証をサポートしたエンドポイントを作成することをお勧めします。

検証に利用するデータを追加することもできますので、ぜひ下記のドキュメントを参考にお試しください。

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

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

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

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

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?