Tài liệu
Nhận webhook
Nhận webhook Ví dụ này nhận event đã được nhà cung cấp ký và chỉ lưu mỗi event một lần. Table Tạo integration_settings dưới dạng table một bản ghi. Field Kiểu Ghi chú providerWebhookSecret string isEncrypted=true , isPublished=false Tạo provider_event . Field Kiểu Ghi chú provide
Nhận webhook
Ví dụ này nhận event đã được nhà cung cấp ký và chỉ lưu mỗi event một lần.
Table
Tạo integration_settings dưới dạng table một bản ghi.
| Field | Kiểu | Ghi chú |
|---|---|---|
providerWebhookSecret |
string | isEncrypted=true, isPublished=false |
Tạo provider_event.
| Field | Kiểu | Ghi chú |
|---|---|---|
providerEventId |
string | Duy nhất |
kind |
string | Kiểu event |
payload |
simple-json | Payload event thô |
processedAt |
datetime | Có thể để trống |
Route
Tạo custom route POST /webhooks/provider. Chỉ cho phép công khai nếu handler bắt buộc kiểm tra chữ ký.
const settingsResult = await #integration_settings.find({
fields: 'providerWebhookSecret',
limit: 1
});
const settings = settingsResult.data?.[0];
if (!settings?.providerWebhookSecret) {
@THROW500('webhook secret is not configured');
}
const signature = @REQ.headers['x-provider-signature'];
const expected = await @HELPERS.$crypto.hmacSha256(
@REQ.rawBody,
settings.providerWebhookSecret,
'hex'
);
if (signature !== expected) {
@THROW401('invalid signature');
}
const eventId = @BODY.id;
if (!eventId) {
@THROW400('event id is required');
}
const existing = await #provider_event.find({
filter: { providerEventId: { _eq: eventId } },
fields: 'id',
limit: 1
});
if (existing.data?.[0]) {
return { received: true, duplicate: true };
}
await #provider_event.create({
data: {
providerEventId: eventId,
kind: @BODY.type || 'unknown',
payload: @BODY
}
});
await @TRIGGER('process-provider-event', { eventId });
return { received: true };
Khi nhà cung cấp ký theo byte của request thô, hãy dùng chính raw body đó để kiểm tra chữ ký.