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

@ngelekanyo/payfast

v1.2.0

Published

Unified PayFast subscription toolkit: core signing/ITN utilities, an Express router, and a browser client

Readme

@ngelekanyo/payfast

Unified PayFast subscription toolkit, published as one package with three subpath exports:

  • @ngelekanyo/payfast/core — signature generation, ITN payload/validation helpers, and the PayFast config loader. Framework-agnostic, usable standalone.
  • @ngelekanyo/payfast/server — an Express router built on top of /core that exposes the initiate/notify/cancel/pause/unpause/fetch endpoints.
  • @ngelekanyo/payfast/client — a browser client (PayFastService) for calling that router from the frontend and submitting the PayFast redirect form.

All three subpaths share a single set of types from src/types, so PaymentData and the other shapes are identical whether you're on the backend or in the browser.

Installation

npm install @ngelekanyo/payfast

Environment setup (server)

PAYFAST_MERCHANT_ID=your_merchant_id
PAYFAST_MERCHANT_KEY=your_merchant_key
PAYFAST_PASSPHRASE=your_passphrase
PAYFAST_API_VERSION=v1
PAYFAST_RETURN_URL=https://yourdomain.com/payment-success
PAYFAST_CANCEL_URL=https://yourdomain.com/payment-cancel
PAYFAST_NOTIFY_URL=https://yourdomain.com/api/payfast/notify
TESTING_MODE=true

/core

import {
  generateSignatureForInitiate,
  pfValidSignature,
  generateApiSignature,
  createITNPayload,
  validateITNWithPayfast,
  isPayfastSourceIp,
  payfastConfig,
} from "@ngelekanyo/payfast/core";

const signature = generateSignatureForInitiate(paymentData, payfastConfig.passphrase);

/server

import express from "express";
import cors from "cors";
import { buildPayfastRouter } from "@ngelekanyo/payfast/server";

const app = express();

const onPaymentUpdate = async (itnData) => {
  // update database, activate subscription
};
const onCancel = async ({ token, subscriptionId, status, payload }) => {};
const onPause = async ({ token, status, payload }) => {};
const onUnpause = async ({ token, status, payload }) => {};
const onFetch = async ({ token, status, payload }) => {};

app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.use(
  "/api/payfast",
  buildPayfastRouter(onPaymentUpdate, onCancel, onPause, onUnpause, onFetch)
);

app.listen(6000);

Exposed routes

| Method | Route | Description | | ------ | --------------------------------------------- | ---------------------------------------------- | | POST | /api/payfast/initiate | Generate PayFast payment data + URL | | POST | /api/payfast/notify | Handle ITN (Instant Transaction Notification) | | POST | /api/payfast/cancel/:token/:subscriptionId | Cancel an active PayFast subscription | | POST | /api/payfast/cancel/:token | Cancel an active PayFast subscription | | POST | /api/payfast/pause/:token | Pause an active subscription | | POST | /api/payfast/unpause/:token | Unpause a paused subscription |

/initiate builds a once-off payment by default. Include subscription_type in the request body to opt into a recurring subscription instead — this adds billing_date/recurring_amount/frequency/cycles with sensible defaults. return_url/cancel_url can be overridden per request (falling back to the configured defaults), and custom_str1-custom_str5 are passed through to PayFast and returned unchanged in the ITN payload. | GET | /api/payfast/fetch/:token | Fetch subscription details |

express and cors are optional peer dependencies — install them yourself when using /server; /client consumers don't need them.

/client

import { initPayFastClient, PayFastService } from "@ngelekanyo/payfast/client";
import type { PaymentData } from "@ngelekanyo/payfast/client";

initPayFastClient("https://your-backend.com");

const paymentData: PaymentData = {
  amount: "99.00",
  item_name: "Pro Plan",
  m_payment_id: "uuid-123",
};

const payfast = new PayFastService();
const response = await payfast.initiatePayment(paymentData);
payfast.submitPayment(response.paymentData, response.payfastUrl);

await payfast.pauseSubscription(token);
await payfast.unpauseSubscription(token);
await payfast.cancelSubscription(token);
await payfast.cancelSubscriptionById(token, subscriptionId);
await payfast.fetchSubscription(token);

Authenticating subscription actions

If your backend verifies that the caller actually owns the subscription being paused, resumed, cancelled, or fetched, wire up an auth token provider so those requests carry an Authorization: Bearer <token> header:

import { setAuthTokenProvider } from "@ngelekanyo/payfast/client";

setAuthTokenProvider(async () => {
  const session = await getCurrentSession();
  return session?.accessToken ?? null;
});

The provider can be sync or async, and returning null simply omits the header. Every method sends it when a provider is configured, including initiatePayment — if your backend creates the payment record before calling /initiate (e.g. inserting a pending subscription row the caller owns via RLS, then passing its id as m_payment_id), your backend can use this header to verify the caller actually owns that record before signing a payment for it.

Security

  • Validates PayFast's signature on every ITN
  • Verifies source IP matches PayFast domains (skipped in sandbox mode)
  • Uses CSRF/session token for authenticated subscription actions, with retry on expired CSRF/session (HTTP 419)
  • /server's router itself does not verify that the caller owns a given subscription token — it trusts whatever your callbacks and your own middleware decide. If that matters for your app, use /client's setAuthTokenProvider to attach a session token to subscription-action requests, and check ownership against it in middleware on your backend before those requests reach this router.

Pause/unpause disclaimer

Pausing a subscription does not cancel it — it only delays future billing by the number of paused cycles, and PayFast extends the subscription end date accordingly. Unpausing early does not adjust the next billing date; billing still resumes after the full pause duration. This package does not manage user access during a pause period — enforce that in your own backend. See the PayFast Developer Docs for current details.

Development

npm install
npm run build   # emits ESM to dist/ and CJS to dist/cjs/
npm test

License

MIT — see LICENSE.