Webhook ペイロードから Java POJO を生成する
汎用的な Webhook エンベロープ(id、type、created、data)を Java POJO に変換します。イベント種別ごとに変わる data フィールドのモデリング方法も解説。
詳細な説明
Java での Webhook ペイロード
ほとんどの Webhook プロバイダ(Stripe、GitHub、Slack、Twilio)はイベントデータをエンベロープに包みます。イベント ID、タイムスタンプ、イベントタイプ、そしてタイプ依存の data フィールドという構造です。このパターンは十分一貫しているため、ひとつの生成 POJO を出発点として使えます。
汎用 Webhook エンベロープ
{
"id": "evt_1NXyz9abcdef",
"type": "invoice.paid",
"created": 1700000000,
"api_version": "2024-04-10",
"livemode": true,
"data": {
"object": {
"id": "in_1NXabc",
"amount_paid": 2500,
"currency": "usd",
"customer": "cus_NffrFeUfNV2Hib"
}
}
}
生成される POJO
package com.example.webhooks;
import com.fasterxml.jackson.annotation.JsonProperty;
public class WebhookEvent {
private String id;
private String type;
private Long created;
@JsonProperty("api_version")
private String apiVersion;
private Boolean livemode;
private Data data;
// アクセサ
}
public class Data {
private Object object; // イベントタイプ依存
// アクセサ
}
data.object のポリモーフィズム対応
object フィールドの形はイベントタイプごとに変わります。3 つの選択肢があります。
1. Map<String, Object>(最もシンプル)
private Map<String, Object> object;
セットアップは最速ですが型安全性は失われます。
2. JsonNode(Jackson のツリーモデル)
private JsonNode object;
固定型に縛られず object.get("amount_paid").asInt() のようにツリーへ問い合わせできます。
3. ポリモーフィックデシリアライズ(最も厳密)
エンベロープを先にデシリアライズし、type で分岐してから object を正しい具象クラスにデシリアライズします。
WebhookEvent event = mapper.readValue(payload, WebhookEvent.class);
switch (event.getType()) {
case "invoice.paid":
Invoice invoice = mapper.convertValue(event.getData().getObject(), Invoice.class);
// 請求書の処理
break;
case "customer.created":
Customer customer = mapper.convertValue(event.getData().getObject(), Customer.class);
// 顧客の処理
break;
}
@JsonTypeInfo のアプローチは ポリモーフィック型のサンプル を参照してください。
Webhook 署名の検証
デシリアライズの 前 に必ず署名を検証してください。
String signature = request.getHeader("Stripe-Signature");
String payload = request.getBody();
if (!verifySignature(payload, signature, webhookSecret)) {
throw new SecurityException("Invalid webhook signature");
}
WebhookEvent event = mapper.readValue(payload, WebhookEvent.class);
HMAC 比較を自前実装せず、必ずプロバイダ公式 SDK のヘルパーを使ってください。
冪等性
Webhook プロバイダは失敗時に再送するため、同じイベント ID が複数回届く可能性があります。イベント ID を永続化して重複をスキップします。
if (eventLog.exists(event.getId())) return; // 処理済み
processEvent(event);
eventLog.record(event.getId());
ユースケース
決済、コミュニケーション、CI/CD、CRM の Webhook はいずれも、型付きエンベロープクラスとイベントタイプ別の処理ロジックを必要とします。エンベロープ POJO の生成は、堅牢な Webhook 取込サービスを作る最初の一歩です。