> ## 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에서 요청하는 즉시 실패한 구독 **갱신** 결제를 다시 시도합니다. 구독에 저장된 결제 수단으로 요금이 청구되며, 자동 [Payment Retries](/features/recovery/payment-retries) 일정과는 독립적으로 실행됩니다.
</Info>

## 수동 재시도란 무엇인가요?

갱신 결제가 실패하면 구독은 `on_hold` 상태로 전환되고, [Payment Retries](/features/recovery/payment-retries)는 백오프 일정에 따라 청구를 다시 시도합니다. 고객이 계정에 금액을 충전했다고 확인했거나 지원팀이 고객과 통화 중인 경우처럼 지금 결제가 성공할 것이라는 사실을 알고 있을 때도 있습니다. 수동 재시기를 사용하면 다음 예약된 재시도를 몇 시간 또는 며칠 동안 기다리는 대신 즉시 한 번 시도할 수 있습니다.

* **갱신 결제만 해당**: 수동 재시도는 구독이 `on_hold` 상태인 동안 구독 갱신 인보이스에 적용됩니다. 최초 결제, 일회성 결제, 플랜 변경 요금 및 주문형 요금은 대상이 아닙니다.
* **고객 조치 불필요**: 구독에 이미 저장된 결제 수단으로 요금이 청구됩니다.
* **자동 재시도와 독립적**: 수동 재시도는 자동 일정의 시도 횟수를 차감하지 않고, 다음 예약된 재시기를 변경하지 않으며, Payment Retries가 꺼져 있어도 작동합니다.
* **결제가 아닌 인보이스 재시도**: 실패한 결제는 시작점일 뿐입니다. Dodo Payments는 해당 결제와 연결된 미결제 갱신 인보이스를 조회하고 그 채무를 청구하므로, 인보이스에서 어떤 실패한 결제를 통해 재시도했는지는 중요하지 않습니다.

## 대시보드에서 재시도

<Steps>
  <Step title="Open the failed payment">
    **Transactions → Payments**로 이동한 다음 실패한 갱신 결제를 클릭하여 **Transaction details** 페이지를 엽니다.
  </Step>

  <Step title="Click Retry Payment Manually">
    오른쪽 상단에서 **Retry Payment Manually**를 클릭합니다. 이 버튼은 결제가 [eligible](#eligibility) 상태인 동안에만 사용할 수 있습니다.
  </Step>

  <Step title="Check the result">
    시도를 위한 새 결제가 생성되고 **Activity Log**에 표시됩니다. 청구에 성공하면 구독은 `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="오류 코드와 메시지, Activity Log 및 Retry Payment Manually 버튼이 표시된 실패한 결제의 Transaction details 페이지" style={{ maxHeight: '500px', width: 'auto' }} width="1285" height="698" data-path="images/recovery/manual-retry-transaction-details.png" />
</Frame>

## 자격 요건

아래의 모든 확인 항목을 통과한 경우에만 수동 재시도가 전송됩니다. **Reason code** 열은 API가 반환하는 값입니다. `GET /payments/{payment_id}/retry`에서는 `reason`로, `POST /payments/{payment_id}/retry`에서는 `code` 오류로 반환됩니다.

| 확인 항목       | 요구 사항                                                                                                                                                                     | Reason code                                     |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| 결제 유형       | 인보이스가 아직 미결제 상태인 구독 **갱신** 결제여야 합니다. 인보이스가 없는 결제, 최초 결제, 일회성 결제, 플랜 변경 요금 및 주문형 요금은 재시도할 수 없습니다.                                                                          | `PAYMENT_NOT_RETRYABLE`                         |
| 구독 상태       | `on_hold`                                                                                                                                                                 | `SUBSCRIPTION_INACTIVE`                         |
| 예약된 취소      | 다음 청구일에 구독이 취소되도록 예약되어 있지 않아야 합니다.                                                                                                                                        | `CHARGE_NOT_ALLOWED_FOR_SCHEDULED_CANCELLATION` |
| 저장된 결제 수단   | 구독에 청구할 저장된 결제 수단이 있어야 합니다.                                                                                                                                               | `SUBSCRIPTION_HAS_NO_PAYMENT_METHOD`            |
| 최근 실패       | 가장 최근 실패가 **soft decline**이어야 합니다. hard decline 또는 분류된 오류 코드가 없는 실패는 재시도할 수 없습니다.                                                                                         | `MANUAL_RETRY_HARD_DECLINE`                     |
| 진행 중인 결제 없음 | 인보이스의 결제 중 어느 것도 `processing` 상태가 아니어야 하며, 아직 기록된 상태가 없어도 안 됩니다. 이는 방금 전송되었지만 아직 결과를 보고하지 않은 수동 또는 자동 시도입니다. 먼저 결과를 기다리세요.                                                | `MANUAL_RETRY_IN_FLIGHT`                        |
| 가장 최근 결제 실패 | 인보이스에서 가장 최근 결제의 상태가 `failed`여야 합니다. `failed`가 아닌 다른 상태(예: `requires_customer_action`, `requires_payment_method` 또는 `cancelled`)의 가장 최근 결제가 있으면 진행 중인 결제가 없어도 재시도가 차단됩니다. | `PREVIOUS_PAYMENT_PENDING`                      |
| 이미 결제되지 않음  | 인보이스의 결제 중 성공한 것이 없어야 합니다.                                                                                                                                                | `MANUAL_RETRY_ALREADY_PAID`                     |
| 재시도 한도      | 인보이스에 전송된 수동 재시도가 3회 미만이고 쿨다운이 지나야 합니다. [Retry Limits](#retry-limits)를 참고하세요.                                                                                             | `MANUAL_RETRY_LIMIT_REACHED`                    |
| 고객          | 고객이 [blocklist](/features/customer-blocklist)에 등록되어 있지 않아야 합니다.                                                                                                           | `PAYMENT_NOT_RETRYABLE`                         |
| 결제 커넥터      | [BYOP](/features/byop) 구독의 경우 커넥터가 활성화되어 있어야 합니다.                                                                                                                         | `BYOP_CONNECTOR_DISABLED`                       |
| Live mode   | Live mode에서 비즈니스의 실시간 결제가 활성화되어 있어야 합니다.                                                                                                                                  | `MERCHANT_NOT_LIVE`                             |

<Note>
  수동 재시도는 한 가지 측면에서 자동 재시도보다 더 제한적입니다. 구독이 `on_hold` 상태여야 합니다. 자동 재시도는 다른 비활성 상태에서도 계속 실행됩니다. 자세한 내용은 [Subscription Status Transitions](/features/recovery/payment-retries#subscription-status-transitions)를 참고하세요.
</Note>

<Warning>
  같은 카드로 hard decline을 재시도해도 성공할 수 없으며, 반복되는 거절은 승인율을 낮춥니다. 사유가 `MANUAL_RETRY_HARD_DECLINE`인 경우 고객에게 결제 수단을 업데이트하도록 요청하세요. [Subscription Dunning](/features/recovery/subscription-dunning)이 이 작업을 자동으로 수행합니다.
</Warning>

## 재시도 한도

각 갱신 인보이스에서는 재시도 사이에 쿨다운을 두고 수동 재시도를 **3**회 허용합니다.

| 수동 재시도 | 사용 가능 시점               |
| ------ | ---------------------- |
| 1      | 결제가 eligible 상태가 되는 즉시 |
| 2      | 첫 번째 재시도 후 1시간 뒤       |
| 3      | 두 번째 재시도 후 3시간 뒤       |

한도는 test mode와 live mode 모두에 적용됩니다. 이 사유로 재시도가 거부되면 API는 `MANUAL_RETRY_LIMIT_REACHED` (HTTP `429`)를 반환합니다. 오류 본문에는 `code`과 `message`만 포함됩니다. 다음 재시도가 언제 가능해지는지 알아보려면 [재시도 상태 확인](#check-whether-a-payment-can-be-retried)을 수행하고 `retry_available_at`를 읽으세요. 세 번의 재시도를 모두 사용하면 `null`입니다.

자동 재시도는 이 한도에 포함되지 않으며, 수동 재시도도 자동 일정의 8회 시도에 포함되지 않습니다.

## 수동 재시도와 자동 재시도 비교

|                            | 수동 재시도                                 | Payment Retries                  |
| -------------------------- | -------------------------------------- | -------------------------------- |
| **트리거**                    | 대시보드 또는 API에서 사용자에 의해 실행               | Dodo Payments가 백오프 일정에 따라 실행     |
| **시점**                     | 즉시                                     | 실패 후 12시간 뒤, 이후 점차 긴 간격으로 실행     |
| **시도 횟수**                  | 인보이스당 3회, 1시간 및 3시간 쿨다운 적용             | 복구 기간 내 인보이스당 최대 8회              |
| **Payment Retries 활성화 필요** | 아니요                                    | 예                                |
| **상호 영향**                  | 없음. 수동 실패는 자동 시도를 예약하거나 변경하지 않습니다.     | 없음. 수동 전송 여부와 관계없이 자동 체인이 계속됩니다. |
| **분석**                     | Recovery 탭의 **Payment retries** 지표에 집계 | 동일한 지표에 집계                       |

## API를 통한 재시도

먼저 자격 요건을 확인한 다음 재시도를 전송하세요. 두 엔드포인트 모두 실패한 결제의 ID를 받습니다.

### 결제 재시도 가능 여부 확인

`GET /payments/{payment_id}/retry`는 자격 요건을 충족하지 않는 결제에 대해 실패하지 않습니다. 대신 `reason` 코드와 함께 `can_retry: false`를 반환하므로, 대시보드 또는 지원 도구에서 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`                                   | 이 엔드포인트에서는 항상 `true`입니다.                                                                                                        |
| `sends_used`, `sends_allowed`, `retry_available_at` | 이 전송 후 재시도 한도 상태입니다. `retry_available_at`는 쿨다운 타이머에만 해당합니다. 이 청구가 성공한 경우에도 설정되며, 이때 인보이스가 결제되어 추가 재시도가 가능해지지 않습니다.              |

### 오류 응답

| HTTP status | Codes                                                                                                                                                                                            | 조치                                                                                                              |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| `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`까지 기다리거나, 세 번의 재시도를 모두 사용했다면 중지하세요. |

모든 코드는 [Error Codes](/api-reference/error-codes) 레퍼런스에 설명되어 있습니다.

## Webhooks

수동 재시도는 일반 결제를 생성하므로 다른 갱신 시도와 동일한 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 스키마입니다.
</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">
    hard decline 후 고객에게 결제 수단을 업데이트하도록 이메일을 보냅니다.
  </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>
