LoginSignup
0
0

More than 1 year has passed since last update.

AstroでStripeのWebhookイベントを処理するREST APIを作成する方法

Posted at

Astroでは、静的なページだけでなくREST APIを作ることもできます。

APIを作成する場合、src/pagesディレクトリ内に、[API Path name].js(TypeScriptでは[API Path name].ts)を作成し、次のようなコードを追加します。

import { APIRoute } from "astro";

export const get: APIRoute = async () => {
    return {
        body: JSON.stringify({
            message: "Hello World"
        })
    }
}

Stripe Webhookの「署名チェック」を行う方法

AstroやNext.jsなどのウェブフレームワークでは、リクエスト内容を簡単に取得できるようなパーサー処理が内蔵されています。

しかしStripeなどの一部サービスでは、Webhookの署名チェック時に「パース前のリクエストBody」が必要になることがあります。

Astroでは2023/04時点でBodyのパーサーをオフにする方法が見つかりませんでしたので、次のような実装で対応します。

import { APIRoute } from "astro";
import Stripe from "stripe";

const STRIPE_SECRET_API_KEY = import.meta.env.STRIPE_SECRET_API_KEY;
const STRIPE_WEBHOOK_SECRET = import.meta.env.STRIPE_WEBHOOK_SECRET;

const stripe = new Stripe(STRIPE_SECRET_API_KEY, {
	apiVersion: "2022-11-15"
});

export const post: APIRoute = async (context) => {
    const { request } = context;
    const signature = request.headers.get("stripe-signature");
    try {
        const body = await request.arrayBuffer();
        const event = stripe.webhooks.constructEvent(
            Buffer.from(body),
          signature,
          STRIPE_WEBHOOK_SECRET
        );
        return {
            body: JSON.stringify({
                message: "ok",
            })
        };
      } catch (err) {
        const errorMessage = `⚠️  Webhook signature verification failed. ${err.message}`
        console.log(errorMessage);
        return new Response(errorMessage, {
            status: 400
        })
      }
}

リクエストBodyをrequest.json()request.text()ではなく、request.arrayBuffer()で取得します。

その後、Buffer.from()を通すことでパース前と同等のデータをconstructEventへ渡すことができます。

[Appendix] 汎用的なハンドラーにする方法

TypeScriptの型をより活用したい場合には、次のような関数を作ることもできます。

export const post: APIRoute = async (context) => {
    return withStripeWebhookVerification(context, async (event) => {
        return {
            body: JSON.stringify(event)
        }

    })
}

まず、Stripe Webhookの型定義をまとめたライブラリをインストールしましょう。

% npm install -D stripe-event-types

続いてWebhookの署名チェックや型定義のカスタマイズを行うwithStripeWebhookVerification関数を作ります。

/// <reference types="stripe-event-types" />
import { EndpointOutput } from "astro";
import { APIContext, APIRoute } from "astro";
import Stripe from 'stripe';
const STRIPE_SECRET_API_KEY = import.meta.env.STRIPE_SECRET_API_KEY;
const STRIPE_WEBHOOK_SECRET = import.meta.env.STRIPE_WEBHOOK_SECRET;

const stripe = new Stripe(STRIPE_SECRET_API_KEY, {
	apiVersion: '2022-11-15'
});


export type StripeWebhookAPIRoute = (event: Stripe.DiscriminatedEvent, context: APIContext) => EndpointOutput | Response | Promise<EndpointOutput | Response>

export const withStripeWebhookVerification = async (context: APIContext, callback: StripeWebhookAPIRoute) => {
    const { request } = context;
    const signature = request.headers.get('stripe-signature');
    try {
        const body = await request.arrayBuffer();
        const event = stripe.webhooks.constructEvent(
            Buffer.from(body),
          signature,
          STRIPE_WEBHOOK_SECRET
        );
        return callback(event as Stripe.DiscriminatedEvent, context);
    } catch (err) {
        const errorMessage = `⚠️  Webhook signature verification failed. ${err.message}`
        console.log(errorMessage);
        return new Response(errorMessage, {
            status: 400
        });
    };
};

あとはWebhook APIにしたいコードで、withStripeWebhookVerificationを呼び出すだけです。

export const post: APIRoute = async (context) => {
    return withStripeWebhookVerification(context, async (event) => {
        switch (event.type) {
            case 'customer.created': {
                console.log(event.data.object.email)
                break
            }
            case 'customer.subscription.created': {
                console.log(event.data.object.billing_cycle_anchor)
                break
            }
            default:
                break
        }
        return {
            body: JSON.stringify(event)
        }

    })
}

参考資料など

0
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
0
0