# CheckoutDonate

`CheckoutDonate` 组件为您的应用程序添加捐赠功能提供了一个灵活且易于集成的解决方案。它支持多种显示模式，从一个简单的打开结账对话框的按钮，到一个由您完全控制的自定义 UI。

该组件必须同时包装在 `PaymentProvider` 和 `DonateProvider` 中才能正常工作。`DonateProvider` 管理应用程序特定范围内的捐赠实例的设置和状态。

## 工作原理

捐赠流程由 `DonateProvider` 和 `CheckoutDonate` 共同协调。以下是其高级概述：

<!-- DIAGRAM_IMAGE_START:flowchart:4:3::1765377352 -->
![CheckoutDonate](assets/diagram/components-checkout-checkout-donate-01.zh.jpg)
<!-- DIAGRAM_IMAGE_END -->

1.  **初始化**：`DonateProvider` 从后端获取并缓存捐赠设置（如预设金额、按钮文本），这些设置由唯一的 `mountLocation` 标识。
2.  **渲染**：`CheckoutDonate` 根据检索到的设置及其 props 渲染一个按钮或自定义 UI。
3.  **交互**：当用户发起捐赠时，`CheckoutDonate` 会打开一个包含为该捐赠预先配置好的 `CheckoutForm` 的对话框。
4.  **支付**：用户通过 `CheckoutForm` 完成支付。
5.  **确认**：支付成功后，将触发 `onPaid` 回调，并且组件会自动刷新支持者列表。

## Provider 设置

在使用 `CheckoutDonate` 之前，您必须使用 `PaymentProvider` 和 `DonateProvider` 包装您的组件树。

```tsx Provider 设置示例 icon=logos:react
import {
  PaymentProvider,
  DonateProvider,
  CheckoutDonate,
} from '@blocklet/payment-react';
import { useSessionContext } from '../hooks/session-context'; // 你的会话上下文钩子

function DonationPage() {
  const { session, connect } = useSessionContext();

  // 确保在渲染 provider 之前已加载会话
  if (!session) {
    return <div>加载中...</div>;
  }

  return (
    <PaymentProvider session={session} connect={connect}>
      <DonateProvider
        mountLocation="unique-page-identifier" // 此捐赠上下文的唯一键
        description="为我的精彩博客文章捐赠"
        defaultSettings={{
          btnText: '支持我',
        }}>
        {/* 你的 CheckoutDonate 组件放在这里 */}
        <CheckoutDonate 
          settings={{
            target: "post-123",
            title: "支持作者",
            description: "如果您觉得这篇文章有帮助，可以请我喝杯咖啡",
            reference: "https://your-site.com/posts/123",
            beneficiaries: [
              {
                address: "z2qa...",
                share: "100",
              },
            ],
          }}
        />
      </DonateProvider>
    </PaymentProvider>
  );
}
```

更多详情，请参阅 [`DonateProvider`](./providers-donate-provider.md) 文档。

## 组件属性

### `DonateProps`

| Prop | Type | Description |
| --- | --- | --- |
| `settings` | `CheckoutDonateSettings` | **必需。** 此特定捐赠实例的配置。 |
| `onPaid` | `(session) => void` | 可选。支付成功后执行的回调函数。 |
| `onError` | `(error) => void` | 可选。发生错误时执行的回调函数。 |
| `mode` | `'default' \| 'inline' \| 'custom'` | 可选。渲染模式。默认为 `'default'`。 |
| `livemode` | `boolean` | 可选。覆盖来自 `PaymentProvider` 的 `livemode`。 |
| `timeout` | `number` | 可选。支付后等待关闭对话框的毫秒数。默认为 `5000`。 |
| `theme` | `'default' \| 'inherit' \| object` | 可选。主题自定义选项。请参阅 [Theming](./guides-theming.md) 指南。 |
| `children` | `(openDialog, donateTotalAmount, supporters, loading, donateSettings) => React.ReactNode` | 可选。仅在 `mode="custom"` 时使用的渲染属性函数。 |

### `CheckoutDonateSettings`

此对象传递给 `settings` 属性，并定义了捐赠的核心细节。

| Property | Type | Description |
| --- | --- | --- |
| `target` | `string` | **必需。** 捐赠目标的唯一标识符（例如，帖子 ID、项目名称）。 |
| `title` | `string` | **必需。** 显示在捐赠对话框顶部的标题。 |
| `description` | `string` | **必需。** 显示在捐赠对话框中的简短描述。 |
| `reference` | `string` | **必需。** 与捐赠相关的 URL，用于参考。 |
| `beneficiaries` | `PaymentBeneficiary[]` | **必需。** 定义资金接收者的对象数组。每个对象需要一个 `address`（收款人的 DID）和 `share`（百分比）。 |
| `amount` | `object` | 可选。配置捐赠金额，包括 `presets`（例如，`['1', '5', '10']`）、默认的 `preset`、`minimum`、`maximum`，以及是否允许 `custom` 金额。 |
| `appearance` | `object` | 可选。自定义外观和感觉，包括 `button`（文本、图标、变体）和 `history` 显示（`'avatar'` 或 `'table'`）。 |

## 用法示例

### 默认模式

这是使用 `CheckoutDonate` 的最简单方法。它会渲染一个打开捐赠对话框的按钮，以及近期支持者的摘要。

```tsx 默认捐赠按钮 icon=logos:react
import {
  PaymentProvider,
  DonateProvider,
  CheckoutDonate,
} from '@blocklet/payment-react';
import { useSessionContext } from '../hooks/session-context';

function App() {
  const { session, connect } = useSessionContext();

  if (!session) {
    return <div>加载会话中...</div>;
  }

  return (
    <PaymentProvider session={session} connect={connect}>
      <DonateProvider
        mountLocation="blog-post-123"
        description="为博客文章 123 捐赠"
        defaultSettings={{
          btnText: '请我喝杯咖啡',
          historyType: 'avatar',
        }}>
        <CheckoutDonate
          settings={{
            target: 'post-123',
            title: '支持作者',
            description: '如果您觉得这篇文章有帮助，可以考虑小额捐赠。',
            reference: 'https://example.com/posts/123',
            beneficiaries: [
              {
                address: 'z2qa...gCLd', // 作者的 DID 地址
                share: '100',
              },
            ],
          }}
          onPaid={() => {
            console.log('捐赠成功！');
          }}
        />
      </DonateProvider>
    </PaymentProvider>
  );
}
```

### 自定义 UI 模式

要完全控制用户界面，请使用 `mode="custom"` 并提供一个渲染属性作为 `children`。此函数让您能够访问捐赠状态，包括筹集到的总金额和支持者列表，从而允许您构建一个完全自定义的显示。

```tsx 自定义捐赠 UI icon=logos:react
import {
  PaymentProvider,
  DonateProvider,
  CheckoutDonate,
} from '@blocklet/payment-react';
import { useSessionContext } from '../hooks/session-context';
import { CircularProgress, Button } from '@mui/material';

function CustomDonationDisplay() {
  const { session, connect } = useSessionContext();

  if (!session) {
    return <div>加载会话中...</div>;
  }

  const donateSettings = {
    target: 'post-123',
    title: '支持作者',
    description: '如果您觉得这篇文章有帮助，可以考虑小额捐赠。',
    reference: 'https://example.com/posts/123',
    beneficiaries: [
      {
        address: 'z2qa...gCLd', // 作者的 DID 地址
        share: '100',
      },
    ],
  };

  return (
    <PaymentProvider session={session} connect={connect}>
      <DonateProvider
        mountLocation="blog-post-123"
        description="为博客文章 123 捐赠">
        <CheckoutDonate mode="custom" settings={donateSettings}>
          {(openDonate, totalAmount, supporters, loading, settings) => (
            <div style={{ border: '1px solid #ccc', padding: '16px', borderRadius: '8px' }}>
              <h2>我们的支持者</h2>
              <p>总捐赠额：<strong>{totalAmount}</strong></p>
              <Button variant="contained" onClick={openDonate}>
                {settings?.appearance?.button?.text || '立即捐赠'}
              </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>
  );
}
```

`children` 函数接收以下参数：

-   `openDonate`：一个手动触发捐赠对话框的函数。
-   `totalAmount`：格式化后的总捐赠金额字符串（例如，`"125.00 T"`）。
-   `supporters`：一个 `DonateHistory` 对象，包含 `supporters` 数组和货币信息。
-   `loading`：一个布尔值，指示是否正在获取支持者数据。
-   `settings`：解析后的捐赠设置，由 `DonateProvider` 和 props 合并而成。