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

# API 凭证范围与认证错误排查

> 哪个 API key 允许调用哪个接口、为什么 Withdraw 必须是独立凭证、Waffo 检查您的 API key、范围、签名和 IP 的确切顺序、如何解读 A0001 / A0002 / A0004 / A0046，以及凭证变更多久生效。

已有三个页面涵盖认证机制：

| 问题 | 页面 |
| - | - |
| 我怎么给请求签名、验证响应和Webhook 通知、生成密钥对？ | [认证与签名 FAQ](/docs/zh/support/auth-signature-faq) |
| 我怎么提交或轮换生产公钥？API 操作类型是什么？ | [生产公钥与 API 操作类型](/docs/zh/support/production-api-key-and-public-key-faq) |
| 我怎么设置通知 URL、开启 IP 白名单、使用两步验证？ | [Portal 自助安全配置](/docs/zh/support/portal-security-config-faq) |

**本页是范围矩阵和排查树**——哪个凭证可以调用
哪个接口，以及不能调用时如何解读错误。

## 一共涉及几个密钥？分别由谁持有？

四样东西，其中只有两样是您需要保密的：

| 项目 | 谁生成 | 谁持有 | 用途 |
| - | - | - | - |
| API key | Waffo，随每个被接受的公钥一起生成 | 您在每个请求上发送 | 标识请求使用的是哪个凭证——因而是哪个商户、哪个范围 |
| 您的私钥 | **您** | 您，仅服务端 | 为您的请求和Webhook 通知响应签名 |
| 您的公钥 | 您（提交给 Waffo） | Waffo | 验证您的签名 |
| Waffo 公钥 | Waffo | 您从 Portal 复制 | 验证 Waffo 的响应和Webhook 通知签名 |

运营上真正重要的关系：\*\*一个 API key 恰好对应一个
公钥。\*\*您发送的 `X-API-KEY` 决定您的签名用哪个公钥
校验——所以 API key 和私钥是配对的，
只换其中一个会失败。

## 为什么我的 Integration 页面显示多个 API key 和公钥？我该用哪个？

因为凭证按操作类别划分范围，您使用的每个类别各持有一个。
您要找的行是其 **API Operation Type** 与
您正在发起的调用匹配的那一行。

每个类别确切覆盖哪些接口：

| 操作类型 | 可调用的接口 |
| - | - |
| **Payin** | `/api/v1/order/create`、`/api/v1/order/inquiry`、`/api/v1/order/cancel`、`/api/v1/order/refund`、`/api/v1/order/capture`、`/api/v1/refund/inquiry`、`/api/v1/subscription/create`、`/api/v1/subscription/inquiry`、`/api/v1/subscription/cancel`、`/api/v1/subscription/manage`、`/api/v1/subscription/change`、`/api/v1/subscription/change/inquiry`、`/api/v1/subscription/update`、`/api/v1/tokenization/generate`、`/api/v1/tokenization/inquiry`、`/api/v1/tokenization/remove`、`/api/v1/merchantconfig/inquiry`、`/api/v1/paymethodconfig/inquiry`、`/api/v1/saved-payment-methods/config`、`/api/v1/userid/inquiry`、`/api/v1/userinfo/inquiry`、`/api/v1/wallet/inquiry`、`/api/v1/chargeback/inquiry`、`/api/v1/chargeback/list`、`/api/v1/chargeback/update`、`/api/v1/chargeback/accept`、`/api/v1/fraud-report/list`、`/api/v1/risk/transactionlimit/create`、`/api/v1/risk/transactionlimit/update`、`/api/v1/risk/transactionlimit/inquiry` |
| **Payout** | `/api/v1/payout/create`、`/api/v1/payout/inquiry`、`/api/v1/account/balance/inquiry` |
| **Payin and payout** | 上面两行的全部 |
| **Withdraw** | `/api/v1/withdraw/consult`、`/api/v1/withdraw/create`、`/api/v1/withdraw/inquiry`、`/api/v1/account/balance/inquiry`、`/api/v1/account/history/inquiry` |
| **Card collection** | `/cardcenter/web/v1/tokenization/submit`——浏览器侧的卡提交接口 |

这张表明确了两个细节：

* \*\*余额查询同时在 Payout 和 Withdraw 范围内。\*\*它是
  用两种凭证都能访问的唯一接口，所以余额检查
  永远不会迫使您增加范围。
* \*\*账户历史仅限 Withdraw。\*\*Payout 凭证无法读取。

## 我用 Payin key 调了 Payout 接口，报权限错误，为什么？

因为范围是按请求路径强制执行的，不是按商户。凭证
有效——只是不允许调用该接口，请求被
以 **`A0004` Permission denied**（HTTP 401）拒绝。

调试时要记住的区别：`A0004` 的意思是"这个 key，错误的
接口"。它不表示您的 key 错了、签名错了，或
账户缺少该产品。请在上表中查找该接口，然后使用
其操作类型包含该接口的凭证行。

## 为什么 Withdraw 不能和 Payin 或 Payout 组合？我两个都需要。

因为提现是把**您自己已结算的资金转出**，而 Payin 和 Payout
是把客户的钱转入或付给第三方。让提现使用独立
凭证，意味着您的收银台服务或 Payout 服务持有的 key
永远无法用来转出您的余额，一个泄露也不意味着
另一个泄露。

Portal 对此是强制而非提醒：选择 Withdraw 会清除
Payin 和 Payout，选择其中任一个会清除 Withdraw。

所以如果您通过 API 使用提现，请规划**至少两个凭证**——
一个用于支付流量，另一个单独用于提现——并把它们
存放在密钥管理器的不同位置，使用不同的访问列表。这
正是分离的意义所在；把两者放在同一个环境文件里就前功尽弃。
提交第二个凭证与第一个是同样的自助流程；
见
[生产公钥与 API 操作类型](/docs/zh/support/production-api-key-and-public-key-faq)。

## 你们按什么顺序检查我的 API key、范围、签名和 IP？

按以下顺序，了解它能告诉您当多处出错时
您会先看到哪个错误：

1. \*\*`X-API-KEY` 是否存在？\*\*否则 → `A0001`。
2. \*\*它是否是已知的、`Active` 的凭证？\*\*否则 → `A0001`。
3. \*\*该凭证的操作类型是否允许此接口？\*\*否则 →
   `A0004`。
4. \*\*`X-SIGNATURE` 是否能用该凭证的公钥验证通过？\*\*否则 →
   `A0002`。
5. \*\*如果您启用了白名单，来源 IP 是否在其中？\*\*否则 →
   `A0046`。

实际影响：\*\*签名错误且来自非白名单 IP 的请求
报的是 `A0002`，不是 `A0046`。\*\*所以不要因为从未见过 `A0046`
就断定白名单没问题——您可能只是从来没有
通过签名检查。请按上述顺序修复错误。

## "invalid API key"和"invalid signature"有什么区别？

它们指向同一对中的不同两半，各有自己的检查清单：

| 错误码 | 信息 | HTTP | 含义 | 检查 |
| - | - | - | - | - |
| `A0001` | Invalid Api secret key | 401 | `X-API-KEY` 缺失、未知，或当前不是 `Active` | 请求头存在？是从**本**环境的 Integration 页面复制的？该 key 的版本是 `Active`，不是 `Pending` 或 `Rejected`？ |
| `A0002` | Invalid signature | 401 | 签名无法用绑定到该 API key 的公钥验证 | 签的是您实际发送的字节？用的是与**该** API key 的公钥配对的私钥？算法 SHA256withRSA、Base64 编码？ |
| `A0004` | Permission denied | 401 | 凭证有效，接口不在其操作类型范围内 | 上文的范围表 |
| `A0046` | Access denied: the request IP is not in the whitelist | 401 | 签名没问题；来源 IP 不在白名单 | 您当前的出口 IP，以及白名单条目 |

密钥轮换后，`A0002` 几乎总是意味着 `X-API-KEY` 和签名
私钥不再彼此对应——请把两者作为一次变更一起切换。

## "merchant status abnormal"或"contract does not allow this operation"是认证问题吗？

不是。那些是账户和协议状态，出现在您的
凭证已被接受**之后**：

| 错误码 | 含义 | 去哪里看 |
| - | - | - |
| `A0006` | 商户状态异常 | 您的账户状态——联系集成支持 |
| `A0010` | 商户合同不允许该操作 | 您的协议不包含该操作或产品 |
| `A0018` | 订单中的支付方式与您的合同不匹配 | 您在该国家和币种下签约的支付方式 |

排查捷径：任何 `A0001` / `A0002` / `A0004` / `A0046` 都是您可以自行修复的*凭证*
问题。`A0006`、`A0010` 和 `A0018` 是*权限*
问题，需要 Waffo 侧的配置变更或不同的
请求。完整列表见
[错误码](/docs/zh/developer-docs/tools-and-references/developer-tools/error-codes)。

## 我必须发送 API 版本请求头吗？

是的，对原始 HTTP 调用：发送 `X-API-VERSION: 1.0.0`。SDK 会自行管理
该请求头——不要在那里设置或覆盖它。

一个值得点名的陷阱，因为从变更日志很容易搞错：您在 API 版本历史中
看到的版本号标识的是**文档和
契约修订**，不能放进这个请求头。请求头的值是
`1.0.0`。见 [API 概览](/docs/zh/developer-docs/integration/api/overview)。

## 激活或轮换密钥后多久能用？

给它**一小段时间**——不是即时的。只有 `Active` 的凭证能验证
签名，请求路径上使用的查询按间隔刷新——
所以刚变为 `Active` 的密钥在一个短窗口内仍可能被拒绝，
而刚停止有效的密钥在同一窗口内仍可能可用。

对轮换的启示：新版本变为 `Active` 后，等过
该窗口再切流量，并保留旧凭证
直到新凭证验证过真实流量。因为多个版本可以同时
`Active`，您永远不需要一个两者都不可用的空档。

IP 白名单的行为不同且更快——见下文。

## 我能在多个商户账户间共用一个 API key 吗？

不能。每个 API key 属于单一商户账户，用账户 A 的凭证
认证的请求只能操作账户 A 的数据。
查询属于账户 B 的订单会失败，即便 key 本身
完全有效。

如果您运营多个商户 ID，请为每个账户保留一套凭证，并
始终用创建该对象的账户的凭证调用。从一开始
就把这种路由建好，比事后改造便宜得多——见
[多品牌与子商户设置](/docs/zh/support/multi-brand-and-sub-merchant-faq)。

## IP 白名单接受什么？编辑多快生效？

| 项目 | 规则 |
| - | - |
| 条目格式 | **IPv4 地址和 IPv4 CIDR 段**——例如 `203.0.113.7` 或 `198.51.100.0/24` |
| IPv6 | 不支持 |
| 主机名 | 不支持——仅地址和网段 |
| 每商户条目数 | 最多 **20** 条；重复会被拒绝 |
| 单条控制 | 一个标签，以及单独的开/关切换 |
| 总开关 | 没有已启用条目时无法打开；删除或停用最后一条已启用条目会关闭它 |
| 生效时间 | **几乎立即**——保存同时会使缓存的查询失效 |

如果 20 条不够，请合并为 CIDR 段而不是逐个列出
主机——一个 `/24` 可替代多达 254 条，稳定的网段
也比每次扩容都变化的列表更省维护。

启用方法和确切的 Portal 字段见
[Portal 自助安全配置](/docs/zh/support/portal-security-config-faq)。

## 我的服务器在代理后面，或者是动态 IP，实际匹配的是什么？

匹配的地址是请求到达 Waffo 时 **forwarded-for 链中的第一个条目**——
即发起客户端地址，而不是最后一跳。如果该值无法解析为 IPv4 地址，
检查记为"无法判定"而不是不匹配。

这一区别决定结果，并且有三种执行策略：

| 策略 | 来源 IP 不在名单 | 来源 IP 无法判定 |
| - | - | - |
| 仅观察 | 放行并记录 | 放行并记录 |
| 执行（默认） | 以 `A0046` **拦截** | 放行并记录 |
| 严格 | 以 `A0046` **拦截** | 以 `A0046` **拦截** |

Portal 只暴露总开关；策略本身由
Waffo 设置。所以如果您的出口是动态的、多区域的，或在
多层之后容器化，请先让集成支持把您设为**仅观察**，
观察记录到的不匹配，等列表完整后再切换到执行。
盲开执行，是配置正确的集成
出现故障的常见方式。

如果您的出口确实无法固定到一组网段，白名单
对您来说就是错误的控制手段——它是可选的且默认关闭，而签名
验证已经在每个请求上强制执行。

## 我怎么知道自己的出口 IP 是什么？

不是从 Waffo——Waffo 看到的地址是您的基础设施
呈现的地址，可靠的来源是您自己的平台：云环境的 NAT 网关或
负载均衡配置，那也是唯一能保证它保持稳定的
地方。

无法从配置获取时的两条实用路径：

* **向您的平台团队询问**支付服务所在环境的**出口网段**，
  并将网段而不是您观察到的地址加入白名单。
  观察到的地址在下次扩容时就会过时。
* **先以仅观察模式运行**（如上所述），让不匹配记录本身
  告诉您在拦截任何请求之前哪些地址在使用。

## 我的 Portal 密码传输时加密了吗？Waffo 员工能看到吗？

您的密码在**提交前就在浏览器中加密**，使用
Waffo 公钥，在 TLS 之上——所以表单永远不会把明文密码放到
线路上，即便在加密通道内也不会。设置或
修改密码时同样如此。

存储侧只保留**单向哈希**，所以没有
可恢复的副本可读：Waffo 没有任何人能取回您的密码，
密码重置会给您一个链接，而不是告诉您旧值。

密码规则、过期、锁定和登录历史见
[Portal 登录、链接与账户状态](/docs/zh/support/portal-account-access-faq)
和[商户 Portal 操作](/docs/zh/support/portal-operations-faq)。

## 我怀疑私钥泄露了，该按什么顺序做？

先轮换，再调查——并记住泄露的私钥让
他人可以冒充您签署请求，其严重程度正如字面所示。

1. **生成新密钥对**，并从 Portal 提交新公钥。您
   现有的凭证在审核期间继续有效，所以这一步不会
   造成停机。
2. **新版本变为 `Active` 后，从 Integration 页面读取其 API key**，
   把 `X-API-KEY` 和签名私钥一起切换。
3. **停止使用旧凭证**，并从您自己的日志确认没有
   流量仍携带旧 API key。
4. \*\*如果尚未开启，打开 IP 白名单。\*\*它是唯一一个
   能让被盗凭证无法从您的基础设施之外使用的控制手段，
   这也是要在需要之前就配置好它的理由。
5. **审查您自己的处理方式**：私钥不得出现在源代码管理、
   镜像、日志或浏览器中。
   [上线检查清单](/docs/zh/developer-docs/getting-started/go-live)要求它
   存放在环境配置或密钥管理服务中。
6. **告知集成支持**发生了什么、何时发生，以便 Waffo 侧
   可以审查旧凭证上的活动。

注意第 2 步的失败模式，因为它最常见：用新密钥对
复用旧 API key，会让您仍然对着旧公钥验证，于是您的
新签名失败，而 Portal 看起来配置正确。

## 我能抓请求和响应报文来调试签名问题吗？

可以，也应该——但请抓**原始字节**，而不是解析后
重新序列化的副本，因为经过您 JSON 库的一次往返本身就是
签名无法验证的头号原因之一。

记什么、不记什么：

| 记录 | 不要记录 |
| - | - |
| 您签名的确切请求体字节 | 私钥，或其任何部分 |
| 您生成的 `X-SIGNATURE` | 完整卡号或其他敏感支付数据 |
| 遮蔽形式的 `X-API-KEY`，足以分辨用了哪个凭证 | 完整的 API key |
| 返回的响应码和错误码 | |

上线检查清单把第二列作为要求而不是建议：
日志不得记录完整密钥或敏感支付信息。短期的、
受访问控制的调试抓取才是正确形态——不是永久的
日志行。

***


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