# 高级错误处理 了解如何理解 HTTP 级别的错误。 本页涵盖两个高级错误处理主题: - [表示错误的 HTTP 响应](https://docs.stripe.com/error-low-level.md#errors-in-http) - [幂等性和重试](https://docs.stripe.com/error-low-level.md#idempotency) 此信息可能不适用于您。Stripe 的官方 [SDK](https://docs.stripe.com/sdks.md) 可以处理涉及 HTTP 和重试的大多数细节。如果您使用客户端库,请从这里开始: - [错误处理](https://docs.stripe.com/error-handling.md) - [错误代码](https://docs.stripe.com/error-codes.md) ## HTTP 中的错误 即使 API 调用失败,我们的客户端库也会通过[引发异常或返回错误值](https://docs.stripe.com/error-handling.md)来提供错误信息。但是,如果您不使用客户端库,或者出现异常情况,则可能需要有关 HTTP 响应以及何时发出它们的低级详细信息。 从 HTTP 的角度来看,错误分为三大类: - [内容错误](https://docs.stripe.com/error-low-level.md#content-errors):API 请求中的内容无效。 - [网络错误](https://docs.stripe.com/error-low-level.md#network-errors):客户端和服务器之间的间歇性通信问题。 - [服务器错误](https://docs.stripe.com/error-low-level.md#server-errors):Stripe 服务器出现问题。 每种类型的错误都需要不同的方法和幂等语义。[响应代码的完整列表](https://docs.stripe.com/error-low-level.md#status-codes)及其含义在本页末尾提供。 ### 内容错误 API 请求的内容无效会导致内容错误。它们返回带有 `4xx` [响应代码](https://docs.stripe.com/api/errors.md#errors-code)的 HTTP 响应。例如,如果您提供的 API 密钥无效,则 API 服务器可能会返回 `401` 错误;如果缺少必需的参数,则返回 `400` 错误。集成应更正原始请求并重新尝试。根据用户错误的类型(例如,卡片被拒绝),可能可以通过编程方式处理问题。在这些情况下,包含 `code` 字段,以帮助集成做出适当的反应。更多详情,请参见[错误代码](https://docs.stripe.com/error-codes.md)。 对于使用幂等性密钥的 `POST` 操作,只要 API 方法开始执行,Stripe 的 API 服务器就会缓存请求的结果,不管它们是什么。如果一个返回 `400` 的请求后面跟着一个具有相同幂等性密钥的新请求,则该请求会返回相同的 `400`。通过修正原始请求来获得成功结果时,生成一个新的幂等性密钥。该操作会包含一些警告。例如,一个被 `429` 限制了速率的请求可能会产生一个具有相同幂等性密钥的不同结果,因为速率限制器在 API 的幂等层之前运行。这同样适用于省略 API 密钥的 `401`,或者大多数发送无效参数的 `400`。即便如此,最安全的策略是始终生成一个新的幂等性密钥 `4xx`。 ### 网络错误 网络错误是因为客户端和服务器之间出现了连接问题,并返回低级错误,如套接字或超时异常。例如,客户端在尝试从 Stripe 的服务器读取信息时可能会超时,或者由于连接过早终止,可能永远不会收到 API 响应。虽然在修复连接问题后,网络错误_看起来_很可能会成功,但有时间歇性问题中可能隐藏着其他错误。 这类错误是最能体现幂等性密钥和请求重试价值的地方。当发生间歇性问题时,客户端通常不知道服务器是否收到了请求。为了得到明确的答案,它们应该用相同的幂等性密钥和相同的参数重试这类请求,直到它们能够从服务器接收到结果。用不同的参数发送相同的幂等性密钥会产生一个错误,指示新请求与原始请求不匹配。 大多数客户端库可以自动生成幂等性密钥和重试请求,但需要进行配置才可实现。它们在第一次失败后迅速进行第一次重试,后续重试则按指数退避时间表进行,前提是单次失败通常是随机事件,而重复失败的模式可能代表长期问题。 #### Ruby ```ruby Stripe.max_network_retries = 2 ``` ### 服务器错误 服务器错误由 Stripe 的服务器问题导致。它们返回一个带有 `5xx` 错误代码的 HTTP 响应。这些错误最难处理,我们努力使其降至最低,但良好的集成在它们出现时会处理它们。 与用户错误一样,幂等层会缓存导致服务器发生错误的 `POST` 突变结果(尤其是 `500`,它是内部服务器错误),因此用相同的幂等性密钥重试它们通常会产生相同的结果。客户端可以用新的幂等性密钥重试请求,但不建议这样做,因为原始密钥可能会产生副作用。 `500` 请求的结果应被视为不确定。最有可能观察到这种情况的时间是在生产事件期间,通常是在此类事件的补救期间。Stripe 工程师会检查失败的请求,并尝试适当地对导致 `500` 的任何突变结果进行对账。虽然对这些请求的幂等缓存响应不会改变,但作为 Stripe 对账工作的一部分,我们将尝试为创建的任何新对象激发 [Webhook](https://docs.stripe.com/webhooks.md)。系统中任何追溯性更改的确切性质在很大程度上取决于请求的类型。例如,如果创建一笔收款时返回了 `500` 错误,但我们检测到该信息已经发送到某个支付网络,则我们会尝试将它向前滚动。如果没有,我们将尝试将其回滚。如果这不能解决问题,您可能仍会看到带有 `500` 错误的请求,这些请求会产生用户可见的副作用。 > 将返回 `500` 错误的请求视为不确定。尽管 Stripe 会尝试以最恰当方式核对其部分状态,并为创建的新对象触发 [Webhook](https://docs.stripe.com/webhooks.md),但无法保证达到理想结果。 要让您的集成能够处理最宽范围的 `500` 错误,请配置 Webhook 处理程序来接收您在正常 API 响应中从未收到的事件对象。将这些新对象与集成本地状态数据进行交叉引用的一种技术是,在使用 API 创建新资源时,通过[元数据](https://docs.stripe.com/api/metadata.md)发送本地标识符。即使 Webhook 是作为对账的一部分稍后生成的,该标识符也会出现在通过 Webhook 发出的对象的元数据字段中。 ## 幂等性 [幂等性](https://stripe.com/blog/idempotency)是一种 Web API 设计原则,定义为能够多次应用同一操作而不改变首次尝试后的结果。这使得在某些情况下重试 API 请求是安全的 — 尤其是当第一次请求由于网络错误而未收到响应时。由于预计会出现一定数量的间歇性故障,客户端需要一种与服务器协调失败请求的方法,而幂等性为此提供了一种机制。 大多数客户端库可以自动生成幂等密钥并重试请求,但您需要进行配置。要对重试进行更精细的控制,请生成[幂等性密钥](https://docs.stripe.com/api/idempotent_requests.md)并编写您自己的重试逻辑。 ### GET 和 DELETE 请求 Stripe API 保证 `GET` 和 `DELETE` 请求的幂等性,因此重试它们始终是安全的。 ### POST 请求 包含[幂等性密钥](https://docs.stripe.com/api/idempotent_requests.md)会使 `POST` 请求具有幂等性,这会促使 API 执行防止重复操作所需的记录保存。只要第二次请求在首次收到密钥后的 24 小时内发出(密钥在系统中 24 小时后过期),客户端就可以安全地重试包含幂等密钥的请求。例如,如果创建对象的请求因网络连接错误而未响应,客户端可以使用相同的幂等密钥重试请求,以确保仅创建一个对象。 ### 发送幂等性密钥 幂等性密钥通过 `Idempotency-Key` 标头发送。对所有发送至 Stripe API 的 `POST` 请求使用它们。大多数官方客户端库可以自动发送这些密钥,只要它们被配置为支持重试。 如果您决定[手动发送幂等性密钥](https://docs.stripe.com/api/idempotent_requests.md),请确保所使用的令牌具有足够的唯一性,至少在过去 24 小时内能够明确标识您账户中的单个操作。生成幂等性密钥有两种常见策略: - 用一个能够生成具有足够随机性令牌的算法,如 UUID v4。 - 从用户附加的对象派生密钥,例如购物车的 ID。这提供了一种相对简单的方法来防止重复提交。 若要识别从服务器重播的先前执行的响应,请查找标头 `Idempotent-Replayed: true`。 ### Stripe-Should-Retry 标头 客户端库不能总是仅根据状态代码或响应正文中的内容确定是否应该重试。当 API 获得额外的信息,知道可重试请求时,就会以 `Stripe-Should-Retry` 标头进行响应。 - `Stripe-Should-Retry` 设置为 `true` 表示客户端应该重试请求。在发出下一个请求之前,客户端仍然应该等待一定的时间(可能根据指数退避时间表确定),避免使 API 过载。 - `Stripe-Should-Retry` 设置为 `false` 意味着客户端_不_应该重试请求,因为它不会有额外的效果。 - 响应中未设置 `Stripe-Should-Retry` 表示 API 无法确定是否可以重试请求。客户端应该回退到响应的其他属性(如状态代码)来做出决定。 Stripe 客户端库中内置的重试机制会自动遵从 `Stripe-Should-Retry`。如果您使用了其中之一,则无需手动处理。 ## HTTP 状态码参考 | | | | | 200 | 正常 | 一切都按预期进行。 | | 400 | 非法请求 | 该请求不可接受,通常是因为缺少所需的参数。 | | 401 | 未经授权 | 未提供有效的 API 密钥。 | | 402 | 请求失败 | 参数有效但请求失败。 | | 403 | 禁止 | API 密钥没有执行请求的权限。 | | 409 | 冲突 | 该请求与另一个请求冲突(可能是由于使用相同的[幂等](https://docs.stripe.com/error-low-level.md#idempotency)密钥)。 | | 424 | 外部依赖失败 | 由于 Stripe 外部的依赖项失败,无法完成该请求。 | | 429 | 请求过多 | 太多请求过快地命中 API。[我们建议对请求进行指数退避](https://docs.stripe.com/error-low-level.md#should-retry)。 | | 500, 502, 503, 504 | 服务器错误 | [Stripe 端出了问题。](https://docs.stripe.com/error-low-level.md#server-errors) |