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

# 批量退款

> Portal 的批量退款 Tab：模板列与限制、上传校验检查什么及检查顺序、重复文件与同一时间只能有一个批次的规则、批次状态，以及逐行失败原因在哪里查看。

批量退款是 Portal **交易**模块下的一个 Tab，与支付、退款、报表并列，也是 Portal 中唯一
可以*发起*退款的地方。文件中的每一行都会变成一条普通的退款记录，因此
前置条件、部分退款、跨币种金额和退还的手续费的行为都与
[退款 FAQ](/docs/zh/support/refund-faq) 中描述的完全一致。

该 Tab 按账户开通——如果你的 Portal 里没有它，说明尚未为你开启。在 Tab 内，
**导出批量退款模板**和**上传批量退款文件**需要批量退款上传权限，
而列表、其筛选项和**导出**对任何能打开该 Tab 的角色都开放：如果你能看到
按钮，就能使用它。

## 整个流程从头到尾是什么样的？

| 步骤 | Portal 操作 | 发生什么 |
| - | - | - |
| 1. 下载模板 | **导出批量退款模板** | 4 列 CSV，带一行示例数据，`Batch_Refund_Template.csv` |
| 2. 上传 | **上传批量退款文件** | 文件作为整体被校验；此时尚未创建任何内容 |
| 3. 预览 | 确认对话框 | 显示记录数、总金额、币种 |
| 4. 确认 | 对话框中的**确认** | 各行重新校验，然后创建批次和退款 |
| 5. 跟踪 | 批次列表筛选与**导出** | 筛选并导出批次列表 |

整个流程是在你的浏览器会话中执行的 Portal 功能；由 API key 签名的集成 API 上
没有批量端点——集成场景请改用单笔退款 API。确认后会生成一个
**退款批次 ID**，格式为 `BRP` + MID + `YYYYMMDD` + 6 位当日序号，例如
`BRP<你的商户 ID>20260225000001`；批次的**批次名称**就是你上传的文件名，所以请给文件
起一个以后能认出来的名字。

## 文件必须是什么样的？

| 模板列 | 必填 | 说明 |
| - | - | - |
| `Original Order Id` | 是 | 被退款支付的 Waffo 收单订单 ID |
| `Refund Amount` | 是 | 大于零，且在该币种的小数精度范围内 |
| `Refund Currency` | 是 | 3 位字母代码，每一行相同，且等于订单的币种 |
| `Refund Reason` | 否 | 自由文本，会带到退款记录上 |

硬性限制：仅限 `.csv`，最大 **2 MB**，最多 **2,000 条记录**，非空且可解析——Portal 在上传前
检查扩展名和大小，服务端会再检查一次。有两点经常让人中招：**删除
示例行**（带有模板示例订单 ID 的行会在其他任何检查之前单独被拒绝），以及
**每个文件只能一种币种**。

## 为什么我的整个文件都被拒绝了？

上传校验是全有或全无的：不会创建任何内容，Portal 会以*行、字段、
消息*的形式列出有问题的行。检查按固定顺序运行，这也是为什么你通常一次只看到一类错误：

1. 文件级检查——格式、大小、是否为空、重复内容、是否已有批次正在进行中。
2. 示例行检查；如果触发，它会是唯一返回的错误。
3. 逐行检查——订单在你的 MID 下存在、文件内订单不重复、金额 > 0、币种有效、
   一致、且等于订单的币种。
4. 每行的单笔退款前置条件——订单已支付且可退款、没有正在进行中的退款、在
   退款窗口期内、金额在剩余可退金额之内、没有争议锁定（[退款 FAQ](/docs/zh/support/refund-faq) 对每一项都有说明）。
5. 批次余额检查，**仅当每一行都通过时**才执行——文件的总额必须能被单笔退款所使用的同一个退款
   资金来源覆盖，否则你会收到一个没有行号的批次级错误（"Insufficient
   merchant refund account capacity for batch refund"）。

稳定币订单（USDT、USDC）不能放进批次：它们需要退款地址，而模板没有
这一列。请单独退这些订单。

## 为什么我无法上传——重复文件，还是有批次正在进行中？

重复检测基于文件**内容**而非文件名，范围限定在你的 MID 内。当
较早的上传仍在等待确认时重新上传相同内容，会返回同一个待确认的上传，这样你可以重新打开确认
对话框；一旦已经从该内容创建了批次，该内容就会被永久拒绝——要重试失败的
行请构建一个新文件，任何改动（哪怕是原因文本）都会使其成为新文件。已通过校验的上传如果搁置不管也会过期：
之后再确认会失败并提示 *File token is invalid or expired*，此时需要重新上传。

另外，只要**你任何一个批次中的任何一笔退款仍在进行中**，上传和确认都会被拒绝（"You
have a batch refund in progress. Please wait for it to complete."），所以请把大批量操作规划为文件队列；通过 API 发起的单笔
退款不受此限制。而且由于确认时会重新校验每一行，一个在确认对话框中搁置期间情况发生了变化的批次
——某订单在别处被退款、窗口期关闭、余额变动——会在
确认时以第一个有问题行的消息失败，而不是创建一个部分无效的批次。

## 批次状态分别是什么意思？

| 状态 | 含义 |
| - | - |
| `INIT` | 瞬态——批次记录创建时设置，随即重新计算。很少可见，状态筛选中也不提供 |
| `PROCESSING` | 至少有一行仍在待处理或进行中 |
| `PARTIAL_SUCCESS` | 所有行已完成；部分成功，部分失败 |
| `FULL_SUCCESS` | 每一行都成功 |
| `FAILED` | 每一行都失败 |

状态由行计数推导而来，而不是单独跟踪，所以随着各行落定它会自行变化。
每个批次显示**总数 / 成功数 / 失败数 / 处理中数**：失败数包括在提交时校验
失败的行，处理中数是尚未开始的行。一行出错不会阻止整个批次——各行
独立处理，这正是 `PARTIAL_SUCCESS` 的含义，而且批次没有重试操作：把失败的
行放进新文件，或者逐笔退款。

## 我在哪里能看到哪些行失败了以及为什么？

点击批次上的**详情**：它会打开**退款** Tab，并按该退款批次 ID 预先筛选，同时带上批次
页面的日期范围。此时每笔退款就是一条普通记录——其详情页显示退款批次 ID，当状态为
*退款失败*时显示失败原因。退款 Tab 也有退款批次 ID 列和筛选项，
所以你可以直接到达同一视图。

批次列表导出（`Batch_Refunds_<timestamp>.csv`）是**批次级汇总**——ID、名称、金额、币种、
状态、四个计数、操作人、创建和更新时间——**不**包含逐行失败原因；
其中的时间使用你浏览器报告的 UTC 偏移。列表本身可按退款批次 ID、状态、币种
和创建时间筛选，打开时默认显示最近三个月。极少数情况下某行失败得太早以至于没有创建退款
记录：它仍计入失败数，但在退款 Tab 里没有可打开的内容——对于这类情况请带上
退款批次 ID 联系支持。

## 这与单笔退款 API 是什么关系？

同一个引擎，不同的入口：生命周期、账户选择和结算处理完全相同。有两个实际
差异。**退款请求 ID**——批次行会得到一个你看不到的系统生成 ID，所以请在 Portal 中按退款
批次 ID 或退款订单 ID 跟踪它们，而不是按你自己的参考号。**通知**——批次行不携带
逐行的通知 URL，所以退款通知会发到你账户配置的默认退款通知 URL；
如果没有配置，批次行不会发送任何Webhook 通知，Portal 是你唯一的查看渠道。

***

文件一直失败？[联系支持](mailto:support@waffo.com)，附上文件，如果已创建批次，请附上退款批次 ID。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.