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

@mandar1045/resync

v0.1.1

Published

Node.js SDK for Resync — the recovery layer for UPI Autopay. Register your autopay customers, report payment events, and let Resync win back failed charges.

Readme

@mandar1045/resync

Node.js SDK for Resync — the recovery layer for UPI Autopay.

Your billing stays on Razorpay or Cashfree. You register your autopay customers with Resync, it watches every expected charge, and when one fails it executes smart retries on your own gateway account. Pricing is success-based: 2% of recovered revenue.

  • Zero runtime dependencies (uses the built-in fetch)
  • Node 18+
  • Full TypeScript types

Install

npm install @mandar1045/resync

Quick start

Get an API key from the Resync dashboard (Settings → API Keys), then:

import { Resync } from "@mandar1045/resync";

const resync = new Resync(process.env.RESYNC_API_KEY!, {
  baseUrl: "http://localhost:8000", // your Resync gateway
});

// 1. Register an existing autopay customer (idempotent on external_ref)
const { autopay, already_existed } = await resync.autopays.register({
  external_ref: "sub_1042",                 // your own id
  customer: { name: "Asha", email: "[email protected]" },
  gateway: "RAZORPAY",
  gateway_mandate_id: "sub_NxA92kD1",       // the gateway's mandate id
  amount_paise: 49900,                      // ₹499.00
  interval: "MONTHLY",
  next_autopay_at: "2026-07-01T10:00:00Z",
  last_payment_at: "2026-06-01T10:02:11Z",
});

That's it — Resync now watches this autopay. If the expected charge doesn't arrive (or you report a failure), recovery starts automatically.

Backfill all your autopays

const result = await resync.autopays.registerBulk(
  customers.map((c) => ({
    external_ref: c.subscriptionId,
    customer: { email: c.email, name: c.name },
    gateway: "RAZORPAY",
    gateway_mandate_id: c.razorpaySubscriptionId,
    amount_paise: c.amountPaise,
    next_autopay_at: c.nextChargeDate,
  })),
);
console.log(`${result.imported} imported, ${result.failed} failed`);

Import straight from your gateway (no per-customer data)

If you'd rather not assemble the list yourself, connect your Razorpay/Cashfree keys in the Resync dashboard (Settings → Gateways) and let Resync read your existing mandates directly from the gateway:

const { imported, updated, failed } = await resync.autopays.importFromGateway({
  gateway: "RAZORPAY", // omit to import from every connected gateway
});
console.log(`${imported} imported, ${updated} updated, ${failed} failed`);

Idempotent — re-running refreshes existing autopays instead of duplicating them. Resync only knows the customers it reads here (or that you register), so run this once after connecting a gateway, or enable background sync to keep it current automatically.

Report payment outcomes (recommended)

The fastest recovery starts the moment you see a failure:

// In your gateway webhook handler:
await resync.events.reportPayment({
  external_ref: "sub_1042",
  status: "FAILED",
  failure_code: "U30",        // insufficient balance → retried on salary day
  amount_paise: 49900,
  payment_ref: "pay_88x1",    // your payment id — makes the call idempotent
});

// And on success, to keep the schedule in sync:
await resync.events.reportPayment({
  external_ref: "sub_1042",
  status: "SUCCESS",
  amount_paise: 49900,
  payment_ref: "pay_88x2",
});

Keep autopays in sync

await resync.autopays.update(autopay.id, {
  next_autopay_at: "2026-08-01T10:00:00Z",
});

await resync.autopays.cancel(autopay.id); // customer cancelled at your end

Receive webhooks

Register an endpoint, then verify every delivery:

import express from "express";
import { constructWebhookEvent } from "@mandar1045/resync";

const app = express();

app.post("/webhooks/resync", express.raw({ type: "*/*" }), (req, res) => {
  try {
    const event = constructWebhookEvent(
      req.body,
      req.header("X-SubFlow-Signature")!,
      process.env.RESYNC_WEBHOOK_SECRET!,
    );
    // event types: payment.failed, payment.succeeded
    console.log("resync event:", event);
    res.status(200).end();
  } catch {
    res.status(401).end();
  }
});

Recovery stats

const stats = await resync.dunning.stats();
console.log(
  `recovered ₹${Number(stats.recovered_amount_paise ?? 0) / 100}`,
  `(${stats.recovery_rate_pct?.toFixed(1)}% of ${stats.total_failed} failures)`,
);

Error handling

Every non-2xx response throws a ResyncError:

import { ResyncError } from "@mandar1045/resync";

try {
  await resync.autopays.get("does-not-exist");
} catch (err) {
  if (err instanceof ResyncError) {
    console.error(err.status, err.code, err.message);
  }
}

API reference

The full REST surface is described in openapi.yaml at the repository root.