@blocklet/payment-react
Version:
Reusable react components for payment kit v2
259 lines (217 loc) • 10 kB
Markdown
# CheckoutDonate
The `CheckoutDonate` component provides a flexible and easy-to-integrate solution for adding donation functionality to your application. It supports various display modes, from a simple button that opens a checkout dialog to a fully custom UI that you control.
This component must be wrapped within both a `PaymentProvider` and a `DonateProvider` to function correctly. The `DonateProvider` manages the settings and state for donation instances within a specific scope of your application.
## How It Works
The donation flow is orchestrated by a combination of `DonateProvider` and `CheckoutDonate`. Here's a high-level overview:
```d2 How It Works icon=graph-ql:diagram
direction: down
user: {
shape: c4-person
}
app: {
label: "Your React App"
checkout-donate: {
label: "CheckoutDonate"
}
checkout-form: {
label: "CheckoutForm (in Dialog)"
}
}
payment-api: {
label: "Payment Backend API"
shape: cylinder
}
# Initial Load
app.checkout-donate -> payment-api: "Fetches settings & supporters"
# Donation Flow
user -> app.checkout-donate: "1. Clicks Donate"
app.checkout-donate -> app.checkout-form: "2. Opens Dialog with CheckoutForm"
app.checkout-form -> payment-api: "3. Processes Payment"
payment-api -> app.checkout-form: "4. Returns Success"
app.checkout-form -> app.checkout-donate: "5. Triggers onPaid callback"
app.checkout-donate -> payment-api: "6. Refetches supporters list"
payment-api -> app.checkout-donate: "7. Returns updated supporters"
```
1. **Initialization**: `DonateProvider` fetches and caches donation settings (like preset amounts, button text) from the backend, identified by a unique `mountLocation`.
2. **Rendering**: `CheckoutDonate` renders a button or custom UI based on the retrieved settings and its props.
3. **Interaction**: When a user initiates a donation, `CheckoutDonate` opens a dialog containing a `CheckoutForm` pre-configured for the donation.
4. **Payment**: The user completes the payment through the `CheckoutForm`.
5. **Confirmation**: After a successful payment, the `onPaid` callback is triggered, and the component automatically refreshes the list of supporters.
## Provider Setup
Before using `CheckoutDonate`, you must wrap your component tree with `PaymentProvider` and `DonateProvider`.
```tsx Provider Setup Example icon=logos:react
import {
PaymentProvider,
DonateProvider,
CheckoutDonate,
} from '/payment-react';
import { useSessionContext } from '../hooks/session-context'; // Your session context hook
function DonationPage() {
const { session, connect } = useSessionContext();
// Ensure session is loaded before rendering providers
if (!session) {
return <div>Loading...</div>;
}
return (
<PaymentProvider session={session} connect={connect}>
<DonateProvider
mountLocation="unique-page-identifier" // A unique key for this donation context
description="Donation for my awesome blog post"
defaultSettings={{
btnText: 'Support Me',
}}>
{/* Your CheckoutDonate component goes here */}
<CheckoutDonate
settings={{
target: "post-123",
title: "Support the Author",
description: "If you find this article helpful, feel free to buy me a coffee",
reference: "https://your-site.com/posts/123",
beneficiaries: [
{
address: "z2qa...",
share: "100",
},
],
}}
/>
</DonateProvider>
</PaymentProvider>
);
}
```
For more details, see the [`DonateProvider`](./providers-donate-provider.md) documentation.
## Component Props
### `DonateProps`
| Prop | Type | Description |
| --- | --- | --- |
| `settings` | `CheckoutDonateSettings` | **Required.** Configuration for this specific donation instance. |
| `onPaid` | `(session) => void` | Optional. Callback function executed after a successful payment. |
| `onError` | `(error) => void` | Optional. Callback function executed if an error occurs. |
| `mode` | `'default' \| 'inline' \| 'custom'` | Optional. The rendering mode. Defaults to `'default'`. |
| `livemode` | `boolean` | Optional. Overrides the `livemode` from `PaymentProvider`. |
| `timeout` | `number` | Optional. Milliseconds to wait before closing the dialog after payment. Defaults to `5000`. |
| `theme` | `'default' \| 'inherit' \| object` | Optional. Theme customization options. See the [Theming](./guides-theming.md) guide. |
| `children` | `(openDialog, donateTotalAmount, supporters, loading, donateSettings) => React.ReactNode` | Optional. A render prop function used only when `mode="custom"`. |
### `CheckoutDonateSettings`
This object is passed to the `settings` prop and defines the core details of the donation.
| Property | Type | Description |
| --- | --- | --- |
| `target` | `string` | **Required.** A unique identifier for the donation target (e.g., a post ID, a project name). |
| `title` | `string` | **Required.** The title displayed at the top of the donation dialog. |
| `description` | `string` | **Required.** A short description displayed in the donation dialog. |
| `reference` | `string` | **Required.** A URL related to the donation, used for reference. |
| `beneficiaries` | `PaymentBeneficiary[]` | **Required.** An array of objects defining who receives the funds. Each object needs an `address` (recipient's DID) and `share` (percentage). |
| `amount` | `object` | Optional. Configures donation amounts, including `presets` (e.g., `['1', '5', '10']`), a default `preset`, `minimum`, `maximum`, and whether `custom` amounts are allowed. |
| `appearance` | `object` | Optional. Customizes the look and feel, including `button` (text, icon, variant) and `history` display (`'avatar'` or `'table'`). |
## Usage Examples
### Default Mode
This is the simplest way to use `CheckoutDonate`. It renders a button that opens a donation dialog, along with a summary of recent supporters.
```tsx Default Donation Button icon=logos:react
import {
PaymentProvider,
DonateProvider,
CheckoutDonate,
} from '/payment-react';
import { useSessionContext } from '../hooks/session-context';
function App() {
const { session, connect } = useSessionContext();
if (!session) {
return <div>Loading session...</div>;
}
return (
<PaymentProvider session={session} connect={connect}>
<DonateProvider
mountLocation="blog-post-123"
description="Donations for Blog Post 123"
defaultSettings={{
btnText: 'Buy me a coffee',
historyType: 'avatar',
}}>
<CheckoutDonate
settings={{
target: 'post-123',
title: 'Support the Author',
description: 'If you found this article helpful, consider a small donation.',
reference: 'https://example.com/posts/123',
beneficiaries: [
{
address: 'z2qa...gCLd', // Author's DID address
share: '100',
},
],
}}
onPaid={() => {
console.log('Donation successful!');
}}
/>
</DonateProvider>
</PaymentProvider>
);
}
```
### Custom UI Mode
For full control over the user interface, use `mode="custom"` and provide a render prop as the `children`. This function gives you access to the donation state, including the total amount raised and a list of supporters, allowing you to build a completely custom display.
```tsx Custom Donation UI icon=logos:react
import {
PaymentProvider,
DonateProvider,
CheckoutDonate,
} from '/payment-react';
import { useSessionContext } from '../hooks/session-context';
import { CircularProgress, Button } from '@mui/material';
function CustomDonationDisplay() {
const { session, connect } = useSessionContext();
if (!session) {
return <div>Loading session...</div>;
}
const donateSettings = {
target: 'post-123',
title: 'Support the Author',
description: 'If you found this article helpful, consider a small donation.',
reference: 'https://example.com/posts/123',
beneficiaries: [
{
address: 'z2qa...gCLd', // Author's DID address
share: '100',
},
],
};
return (
<PaymentProvider session={session} connect={connect}>
<DonateProvider
mountLocation="blog-post-123"
description="Donations for Blog Post 123">
<CheckoutDonate mode="custom" settings={donateSettings}>
{(openDonate, totalAmount, supporters, loading, settings) => (
<div style={{ border: '1px solid #ccc', padding: '16px', borderRadius: '8px' }}>
<h2>Our Supporters</h2>
<p>Total Donations: <strong>{totalAmount}</strong></p>
<Button variant="contained" onClick={openDonate}>
{settings?.appearance?.button?.text || 'Donate Now'}
</Button>
{loading ? (
<CircularProgress style={{ marginTop: '16px' }} />
) : (
<ul style={{ listStyle: 'none', padding: 0, marginTop: '16px' }}>
{(supporters.supporters || []).map((supporter) => (
<li key={supporter.id}>
<span>{supporter.customer?.name}</span>
</li>
))}
</ul>
)}
</div>
)}
</CheckoutDonate>
</DonateProvider>
</PaymentProvider>
);
}
```
The `children` function receives the following arguments:
- `openDonate`: A function to manually trigger the donation dialog.
- `totalAmount`: A formatted string of the total amount donated (e.g., `"125.00 T"`).
- `supporters`: A `DonateHistory` object containing the `supporters` array and currency info.
- `loading`: A boolean indicating if the supporter data is being fetched.
- `settings`: The resolved donation settings, merged from `DonateProvider` and props.