Webサービスというのは基本的にプル型です。クライアント側からアクセスしてはじめてデータを受け取ったり、逆に登録したりします。しかし購入が発生した時や、何からのイベントが発生したときにそれをすぐに知りたいというニーズは強くあります。
それを可能にするのがWebhookです。通常はWeb API利用側がサーバにアクセスするところを、逆にサーバからWeb API利用者側の指定するURLにアクセスしてもらう仕組みです。簡単なシーケンス図は次のようになります。
有名なところではGitHubのWebhookがあって、リポジトリへのコードプッシュがあったり、マージした時に通知を受け取れる仕組みがあります。またSlackもチャットメッセージが来た時にWebhookで通知してもらえます。同様にPAY.JPでもWebhookが用意されていますので、取引が発生したり、定期課金が作成されたタイミングなどで通知を受け取れます。
注意点として、Webhookを完全に信用するのは危険です。100%確実に届くとは言い切れません。Webhookを受け取るサーバが落ちているケースもあるでしょう。PAY.JPのWebhookでは400または500系エラーを受け取った際には3分間隔で3回までリトライしますが、それを超えると通知を行いませんので注意してください。これはWebhookをサポートするサービス全般に言えるもので、データをすべて受け取るのはWebhookではなく通常のWeb APIを用いるのが良いでしょう。
この記事ではそんなWebhookを実装するための方法を紹介します。
サーバの準備
WebhookはPAY.JPからあらかじめ指定したサーバを呼び出す仕組みです。つまりサーバはインターネット上に公開されている必要があります。クラウドなどを使ってサーバを立ち上げるのが一番良いのですが、開発時には ngrok を使うのが便利です。これは ngrok のサービスとローカルのHTTPサーバがつながり、ローカルコンピュータをインターネット上に公開できる仕組みです(厳密には違いますが、ここでは簡略化しています)。昔はダイナミックDNSで同様の仕組みが提供されていましたが、もっと手軽に使える技術になります。
今回のサンプルは Node.js + Express で作成してあります。Node.js はJavaScriptをサーバ上で実行する技術で、ExpressはNode.jsでよく使われるWebアプ
リケーションフレームワークです。そして、PAY.JPで取引が発生すると /hooks
が呼ばれるようになっています。コードは後で解説します。
router.post('/hooks', (req, res, next) => { // Webhook処理 if (req.headers['x-payjp-webhook-token'] !== config.webhook_token) { // 不正なアクセス return res.status(401).send({}); } if (req.body.type === 'charge.succeeded') { const charge = req.body.data; console.log('決済が成功しました'); console.log(`id => ${charge.id}`); console.log(`card.last4e => ${charge.card.last4}`); console.log(`card.exp => ${charge.card.exp_year}/${charge.card.exp_month}`); console.log(`price => ${charge.amount}(${charge.currency})`); } res.send({}); });
設定を変更する
PAY.JPの管理画面で取得できる各種設定情報を config.json
の中に記述します。config.example.json
というファイルがあるので、 config.json
に名前を変更してください。内容は最初、次のようになっているはずです。
{ "public_key": "YOUR_PUBLIC_KEY", "secret_key": "YOUR_SECRET_KEY", "webhook_token": "YOUR_WEBHOOK_TOKEN" }
これのテスト公開鍵、テスト秘密鍵をそれぞれ書き換えてください。
{ "public_key": "pk_...e5", "secret_key": "sk_...24", "webhook_token": "whook_4a...f8" }
次にローカルコンピュータでサーバを立ち上げます。
$ node ./bin/www
そして ngrok をインストールします。
brew install cask ngrok
インストールしたら、以下のコマンドでローカルコンピュータにインターネット上からアクセスできるようになります。
$ ngrok http 3000 ngrok by @inconshreveable (Ctrl+C to quit) Session Status online Account Atsushi Mint Nakatsugawa (Plan: Free) Version 2.2.8 Region United States (us) Web Interface http://127.0.0.1:4040 Forwarding http://001ca638.ngrok.io -> localhost:3000 Forwarding https://001ca638.ngrok.io -> localhost:3000 Connections ttl opn rt1 rt5 p50 p90
一時的に使えるURL(今回の場合は https://001ca638.ngrok.io
)ができましたので、このURL + /hooks をPAY.JPの管理画面にてWebhookするURLとして登録します。
これで準備完了です。
取引を行う
ではテストで決済を行ってみます。決済は http://localhost:3000/
でできるようになっています。アクセスすると 100円支払うという表示とボタンが'確認できるはずです。
ボタンを押すとPAY.JPのクレジットカード入力フォームが表示されます。テストのカード番号はこちらを参考にしてください。
クレジットカード番号を入力すると、 http://localhost:3000/pay が呼ばれて1000円の決済処理が行われます。決済処理が無事完了すると、次のような場面が表示されるでしょう。
Webhookが呼ばれる
そして少し待つとPAY.JPからWebhookが呼ばれます。ターミナルなどの画面に次のように表示されたら成功です。
決済が成功しました id => ch_84970e95eb7f66e21a41a4dac60ae card.last4e => 4242 card.exp => 2020/10 price => 1000(jpy)
Webhookの検証
WebhookのURLには特にセキュリティがありません。外部から誰でも呼べてしまうアドレスになります。そこで、呼ばれた内容が正しいことを検証する方法としてリクエストの X-Payjp-Webhook-Token
ヘッダーを確認してください。この内容はPAY.JPの管理画面で確認できますので、リクエストに踏まれるヘッダーと同じものであればPAY.JPからの正当なWebhookだと確認できます。
イベントの種類
なお、PAY.JPでは以下のイベントの際にWebhookを呼び出します。詳細はこちらで確認できます。それぞれにイベント種別(type)が設定されていますので、それを見て何のイベントであるかを知ることができます。
- 支払い関係
- 成功
- 失敗
- 更新
- 返金
- 確定
- トークン関係
- 作成
- 顧客関係
- 作成
- 更新
- 削除
- カード作成
- カード更新
- カード削除
- プラン関係
- 作成
- 更新
- 削除
- 定期課金
- 作成
- 更新
- 削除
- 停止
- 再開
- キャンセル
- 期間更新
- 入金関係
- 内容確定
PAY.JPから送られてきたデータを使って、チャットにメッセージを流したり、メールを送信するといった処理につなげるのも良いでしょう。前述の通り、100%の信頼性を前提にするのはお勧めしませんが、通知を使うことで業務の生産性をさらに高めることができるはずです。