diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a42a661..c3d9f4b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @razor-x +* @seamapi/sdk diff --git a/README.md b/README.md index e0146c0..8043ce0 100644 --- a/README.md +++ b/README.md @@ -74,22 +74,27 @@ $ npm install -D @seamapi/types@latest ## Usage -First, create a webhook using the Seam API or Seam Console -and obtain a Seam webhook secret. +The Seam API implements webhooks using [Svix](https://www.svix.com). +This SDK exports a thin wrapper `SeamWebhook` around the svix package. +Use it to parse and validate [Seam webhook events](https://docs.seam.co/latest/developer-tools/webhooks). + +Refer to the [Svix docs on Consuming Webhooks](https://docs.svix.com/receiving/introduction) +for an in-depth guide on best-practices for handling webhooks in your application. > [!TIP] -> This example is for [Express], see the [Svix docs for more examples in specific frameworks](https://docs.svix.com/receiving/verifying-payloads/how). +> This example is for [Express](https://expressjs.com/), +> see the [Svix docs for more examples in specific frameworks](https://docs.svix.com/receiving/verifying-payloads/how). ```js +import { env } from 'node:process' + import { SeamWebhook } from '@seamapi/webhook' import express from 'express' import bodyParser from 'body-parser' -import { storeEvent } from './store-event.js' - const app = express() -const webhook = new SeamWebhook(process.env.SEAM_WEBHOOK_SECRET) +const webhook = new SeamWebhook(env.SEAM_WEBHOOK_SECRET) app.post( '/webhook', @@ -97,11 +102,10 @@ app.post( (req, res) => { let data try { - data = webhook.verify(payload, headers) + data = webhook.verify(req.body, req.headers) } catch { return res.status(400).send() } - storeEvent(data, (err) => { if (err != null) { return res.status(500).send() @@ -110,9 +114,16 @@ app.post( }) }, ) -``` -[Express]: https://expressjs.com/ +const storeEvent = (data, callback) => { + console.log(data) + callback() +} + +app.listen(8080, () => { + console.log('Ready to receive webhooks at http://localhost:8080/webhook') +}) +``` ## Development and Testing diff --git a/package-lock.json b/package-lock.json index b2a7cec..64c7028 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/webhook", - "version": "1.0.0-rc.1", + "version": "1.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/webhook", - "version": "1.0.0-rc.1", + "version": "1.1.1", "license": "MIT", "dependencies": { "svix": "^1.15.0" diff --git a/package.json b/package.json index c874c63..6d97a33 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/webhook", - "version": "1.0.0-rc.1", + "version": "1.1.1", "description": "Webhook SDK for the Seam API written in TypeScript.", "type": "module", "main": "index.js", diff --git a/src/lib/index.ts b/src/lib/index.ts index 9092bf1..bc7b291 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1 +1,2 @@ export * from './seam-webhook.js' +export { WebhookVerificationError as SeamWebhookVerificationError } from 'svix' diff --git a/src/lib/seam-webhook.ts b/src/lib/seam-webhook.ts index acfeee5..2a3210e 100644 --- a/src/lib/seam-webhook.ts +++ b/src/lib/seam-webhook.ts @@ -9,6 +9,10 @@ export class SeamWebhook { } verify(payload: string, headers: Record): SeamEvent { - return this.#webhook.verify(payload, headers) as SeamEvent + const normalizedHeaders = Object.fromEntries( + Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]), + ) + + return this.#webhook.verify(payload, normalizedHeaders) as SeamEvent } }