# 自主代理的支付 API 访问：按操作批准

给自主代理一个支付凭据，让它可以退款、扣款和改写订阅，这是一个危险的捷径。这些操作影响的事实不同，失败方式不同，也应该对应不同的人工决定。

自主代理的支付 API 访问应该从沙盒开始，但沙盒只能帮助你确认操作路径是否可行。它无法回答代理是否应该进行某项财务变更。应围绕操作、目标记录和后果建立批准边界，再把这套边界带入生产环境。

我见过一些团队把支付访问权限当成一个勾选框，因为 API 调用本身看起来很短。退款端点可能只需要标识符和金额，扣款请求甚至可能没有请求体，订阅更新也可能看起来只是修改一个字段。请求越短，人们越容易低估它背后的决定。

## 沙盒成功证明的是路由，不是判断力

沙盒可以证明代理能够识别对象、提交请求、处理错误并读取响应，同时不会移动真实资金。但它无法证明输入可信，无法证明代理的判断符合你的客服政策，也无法证明审核人能在请求执行前识别出错误。

有意把最初的沙盒范围控制得很窄。使用名称能直接说明场景的虚拟客户和订单。让每个测试数据只承担一个目的：部分扣款的授权、已全部扣款的支付、曾经发生部分退款的支付、会按比例计费的有效订阅，以及必须在计费周期结束时取消的订阅。随机测试数据会制造虚假的信心，因为没人说得清某个结果意味着什么。

在允许代理调用任何接口前，先写出操作契约。这不是给 API 网关用的政策文件，而是一份产品、财务和工程团队可以共同审阅的简明材料。

```yaml
environment: sandbox
operations:
  capture:
    approval: per_call
    reviewer_must_see:
      - authorization_amount
      - requested_amount
      - order_status
      - authorization_expiry
  refund:
    approval: per_call
    reviewer_must_see:
      - original_payment
      - total_refunded
      - requested_amount
      - customer_request_reference
  subscription_change:
    approval: per_call
    reviewer_must_see:
      - current_plan
      - proposed_plan
      - proration_effect
      - billing_anchor
      - cancellation_state
```

这份清单可以避免一个常见故障：有人在客服班次开始时批准了一个代理流程，后来才发现，带即时抵扣的降级和人工扣款都在那个含糊的批准下执行了。清单会在生产环境替你暴露缺失的信息。

为每个测试数据运行两次。第一次让代理提出操作，然后拒绝它。确认拒绝不会触发带有新请求标识符的下游重试。第二次批准操作，并将服务商响应与预期记录状态进行比较。还要测试请求离开网关后发生 API 错误的情况。相比顺利路径，重试路径造成的支付事故更多。

沙盒数据也可能带来麻烦。代理删除测试数据、修改共享测试订阅，或者产生数千条噪声事件，都会拖慢所有正在验证版本的人。虽然没有直接的财务损失，但控制问题是真实存在的。沙盒批准应该比生产环境更轻量，而不是完全没有。

## 退款权限需要来源记录和严格的金额校验

生产环境中的退款应逐笔批准，因为它会把钱退回客户，而且原始支付本身不足以证明退款合理。客服对话、履约失败、欺诈审查或合同条款才是退款依据。代理需要足够证据来提出建议，但不能把「把这件事处理好」这样的模糊话变成未经审核的扣款。

要求拟议操作绑定到原始支付对象，而不是客户姓名或可能对应多笔收费的订单号。审核人应看到原始金额和币种、既有退款、请求金额，以及客户请求或内部工单的引用。如果服务商支持部分退款，应根据服务商的最新状态计算剩余可退款金额，而不是依赖本地缓存的余额。

最危险的建议是自动批准小额退款。这个建议看似能缩短工单处理时间，而且很多小金额让人觉得无关紧要。但退款次数没有上限，代理可能选错支付对象，小额退款也可能违反政策。逐笔批准只需片刻，纠正错误退款却往往需要尴尬地联系客户，而且支付渠道可能无法撤回。

即使每笔调用都由人批准，也要设置金额预期。如果代理请求的金额超过原始扣款，或超过剩余可退款金额，操作层应在询问人工之前拒绝请求。不要把这项检查写成代理提示，而要在凭据和请求执行所在的位置强制执行。

一条好的批准记录应该像支付专员的工作备注，而不是模型对话记录：

```text
Refund request
Payment: pay_123
Original captured: 84.00 USD
Already refunded: 20.00 USD
Requested: 64.00 USD
Reason reference: case_481
Expected result: payment fully refunded
```

理由引用很重要。它让日后处理争议的人能够追溯操作原因，同时不必把客户通信或支付凭据放进代理提示。代理的摘要可以很短，但源记录应保存在代理对话之外。

不要把退款批准当成客户身份验证的替代品。支付 API 通常知道某个支付对象，却不知道聊天参与者是否是账户所有者。应在代理提出财务操作前，把身份核验放进客服流程。

## 即使已有授权，扣款仍然是收款

支付扣款应单独逐笔批准，因为授权和收款代表客户状态的不同阶段。客户可能授权了一个最高金额，但企业仍需要判断订单是否已经发货、最终金额是否变化，以及授权是否仍然有效。

扣款请求很容易给人一种虚假的安全感。代理看到一笔已授权支付，又看到运输标签，可能就认为应该扣款。但标签可能已经作废，订单可能被拆分，库存可能延期，或者人工已经安排了其他结算方式。代理无法根据一个状态字段推断是否应该收款。

让审核人看到四项事实：已授权金额、拟议金额、履约状态和授权到期时间。如果允许部分扣款，还要显示按照服务商规则是否可以进行后续扣款。这些规则因支付方式和服务商而异，因此应以服务商响应为事实来源，不要把假设写进提示。

把请求金额不一致单独作为一次批准时刻。扣除全部授权金额和按降低后的最终金额扣款，背后的解释不同。批准卡应清楚写出差异，例如：「已授权 100.00 USD，请求扣款 86.50 USD，原因是移除了一个商品。」如果界面隐藏原始授权，审核人就无法发现差异。

不要因为代理可以创建订单，就授予它扣款能力。创建订单是内部意图，扣款是外部金融操作。分开这两种能力也有助于应对事故：如果履约集成开始出现异常，可以关闭收款而不影响普通订单查询。

在沙盒中创建一笔应成功扣款的授权、一笔应保持未扣款的授权，以及一笔模拟授权过期的测试数据。代理对第二笔不应提出操作，对第三笔应显示异常。如果它只是重试这两种状态，你测试的只是请求格式，而不是判断力。

## 订阅编辑会带来延迟的财务后果

订阅变更应逐笔批准，因为它的影响可能出现在下一张账单中，而不是 API 立即返回的响应里。危险字段不只是套餐标识符。数量、计费锚点、试用日期、取消设置、折扣、税务设置和按比例计费方式，都可能改变客户支付或获得的内容。

不要批准只显示新套餐的订阅请求。审核人需要看到变更前后的对照：当前套餐和数量、拟议套餐和数量、当前续费日期、计划中的续费日期、取消状态，以及服务商提供的预计按比例计费或账单结果。没有这些对比，审核人看到的只是产品名称，却看不到账单影响。

订阅操作需要使用明确的术语。续费时降级、立即降级并抵扣、周期结束时取消，以及立即取消，不能混为一谈。让代理从客服流程中选择一种明确意图。如果客户请求含糊，应退回澄清，而不是让代理自行选择计费后果。

最糟糕的订阅故障往往看起来符合管理流程。API 返回成功，账户也显示了预期的套餐名称，但客户之后才发现账单异常或访问权限丢失。因此，批准内容必须包括预期的财务和访问结果，而不只是变更字段。

沙盒场景应包括周期中途减少数量、会产生按比例计费的升级，以及安排在周期结束时取消的订阅。确认服务商的沙盒响应和生成的账单对象。然后让审核人拒绝同一个变更，并确认代理不会尝试相近的操作，例如把数量设为零，而不是设置取消日期。

## 幂等性可以防止重试，不能赋予错误权限

网络故障或响应不确定时，幂等性可以防止请求重复执行，但它无法让未经授权或判断错误的操作变得安全。团队经常把这两种控制混为一谈，因为它们似乎都和重复退款有关。其实它们不是同一种控制，失效方向也不同。

Stripe 关于幂等请求的文档说明，它会为幂等键保存第一次请求的状态码和响应体，包括服务器错误响应；之后使用同一幂等键的请求会得到相同结果。文档还说明，后续请求必须使用匹配的参数。这种行为很有用，但它无法阻止代理为同一笔退款生成新的幂等键，也无法告诉你这笔退款是否本来就应该存在。

一次经过人工批准的意图，应使用一个稳定的操作标识符。执行前生成它，将它和批准记录在一起，并在完全相同的请求重试时继续使用。不要从当前时间推导标识符，也不要允许代理在超时后替换它。

沙盒测试应主动模拟不确定性：

1. 批准对一笔测试支付进行部分退款，并分配一个操作标识符。
2. 提交请求，然后让调用方表现得像是丢失了响应。
3. 使用相同标识符和相同金额重试。
4. 检查支付状态，确认服务商报告的退款只有一笔。
5. 使用改变后的金额再次发起相同请求，确认操作层拒绝它，而不是默默把它当成重试。

最后一项检查能发现一个危险的错误。开发者可能意外复用了标识符，而代理在读取新的客服备注后改变了请求金额。服务商返回的参数不匹配响应是在提醒你，两个不同意图被混淆了。应在审计记录中保留两次请求，并要求新的金额重新获得人工决定。

幂等性也无法处理并发判断。两个代理运行可能分别使用不同标识符提出同一笔退款。执行前应获取最新支付状态并检查已有退款。更好的做法是在网关中锁定同一支付对象上的操作，让第二个请求等待第一个结果。审核人不应该靠同时处理两张批准卡来阻止重复资金流动。

## 批准界面应该展示决定，而不是 API 数据块

只有当人可以根据屏幕上的事实作出决定时，批准界面才有用。原始端点名称、大段 JSON 数据和一个「允许」按钮，会把解读工作推给没有时间和上下文的审核人。

退款界面应先说明企业将要付出多少钱，并标识原始支付。扣款界面应先说明即将收取多少钱，并比较授权金额和请求扣款金额。订阅变更界面应先展示变更前后的计费状态。然后再把服务商对象标识符和原始请求放在摘要后面，供调查使用，而不是作为主要界面。

批准必须绑定到精确请求。如果审核人批准的是订阅降级，操作层之后不能添加即时发票支付、修改数量或切换目标订阅。应在执行记录中对已审核字段进行哈希或以其他方式绑定，重要字段发生变化时使批准失效。

逐会话批准仍然有用。它可以确认某个代理进程能在限定运行期间请求操作，并显示进程身份、启动者和工作范围。但它不代表该进程启动后可以自行发明并执行所有支付操作。会话批准回答的是「这个进程可以请求工作吗？」逐次批准回答的是「这项具体金融操作应该发生吗？」

不要为常规读取操作设置批准弹窗。代理需要查看支付状态、获取订阅信息和读取既有退款状态，才能提出有用的建议。如果每次读取都弹窗，人们会盲目批准，或者直接关闭批准机制。应把中断留给状态变更，并确保读取路径不会向代理暴露凭据。

## 凭据应放在操作边界之后

代理永远不应拿到支付密钥，即使只是暂时拿到，因为代理进程可能把它写进日志、Shell 历史、源文件、聊天上下文，或者发给其他服务。事后隐藏密钥并不能修复你没发现的副本。

把凭据放进一个操作边界。它接收受约束的请求，自己注入凭据，再返回服务商结果。代理可以请求读取支付信息或提出退款，但不能打印密钥，也不能把密钥用于你没有开放的端点。在这个边界上分开沙盒和生产凭据，避免代理通过自己写入的环境变量切换环境。

Sallyport 会把 API 和 SSH 凭据保存在 Mac 上的加密保险库中，并在不把凭据传给连接的代理的情况下执行受支持的 HTTP 调用。它的保险库门控、会话授权和可选的每次使用批准，与支付场景很匹配，尤其适合将逐次批准保留给金融写入操作。

操作边界需要校验的不只是身份验证。它还应拒绝通过沙盒路由发送的生产请求，阻止不支持的 HTTP 方法，验证标识符是否符合预期类型，并要求指定金融操作有已记录的批准。这些是执行约束，不是模型方便时可以遵守的建议。

不要把宽泛的支付服务商密钥当成开发便利。如果代理只需要读取支付、创建退款、执行扣款和进行有限的订阅更新，就只开放这些调用。账户管理、打款、争议处理或客户数据权限会无故扩大泄露影响范围。

## 审计轨迹必须同时说明操作和授权

支付服务商的事件历史可以告诉你发生了退款或订阅更新，但通常无法说明是哪个代理进程请求的、它考虑了什么证据、是否有人批准，以及重试前提交了什么请求。应保留一份执行记录来回答这些问题，不要把完整代理对话当成唯一证据。

记录环境、操作、目标对象、提交字段、操作标识符、服务商响应、代理会话身份、批准结果、审核人身份和时间顺序。涉及金额的操作应将金额和币种保存为独立字段。订阅变更应保存变更前状态和计划中的变更后状态。应保存客服工单或履约记录的引用，除非确有必要，不要复制敏感客户文本。

让审计日志在出现争议时真正有用。如果客户说退款错误，运营人员应能还原整条链路：代理读取支付状态，提出了绑定工单的部分退款，有人批准了精确金额，网关提交了一次请求，服务商返回了退款对象。如果链路存在缺口，就修复系统，不要要求员工几周后凭记忆还原经过。

防篡改证据很重要，因为支付事故经常会演变成访问事故。拥有管理员权限的人不应能悄悄删除不利的操作记录。Sallyport 会将代理会话和单次调用写入加密的哈希链审计日志，`sp audit verify` 命令可以在线下验证这条链，而不需要保险库密钥。

让审计复查保持实用。关注同一笔支付上反复被拒绝的请求、超时后金额发生变化的请求、来自新进程身份的大量尝试，以及导致账单行为异常的订阅变更。这些模式能在问题扩大为大规模财务清理前，提醒你集成或代理存在混乱。

## 生产访问应依据证据，而不是日历

只有当沙盒运行证明代理提出了正确操作、审核人能看到足够上下文、重试保持幂等，而且拒绝确实会停止执行后，才应进入生产支付操作。比起固定数量的成功测试调用，覆盖生产最终会遇到的失败场景更有价值。

如果流程允许，可以先从读取权限和操作建议开始线上运行。然后选择一种操作和一个很窄的业务场景，例如只处理已有结案客服工单且原始支付已验证的退款。在各自的沙盒测试数据、批准视图和撤销流程得到验证前，继续关闭扣款和订阅变更。

首次线上调用前，演练撤销流程。锁定凭据边界，终止代理会话，确认待处理批准无法执行，并验证审计记录仍然可用。要在所有人都冷静时完成这件事。支付事故发生时，绝不是发现撤销必须有人找到正确终端命令的好时机。

不要因为代理表现良好了一周，就逐步放松逐次审核。只有当你能明确说出一个范围受限的操作、可靠的授权来源、可衡量的错误路径，以及负责复查异常的人时，才考虑放宽。退款、扣款和订阅编辑很少会同时满足这些条件。它们的后果不同，因此批准要求也应分开。
