unuspay-sdk
v0.1.1
Published
Official UnusPay SDK for webhook verification
Maintainers
Readme
unuspay-sdk
Official UnusPay SDK for webhook signature verification.
Installation
npm install unuspay-sdk
# or
yarn add unuspay-sdk
# or
pnpm add unuspay-sdkUsage
Express
import express from 'express'
import { Webhook, WebhookVerificationError } from 'unuspay-sdk'
const app = express()
// Create a reusable webhook verifier instance
const webhook = new Webhook(process.env.WEBHOOK_SECRET!)
// IMPORTANT: Use raw body for webhook routes
app.post('/webhook',
express.raw({ type: 'application/json' }),
(req, res) => {
try {
const event = webhook.verify(
req.body.toString(),
req.headers['x-webhook-signature'] as string,
req.headers['x-webhook-timestamp'] as string
)
// Handle the event
switch (event.type) {
case 'order.completed':
console.log('Order completed:', event.data)
break
case 'payment_link.created':
console.log('Payment link created:', event.data)
break
}
res.json({ received: true })
} catch (error) {
if (error instanceof WebhookVerificationError) {
console.error('Webhook verification failed:', error.message)
return res.status(401).json({ error: error.message })
}
throw error
}
}
)Next.js (App Router)
// app/api/webhook/route.ts
import { Webhook, WebhookVerificationError } from 'unuspay-sdk'
import { NextRequest, NextResponse } from 'next/server'
// Create a reusable webhook verifier instance
const webhook = new Webhook(process.env.WEBHOOK_SECRET!)
export async function POST(req: NextRequest) {
try {
const payload = await req.text() // Get raw body
const event = webhook.verify(
payload,
req.headers.get('x-webhook-signature')!,
req.headers.get('x-webhook-timestamp')!
)
// Handle event...
console.log('Received event:', event.type)
return NextResponse.json({ received: true })
} catch (error) {
if (error instanceof WebhookVerificationError) {
return NextResponse.json({ error: error.message }, { status: 401 })
}
throw error
}
}Bun / Elysia
import { Elysia } from 'elysia'
import { Webhook } from 'unuspay-sdk'
// Create a reusable webhook verifier instance
const webhook = new Webhook(process.env.WEBHOOK_SECRET!)
new Elysia()
.post('/webhook', async ({ request }) => {
const payload = await request.text()
const event = webhook.verify(
payload,
request.headers.get('x-webhook-signature')!,
request.headers.get('x-webhook-timestamp')!
)
return { received: true, type: event.type }
})
.listen(3000)API Reference
Webhook class
The main class for webhook signature verification.
Constructor
new Webhook(secret: string, config?: WebhookConfig)Parameters:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| secret | string | Yes | Your webhook secret (starts with whsec_) |
| config | WebhookConfig | No | Optional configuration |
WebhookConfig:
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| maxAgeSeconds | number | 300 | Max event age in seconds |
verify(payload, signature, timestamp)
Verifies a webhook signature and returns the parsed payload.
Parameters:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| payload | string | Yes | Raw request body (NOT parsed JSON) |
| signature | string | Yes | Value of X-Webhook-Signature header |
| timestamp | string \| number | Yes | Value of X-Webhook-Timestamp header |
Returns: Parsed event object
Throws: WebhookVerificationError if verification fails
Example:
const webhook = new Webhook(process.env.WEBHOOK_SECRET!)
const event = webhook.verify(
rawBody,
headers['x-webhook-signature'],
headers['x-webhook-timestamp']
)WebhookVerificationError
Error thrown when verification fails.
Properties:
message: Human-readable error messagecode: Error code ('INVALID_SIGNATURE'|'TIMESTAMP_EXPIRED'|'MISSING_HEADERS')
Important: Raw Body Required
Webhook signature verification requires the raw request body (exact bytes as received). If your framework parses JSON automatically, the signature will fail.
Common solutions:
- Express: Use
express.raw({ type: 'application/json' })middleware - Next.js: Use
await req.text()instead ofawait req.json() - Fastify: Use
{ parseAs: 'string' }in route config
License
MIT
