Advanced

Webhook:Claude Code 实战指南

了解webhook:Claude Code 实战. 包含实用技巧和代码示例。

Webhook とは

Webhookは事件発生時にHTTP请求で外部システムに通知する仕組みです。決済完成通知、Git push事件、表单发送など多くの場面で使われます。借助 Claude Code,堅牢なWebhook实现を高效地构建可以。

Webhook接收側の实现

import express from "express";
import crypto from "crypto";

const app = express();

// rawBodyを保持する中间件
app.use("/webhooks", express.raw({ type: "application/json" }));

// 署名验证
function verifyWebhookSignature(
  payload: Buffer,
  signature: string,
  secret: string
): boolean {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expected}`)
  );
}

app.post("/webhooks/stripe", async (req, res) => {
  const signature = req.headers["stripe-signature"] as string;
  const secret = process.env.STRIPE_WEBHOOK_SECRET!;

  // 署名验证
  if (!verifyWebhookSignature(req.body, signature, secret)) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = JSON.parse(req.body.toString());

  // 冪等性チェック
  const processed = await redis.get(`webhook:${event.id}`);
  if (processed) {
    return res.json({ status: "already_processed" });
  }

  try {
    await processWebhookEvent(event);

    // 処理済み作为マーク(24时间保持)
    await redis.set(`webhook:${event.id}`, "1", "EX", 86400);

    res.json({ status: "processed" });
  } catch (error) {
    console.error("Webhook processing failed:", error);
    res.status(500).json({ error: "Processing failed" });
  }
});

async function processWebhookEvent(event: any) {
  switch (event.type) {
    case "payment_intent.succeeded":
      await handlePaymentSuccess(event.data.object);
      break;
    case "customer.subscription.updated":
      await handleSubscriptionUpdate(event.data.object);
      break;
    case "invoice.payment_failed":
      await handlePaymentFailed(event.data.object);
      break;
    default:
      console.log(`Unhandled event type: ${event.type}`);
  }
}

Webhook发送側の实现

interface WebhookConfig {
  id: string;
  url: string;
  secret: string;
  events: string[];
  active: boolean;
}

class WebhookSender {
  private maxRetries = 3;
  private retryDelays = [1000, 5000, 30000]; // ms

  async send(
    config: WebhookConfig,
    event: string,
    payload: Record<string, unknown>
  ): Promise<void> {
    const body = JSON.stringify({
      id: crypto.randomUUID(),
      event,
      timestamp: new Date().toISOString(),
      data: payload,
    });

    const signature = this.sign(body, config.secret);

    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      try {
        const response = await fetch(config.url, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "X-Webhook-Signature": `sha256=${signature}`,
            "X-Webhook-Event": event,
            "X-Webhook-Delivery": crypto.randomUUID(),
          },
          body,
          signal: AbortSignal.timeout(10000),
        });

        if (response.ok) {
          await this.logDelivery(config.id, event, "success", attempt);
          return;
        }

        if (response.status >= 400 && response.status < 500) {
          // 客户端错误はリトライしない
          await this.logDelivery(config.id, event, "client_error", attempt);
          return;
        }

        throw new Error(`HTTP ${response.status}`);
      } catch (error) {
        if (attempt < this.maxRetries) {
          await new Promise((r) => setTimeout(r, this.retryDelays[attempt]));
        } else {
          await this.logDelivery(config.id, event, "failed", attempt);
          throw error;
        }
      }
    }
  }

  private sign(payload: string, secret: string): string {
    return crypto
      .createHmac("sha256", secret)
      .update(payload)
      .digest("hex");
  }

  private async logDelivery(
    configId: string,
    event: string,
    status: string,
    attempts: number
  ) {
    await prisma.webhookDelivery.create({
      data: { configId, event, status, attempts },
    });
  }
}

Webhook注册API

const router = express.Router();

router.post("/api/webhooks", async (req, res) => {
  const { url, events } = req.body;

  // URLの验证
  try {
    const parsed = new URL(url);
    if (parsed.protocol !== "https:") {
      return res.status(400).json({ error: "HTTPS required" });
    }
  } catch {
    return res.status(400).json({ error: "Invalid URL" });
  }

  // シークレットの生成
  const secret = crypto.randomBytes(32).toString("hex");

  const webhook = await prisma.webhookConfig.create({
    data: {
      url,
      events,
      secret,
      active: true,
      userId: req.user!.id,
    },
  });

  // シークレットは创建時のみ返す
  res.status(201).json({
    id: webhook.id,
    url: webhook.url,
    events: webhook.events,
    secret,
  });
});

事件のディスパッチ

class EventDispatcher {
  private sender = new WebhookSender();

  async dispatch(event: string, payload: Record<string, unknown>) {
    // 該当事件を購読しているWebhookを获取
    const configs = await prisma.webhookConfig.findMany({
      where: {
        active: true,
        events: { has: event },
      },
    });

    // 并行で发送(队列に入れる場合はBullMQを使用)
    const results = await Promise.allSettled(
      configs.map((config) => this.sender.send(config, event, payload))
    );

    const failed = results.filter((r) => r.status === "rejected");
    if (failed.length > 0) {
      console.error(`${failed.length}/${configs.length} webhooks failed`);
    }
  }
}

// Usage example
const dispatcher = new EventDispatcher();

// 注文完成時
async function completeOrder(orderId: string) {
  const order = await prisma.order.update({
    where: { id: orderId },
    data: { status: "completed" },
  });

  await dispatcher.dispatch("order.completed", {
    orderId: order.id,
    total: order.total,
    customerId: order.customerId,
  });
}

通过 Claude Codeの活用

Webhook实现を让 Claude Code依頼する例です。异步処理相关内容请参阅作业队列・异步処理、事件设计は事件駆動架构也请参阅。

Webhookシステムを実装して。
- 受信: Stripe/GitHub Webhookの署名検証
- 送信: HMAC署名付き、リトライ機能付き
- 冪等性の保証
- 配信ログとモニタリング
- 登録・管理用のREST API

Webhookの安全相关内容请参阅OWASP Webhook Security。Claude Code的用法请参阅官方文档中可以查看。

总结

Webhookはシステム間联动の重要なパターンです。借助 Claude Code,署名验证、リトライ、冪等性保証を含む堅牢なWebhook基盤を高效地构建可以。

#Claude Code #Webhook #API design #security #非同期処理