[stripe][AWS] Stripe の Event を CloudWatch custom metrics に記録

[Stripe](https://stripe.com/) では、各種イベントを Webhook としてぶん投げてくれる仕組みがあります。
以前、その Webhook を [Serverless Framework](https://serverless.com/) 使って API Gateway + Lambda で処理する記事書きました。
[Stripe の Webhook を API Gateway で受け取って Lambda で処理する](http://qiita.com/wokamoto/items/e0973773d81fa4c03406)

それを応用して、Event Type ごとに CloudWatch のカスタムメトリクスに記録しておくとテンションあがるかなーと思って、こんなん書いてみました。

# Stripe の Event を CloudWatch custom metrics に記録

## Lambda で Webhook 受け取って CloudWatch にカスタムメトリクス登録

Lambda は node.js 6.10 でやりましょう。
ソースはこんな感じです。

```js:handler.js
'use strict';

module.exports.incoming = (event, context, callback) => {
    const stripe = require('stripe')(process.env.STRIPE_API_KEY); // eslint-disable-line
    let response = {
        statusCode: 200,
        body: ''
    };

    try {
        // Parse Stripe Event
        const jsonData = JSON.parse(event.body); // https://stripe.com/docs/api#event_object

        // Verify the event by fetching it from Stripe
        console.log("Stripe Event: %j", jsonData); // eslint-disable-line
        stripe.events.retrieve(jsonData.id, (err, stripeEvent) => {
            const AWS = require('aws-sdk');
            const cloudwatch = new AWS.CloudWatch({apiVersion: '2010-08-01'});
            const eventType = stripeEvent.type ? stripeEvent.type : '';
            const params = {
                Namespace: process.env.NAMESPACE,
                MetricData: [
                    {
                        MetricName: eventType,
                        Dimensions: [{
                            Name: process.env.DIMENSION,
                            Value: process.env.STAGE
                        }],
                        Timestamp:  new Date,
                        Unit: 'Count',
                        Value: '1'
                    }
                ]
            };
            cloudwatch.putMetricData(params, (err) => {
                if (err) {
                    console.log('PutMetricData Error: %j', err);  // eslint-disable-line
                }
                response.body = JSON.stringify({
                    message: 'Stripe webhook incoming!',
                    stage: process.env.STAGE,
                    eventType: eventType
                });
                return callback(null,response);
            });
        });
    } catch (err) {
        response.statusCode = 501;
        response.body = JSON.stringify(err);
        callback(err,response);
    }
};
```

stripe.events.retrieve() で、送られてきたデータが本当に Stripe からのデータかどうかをチェックして、問題なければ CloudWatch のカスタムメトリクスに登録してる感じすね。

## Serverless framework で API Gateway の endpoint 用意

API GateWay + Lambda の設定は苦行でしたが、Serverless framework を使用することで途端に簡単になりました。
こんな感じの serverless.yml を用意してやればいいです。

```yaml:serverless.yml
service: put-stripe-event-to-custom-metrics

custom:
  stage:  ${opt:stage, self:provider.stage}
  logRetentionInDays:
    test: "14"         # stage[test]は14日
    live: "90"         # stage[live]は90日
    default: "3"       # stageが見つからなかったらこれにfallbackするために設定
  stripeAPIKey:
    test: "__STRIPE_TEST_API_SECRETKEY_HERE___"         # stage[test]用の Stripe API Key
    live: "__STRIPE_LIVE_API_SECRETKEY_HERE___"         # stage[live]用の Stripe API Key
    default: "__STRIPE_TEST_API_SECRETKEY_HERE___"      # stageが見つからなかったらこれにfallbackするために設定

provider:
  name: aws
  runtime: nodejs6.10
  stage: test
  region: us-east-1
  memorySize: 128
  timeout: 60

  iamRoleStatements:
    - Effect: Allow
      Action:
        - cloudwatch:PutMetricData
      Resource: "*"

  environment:
    STAGE: ${self:custom.stage}

# you can add packaging information here
package:
  include:
    - node_modules/**
  exclude:
    - .git/**
    - package.json

functions:
  incoming:
    handler: handler.incoming
    events:
      - http:
          path: stripe/incoming
          method: post
    environment:
      NAMESPACE: "___SERVICENAME__HERE__"
      DIMENSION: "Stripe"
      STRIPE_API_KEY: ${self:custom.stripeAPIKey.${self:custom.stage}, self:custom.stripeAPIKey.default}

resources:
  Resources:
    IncomingLogGroup:
      Properties:
        RetentionInDays: ${self:custom.logRetentionInDays.${self:custom.stage}, self:custom.logRetentionInDays.default}
```

serverless でのデフォルトに従うと、開発環境の stage は `dev` になりますが、ここは Stripe のお作法に従って開発環境は `test`、本番環境は `live` としましょうか。
environment として、以下の 4つを渡してますので、これを Lambda 内部では `process.env.{environment name}` で参照できます。

- STAGE
- NAMESPACE
- DIMENSION
- STRIPE_API_KEY

サンプルなので Stripe API KEY もそのまま渡してますが、必要なら暗号化するなりしてください。

`environment` の `STRIPE_API_KEY` とか、`Resources` のロググループの期限とかは stage によって切り替えています。
この辺は @sawanoboly の以下の記事が参考になります。
[Serverless Frameworkで、AWS Lambda function用に作られるCloudWatch Logsの期限を指定する](http://qiita.com/sawanoboly/items/670defb75931f3fd5274)

## Deploy !

### 前準備
deploy したいところですが npm パッケージ `stripe` を内部で使用しているので、まずは `npm install` で stripe パッケージを取ってきましょう。

```bash
$ npm install stripe
```

### いよいよデプロイ

もう、何も恐れる必要はありません。deploy してあげてください。

```bash
$ serverless deploy -v
 :
Serverless: Stack update finished...
Service Information
service: put-stripe-event-to-custom-metrics
stage: test
region: us-east-1
api keys:
  None
endpoints:
  POST - https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/test/stripe/incoming
functions:
  incoming: put-stripe-event-to-custom-metrics-test-incoming

Stack Outputs
ServiceEndpoint: https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/test
ServerlessDeploymentBucketName: put-stripe-event-to-cust-serverlessdeploymentbuck-xxxxxxxxxxxx
IncomingLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:000000000000:function:put-stripe-event-to-custom-metrics-test-incoming:1
```

`endpoint (https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/test/stripe/incoming)` に表示される URL が API Gateway の endpoint です。
これを Stripe の [Webhook 登録画面](https://dashboard.stripe.com/account/webhooks) で登録してあげればオッケーです。

本番環境( live )にデプロイする場合は、以下の感じで

```bash
$ serverless deploy -v -s live
```

やったー、CloudWatch のカスタムメトリクスに登録されてるよ！
<img width="1256" alt="Screen Shot 2017-06-13 at 17.17.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/25964/5237888e-ae79-041c-e923-18cd0d6c3561.png">

これで、Stripe からのチャリンチャリンが CloudWatch で確認できるようになりました。
可視化大事ですね。

現場からは以上です。