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

@pay-normalize/flutterwave

v0.1.1

Published

Flutterwave v4 connector for pay-normalize: webhooks, retrieve-charge, and settlements (EXPERIMENTAL).

Downloads

267

Readme

@pay-normalize/flutterwave

Flutterwave v4 connector — normalizes webhooks, retrieve-charge responses, and settlement API responses into StandardizedTransaction.

Status: EXPERIMENTAL (connector version: "0.1.0"). The signature scheme is confirmed against Flutterwave's authoritative docs and the connector matches it, but fixtures are docs-derived. Graduation still wants one real signed delivery with headers (NOT_DOING.md §11).

import { flutterwave } from "@pay-normalize/flutterwave";

See ../../docs/INTEGRATION.md and ../../docs/SECURITY.md.


Signature

  • Header: flutterwave-signature
  • Scheme: HMAC-SHA256 over the raw body, base64, constant-time compared.
  • Secret is your dashboard secret hash, passed as an argument.

Flutterwave's docs contain a self-contradiction (one inline sample compares the header directly to the plain secret). The connector implements the authoritative HMAC scheme; a test pins that the plain-secret comparison correctly fails. See SECURITY.md.


Webhooks (parseFlutterwaveWebhook)

Handles charge.completed; other event types (transfer.completed, refunds, future types) surface as unknown_event.

Notable payload quirks the connector absorbs:

  • Delivery id field varies: id on mobile_money deliveries, webhook_id on card / bank_transfer deliveries. Both are accepted → providerEventId.
  • created_datetime changes type between samples: ISO-8601 string in one, float epoch seconds (1735116842.116) in another, while the envelope timestamp is epoch milliseconds. resolveFlwTimestamp resolves all three by magnitude (< 1e12 → seconds, ≥ 1e12 → ms). Nanosecond-precision ISO strings truncate cleanly to ms.
  • Amounts are main-unit decimals, multi-currency (2000 NGN, 2500 KES, 1500.25). Converted to minor units via parseNairaDecimalString; non-NGN is tagged and passed through — no FX.

Identity: chargeDedupeKey(id)flutterwave:charge:<chargeId>, shared with the retrieve-charge parser so webhook- and API-sourced rows upsert together.


Verify-before-value

Flutterwave is emphatic: re-query before giving value.

Retrieve charge

  • parseFlutterwaveCharge(response) — normalizes a GET /charges/:id response. Their sample omits a timestamp, so a bare call returns parse_error with code ERR_TIMESTAMP_MISSING.
  • parseFlutterwaveChargeAt(response, fetchedAt) — the overload: supply the host's fetch time as the occurrence time for timestamp-less responses.

Charge responses can carry a typed fees[] array (vat, app, merchant, stamp_duty, …). When present, fees are summed and net = amount − Σfees. Webhook charges omit fees → Σ = 0net = amount.

Settlements

  • parseFlutterwaveSettlement(record) — one settlement record. The GET /settlements/:id response is enveloped ({ status, message, data, meta }); pass response.data. First real settlementDate (due_datetime), with transaction_datetime preferred as occurredAt. A settlement is a batch (charge_count may be > 1) but normalizes as one credit — one money movement into your bank/wallet.
  • parseFlutterwaveSettlementList(response) — a full list response, row-isolated: one bad record doesn't sink the page.

Money invariant: net_amount = gross_amount − Σfees − chargeback − refund. All three deductions (typed fees[], chargeback, refund) are withheld before settlement; the connector refuses any record where the breakdown doesn't reconcile. feeInKobo on the output aggregates all pre-settlement deductions (so the schema's net = amount − fee holds); the typed breakdown is preserved in rawProviderPayload.

Status vocabulary (full v4 enum): completed / completed-offlineSUCCESSFUL; disburse-pending / pending / reviewed / approved / processing / flagged / on-holdPENDING (money not yet landed, reversible via the rank guard); failedFAILED. Anything outside the table is a parse_error, loud not guessed.


Settlement files

parseSettlementFile(buffer) throws UnsupportedFileFormatError — CSV export parsing isn't fixture-verified. Use the settlement API parsers above instead.


Exports

| Export | Type | |---|---| | flutterwave | Connector | | parseFlutterwaveWebhook | (rawBody) => ParseResult | | parseFlutterwaveCharge / parseFlutterwaveChargeAt | retrieve-charge parsers | | parseFlutterwaveSettlement / parseFlutterwaveSettlementList | settlement parsers | | verifyFlutterwaveSignature | (input) => boolean | | resolveFlwTimestamp | the three-format timestamp resolver | | chargeDedupeKey | (id) => string | | SIGNATURE_HEADER | "flutterwave-signature" | | CHARGE_EVENT_TYPE | "charge.completed" | | PROVIDER | "flutterwave" |

Depends on @pay-normalize/core and zod.