Webhook ペイロードから Java POJO を生成する

汎用的な Webhook エンベロープ(id、type、created、data)を Java POJO に変換します。イベント種別ごとに変わる data フィールドのモデリング方法も解説。

Real-world

詳細な説明

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 取込サービスを作る最初の一歩です。

試してみる — JSON to Java

フルツールを開く