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

# 手动支付重试

> 无需等待下一次自动重试，直接从控制面板或 API 按需重试失败的订阅续费支付。

<Info>
  手动重试会在您发起请求后立即重新尝试失败的订阅**续费**支付，您可以从支付详情页或通过 API 发起。它会向订阅中保存的支付方式收费，并独立于自动 [支付重试](/features/recovery/payment-retries) 计划运行。
</Info>

## 什么是手动重试？

当续费支付失败时，订阅会进入 `on_hold` 状态，[支付重试](/features/recovery/payment-retries) 会按照退避计划重新尝试扣款。有时您知道现在扣款会成功：客户确认已经为账户充值，或者您的支持团队正在与客户通话。手动重试允许您立即发起一次尝试，而不是等待数小时或数天后的下一次计划重试。

* **仅限续费支付**：手动重试适用于订阅处于 `on_hold` 状态时的订阅续费发票。首次支付、一次性支付、方案变更费用和按需费用均不符合条件。
* **无需客户操作**：扣款会使用订阅中已保存的支付方式。
* **独立于自动重试**：手动重试不会消耗自动计划中的一次尝试，不会改变下一次计划重试时间，即使已关闭支付重试也可以使用。
* **重试发票，而不是支付**：失败支付只是入口。Dodo Payments 会查找其对应的未结续费发票并收取该笔欠款，因此从发票上的哪笔失败支付发起重试并不重要。

## 从控制面板重试

<Steps>
  <Step title="Open the failed payment">
    前往 **交易 → 支付**，点击失败的续费支付以打开其**交易详情**页面。
  </Step>

  <Step title="Click Retry Payment Manually">
    点击右上角的**手动重试支付**。仅当支付[符合条件](#eligibility)时，该按钮才可用。
  </Step>

  <Step title="Check the result">
    系统会为此次尝试创建一笔新支付，并将其显示在**活动日志**中。如果扣款成功，订阅会恢复为 `active`，下一次账单日期也会正常顺延。如果支付处理方尚未结算该笔扣款，支付会显示为进行中，直到 `payment.succeeded` 或 `payment.failed` webhook 报告结果。
  </Step>
</Steps>

<Frame caption="Retry Payment Manually on the transaction details page of a failed renewal">
  <img src="https://mintcdn.com/dodopayments/0duTS18kYi2NwQ3m/images/recovery/manual-retry-transaction-details.png?fit=max&auto=format&n=0duTS18kYi2NwQ3m&q=85&s=5537fa5eff17cbe91a53599f887a26e7" alt="显示失败支付错误代码和消息、活动日志以及手动重试支付按钮的交易详情页面" style={{ maxHeight: '500px', width: 'auto' }} width="1285" height="698" data-path="images/recovery/manual-retry-transaction-details.png" />
</Frame>

## 符合条件

只有通过以下所有检查时，系统才会发送手动重试。**原因代码**列显示 API 返回的内容：在 `GET /payments/{payment_id}/retry` 上为 `reason`，在 `POST /payments/{payment_id}/retry` 上为错误 `code`。

| 检查项      | 要求                                                                                                                        | 原因代码                                            |
| -------- | ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| 支付类型     | 订阅**续费**支付，且其发票仍处于未结状态。没有发票的支付、首次支付、一次性支付、方案变更费用和按需费用均无法重试。                                                               | `PAYMENT_NOT_RETRYABLE`                         |
| 订阅状态     | `on_hold`                                                                                                                 | `SUBSCRIPTION_INACTIVE`                         |
| 计划取消     | 订阅未计划在下一次账单日期取消。                                                                                                          | `CHARGE_NOT_ALLOWED_FOR_SCHEDULED_CANCELLATION` |
| 已保存的支付方式 | 订阅中有可用于扣款的已保存支付方式。                                                                                                        | `SUBSCRIPTION_HAS_NO_PAYMENT_METHOD`            |
| 最近一次失败   | 最近一次失败是**软拒付**。硬拒付或没有归类错误代码的失败无法重试。                                                                                       | `MANUAL_RETRY_HARD_DECLINE`                     |
| 没有进行中的支付 | 发票上没有支付处于 `processing` 状态，且没有支付缺少已记录的状态。这表示某次手动或自动尝试刚刚发出，尚未返回结果。请先等待其结果。                                                  | `MANUAL_RETRY_IN_FLIGHT`                        |
| 最新支付失败   | 发票上最近的支付状态为 `failed`。最新支付若处于其他状态，例如 `requires_customer_action`、`requires_payment_method` 或 `cancelled`，即使没有进行中的支付，也会阻止重试。 | `PREVIOUS_PAYMENT_PENDING`                      |
| 尚未支付成功   | 发票上没有支付成功。                                                                                                                | `MANUAL_RETRY_ALREADY_PAID`                     |
| 重试次数限制   | 该发票发送的手动重试少于 3 次，且冷却时间已结束。请参阅[重试限制](#retry-limits)。                                                                       | `MANUAL_RETRY_LIMIT_REACHED`                    |
| 客户       | 客户不在您的[阻止列表](/features/customer-blocklist)中。                                                                              | `PAYMENT_NOT_RETRYABLE`                         |
| 支付连接器    | 对于 [BYOP](/features/byop) 订阅，连接器已启用。                                                                                      | `BYOP_CONNECTOR_DISABLED`                       |
| 实时模式     | 在实时模式下，您的业务已启用实时支付。                                                                                                       | `MERCHANT_NOT_LIVE`                             |

<Note>
  手动重试在一个方面比自动重试更严格：它要求订阅处于 `on_hold` 状态。对于其他非 active 状态，自动重试仍会继续运行；请参阅[订阅状态转换](/features/recovery/payment-retries#subscription-status-transitions)。
</Note>

<Warning>
  对同一张卡重试硬拒付不会成功，反复拒付还会损害您的授权率。当原因是 `MANUAL_RETRY_HARD_DECLINE` 时，请让客户改用其他支付方式。[订阅催收](/features/recovery/subscription-dunning)会自动执行此操作。
</Warning>

## 重试限制

每张续费发票允许进行**3**次手动重试，重试之间有冷却时间：

| 手动重试 | 可用时间        |
| ---- | ----------- |
| 1    | 支付符合条件后立即可用 |
| 2    | 第一次重试后 1 小时 |
| 3    | 第二次重试后 3 小时 |

这些限制同时适用于测试模式和实时模式。因该原因拒绝重试时，API 返回 `MANUAL_RETRY_LIMIT_REACHED`（HTTP `429`）。错误正文仅包含 `code` 和 `message`。要了解下一次重试何时可用，请[检查重试状态](#check-whether-a-payment-can-be-retried)，并读取 `retry_available_at`。三次重试全部用完后，该字段为 `null`。

自动重试不会计入此限制，手动重试也不会计入自动计划的 8 次尝试。

## 手动重试与自动重试

|                | 手动重试                        | 支付重试                  |
| -------------- | --------------------------- | --------------------- |
| **触发方式**       | 您从控制面板或 API 触发              | Dodo Payments 按退避计划触发 |
| **时间**         | 立即                          | 失败后 12 小时，然后逐步延后      |
| **尝试次数**       | 每张发票 3 次，冷却时间依次为 1 小时和 3 小时 | 每张发票最多 8 次，在恢复窗口内进行   |
| **是否需要启用支付重试** | 否                           | 是                     |
| **对另一方的影响**    | 无。手动失败不会安排或移动自动尝试。          | 无。无论是否发起手动重试，自动链都会继续。 |
| **分析**         | 计入恢复选项卡中的**支付重试**指标         | 计入相同指标                |

## 通过 API 重试

先检查是否符合条件，然后发送重试请求。两个 endpoint 都需要传入失败支付的 ID。

### 检查支付是否可以重试

`GET /payments/{payment_id}/retry` 对不符合条件的支付不会失败。相反，它会返回 `can_retry: false`，并提供 `reason` 代码，以便您的控制面板或支持工具显示与 Dodo Payments 控制面板相同的状态。此操作需要 **Viewer** 角色。

<CodeGroup>
  ```typescript Node.js theme={null}
  import DodoPayments from 'dodopayments';

  const client = new DodoPayments({
    bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  });

  const state = await client.payments.retrieveRetryState('pay_0NmDtkE0iRvmeTcT6t0ol');

  if (state.can_retry) {
    console.log(`Retry available. ${state.sends_used}/${state.sends_allowed} used.`);
  } else {
    console.log(`Cannot retry: ${state.reason}. Next window: ${state.retry_available_at}`);
  }
  ```

  ```python Python theme={null}
  import os
  from dodopayments import DodoPayments

  client = DodoPayments(bearer_token=os.environ["DODO_PAYMENTS_API_KEY"])

  state = client.payments.retrieve_retry_state("pay_0NmDtkE0iRvmeTcT6t0ol")

  if state.can_retry:
      print(f"Retry available. {state.sends_used}/{state.sends_allowed} used.")
  else:
      print(f"Cannot retry: {state.reason}. Next window: {state.retry_available_at}")
  ```

  ```bash cURL theme={null}
  curl https://live.dodopayments.com/payments/pay_0NmDtkE0iRvmeTcT6t0ol/retry \
    -H "Authorization: Bearer $DODO_PAYMENTS_API_KEY"
  ```
</CodeGroup>

```json Response theme={null}
{
  "can_retry": false,
  "reason": "MANUAL_RETRY_LIMIT_REACHED",
  "sends_used": 1,
  "sends_allowed": 3,
  "retry_available_at": "2026-08-26T16:51:00Z"
}
```

| 字段                   | 描述                                                |
| -------------------- | ------------------------------------------------- |
| `can_retry`          | 如果此时可以发送重试，则为 `true`。                             |
| `reason`             | 重试会失败时返回的代码。当 `can_retry` 为 `true` 时，该字段为 `null`。 |
| `sends_used`         | 已针对该发票发送的手动重试次数。                                  |
| `sends_allowed`      | 始终为 `3`。                                          |
| `retry_available_at` | 下一次手动重试可用的时间。当没有剩余重试，或拒绝原因与冷却时间无关时，该字段为 `null`。   |

### 发送手动重试

`POST /payments/{payment_id}/retry` 会创建一笔新支付并使用已保存的支付方式扣款。此操作需要 **Editor** 角色。

<CodeGroup>
  ```typescript Node.js theme={null}
  const retry = await client.payments.retry('pay_0NmDtkE0iRvmeTcT6t0ol');

  console.log(retry.payment_id, retry.status);
  ```

  ```python Python theme={null}
  retry = client.payments.retry("pay_0NmDtkE0iRvmeTcT6t0ol")

  print(retry.payment_id, retry.status)
  ```

  ```bash cURL theme={null}
  curl -X POST https://live.dodopayments.com/payments/pay_0NmDtkE0iRvmeTcT6t0ol/retry \
    -H "Authorization: Bearer $DODO_PAYMENTS_API_KEY"
  ```
</CodeGroup>

```json Response theme={null}
{
  "payment_id": "pay_2IjeQm4hqU6RA4Z4kwDee",
  "invoice_id": "inv_9Kp2mQ7vRt4LxYw3",
  "status": "processing",
  "retry_attempt": 1,
  "is_manual_retry": true,
  "sends_used": 1,
  "sends_allowed": 3,
  "retry_available_at": "2026-08-26T16:51:00Z"
}
```

| 字段                                                | 描述                                                                              |
| ------------------------------------------------- | ------------------------------------------------------------------------------- |
| `payment_id`                                      | 为此次尝试创建的新支付。                                                                    |
| `invoice_id`                                      | 被扣款的续费发票。                                                                       |
| `status`                                          | 扣款结果。`processing` 表示支付处理方尚未结算。`null` 表示响应返回前尚未记录结果。在这两种情况下，支付 webhook 都会报告最终结果。 |
| `retry_attempt`                                   | 此次尝试在该发票手动重试中的序号，从 `1` 开始。                                                      |
| `is_manual_retry`                                 | 此 endpoint 始终为 `true`。                                                          |
| `sends_used`、`sends_allowed`、`retry_available_at` | 此次发送后的重试限制状态。`retry_available_at` 仅表示冷却计时器。即使此次扣款成功，该字段也会被设置；此时发票已支付，不会再有新的重试。  |

### 错误响应

| HTTP 状态 | 代码                                                                                                                                                                                         | 操作                                                                                       |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
| `404`   | `NOT_FOUND`                                                                                                                                                                                | 该支付不属于您的业务。                                                                              |
| `409`   | `MANUAL_RETRY_IN_FLIGHT`、`PREVIOUS_PAYMENT_PENDING`、`CHARGE_NOT_ALLOWED_FOR_SCHEDULED_CANCELLATION`                                                                                        | 暂时无法执行，或需要先更改其他条件。等待进行中的支付或待处理支付进入最终状态，或取消计划中的取消操作。                                      |
| `422`   | `PAYMENT_NOT_RETRYABLE`、`SUBSCRIPTION_INACTIVE`、`SUBSCRIPTION_HAS_NO_PAYMENT_METHOD`、`MANUAL_RETRY_HARD_DECLINE`、`MANUAL_RETRY_ALREADY_PAID`、`BYOP_CONNECTOR_DISABLED`、`MERCHANT_NOT_LIVE` | 该支付无法重试。请勿重复调用。                                                                          |
| `429`   | `MANUAL_RETRY_LIMIT_REACHED`                                                                                                                                                               | [检查重试状态](#check-whether-a-payment-can-be-retried)，等待 `retry_available_at`，或在三次重试全部用完后停止。 |

每个代码均在 [错误代码](/api-reference/error-codes)参考中进行了说明。

## Webhook

手动重试会创建普通支付，因此会触发与任何续费尝试相同的 webhook：

| 事件                   | 触发时机                                        |
| -------------------- | ------------------------------------------- |
| `payment.succeeded`  | 重试扣款成功。订阅重新激活时，随后会触发 `subscription.active`。 |
| `payment.failed`     | 重试被拒付。订阅保持 `on_hold` 状态，手动失败不会安排自动重试。       |
| `payment.processing` | 支付处理方已接受扣款，但尚未完成结算。                         |

在这些事件的支付对象中，`retry_attempt` 为 `1` 或更高，并且已设置 `subscription_id`，与自动重试完全相同。如果需要区分手动尝试和计划尝试，请保留重试响应中的 `payment_id`。

<Card title="Payment Webhook Payloads" icon="webhook" href="/developer-resources/webhooks/intents/payment">
  支付事件的完整 payload schema。
</Card>

## 相关内容

<CardGroup cols={2}>
  <Card title="Subscription Payment Retries" icon="arrow-rotate-right" href="/features/recovery/payment-retries">
    与手动重试并行运行的自动退避计划。
  </Card>

  <Card title="Subscription Dunning" icon="repeat" href="/features/recovery/subscription-dunning">
    硬拒付后发送邮件，请客户更新其支付方式。
  </Card>

  <Card title="Handle Payment Failures" icon="screwdriver-wrench" href="/developer-resources/handle-payment-failures">
    读取拒付代码，并判断何时值得重试。
  </Card>

  <Card title="Error Codes" icon="triangle-exclamation" href="/api-reference/error-codes">
    每个 `MANUAL_RETRY_*` 代码、其触发条件及消息。
  </Card>
</CardGroup>
