关于 encore-webhook
encore-webhook 是为 Encore 服务编写第三方 Webhook 接收端点的技能。它说明如何读取原始请求体、验证签名,并快速确认请求后将后续工作交给 Pub/Sub。
主要功能
- 01使用 api.raw 接收 Webhook
- 02读取未经解析的请求体
- 03验证提供方签名
- 04通过 secret(...) 保存签名密钥
- 05快速响应并将任务交给 Pub/Sub
适用场景
- 01接收 Stripe 或 GitHub Webhook
- 02在处理前验证 Webhook 载荷
- 03避免慢任务拖延 Webhook 响应
- 04将已验证事件排入异步队列
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ encoredev/ skills --skill encore-webhook安装后在 Claude Code 中输入/encore-webhook确认已加载。
详细介绍
Encore Webhook 端点
说明
使用 api.raw(...) 接收来自第三方服务的入站 Webhook。原始端点可让你直接访问 Node.js 风格的请求和响应对象;验证签名需要这些对象(验证通常需要未经解析的原始请求正文)。
1. 导入
import { api } from "encore.dev/api";
2. 使用 api.raw 定义端点
export const stripeWebhook = api.raw(
{ expose: true, path: "/webhooks/stripe", method: "POST" },
async (req, res) => {
const sig = req.headers["stripe-signature"];
// Read raw body
const chunks: Buffer[] = [];
for await (const chunk of req) chunks.push(chunk);
const rawBody = Buffer.concat(chunks).toString("utf8");
// Verify signature, parse event...
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ received: true }));
}
);
3. 验证签名
大多数提供商都会对 Webhook 签名。使用 encore.dev/config 中的 secret(...) 读取密钥(请参阅 encore-secret 技能),并在信任负载之前验证签名:
import { secret } from "encore.dev/config";
const stripeWebhookSecret = secret("StripeWebhookSecret");
// inside handler:
import Stripe from "stripe";
const stripe = new Stripe(stripeApiKey());
const event = stripe.webhooks.constructEvent(rawBody, sig, stripeWebhookSecret());
对于 GitHub,请使用 Webhook 密钥,根据原始请求正文验证 X-Hub-Signature-256 标头中的 HMAC-SHA256。
常见提供商
| 提供商 | 签名标头 | 验证方式 |
|---|---|---|
| Stripe | Stripe-Signature | stripe.webhooks.constructEvent(rawBody, sig, secret) |
| GitHub | X-Hub-Signature-256 | 对原始请求正文执行 HMAC-SHA256 |
| Slack | X-Slack-Signature | 对 v0:{timestamp}:{rawBody} 执行 HMAC-SHA256 |
| Shopify | X-Shopify-Hmac-Sha256 | 对原始请求正文执行 HMAC-SHA256(base64) |
| Twilio | X-Twilio-Signature | 对 URL 和排序后的表单字段执行 HMAC-SHA1 |
始终快速响应
Webhook 发送方会在收到非 2xx 响应或响应缓慢时重试。验证负载后,请立即使用 2xx 确认收到,然后通过 Pub/Sub 将实际工作加入队列(请参阅 encore-pubsub),而不是在请求处理程序中执行。
import { Topic } from "encore.dev/pubsub";
interface StripeEvent { id: string; type: string; data: unknown; }
const stripeEvents = new Topic<StripeEvent>("stripe-events", {
deliveryGuarantee: "at-least-once",
});
// inside the raw handler, after verification:
await stripeEvents.publish({ id: event.id, type: event.type, data: event.data });
res.writeHead(200); res.end();
指南
- 仅将
api.raw用于 Webhook 和其他底层 HTTP 集成。 - 在信任负载之前,始终验证提供商的签名。
- 始终快速返回 2xx 响应——将耗时工作交给 Pub/Sub。
- 使用
secret(...)存储签名密钥;切勿将其直接写入代码。 - 对于你自己服务中的带类型 JSON 端点,请使用
encore-api技能中的普通api(...)。
评论
支持 Markdown