Amazon EventBridgeのイベントソースをStripeにする

この記事はJP_Stripesアドベントカレンダーの記事です。

2019年のre:invent系列で発表されたサービスのひとつ、EventBridge。

これをStripeと連携させるサンプルプロジェクトがGitHubにありましたので、さっそく試してみました。

セットアップ

Serverless Frameworkのセットアップ

まずはデプロイに使うServerless Frameworkをインストールします。

$ npm i -g serverless

ちなみにここで使うプロジェクトは、version 1.54.0 ~ 2.0.0の間である必要があります。古いバージョンを使われている方はアップデートしておきましょう。

プロジェクトのダウンロード

プロジェクト一式をGitHubから持ってきます。

$ git clone https://github.com/rangle/stripe-eventbridge.git
$ cd stripe-eventbridge

依存スタックの作成

SNSとSecret Managerのリソースを作成するためのスタックをまず起動します。

$ cd stacks
$ pwd
/path/to/stripe-eventbridge/stacks

$ sls deploy

Service Information
service: stripe-eventbridge-deps
stage: dev
region: us-east-1
stack: stripe-eventbridge-deps-dev
resources: 4
api keys:
  None
endpoints:
  None
functions:
  None
layers:
  None

このように出力されればOKです。

メインスタックの作成

続いてStripeからのWebhookをEventBridgeに流すAPIをデプロイします。

$ cd ../
$ pwd
/path/to/stripe-eventbridge

$ npm install

$ sls deploy
Service Information
service: stripe-eventbridge
stage: dev
region: us-east-1
stack: stripe-eventbridge-dev
resources: 16
api keys:
  None
endpoints:
  POST - https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/stripe/webhook
functions:
  stripe-webhook: stripe-eventbridge-dev-stripe-webhook
layers:
  None
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.

APIのURLをコピーしておきましょう。

StripeダッシュボードでWebhookを設定

StripeのWebhookを設定します。先ほど作成したスタックのAPIをエンドポイントに指定します。

今回はこのような形で設定しました。

署名シークレットを取得

Secret Managerに登録するシークレットを取得します。

作成したwebhookの詳細画面から署名シークレットが取得できますので、取得しましょう。

Secret Managerに署名シークレットを登録

AWSのSecret Managerに取得したシークレットを登録します。すでに「stripe-webhook-endpoint-secret」というシークレットが発行されていますので、ここに追加しましょう。

[シークレットの値を設定する]から追加できます。

プレーンテキストで追加します。

ここまででEventBridgeにイベントを送る部分を実装しました。

イベントを受け取るLambdaを作る

ここからはEventBridgeからきたイベントを処理するLambdaを作ります。

Serverless Frameworkでさくっと作成したもの

動かすコードはひとまずシンプルなこちらにします。

$ vim index.js
module.exports.eventSubscriber = async (event) => {
  console.log(JSON.stringify(event))
  return {
    message: 'Hello World',
    event
  }
}

yamlでのLambda定義はこちら

functions:
  event-subscriber:
    handler: handler.eventSubscriber
    events:
      - eventBridge:
          pattern:
            source:
              - Stripe
            detail-type:
              - payment_intent.succeeded
              - customer.created
              - charge.succeeded

これでデプロイすると、EventBridgeのセットアップ完了です。

ルールを見てみる

EventBridgeのイベント> ルールから作成されたルールを確認できます。

YAMLで書いた内容そのままできていますね。

動かしてみる

さっそく動かしてみましょう。

customer.createdイベントをsubscribeさせているので、これをまず使ってみます。

CloudWatchのメトリクスをみると、実行されていることがわかります。

Subscriber側のCloudWatch Logsをみると、StripeのWebhookから送られてきた値が確認できます。

{
  "version": "0",
  "id": "d605ab01-0929-3611-e8a6-274e47638db3",
  "detail-type": "customer.created",
  "source": "Stripe",
  "account": "372284591230",
  "time": "2019-12-13T10:36:34Z",
  "region": "us-east-1",
  "resources": [],
  "detail": {
    "id": "evt_1FpB24DHnG67uihbAp2ahwld",
    "object": "event",
    "api_version": "2019-02-11",
    "created": 1576231560,
    "data": {
      "object": {
        "id": "cus_GLsnjVzuUF4zhQ",
        ...
    },
    "livemode": false,
    "pending_webhooks": 1,
    "request": {
      "id": "req_4N2kWCRsmCdhc8",
      "idempotency_key": "039455f2-3367-46b5-b2dc-dead83ee78dd"
    },
    "type": "customer.created"
  }
}

detailの中がStripeからの値ですね。

実装チラ見

ざっとソースを見た所、肝はlib/eventBridge.jsの様子です。


const AWS = require('aws-sdk');
const { promisify } = require('util');

const eventbridge = new AWS.EventBridge();
eventbridge.shipIt = promisify(eventbridge.putEvents)

module.exports = {
    sendToEventBridge : async (bridgeName, event) => {
        const {id, type} = event
    
        console.log(`Sending event ${id} of type ${type} to the ${bridgeName} event bus on AWS EventBridge`)
        const params = {
            Entries: [
              {
                Detail: JSON.stringify(event),
                DetailType: type,
                EventBusName: 'default', //bridgeName,
                Resources: [],
                Source: 'Stripe',
                Time: new Date()
              },
            ]
        };

        await eventbridge.shipIt(params);
    }
}

Sourceにオリジナルなキーを指定し、DetailにString化した形でデータを投げつけています。EventBusNameやResourcesあたりがちょっと不明なので、この辺りは続けて調査かなと思います。

使い所など

EventBridgeにshipItでデータを投げ込むことで、EventBridgeに設定されたルールに基づいてAWS上で複数の処理を動かすことができます。

ですのでStripeであれば、Stripe上のイベントをShipItして、後続のLambdaがCRMへの連携や社内通知などの処理を行うということができるようになります。

問題点としては、StripeのWebhook自体はEventbridgeに投げ込むだけになるために、そこでエラーを検知することが難しくなります。

今回のサンプルでもSNSを使うようにしていますが、subscribeする側の異常を検知できるように設定してやる必要があります。

Serverless Frameworkを使うか否か

これは個人的な意見ですが、ちょっとEventBridgeを使い込むには難しいかなと思います。

というのも、Serverless Frameworkの書き方ではLambdaに対してEventBridgeを割り当てて作成する「1:1」のイメージです。が、EventBridgeの使い方としてはEvenrBridgeに対してLambdaを割り当てる「1:多」の構図になります。

複数のイベントをsubscribeさせたいということを考えると、EventBridgeはEventBridgeで管理して、そこにLambdaなどのリソースをARNなどで登録していく形がよいかなと思います。

Comment