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

@bakissation/tasdid-adapters

v1.2.1

Published

Lightweight, vendor-agnostic framework bindings for @bakissation/tasdid — mount the SATIM payment lifecycle (start/return/reconcile/refund) on the Web Fetch API: Next.js App Router, Hono, Remix, SvelteKit, Cloudflare Workers, Bun, Deno.

Readme

@bakissation/tasdid-adapters

Lightweight, vendor-agnostic framework bindings for @bakissation/tasdid. Mount the SATIM (CIB/Edahabia) payment lifecycle — start / return / reconcile / refund — as routes, in a few lines, on the Web Fetch API: Next.js App Router, Hono, Remix, SvelteKit, Cloudflare Workers, Bun, Deno.

npm CI license

npm i @bakissation/tasdid-adapters

Why

Wiring the payment lifecycle into a route is the same boring glue every time: validate the body, call tasdid, map errors to HTTP, redirect the buyer. This does it once — and binds to the Web Fetch standard, not a framework, so it survives framework majors (Next 14→15→16…) untouched and runs on any Fetch runtime. No framework dependency, no vendor lock-in. Orchestration stays in tasdid; this is thin.

Quick start (Next.js App Router)

// lib/pay.ts
import { createCheckout, createPostgresStore } from '@bakissation/tasdid';
import { createSatimClient } from '@bakissation/satim';
import { createFetchHandlers } from '@bakissation/tasdid-adapters/fetch';

const store = createPostgresStore(pgPool);
const checkout = createCheckout({ satim: createSatimClient(satimConfig), store });

export const pay = createFetchHandlers(checkout, {
  successUrl: '/thanks',
  failUrl: '/payment-failed',
  store,                                   // enables the reconcile sweep
  authorize: ({ headers }) =>              // guards refund + reconcile (you pick the scheme)
    headers.authorization === `Bearer ${process.env.PAY_ADMIN_TOKEN}`,
});
// app/api/pay/route.ts
import { pay } from '@/lib/pay';
export const dynamic = 'force-dynamic';
export const POST = pay.start;

Each remaining route file is the same one line:

// app/api/pay/return/route.ts     → export const GET  = pay.handleReturn   // SATIM redirects buyer here
// app/api/pay/reconcile/route.ts  → export const GET  = pay.reconcile      // scheduled sweep (guarded)
// app/api/pay/refund/route.ts     → export const POST = pay.refund         // admin (guarded)

The browser navigates to the returned redirectUrl (full page — SATIM's hosted page, an independent context = PCI SAQ-A). On return, handleReturn reconfirms against the gateway (never trusts the redirect) and 303s the buyer to successUrl/failUrl with ?payment=<id>.

The reconcile sweep — schedule it with anything

SATIM has no webhooks, so a periodic sweep is how abandoned/expired orders settle. pay.reconcile is just a guarded GET — hit it from any scheduler (system cron, a CI schedule, a worker/queue, your platform's cron). Vendor-agnostic by design; auth is your authorize hook:

curl -H "Authorization: Bearer $PAY_ADMIN_TOKEN" https://yourapp/api/pay/reconcile

It returns operational counts (paid/failed/expired/refunded/stillPending) plus a failures list — each { paymentId, code }, where code is a safe reason (e.g. REFUND_FAILED, or UNKNOWN) so you can triage a sweep without leaking gateway internals.

Other runtimes (same handlers)

// Hono / Workers / Bun / Deno
app.post('/api/pay', (c) => pay.start(c.req.raw));
app.get('/api/pay/return', (c) => pay.handleReturn(c.req.raw));

// React Router v7 / Remix — resource routes ({ request } is a Web Request)
export const action = ({ request }) => pay.start(request);        // app/routes/api.pay.tsx
export const loader = ({ request }) => pay.handleReturn(request); // app/routes/api.pay.return.tsx

Express / Connect / Fastify (Node http)

Not Fetch-native? The /node entry binds to Node's http (IncomingMessageServerResponse) — same handlers, same core, still zero framework deps.

import { createNodeHandlers } from '@bakissation/tasdid-adapters/node';
const pay = createNodeHandlers(checkout, { successUrl: '/thanks', failUrl: '/payment-failed', store, authorize });

// Express / Connect — req/res are Node's objects, so mount directly
app.use(express.json());
app.post('/api/pay',           pay.start);
app.get ('/api/pay/return',    pay.handleReturn);
app.get ('/api/pay/reconcile', pay.reconcile);
app.post('/api/pay/refund',    pay.refund);

// Fastify — pass the parsed body and hijack the reply (Fastify drains req.raw)
fastify.post('/api/pay', (req, reply) => { reply.hijack(); return pay.start(req.raw, reply.raw, { body: req.body }); });

// NestJS rides /node. Express platform (default) — req/res are the native objects:
@Post() start(@Req() req: Request, @Res() res: Response) { return pay.start(req, res); }
// Fastify platform — hijack + raw: res.hijack(); return pay.start(req.raw, res.raw, { body: req.body });

Options

| Option | | |---|---| | successUrl / failUrl | path/URL, or (result) => string, for the return redirect | | store | the same PaymentStore your checkout uses — required for reconcile | | authorize | guard for refund + reconcile; you choose the scheme (bearer, session, IP…) | | sweepLimit | max payments per reconcile run | | onError | override the TasdidError → HTTP mapping |

Errors map by code: INVALID_INPUT → 400, NOT_FOUND → 404, refund/transition conflicts → 409, gateway failures → 502, else 500. Bodies are generic ({ error, code }) — no gateway internals, no card data.

Footprint

Zero framework dependency. Peers: @bakissation/tasdid + @bakissation/dinar (you already have them). One package, subpath entries — /fetch (Next.js App Router, Hono, Remix / React Router v7, SvelteKit, Workers, Bun, Deno) + /node (Express, Connect, Fastify, NestJS), same headless core (the . export, createPaymentHandlers).

License

MIT © Abdelbaki Berkati

Credits

Built and maintained by Abdelbaki Berkatiberkati.xyz · @bakissation.