ANTIGRAVITY LABEN
記事一覧/アプリ開発
アプリ開発/2026-04-27中級

AntigravityでWebhook受信エンドポイントを取りこぼしなく作る実践パターン

StripeやGitHubからのWebhookを確実に処理するエンドポイントの設計・実装・テストを、Antigravity を使って効率的に進めるための実践パターンをまとめました。

antigravity435webhook3stripe9github5cloudflare-workers8app-dev47

Stripe の決済が完了したのに、自分のサーバーでは記録が残っていありません。原因を追ったら Webhook の応答が 500 を返していて、Stripe 側で自動再送中だった——個人開発で SaaS を運用していると、こういう「届いているのに処理されない」状況に必ず一度はぶつかります。Webhook は受信側の小さな実装ミスが、決済の取りこぼしや状態の不整合という形で後から効いてくる、地味に怖い領域です。

ここではAntigravity の AI エージェントを実装の伴走者として使いながら、Stripe や GitHub からの Webhook を取りこぼさずに処理するエンドポイントを作る実践パターンをまとめました。フレームワークは Next.js(App Router)+ Cloudflare Workers を例にしますが、考え方は他のスタックでもそのまま使えます。

Antigravity でWebhook開発を進めると何が違うのか

Webhook の実装は、ローカルで動かしにくいことが最大のハードルです。Stripe CLI で擬似イベントを送れますが、それでも署名検証、冪等性、リトライ動作の3つを同時に検証するのは骨が折れます。私は当初 VS Code でこの作業をやっていましたが、Antigravity に乗り換えてから、エージェントに「Stripe の checkout.session.completed を受けて、KV に冪等キーを記録するエンドポイントを作って」と頼むと、署名検証のテストコードまで含めて出してくれるので、設計の精度が一段上がりました。

特に効いたのが、Manager Surface でエージェントの作業履歴を残しながら、複数の検証パターンを並行で試せることです。「冪等性チェックを Set にする vs KV にする」のような設計判断を、両方コードを生成させて比較できるのは、一人で開発しているとなかなかできない検証です。

Webhook 受信エンドポイントが満たすべき3つの要件

実装に入る前に、何を守れば「取りこぼさない」エンドポイントになるのか整理します。

ひとつ目は応答時間です。Stripe は受信側が 2xx を返すまで最大3日間リトライしますが、応答が30秒を超えるとタイムアウト扱いになります。重い処理をエンドポイント内でやってはいけません。

ふたつ目は署名検証です。リクエストヘッダーに含まれる署名を検証しないと、誰でも偽のイベントを送り込めてしまいます。これは認可の根幹なので省略不可です。

みっつ目は冪等性です。Stripe・GitHub・Slack いずれも、ネットワーク事情で同じイベントを2回送ることがあります。同じイベントを2回処理しても結果が変わらない設計が必須になります。

Stripe Webhook の最小実装(Next.js + Cloudflare Workers)

ここから実装に入ります。Next.js の Route Handler として書きますが、Cloudflare Workers 上で動く前提なので Stripe.createSubtleCryptoProvider() を使う点に注意してください。

// src/app/api/webhook/stripe/route.ts
import Stripe from "stripe";
import { getCloudflareContext } from "@opennextjs/cloudflare";
 
export const runtime = "edge";
 
export async function POST(request: Request) {
  const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
    apiVersion: "2024-10-28.acacia",
  });
 
  // 重要: Cloudflare Workers では非同期版を使う
  const signature = request.headers.get("stripe-signature");
  if (!signature) {
    return new Response("Missing signature", { status: 400 });
  }
 
  // 生のリクエストボディを取得(パース前)
  const body = await request.text();
 
  let event: Stripe.Event;
  try {
    event = await stripe.webhooks.constructEventAsync(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!,
      undefined,
      Stripe.createSubtleCryptoProvider()
    );
  } catch (err) {
    console.error("Webhook signature verification failed:", err);
    return new Response("Invalid signature", { status: 400 });
  }
 
  // 冪等性チェック(後述)
  const { env } = getCloudflareContext();
  const idempotencyKey = `webhook:stripe:${event.id}`;
  const existing = await env.KV.get(idempotencyKey);
  if (existing) {
    // すでに処理済み — 200 で応答してリトライを止める
    return new Response("Already processed", { status: 200 });
  }
 
  // 実処理は別関数に切り出す
  await handleEvent(event, env);
 
  // 処理完了マーカー(24時間保持)
  await env.KV.put(idempotencyKey, "1", { expirationTtl: 86400 });
 
  return new Response("OK", { status: 200 });
}

このコードのポイントは、stripe.webhooks.constructEventAsync を Subtle Crypto Provider 経由で呼んでいる箇所です。Node.js 環境でよく見る constructEvent(同期版)は Cloudflare Workers では動きません。私はこれで2時間溶かしました。

署名検証で詰まりやすい3つの落とし穴

実装中によくぶつかる問題と、その回避策を挙げておきます。

ひとつ目は、リクエストボディを JSON.parse してから署名検証してしまうケースです。署名は生のバイト列に対して計算されているので、一度パースすると検証が必ず失敗します。request.text() で生の文字列を受け取り、それをそのまま constructEventAsync の第一引数に渡してください。

ふたつ目は、Webhook シークレットを取り違えるケースです。Stripe ダッシュボードでは「ローカル開発用」と「本番用」のシークレットが別物として発行されます。Stripe CLI でリレーしているときは CLI が出力する whsec_... を使う必要があります。本番のシークレットをローカルテストに使うと、署名は一見通っているように見えてイベントが飛んでこない、という不可解な状態になります。

みっつ目は、ボディの文字エンコーディングです。Cloudflare Workers のリクエストは UTF-8 ですが、何らかの中間プロキシを噛ませていると BOM が混入することがあります。request.text() の戻り値の長さがダッシュボードのペイロード長と一致しているか、最初に確認するのが近道です。

冪等性をKVで保証する設計パターン

冪等性の実装は、Stripe や GitHub が払い出す event.id をキーにして「処理済みかどうか」を記録するのがシンプルで強固です。先ほどのコードで使った Cloudflare KV を例にすると、こうなります。

async function ensureIdempotent(
  env: Env,
  eventId: string,
  source: "stripe" | "github" | "slack"
): Promise<{ alreadyProcessed: boolean }> {
  const key = `webhook:${source}:${eventId}`;
  const existing = await env.KV.get(key);
 
  if (existing) {
    return { alreadyProcessed: true };
  }
 
  // まずマーカーだけ書いて、処理失敗時にリトライできるようにする
  // 実処理が成功したら status を "done" に更新
  await env.KV.put(key, "processing", { expirationTtl: 600 });
  return { alreadyProcessed: false };
}

ここで意識したいのは、「処理中マーカー」と「処理完了マーカー」を分けることです。処理中マーカーだけだと、エンドポイント内でクラッシュした場合に永遠に「処理中」のまま残ってしまいます。処理完了時に上書きする運用にすれば、TTL の範囲内で次のリトライが拾い直してくれます。

長期保管が必要なら D1 や Postgres に切り替えますが、24時間以内に同じ event.id で重複が来るかどうかをチェックするだけなら KV で十分というのが個人的な感覚です。Cloudflare KV の結果整合性は、Webhook 用途では実害になりません。

Antigravity 上でのテストと検証ワークフロー

開発中のテストは、Stripe CLI のリレー機能と Antigravity のターミナル統合を組み合わせるのが一番楽です。

# ターミナル1: Next.js 開発サーバー
npm run dev
 
# ターミナル2: Stripe CLI で Webhook をローカルにリレー
stripe listen --forward-to localhost:3000/api/webhook/stripe
 
# ターミナル3: テストイベントを送る
stripe trigger checkout.session.completed

Antigravity のターミナルは複数タブを横に並べられるので、3つのプロセスの出力を同時に追えます。エージェントに「いまの checkout.session.completed のレスポンスログから、署名検証が成功しているか確認して」と聞くと、ログを読んでくれて、失敗していれば原因の候補まで挙げてくれます。

GitHub Webhook も同じ要領で、gh webhook forward を使えば本番のリポジトリイベントをローカルに飛ばせます。Slack はローカル転送の公式ツールがないので、Cloudflare Tunnel か ngrok を経由させるのが現実的です。

全体を振り返って:明日のあなたが取りこぼさないために

Webhook の取りこぼしは、リリース直後ではなくユーザーが増えたあとに顕在化します。早めに「署名検証 → 冪等性記録 → 実処理 → 完了マーカー」という4段階を分離した設計に直しておくと、後から監視を足したりリトライ戦略を変えたりするときに楽です。

今日からできる一歩としては、いま動いている自分の Webhook エンドポイントを Antigravity で開いて、エージェントに「このエンドポイントが冪等性を満たしているか診断して」と聞いてみるのがおすすめです。10分で済む診断でも、本番で問題が起きる前に気づけることが多くあります。

決済まわりの設計をもっと深く

関連する記事として、AntigravityでのStripe決済フロー全体像Cloudflare KVを使ったエッジキャッシュとレート制限 も合わせて読むと、Webhook を含む決済基盤全体の設計が見えてきます。

シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

アプリ開発2026-04-28
Antigravity で SaaS を丸ごと作る — Stripe 課金・認証・デプロイを1つのIDEで完結させる
Antigravity IDE だけで SaaS プロダクトを構築する全工程を解説します。Supabase認証、Stripe課金、Cloudflare Workersデプロイまで、IDEを離れずに完結させる実践ガイドです。
アプリ開発2026-04-11
Antigravity × Convex でリアルタイム協調 SaaS を構築する:型安全なデータ同期から Stripe 課金まで
Antigravity IDE と Convex を組み合わせてリアルタイム協調 SaaS を構築する完全ガイド。スキーマ設計から Clerk 認証、ファイルストレージ、Stripe 課金まで本番対応の実装コードを体系的に解説します。
アプリ開発2026-04-06
Antigravity × マルチテナント SaaS 実装ガイド:Supabase RLS × Stripe メーター課金 × RBAC 権限制御
Antigravity を使ってマルチテナント SaaS を本番レベルで構築する完全ガイド。Supabase の Row Level Security でテナント分離、Stripe メーター課金で従量制プラン、RBAC で権限制御を実装する設計パターンと実装コードを詳解します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →