@tender-cash/donation-sdk
v1.0.0
Published
Tender crypto donation widget for React
Maintainers
Readme
@tender-cash/donation-sdk
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.mjsrequire->dist/index.cjstypes->dist/types/index.d.ts
Installation
Using yarn:
yarn add @tender-cash/donation-sdkUsing npm:
npm install @tender-cash/donation-sdkExports
TenderDonationSdk— the widget componentTenderDonationProps— component propsTenderDonationRef— imperative handle typeDonationCampaign,Donor,DonationMeta,StartDonationParamsIDonationData,DonationStatusProps,onFinishResponseTenderEnvironments,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 declarationsLicense
MIT
