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

@digibuffer/cone-pay

v0.3.1

Published

Minimal, reusable Razorpay integration core — create orders/plans/subscriptions on the server, open the checkout modal on the client, verify checkout/webhook signatures. No webhook handling or app-specific logic.

Readme

@digibuffer/cone-pay

Minimal, reusable Razorpay integration for Node/Next.js apps.

Scope is deliberately narrow: this package only does the parts that are identical in every integration — creating an order/plan/subscription on the server, opening the checkout modal on the client, and verifying the signature the modal hands back. It does not handle webhooks, entitlement/subscription state, or anything about what a payment unlocks in your app — that logic differs per app and belongs there, not here.


Installation

npm install @digibuffer/cone-pay

Server — create an order or subscription

// src/lib/razorpay.ts
import { createRazorpayClient } from "@digibuffer/cone-pay"

export const razorpay = createRazorpayClient({
  keyId: process.env.RAZORPAY_KEY_ID!,
  keySecret: process.env.RAZORPAY_KEY_SECRET!,
})

One-time payment:

// app/api/checkout/route.ts
const order = await razorpay.createOrder({
  amount: 49900, // ₹499.00, smallest currency unit
  currency: "INR",
  receipt: `order_${userId}_${Date.now()}`,
})
return Response.json({ orderId: order.id, amount: order.amount })

Recurring payment:

const plan = await razorpay.createPlan({
  period: "monthly",
  interval: 1,
  name: "Pro plan",
  amount: 99900,
})

const subscription = await razorpay.createSubscription({
  planId: plan.id,
  totalCount: 12, // bill 12 times, then stop
})

Client — open the checkout modal

"use client"

import { openRazorpayCheckout, RazorpayCheckoutDismissedError } from "@digibuffer/cone-pay/client"

async function handlePay() {
  const { orderId, amount } = await fetch("/api/checkout", { method: "POST" }).then((r) => r.json())

  try {
    const payment = await openRazorpayCheckout({
      key: process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID!,
      orderId,
      amount,
      name: "Your App",
      prefill: { email: user.email },
    })

    // Send this to your own API route to verify + fulfill.
    await fetch("/api/checkout/confirm", {
      method: "POST",
      body: JSON.stringify(payment),
    })
  } catch (err) {
    if (err instanceof RazorpayCheckoutDismissedError) return // user closed the modal
    throw err
  }
}

Server — verify the payment before fulfilling it

The modal's handler response is not proof of payment on its own — always verify the signature server-side before granting anything.

// app/api/checkout/confirm/route.ts
import { verifyRazorpayOrderSignature } from "@digibuffer/cone-pay"

const body = await req.json()
const ok = verifyRazorpayOrderSignature({
  orderId: body.razorpay_order_id,
  paymentId: body.razorpay_payment_id,
  signature: body.razorpay_signature,
  keySecret: process.env.RAZORPAY_KEY_SECRET!,
})

if (!ok) return new Response("Invalid signature", { status: 400 })

// Now it's your app's job: mark the order paid, grant the entitlement, etc.

For a subscription payment, use verifyRazorpaySubscriptionSignature (same shape, takes subscriptionId instead of orderId) with razorpay_subscription_id from the response.


Server — verify a webhook delivery

Webhooks are the reliable source of truth for async events (a renewal charge, a delayed payment capture) — the checkout-confirm step above is just a fast UX shortcut, not something to rely on alone. Verifying the delivery is mechanical; deciding what to do with it is your app's job.

// app/api/webhooks/razorpay/route.ts
import { verifyRazorpayWebhookSignature } from "@digibuffer/cone-pay"

const body = await req.text() // raw body — signing is over exact bytes, don't parse first
const signature = req.headers.get("x-razorpay-signature") ?? ""

if (!verifyRazorpayWebhookSignature(body, signature, process.env.RAZORPAY_WEBHOOK_SECRET!)) {
  return new Response("Invalid signature", { status: 400 })
}

const event = JSON.parse(body)
// Now it's your app's job: log the event, dispatch on event.event, update your own tables.

The webhook secret is configured separately from your API key secret — set it when you register the webhook URL in the Razorpay dashboard.


What's intentionally not in here

  • Webhook handling. Razorpay's webhooks (payment.captured, subscription.charged, etc.) are the reliable source of truth for async/offline events — this package verifies a delivery's signature, but what you do with it — update a DB row, send an email, revoke access — is 100% app-specific. Dispatch and handle events in each app.
  • Entitlement / subscription state. Whether a user is "pro", when their access expires, what a plan unlocks — that's your app's data model, not this package's.
  • Currency formatting, pricing pages, invoices, refunds. Out of scope for the same reason.

API reference

@digibuffer/cone-pay (server)

  • createRazorpayClient({ keyId, keySecret }){ createOrder, createPlan, createSubscription, fetchSubscription, fetchPayment, cancelSubscription }
    • fetchSubscription(subscriptionId) / fetchPayment(paymentId) — read current state straight from Razorpay's API, for persisting it right after checkout closes instead of waiting on a webhook (self-hosted/local setups can't always receive one)
    • cancelSubscription({ subscriptionId, cancelAtCycleEnd? }) — immediately by default, or at the end of the current billing cycle
  • verifyRazorpayOrderSignature({ orderId, paymentId, signature, keySecret })boolean
  • verifyRazorpaySubscriptionSignature({ subscriptionId, paymentId, signature, keySecret })boolean
  • verifyRazorpayWebhookSignature(body, signature, webhookSecret)boolean

@digibuffer/cone-pay/client (browser, "use client")

  • loadRazorpayCheckout()Promise<void> — injects checkout.js once
  • openRazorpayCheckout(options)Promise<RazorpayPaymentSuccess> — rejects with RazorpayCheckoutDismissedError if closed without paying