> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tender.cash/llms.txt
> Use this file to discover all available pages before exploring further.

# Donation SDK

> Collect crypto donations in your React application with the Tender donation widget

## Overview

`@tender-cash/donation-sdk` is a React widget for collecting cryptocurrency donations.

It 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 — followed by a deposit screen with a QR code, wallet address and countdown.

The widget mounts inside a Shadow DOM root, so its styles never leak into (or inherit from) your page.

<Card title="@tender-cash/donation-sdk" icon="hand-holding-heart" href="https://www.npmjs.com/package/@tender-cash/donation-sdk">
  Donation widget package for React
</Card>

***

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @tender-cash/donation-sdk
  ```

  ```bash yarn theme={null}
  yarn add @tender-cash/donation-sdk
  ```

  ```bash pnpm theme={null}
  pnpm add @tender-cash/donation-sdk
  ```
</CodeGroup>

<Info>
  React 16.8 or later is required — `react` and `react-dom` are peer dependencies.
</Info>

***

## Quickstart

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

```tsx theme={null}
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}
      campaign={{ goal: 36480 }}
      presetAmounts={[25, 50, 100, 250]}
      closeModal={() => console.log('closed')}
    />
  );
}
```

<Note>
  The Tender API does not store a fundraising target, so pass `campaign={{ goal }}` yourself to render the progress ring.
</Note>

***

## Opening the widget imperatively

Hold a ref and call `openDonation` when the donor clicks your own button. With a ref attached, the component renders nothing until then.

```tsx theme={null}
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,
            campaign: { name: 'Riverbend Relief Foundation' }
          })
        }
      >
        Donate
      </button>

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

***

## API Reference

### Component Props (`TenderDonationProps`)

#### Required Props

| Prop           | Type                                             | Description                                            |
| -------------- | ------------------------------------------------ | ------------------------------------------------------ |
| `accessId`     | `string`                                         | Your Tender merchant Access ID.                        |
| `fiatCurrency` | `string`                                         | Currency the donation is denominated in, e.g. `"USD"`. |
| `env`          | `"sandbox"` \| `"test"` \| `"live"` \| `"local"` | Which Tender API to talk to.                           |

#### Optional Props

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

### `DonationCampaign`

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 derived 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 rendered under the campaign name.                                      |

### `Donor`

| Field       | Type      | Description                               |
| ----------- | --------- | ----------------------------------------- |
| `name`      | `string`  | Donor name.                               |
| `email`     | `string`  | Donor email.                              |
| `anonymous` | `boolean` | Whether the donation is made anonymously. |

### Ref Methods (`TenderDonationRef`)

| 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.                                                                                                                          |

***

## 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 passed 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 carries on with whatever `campaign` props were supplied — a missing header never blocks a donation.

***

## 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.     |
| `pending`          | Awaiting funds.                                |
| `cancelled`        | The donor cancelled, or the window expired.    |
| `failed` / `error` | The donation could not be processed.           |

### Callback Data (`onFinishResponse`)

```typescript theme={null}
interface onFinishResponse {
  status: "partial-payment" | "completed" | "overpayment" | "pending" | "error" | "cancelled" | "failed";
  message: string;
  data: IDonationData | undefined;
}
```

`data` carries the transaction id, wallet address, chain, asset, amounts, and — when known — the campaign id and donor details.

```typescript theme={null}
interface IDonationData {
  id?: string;
  amount?: number;
  coinAmount?: number;
  coin?: string;
  chain?: string;
  address?: string;
  amountPaid?: string;
  balance?: string;
  excess?: string;
  status?: DonationStatusProps;
  campaignId?: string;
  donorName?: string;
  donorEmail?: string;
  anonymous?: boolean;
}
```

***

## Routing calls through your backend

Pass `apiRequest` to keep credentials server-side. The SDK then calls your function instead of the Tender API directly, so request signing and secrets never reach the browser.

```tsx theme={null}
<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' }}
/>
```

```typescript TenderApiRequest theme={null}
type TenderApiRequest = (options: {
  path: string;
  method?: string;
  body?: unknown;
}) => Promise<any>;
```

***

## Exports

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

***

## Features

* Shadow DOM style isolation
* Automatic campaign resolution from your Access ID
* Fundraising progress ring
* Fiat/crypto amount toggle with live rates
* Preset donation amounts and min/max limits
* Optional donor detail capture and anonymous donations
* Auto-open or programmatic control with refs
* Optional backend proxy to keep credentials server-side
* Full TypeScript support

***

## Next Steps

<CardGroup cols={2}>
  <Card title="API Reference" icon="book" href="/api-reference/introduction">
    Explore the full API documentation
  </Card>

  <Card title="Webhooks" icon="webhook" href="/get-started/webhooks">
    Set up webhook notifications
  </Card>

  <Card title="GitHub" icon="github" href="https://github.com/tender-cash">
    View source repositories
  </Card>

  <Card title="Support" icon="envelope" href="mailto:support@tender.cash">
    Contact our support team
  </Card>
</CardGroup>
