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

@tender-cash/donation-sdk

v1.0.0

Published

Tender crypto donation widget for React

Readme

@tender-cash/donation-sdk

License: MIT

React SDK for collecting crypto donations with Tender Cash.

The widget renders a donation form — campaign header with fundraising progress, amount entry with a fiat/crypto toggle, preset amounts, asset and network pickers, and a fee breakdown — then a deposit screen with a QR code, wallet address and countdown. It mounts inside a shadow root, so its styles never leak into (or inherit from) your page.

Packaging Contract

The package ships with explicit dual-module exports and generated types:

  • import -> dist/index.mjs
  • require -> dist/index.cjs
  • types -> dist/types/index.d.ts

Installation

Using yarn:

yarn add @tender-cash/donation-sdk

Using npm:

npm install @tender-cash/donation-sdk

Exports

  • TenderDonationSdk — the widget component
  • TenderDonationProps — component props
  • TenderDonationRef — imperative handle type
  • DonationCampaign, Donor, DonationMeta, StartDonationParams
  • IDonationData, DonationStatusProps, onFinishResponse
  • TenderEnvironments, TenderApiRequest

Quick Start

The widget fetches its campaign from the Tender API using your accessId, so the header fills itself in — no campaign props required.

import { TenderDonationSdk, onFinishResponse } from "@tender-cash/donation-sdk";

function DonateComponent() {
  const handleEventResponse = (response: onFinishResponse) => {
    console.log("SDK Response:", response);
  };

  return (
    <TenderDonationSdk
      accessId="YOUR_ACCESS_ID"
      fiatCurrency="USD"
      env="test"
      onEventResponse={handleEventResponse}
      // Optional: the API has no fundraising target, so pass one to show
      // the progress ring.
      campaign={{ goal: 36480 }}
      presetAmounts={[25, 50, 100, 250]}
      closeModal={() => console.log("closed")}
    />
  );
}

Campaign resolution

GET /system/campaign resolves your accessId to its agent and returns the merchant profile plus that agent's transaction stats:

| Widget field | Source | | --- | --- | | name | Merchant's merchantName, falling back to the agent name. | | logo | Merchant's logo, falling back to avatar. | | description | Merchant's merchantDescription. | | raised | Summed USD value of the agent's completed transactions. | | id | The agent reference, used as the donation reference. |

Anything you pass in campaign overrides the fetched value. The goal is the exception — the API does not store a fundraising target, so pass campaign={{ goal }} yourself. The progress ring only renders when a goal is available; without one the widget shows the raised amount alone.

If the campaign request fails, the widget logs nothing and carries on with whatever campaign props were supplied — a missing header never blocks a donation.

Opening the widget imperatively

Hold a ref and call openDonation when the donor clicks your own button. The component renders nothing until then.

import { useRef } from "react";
import { TenderDonationSdk, TenderDonationRef } from "@tender-cash/donation-sdk";

function DonateButton() {
  const donationRef = useRef<TenderDonationRef>(null);

  return (
    <>
      <button
        onClick={() =>
          donationRef.current?.openDonation({
            referenceId: "donation-123",
            amount: 50, // optional: pre-fills the amount field
            campaign: { name: "Riverbend Relief Foundation" },
          })
        }
      >
        Donate
      </button>

      <TenderDonationSdk
        ref={donationRef}
        accessId="YOUR_ACCESS_ID"
        fiatCurrency="USD"
        env="test"
      />
    </>
  );
}

Props

| Prop | Type | Required | Description | | --- | --- | --- | --- | | accessId | string | yes | Merchant access identifier. | | fiatCurrency | string | yes | Currency the donation is denominated in, e.g. "USD". | | env | "sandbox" \| "test" \| "live" \| "local" | yes | Which Tender API to talk to. | | campaign | DonationCampaign | no | Overrides for the fetched campaign. Pass goal here to show the progress ring. | | autoOpen | boolean | no | Opens on mount instead of showing the inline donate button. Defaults to true unless you attach a ref. | | amount | number | no | Pre-fills the amount field. The donor can still change it. | | presetAmounts | number[] | no | Preset chips under the amount input. Defaults to [25, 50, 100, 250]. | | minAmount | number | no | Minimum accepted donation. Defaults to 1. | | maxAmount | number | no | Maximum accepted donation. | | donor | Donor | no | Pre-filled donor name / email / anonymous flag. | | collectDonorDetails | boolean | no | Shows name and email fields on the form. Defaults to false. | | referenceId | string | no | Your reference for this donation. Falls back to campaign.id. | | donationExpirySeconds | number | no | Deposit countdown length. Defaults to 1800 (30 minutes). | | confirmationInterval | number | no | Status polling interval in ms. Defaults to 5000. | | meta | DonationMeta | no | Extra metadata forwarded to the Tender API. | | theme | "light" \| "dark" | no | Defaults to "light". | | onEventResponse | (data: onFinishResponse) => void | no | Status callback. | | closeModal | () => void | no | Called when the donor closes the widget. | | apiBaseUrl | string | no | Overrides the API base URL for the chosen env. | | apiRequest | TenderApiRequest | no | Routes every API call through your backend. |

DonationCampaign

| Field | Type | Description | | --- | --- | --- | Every field is optional — each one overrides what the API returned.

| Field | Type | Description | | --- | --- | --- | | id | string | Donation reference. Defaults to the agent reference. | | name | string | Header name. Defaults to the merchant name. | | logo | string | Logo URL. Falls back to initials from name. | | goal | number | Fundraising target. Not returned by the API — pass it to show the ring. | | raised | number | Amount raised. Defaults to the agent's completed transaction value. | | description | string | Line under the campaign name. |

Ref methods

| Method | Description | | --- | --- | | openDonation(params?) | Opens the widget on the donation form. params may carry referenceId, amount, campaign, donor, donationExpirySeconds and meta. | | dismiss() | Closes the widget. |

Amount entry

The amount field accepts either fiat or crypto. The toggle beside it swaps the denomination and converts the current value using the rate Tender returns for the selected asset, so the donation stays worth the same either way. When the API prices an asset, the form also shows the rate and a Total send line — the donation plus the network fee for the chosen chain.

Donation status events

onEventResponse fires as the donation progresses:

| status | Meaning | | --- | --- | | completed | Full amount received. | | partial-payment | Some funds received; a balance is outstanding. | | overpayment | More than the entered amount was received. | | cancelled | The donor cancelled, or the window expired. | | failed / error | The donation could not be processed. |

The callback receives { status, message, data }, where data carries the transaction id, wallet address, chain, asset, amounts, and — when known — the campaign id and donor details.

Routing calls through your backend

Pass apiRequest to keep credentials server-side. The SDK then calls your function instead of the Tender API directly:

<TenderDonationSdk
  accessId="YOUR_ACCESS_ID"
  fiatCurrency="USD"
  env="live"
  apiRequest={async ({ path, method, body }) => {
    const response = await fetch(`/api/tender${path}`, {
      method,
      headers: { "Content-Type": "application/json" },
      body: body ? JSON.stringify(body) : undefined,
    });
    const payload = await response.json();
    return payload.data;
  }}
  campaign={{ name: "Riverbend Relief Foundation" }}
/>

Development

yarn install
yarn dev      # run the local harness in test/
yarn test     # vitest
yarn build    # vite library build + type declarations

License

MIT