npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bevingh/payments

v0.1.0

Published

Mono-package (KD17): BaseProvider + in-tree drivers, verifyWebhookSignature(rawBody) HMAC helper, inbound verify-required contract, outbound dispatcher.

Readme

@bevingh/payments

Phase 3 / PR-16 — extracted (steps 1–4). Mono-package (KD17). Highest priority: raw-body HMAC verification.

Purpose

BaseProvider + in-tree drivers, verifyWebhookSignature(rawBody), inbound verify-required contract, outbound signed dispatcher. Not product fulfillment domain.

| Field | Value | |---|---| | surfaceShape | pure_core_plus_express_adapter | | dependsOnPackages | @bevingh/errors | | extractionOrderHint | 6 | | status | extracted (steps 1–4) |

Step 1 — verifyWebhookSignature (do this first everywhere)

Paystack algorithm confirmed from conduit: HMAC-SHA512 over raw body, timingSafeEqual.

import { verifyWebhookSignature } from '@bevingh/payments';

// req.rawBody = Buffer from express raw capture — NOT req.body object
const ok = verifyWebhookSignature(req.rawBody, req.headers['x-paystack-signature'], secret);

| Export | Role | |---|---| | WebhookRawBody | Buffer \| string only — objects rejected at runtime | | assertWebhookRawBody | boundary guard | | verifyWebhookSignature | sha512 default (Paystack) | | verifyWebhookSignatureWithSecrets | try live + test keys |

Broken pattern (do not port): createHmac(...).update(JSON.stringify(req.body)) — UVT nomination, Academicx, imep, Texify, Didipay Moolre path.

Step 2 — Providers (KD17 mono-package)

| Driver | Source | Notes | |---|---|---| | PaystackProvider | conduit | verify uses Step 1; charge/checkout need injected http | | KorbaProvider | payment-gatway korba.js | debit/verify remote; no HMAC in harvest | | Moolre | deferred | needs_human_decision — Didipay vs mirrly trust models differ | | Hubtel | deferred | no HMAC scheme in harvested imep code |

Step 3 — Inbound contract (verify required)

await processInboundWebhook({
  rawBody: req.rawBody,
  signature: req.headers['x-paystack-signature'],
  secrets: [liveKey, testKey],
  handle: async (body) => {
    // only runs after HMAC ok
    // prefer @bevingh/fulfillment attemptTransition for status flips
  },
});

There is no skipVerification flag. pg/ussd unsigned inbound is the gap this closes.

Status flips: compose with @bevingh/fulfillment attemptTransition (compatible shapes; soft dependency via docs only).

Step 4 — Outbound dispatcher

import { dispatchSignedWebhook, signOutboundWebhook } from '@bevingh/payments';

// Conduit → consumer: HMAC-SHA256 over JSON.stringify(payload), X-Conduit-Signature
await dispatchSignedWebhook({
  url: product.webhookUrl,
  payload: { event: 'payment.completed', ref },
  secret: process.env.WEBHOOK_SECRET!,
  post: async (url, body, headers) => axios.post(url, body, { headers }),
});

Outbound does sign canonical JSON (producer-controlled). That is different from inbound Paystack raw-body verify.

Express adapter

@bevingh/payments/adapters/express → createPaystackWebhookMiddleware
Requires req.rawBody; fails loud if missing (no JSON.stringify(req.body) fallback).

Open questions (not silently resolved)

Moolre

Didipay uses HMAC over JSON.stringify; mirrly often trusts session gate only. No unified driver in this PR. Product decision required.

Hubtel (imep)

No signature verification in harvested controller. Needs external docs research for Hubtel’s real scheme — not fabricated here.

mustNotContain (verified)

| Constraint | Status | |---|---| | JSON.stringify HMAC for inbound verify | Forbidden / not implemented | | Ticket/vote/registration domain | OK | | Wallet balance mutation | OK | | Tax LedgerEntry | OK |

Tests

npm run test -w @bevingh/payments
npm run build -w @bevingh/payments
  • Valid / tampered body / tampered sig
  • JSON.stringify vs raw bytes demonstration
  • Inbound: handle never runs without verify
  • Paystack checkout amount*100 with mock HTTP
  • Korba debit mock
  • Outbound sha256 sign + header

Champion files (read-only)

  • conduit BaseProvider, PaystackProvider, webhooks.js, webhookDispatcherService.js
  • payment-gatway korba.js