バイブコーディング#Webhook#API#連携
Webhook連携の作り方|外部サービスとリアルタイム連携する方法
Webhookを使って外部サービスと連携する方法。Stripe、GitHub、Slackなどからの通知を受け取る実装を解説。
Webhook連携の作り方
外部サービスから「何か起きた」を受け取るWebhook。
Webhookとは
[外部サービス] --イベント発生--> [あなたのAPI]
例:
- Stripeで支払い完了 → 注文確定処理
- GitHubでプッシュ → デプロイ実行
- Slackでメッセージ → ボット応答
基本実装
エンドポイント作成
Webhookを受け取るAPIエンドポイントを作って:
- POST /api/webhooks/stripe
- リクエストボディをパース
- 署名検証
- イベントタイプに応じた処理
生成されるコード:
// app/api/webhooks/stripe/route.ts
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!
export async function POST(request: Request) {
const body = await request.text()
const signature = request.headers.get('stripe-signature')!
let event: Stripe.Event
// 署名検証
try {
event = stripe.webhooks.constructEvent(body, signature, webhookSecret)
} catch (err) {
return Response.json({ error: 'Invalid signature' }, { status: 400 })
}
// イベント処理
switch (event.type) {
case 'checkout.session.completed':
await handleCheckoutComplete(event.data.object)
break
case 'customer.subscription.updated':
await handleSubscriptionUpdate(event.data.object)
break
default:
console.log(`Unhandled event: ${event.type}`)
}
return Response.json({ received: true })
}
Stripe Webhook
セットアップ
1. Stripeダッシュボード → Webhooks
2. エンドポイントを追加
3. イベントを選択
4. Webhook Secretをコピー
処理するイベント
// 支払い完了
case 'checkout.session.completed':
// 注文を確定
// メール送信
break
// サブスク更新
case 'customer.subscription.updated':
// ユーザーのプラン更新
break
// 支払い失敗
case 'invoice.payment_failed':
// リマインドメール
break
GitHub Webhook
リポジトリへのプッシュ
// app/api/webhooks/github/route.ts
import crypto from 'crypto'
export async function POST(request: Request) {
const body = await request.text()
const signature = request.headers.get('x-hub-signature-256')
// 署名検証
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', process.env.GITHUB_WEBHOOK_SECRET!)
.update(body)
.digest('hex')
if (signature !== expectedSignature) {
return Response.json({ error: 'Invalid signature' }, { status: 401 })
}
const event = JSON.parse(body)
const eventType = request.headers.get('x-github-event')
switch (eventType) {
case 'push':
// デプロイをトリガー
break
case 'issues':
// Issue通知
break
}
return Response.json({ received: true })
}
Slack Webhook
Slackからのイベント
// app/api/webhooks/slack/route.ts
export async function POST(request: Request) {
const body = await request.json()
// URL検証(初回のみ)
if (body.type === 'url_verification') {
return Response.json({ challenge: body.challenge })
}
// イベント処理
if (body.event.type === 'message') {
// メッセージに応答
await sendSlackMessage(body.event.channel, 'Hello!')
}
return Response.json({ ok: true })
}
ローカル開発
ngrokでトンネル
# ngrokをインストール
npm install -g ngrok
# トンネル開始
ngrok http 3000
# 表示されたURLをWebhook URLに設定
# https://xxxx.ngrok.io/api/webhooks/stripe
Stripe CLIでテスト
# Stripe CLIでローカルにフォワード
stripe listen --forward-to localhost:3000/api/webhooks/stripe
# テストイベント送信
stripe trigger checkout.session.completed
セキュリティ
署名検証は必須
// ❌ 署名検証なし(危険)
const body = await request.json()
processEvent(body)
// ⭕ 署名検証あり
const signature = request.headers.get('x-signature')
if (!verifySignature(body, signature)) {
return Response.json({ error: 'Invalid' }, { status: 401 })
}
べき等性
// 同じイベントが複数回来ても大丈夫にする
const eventId = event.id
const processed = await db.webhookEvents.findUnique({ where: { eventId } })
if (processed) {
return Response.json({ already_processed: true })
}
await processEvent(event)
await db.webhookEvents.create({ data: { eventId } })
監視とロギング
// すべてのWebhookをログ
console.log({
type: event.type,
id: event.id,
timestamp: new Date().toISOString(),
})
// エラー時はアラート
try {
await processEvent(event)
} catch (error) {
await notifyError(error, event)
throw error
}
次のステップ
参考文献・引用元
- [1]Stripe Webhooks- Stripe
- [2]GitHub Webhooks- GitHub