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

# Thử lại thanh toán thủ công

> Thử lại khoản thanh toán gia hạn subscription bị lỗi theo yêu cầu từ dashboard hoặc API thay vì chờ lần thử lại tự động tiếp theo.

<Info>
  Thử lại thủ công sẽ thử lại khoản thanh toán **gia hạn** subscription bị lỗi ngay khi bạn yêu cầu, từ trang chi tiết thanh toán hoặc thông qua API. Tính năng này tính phí vào payment method đã lưu trên subscription và hoạt động độc lập với lịch [Payment Retries](/features/recovery/payment-retries) tự động.
</Info>

## Thử lại thủ công là gì?

Khi khoản thanh toán gia hạn bị lỗi, subscription chuyển sang `on_hold` và [Payment Retries](/features/recovery/payment-retries) sẽ thử tính phí lại theo lịch back-off. Đôi khi bạn biết khoản thanh toán sẽ thành công ngay lúc này: khách hàng đã xác nhận họ nạp thêm tiền vào tài khoản, hoặc đội ngũ hỗ trợ của bạn đang trao đổi với họ. Thử lại thủ công cho phép bạn gửi một lần thử ngay lập tức thay vì chờ hàng giờ hoặc hàng ngày cho lần thử theo lịch tiếp theo.

* **Chỉ áp dụng cho khoản thanh toán gia hạn**: Thử lại thủ công áp dụng cho invoice gia hạn subscription khi subscription đang ở trạng thái `on_hold`. Khoản thanh toán đầu tiên, khoản thanh toán một lần, phí thay đổi plan và phí theo yêu cầu không đủ điều kiện.
* **Không cần khách hàng thực hiện thao tác**: Khoản phí được gửi đến payment method đã lưu trên subscription.
* **Độc lập với các lần thử lại tự động**: Một lần thử lại thủ công không sử dụng một lần thử trong lịch tự động, không thay đổi lần thử theo lịch tiếp theo và vẫn hoạt động ngay cả khi Payment Retries bị tắt.
* **Thử lại invoice, không phải payment**: Payment bị lỗi chỉ là điểm bắt đầu. Dodo Payments tìm invoice gia hạn đang mở phía sau payment đó và thu khoản công nợ này, vì vậy việc bạn thử lại payment bị lỗi nào trên invoice không quan trọng.

## Thử lại từ Dashboard

<Steps>
  <Step title="Open the failed payment">
    Đi tới **Transactions → Payments** và nhấp vào payment gia hạn bị lỗi để mở trang **Transaction details**.
  </Step>

  <Step title="Click Retry Payment Manually">
    Nhấp vào **Retry Payment Manually** ở góc trên bên phải. Nút này chỉ khả dụng khi payment [đủ điều kiện](#eligibility).
  </Step>

  <Step title="Check the result">
    Một payment mới được tạo cho lần thử và xuất hiện trong **Activity Log**. Nếu khoản phí thành công, subscription trở lại trạng thái `active` và ngày thanh toán tiếp theo được dời như bình thường. Nếu payment processor chưa hoàn tất việc quyết toán khoản phí, payment sẽ hiển thị là đang được xử lý cho đến khi webhook `payment.succeeded` hoặc `payment.failed` báo kết quả.
  </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="Trang chi tiết giao dịch của một payment bị lỗi, hiển thị mã lỗi và thông báo lỗi, Activity Log và nút Retry Payment Manually" style={{ maxHeight: '500px', width: 'auto' }} width="1285" height="698" data-path="images/recovery/manual-retry-transaction-details.png" />
</Frame>

## Điều kiện đủ tư cách

Một lần thử lại thủ công chỉ được gửi khi tất cả các bước kiểm tra dưới đây đều đạt. Cột **Reason code** là giá trị API trả về: trong `reason` trên `GET /payments/{payment_id}/retry` và dưới dạng lỗi `code` trên `POST /payments/{payment_id}/retry`.

| Kiểm tra                      | Yêu cầu                                                                                                                                                                                                                                                                        | Reason code                                     |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------- |
| Loại payment                  | Payment **gia hạn** subscription có invoice vẫn đang mở. Payment không có invoice, payment đầu tiên, payment một lần, phí thay đổi plan và phí theo yêu cầu không thể được thử lại.                                                                                            | `PAYMENT_NOT_RETRYABLE`                         |
| Trạng thái subscription       | `on_hold`                                                                                                                                                                                                                                                                      | `SUBSCRIPTION_INACTIVE`                         |
| Hủy theo lịch                 | Subscription không được lên lịch hủy vào ngày thanh toán tiếp theo.                                                                                                                                                                                                            | `CHARGE_NOT_ALLOWED_FOR_SCHEDULED_CANCELLATION` |
| Payment method đã lưu         | Subscription có payment method đã lưu để tính phí.                                                                                                                                                                                                                             | `SUBSCRIPTION_HAS_NO_PAYMENT_METHOD`            |
| Lỗi gần nhất                  | Lỗi gần nhất là **soft decline**. Hard decline hoặc lỗi không có error code được phân loại không thể được thử lại.                                                                                                                                                             | `MANUAL_RETRY_HARD_DECLINE`                     |
| Không có giao dịch đang xử lý | Không có payment nào trên invoice ở trạng thái `processing` hoặc chưa có trạng thái được ghi nhận. Đây là một lần thử, thủ công hoặc tự động, vừa được gửi nhưng chưa trả về kết quả. Trước tiên, hãy chờ kết quả của lần thử đó.                                              | `MANUAL_RETRY_IN_FLIGHT`                        |
| Payment mới nhất bị lỗi       | Payment mới nhất trên invoice có trạng thái `failed`. Payment mới nhất ở bất kỳ trạng thái nào khác không phải `failed`, chẳng hạn `requires_customer_action`, `requires_payment_method` hoặc `cancelled`, sẽ chặn việc thử lại ngay cả khi không có giao dịch nào đang xử lý. | `PREVIOUS_PAYMENT_PENDING`                      |
| Chưa được thanh toán          | Không có payment nào trên invoice thành công.                                                                                                                                                                                                                                  | `MANUAL_RETRY_ALREADY_PAID`                     |
| Giới hạn thử lại              | Có ít hơn 3 lần thử lại thủ công đã được gửi trên invoice và thời gian chờ đã kết thúc. Xem [Giới hạn thử lại](#retry-limits).                                                                                                                                                 | `MANUAL_RETRY_LIMIT_REACHED`                    |
| Khách hàng                    | Khách hàng không nằm trong [blocklist](/features/customer-blocklist) của bạn.                                                                                                                                                                                                  | `PAYMENT_NOT_RETRYABLE`                         |
| Payment connector             | Đối với subscription [BYOP](/features/byop), connector đã được bật.                                                                                                                                                                                                            | `BYOP_CONNECTOR_DISABLED`                       |
| Live mode                     | Trong live mode, doanh nghiệp của bạn đã bật live payments.                                                                                                                                                                                                                    | `MERCHANT_NOT_LIVE`                             |

<Note>
  Thử lại thủ công có một điểm hạn chế hơn so với các lần thử lại tự động: subscription phải ở trạng thái `on_hold`. Các lần thử lại tự động vẫn tiếp tục chạy đối với những trạng thái không phải active khác; xem [Subscription Status Transitions](/features/recovery/payment-retries#subscription-status-transitions).
</Note>

<Warning>
  Thử lại một hard decline trên cùng thẻ không thể thành công và các lần bị từ chối lặp lại sẽ làm giảm tỷ lệ authorization của bạn. Khi lý do là `MANUAL_RETRY_HARD_DECLINE`, hãy yêu cầu khách hàng cập nhật payment method thay thế. [Subscription Dunning](/features/recovery/subscription-dunning) thực hiện việc này tự động.
</Warning>

## Giới hạn thử lại

Mỗi invoice gia hạn cho phép **3** lần thử lại thủ công, với thời gian chờ giữa các lần:

| Lần thử lại thủ công | Khả dụng                      |
| -------------------- | ----------------------------- |
| 1                    | Ngay khi payment đủ điều kiện |
| 2                    | 1 giờ sau lần đầu tiên        |
| 3                    | 3 giờ sau lần thứ hai         |

Các giới hạn áp dụng cho cả test mode và live mode. Khi một lần thử lại bị từ chối vì lý do này, API trả về `MANUAL_RETRY_LIMIT_REACHED` (HTTP `429`). Nội dung lỗi chỉ chứa `code` và `message`. Để biết khi nào có thể thực hiện lần thử lại tiếp theo, [kiểm tra trạng thái thử lại](#check-whether-a-payment-can-be-retried) và đọc `retry_available_at`. Giá trị này là `null` khi cả ba lần thử đã được sử dụng.

Các lần thử lại tự động không được tính vào giới hạn này và các lần thử lại thủ công không được tính vào 8 lần thử của lịch tự động.

## Thử lại thủ công và tự động

|                               | Thử lại thủ công                                                                 | Payment Retries                                                |
| ----------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| **Kích hoạt**                 | Bạn, từ dashboard hoặc API                                                       | Dodo Payments, theo lịch back-off                              |
| **Thời điểm**                 | Ngay lập tức                                                                     | 12 giờ sau khi lỗi, sau đó muộn dần theo từng lần              |
| **Số lần thử**                | 3 lần mỗi invoice, với thời gian chờ 1 giờ rồi 3 giờ                             | Tối đa 8 lần mỗi invoice, trong recovery window                |
| **Cần bật Payment Retries**   | Không                                                                            | Có                                                             |
| **Ảnh hưởng đến bên còn lại** | Không. Một lần thử thủ công bị lỗi không lên lịch hoặc thay đổi lần thử tự động. | Không. Chuỗi tự động vẫn tiếp tục bất kể các lần gửi thủ công. |
| **Analytics**                 | Được tính trong các chỉ số **Payment retries** trên tab Recovery                 | Được tính trong cùng các chỉ số                                |

## Thử lại qua API

Trước tiên hãy kiểm tra điều kiện đủ tư cách, sau đó gửi yêu cầu thử lại. Cả hai endpoint đều nhận ID của payment bị lỗi.

### Kiểm tra payment có thể được thử lại hay không

`GET /payments/{payment_id}/retry` không bao giờ báo lỗi đối với payment không đủ điều kiện. Thay vào đó, endpoint trả về `can_retry: false` cùng với mã `reason`, để dashboard hoặc công cụ hỗ trợ của bạn có thể hiển thị cùng trạng thái như dashboard Dodo Payments. Endpoint này yêu cầu vai trò **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"
}
```

| Trường               | Mô tả                                                                                                                                           |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `can_retry`          | `true` khi lần thử lại sẽ được gửi ngay lúc này.                                                                                                |
| `reason`             | Mã lỗi mà lần thử lại sẽ trả về. `null` khi `can_retry` là `true`.                                                                              |
| `sends_used`         | Số lần thử lại thủ công đã được gửi trên invoice này.                                                                                           |
| `sends_allowed`      | Luôn là `3`.                                                                                                                                    |
| `retry_available_at` | Thời điểm lần thử lại thủ công tiếp theo được mở. `null` khi không còn lần thử lại nào hoặc khi việc từ chối không liên quan đến thời gian chờ. |

### Gửi lần thử lại thủ công

`POST /payments/{payment_id}/retry` tạo payment mới và tính phí vào payment method đã lưu. Endpoint này yêu cầu vai trò **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"
}
```

| Trường                                              | Mô tả                                                                                                                                                                                                                            |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `payment_id`                                        | Payment mới được tạo cho lần thử này.                                                                                                                                                                                            |
| `invoice_id`                                        | Invoice gia hạn đã được tính phí.                                                                                                                                                                                                |
| `status`                                            | Kết quả của khoản phí. `processing` nghĩa là processor chưa quyết toán khoản phí. `null` nghĩa là chưa ghi nhận kết quả nào trước khi phản hồi được trả về. Trong cả hai trường hợp, webhook payment sẽ báo kết quả cuối cùng.   |
| `retry_attempt`                                     | Vị trí của lần thử này trong số các lần thử lại thủ công trên invoice, bắt đầu từ `1`.                                                                                                                                           |
| `is_manual_retry`                                   | Luôn là `true` trên endpoint này.                                                                                                                                                                                                |
| `sends_used`, `sends_allowed`, `retry_available_at` | Trạng thái giới hạn thử lại sau lần gửi này. `retry_available_at` chỉ là đồng hồ thời gian chờ. Giá trị này được thiết lập ngay cả khi khoản phí thành công; khi đó invoice đã được thanh toán và không mở thêm lần thử lại nào. |

### Phản hồi lỗi

| HTTP status | Codes                                                                                                                                                                                            | Cách xử lý                                                                                                                                                   |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `404`       | `NOT_FOUND`                                                                                                                                                                                      | Payment không thuộc về doanh nghiệp của bạn.                                                                                                                 |
| `409`       | `MANUAL_RETRY_IN_FLIGHT`, `PREVIOUS_PAYMENT_PENDING`, `CHARGE_NOT_ALLOWED_FOR_SCHEDULED_CANCELLATION`                                                                                            | Tạm thời chưa thể thực hiện hoặc cần thay đổi điều gì đó trước. Hãy chờ payment đang xử lý hoặc đang chờ đạt trạng thái cuối cùng, hoặc xóa lịch hủy.        |
| `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` | Payment này không thể được thử lại. Không lặp lại lời gọi.                                                                                                   |
| `429`       | `MANUAL_RETRY_LIMIT_REACHED`                                                                                                                                                                     | [Kiểm tra trạng thái thử lại](#check-whether-a-payment-can-be-retried) và chờ đến khi `retry_available_at`, hoặc dừng lại khi cả ba lần thử đã được sử dụng. |

Mọi code đều được mô tả trong tài liệu tham khảo [Error Codes](/api-reference/error-codes).

## Webhook

Thử lại thủ công tạo một payment thông thường, vì vậy các webhook giống như đối với mọi lần thử gia hạn khác sẽ được kích hoạt:

| Event                | Kích hoạt khi                                                                                                                                     |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `payment.succeeded`  | Lần thử lại đã được tính phí. `subscription.active` được gửi tiếp theo khi subscription được kích hoạt lại.                                       |
| `payment.failed`     | Lần thử lại bị từ chối. Subscription vẫn ở trạng thái `on_hold` và không có lần thử lại tự động nào được lên lịch từ một lần thử thủ công bị lỗi. |
| `payment.processing` | Processor đã chấp nhận khoản phí nhưng chưa quyết toán.                                                                                           |

Trên payment object trong các event này, `retry_attempt` là `1` hoặc cao hơn và `subscription_id` được thiết lập, chính xác như đối với lần thử lại tự động. Hãy lưu `payment_id` từ phản hồi thử lại nếu bạn cần phân biệt lần thử thủ công với lần thử theo lịch.

<Card title="Payment Webhook Payloads" icon="webhook" href="/developer-resources/webhooks/intents/payment">
  Schema payload đầy đủ cho các event payment.
</Card>

## Liên quan

<CardGroup cols={2}>
  <Card title="Subscription Payment Retries" icon="arrow-rotate-right" href="/features/recovery/payment-retries">
    Lịch back-off tự động chạy song song với các lần thử lại thủ công.
  </Card>

  <Card title="Subscription Dunning" icon="repeat" href="/features/recovery/subscription-dunning">
    Gửi email cho khách hàng để cập nhật payment method sau một hard decline.
  </Card>

  <Card title="Handle Payment Failures" icon="screwdriver-wrench" href="/developer-resources/handle-payment-failures">
    Đọc decline code và quyết định khi nào việc thử lại đáng thực hiện.
  </Card>

  <Card title="Error Codes" icon="triangle-exclamation" href="/api-reference/error-codes">
    Mọi code `MANUAL_RETRY_*`, trigger và thông báo tương ứng.
  </Card>
</CardGroup>
