Use this file to discover all available pages before exploring further.
OwnPay’s Node.js integration leverages the native fetch API introduced in Node.js 18, so no external HTTP library is needed. The guide below provides a fully typed TypeScript client, practical Express.js route examples, and a production-grade webhook handler — everything you need to accept payments and react to events in a modern Node.js application.
Required for native fetch support. On Node.js 16 or earlier, substitute fetch with node-fetch or axios.
OwnPay API Key
Generate one in Admin → Developer Hub → API Keys. Keys are shown only once — store yours immediately.
If you’re using ESM ("type": "module" in package.json), use import syntax as shown in the examples. For CommonJS projects, substitute import/export with require/module.exports.
After payment, OwnPay redirects to your redirect_url with ?intent_id=<id>&status=paid. Always verify status server-side — never trust query parameters alone:
OwnPay signs every webhook with an HMAC-SHA256 signature. The X-OwnPay-Signature header contains sha256=<hex>, computed against timestamp + "." + rawBody.
You must use express.raw() (not express.json()) on your webhook route. If the body is parsed to an object before signature verification, the byte representation changes and the HMAC check fails.
import crypto from 'node:crypto'import type { Request, Response } from 'express'function verifyOwnPaySignature(req: Request, secret: string): boolean { const signature = req.headers['x-ownpay-signature'] as string | undefined const timestamp = req.headers['x-ownpay-timestamp'] as string | undefined if (!signature || !timestamp) return false // Replay protection: reject events older than 5 minutes if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false const rawBody = req.body as Buffer const hmac = crypto .createHmac('sha256', secret) .update(`${timestamp}.${rawBody.toString()}`) .digest('hex') return crypto.timingSafeEqual( Buffer.from(`sha256=${hmac}`), Buffer.from(signature), )}// Webhook endpoint — must use express.raw() to preserve the raw bodyapp.post( '/webhooks/ownpay', express.raw({ type: 'application/json' }), (req: Request, res: Response) => { const secret = process.env.OWNPAY_WEBHOOK_SECRET! if (!verifyOwnPaySignature(req, secret)) { return res.status(401).send('Invalid signature') } const event = JSON.parse((req.body as Buffer).toString()) switch (event.event) { case 'payment.paid': handlePaymentPaid(event.data) break case 'payment.failed': handlePaymentFailed(event.data) break case 'payment.refunded': handlePaymentRefunded(event.data) break default: // Unknown events are safely ignored break } res.status(200).send('OK') },)
Return HTTP 200 as fast as possible. OwnPay marks delivery as failed after 10 seconds and retries with exponential backoff. Defer slow operations (database writes, emails, queued jobs) to an async worker.