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

@seedhape/react

v0.3.13

Published

SeedhaPe React components — PaymentButton, PaymentModal, usePayment hook

Readme

@seedhape/react

React components and hooks for SeedhaPe — zero-fee UPI payment verification for Indian merchants.

npm install @seedhape/react @seedhape/sdk
pnpm add @seedhape/react @seedhape/sdk

Peer requirements: React ≥ 18. Works with Next.js 14/15 App Router and Pages Router.


Overview

This package provides three levels of integration:

| Export | Use when | |--------|----------| | <PaymentButton> | You want a single drop-in button — handles order creation and modal entirely | | <PaymentModal> | You create the order yourself and want explicit control over when the modal opens | | usePayment | You're building a fully custom payment UI and need the state machine |

All components share configuration through SeedhaPeProvider.


Setup: SeedhaPeProvider

Wrap your app (or just the checkout subtree) once. The provider takes an onCreateOrder function that you implement — this keeps your API key on the server and out of client bundles.

Next.js App Router (server actions)

// app/layout.tsx
import { SeedhaPeProvider } from '@seedhape/react';
import { SeedhaPe } from '@seedhape/sdk';

async function createOrder(opts: CreateOrderOptions): Promise<OrderData> {
  'use server';
  const sp = new SeedhaPe({ apiKey: process.env.SEEDHAPE_API_KEY! });
  return sp.createOrder(opts);
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <SeedhaPeProvider onCreateOrder={createOrder}>
          {children}
        </SeedhaPeProvider>
      </body>
    </html>
  );
}

Next.js Pages Router (API route)

// pages/_app.tsx
import { SeedhaPeProvider } from '@seedhape/react';

async function createOrder(opts) {
  const res = await fetch('/api/create-order', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(opts),
  });
  if (!res.ok) throw new Error('Order creation failed');
  return res.json();
}

export default function App({ Component, pageProps }) {
  return (
    <SeedhaPeProvider onCreateOrder={createOrder}>
      <Component {...pageProps} />
    </SeedhaPeProvider>
  );
}
// pages/api/create-order.ts
import { SeedhaPe } from '@seedhape/sdk';

const sp = new SeedhaPe({ apiKey: process.env.SEEDHAPE_API_KEY! });

export default async function handler(req, res) {
  if (req.method !== 'POST') return res.status(405).end();
  const order = await sp.createOrder(req.body);
  res.json(order);
}

Provider props

| Prop | Type | Required | Description | |------|------|----------|-------------| | onCreateOrder | (opts: CreateOrderOptions) => Promise<OrderData> | Yes | Called whenever a payment is initiated. Implement this on the server — your API key must never reach the browser. | | baseUrl | string | No | Override the API base URL. Default: https://seedhape.onrender.com. |


<PaymentButton>

The simplest integration. One component creates the order and opens the payment modal on click.

import { PaymentButton } from '@seedhape/react';

function CheckoutPage() {
  return (
    <PaymentButton
      amount={49900}                         // paise — ₹499
      description="Pro subscription"
      expectedSenderName="Rahul Sharma"      // strongly recommended
      customerEmail="[email protected]"
      metadata={{ planKey: 'PRO', userId: 'usr_abc' }}
      onSuccess={(result) => {
        console.log('Payment verified!', result.orderId);
        router.push('/dashboard?upgraded=true');
      }}
      onExpired={(orderId) => {
        console.log('Order expired:', orderId);
      }}
    >
      Pay ₹499 →
    </PaymentButton>
  );
}

PaymentButton props

| Prop | Type | Required | Description | |------|------|----------|-------------| | amount | number | Yes | Amount in paise. ₹1 = 100 paise. | | description | string | No | Order description, shown on the payment page. | | expectedSenderName | string | No | Payer's UPI-registered name. Strongly recommended — improves auto-matching and reduces disputes. | | customerEmail | string | No | Stored on the order and echoed in webhooks. | | customerPhone | string | No | Stored on the order and echoed in webhooks. | | metadata | Record<string, unknown> | No | Arbitrary JSON echoed verbatim in all webhook payloads. | | onSuccess | (result: PaymentResult) => void | No | Called when payment is confirmed (VERIFIED or RESOLVED). | | onExpired | (orderId: string) => void | No | Called when the order expires. | | className | string | No | Custom CSS class. When provided, all default styles are removed — style the button entirely yourself. | | children | ReactNode | No | Button label. Default: "Pay Now". |

Default styling (applied when className is omitted): green background (#16a34a), white text, rounded corners, 12px padding. Override with className to use your own design system.


<PaymentModal>

For when you create the order yourself and want to control exactly when the modal opens and closes.

import { useState } from 'react';
import { PaymentModal } from '@seedhape/react';
import { useSeedhaPeContext } from '@seedhape/react';

function CustomCheckout() {
  const { onCreateOrder } = useSeedhaPeContext();
  const [orderId, setOrderId] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);

  async function handlePay() {
    setLoading(true);
    try {
      const order = await onCreateOrder({ amount: 49900, description: 'Pro plan' });
      setOrderId(order.id);
    } finally {
      setLoading(false);
    }
  }

  return (
    <>
      <button onClick={handlePay} disabled={loading}>
        {loading ? 'Creating order…' : 'Proceed to payment'}
      </button>

      {orderId && (
        <PaymentModal
          orderId={orderId}
          open={true}
          onClose={() => setOrderId(null)}
          onSuccess={(result) => {
            setOrderId(null);
            console.log('Paid! Amount:', result.amount / 100);
          }}
          onExpired={(id) => {
            setOrderId(null);
            console.warn('Expired:', id);
          }}
        />
      )}
    </>
  );
}

PaymentModal props

| Prop | Type | Required | Description | |------|------|----------|-------------| | orderId | string | Yes | SeedhaPe order ID from your server. | | open | boolean | Yes | Whether the modal is visible. Set to false to hide without destroying state. | | onClose | () => void | Yes | Called when the user dismisses the modal (backdrop click, Escape key, or close button). | | onSuccess | (result: PaymentResult) => void | No | Called when payment is confirmed. The modal auto-closes after 2.5 s. | | onExpired | (orderId: string) => void | No | Called after the dispute upload UI is shown and the order is confirmed expired. |

Modal behaviour:

  • If expectedSenderName was not set when the order was created, the modal shows a name input step first, then stores it on the order before showing the QR code.
  • Polls order status every 3 seconds.
  • Countdown timer turns amber at 5 minutes remaining, red at 1 minute.
  • On expiry or dispute, shows a screenshot upload UI so the customer can submit evidence.
  • Adds a beforeunload warning while payment is in progress.

usePayment hook

Full access to the payment state machine. Use this when you need to build a completely custom UI — embedded in a drawer, split across steps, etc.

import { usePayment } from '@seedhape/react';

function PaymentFlow() {
  const { state, createPayment, onSuccess, onExpired, reset } = usePayment();

  async function start() {
    await createPayment({
      amount: 49900,
      description: 'Pro subscription',
      expectedSenderName: 'Rahul Sharma',
    });
  }

  if (state.phase === 'idle') {
    return <button onClick={start}>Pay ₹499</button>;
  }

  if (state.phase === 'creating') {
    return <Spinner />;
  }

  if (state.phase === 'pending') {
    return (
      <div>
        <img src={state.order.qrCode} alt="Scan to pay" />
        <a href={state.order.upiUri}>Open UPI App</a>
        <p>Waiting for payment…</p>
      </div>
    );
  }

  if (state.phase === 'verified') {
    return <p>Payment confirmed! ₹{state.result.amount / 100}</p>;
  }

  if (state.phase === 'expired') {
    return (
      <>
        <p>Order expired</p>
        <button onClick={reset}>Try again</button>
      </>
    );
  }

  if (state.phase === 'error') {
    return (
      <>
        <p>Error: {state.error}</p>
        <button onClick={reset}>Try again</button>
      </>
    );
  }
}

State machine

type PaymentState =
  | { phase: 'idle' }
  | { phase: 'creating' }
  | { phase: 'pending';  order: OrderData }
  | { phase: 'verified'; result: PaymentResult }
  | { phase: 'expired';  orderId: string }
  | { phase: 'error';    error: string }

Hook return value

| Key | Type | Description | |-----|------|-------------| | state | PaymentState | Current phase + phase-specific data | | createPayment | (opts: CreateOrderOptions) => Promise<OrderData> | Transition idle → creating → pending. Calls onCreateOrder from context. | | onSuccess | (result: PaymentResult) => void | Manually transition to verified. Use when you're polling status yourself. | | onExpired | (orderId: string) => void | Manually transition to expired. | | reset | () => void | Return to idle from any state. |


TypeScript types

Exported from @seedhape/react:

import type { PaymentButtonProps, PaymentModalProps } from '@seedhape/react';

Core types (OrderData, OrderStatus, PaymentResult, CreateOrderOptions) are re-exported from @seedhape/sdk.


Notes on key security

The provider's onCreateOrder is the only place your SeedhaPe API key should appear. It must run on the server. Never pass apiKey to a component or include it in a client bundle.

❌ const sp = new SeedhaPe({ apiKey: 'sp_live_...' })  // in a React component
✓  'use server'  /  API route  /  Express handler

License

Proprietary. All rights reserved.