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

# Hướng Dẫn Tích Hợp Đăng Ký

> Hướng dẫn này sẽ giúp bạn tích hợp Sản Phẩm Đăng Ký Dodo Payments vào trang web của bạn.

## Điều Kiện Tiên Quyết

Để tích hợp API Dodo Payments, bạn cần:

* Một tài khoản thương nhân Dodo Payments
* Thông tin xác thực API (khóa API và khóa bí mật webhook) từ bảng điều khiển

Để có hướng dẫn chi tiết hơn về các điều kiện tiên quyết, hãy kiểm tra [phần này](/developer-resources/integration-guide#dashboard-setup).

## Tích Hợp API

### Phiên Thanh Toán

Sử dụng Phiên Thanh toán để bán sản phẩm đăng ký qua một trang thanh toán được lưu trữ an toàn. Truyền sản phẩm đăng ký của bạn vào `product_cart` và chuyển hướng khách hàng đến `checkout_url` được trả về.

<Tip>
  **Thanh toán hỗn hợp**: Bạn có thể kết hợp sản phẩm đăng ký với sản phẩm thanh toán một lần trong cùng một phiên thanh toán. Điều này cho phép các trường hợp sử dụng như phí cài đặt với đăng ký, gói phần cứng cùng SaaS, và hơn thế nữa. Xem [Hướng dẫn Phiên Thanh toán](/developer-resources/checkout-session) để biết ví dụ.
</Tip>

<Tabs>
  <Tab title="Node.js SDK">
    ```javascript theme={null}
    import DodoPayments from 'dodopayments';

    const client = new DodoPayments({
      bearerToken: process.env.DODO_PAYMENTS_API_KEY,
      environment: 'test_mode', // defaults to 'live_mode'
    });

    async function main() {
      const session = await client.checkoutSessions.create({
        product_cart: [
          { product_id: 'prod_subscription_monthly', quantity: 1 }
        ],
        // Optional: configure trials for subscription products
        subscription_data: { trial_period_days: 14 },
        customer: {
          email: 'subscriber@example.com',
          name: 'Jane Doe',
        },
        return_url: 'https://example.com/success',
      });

      console.log(session.checkout_url);
    }

    main();
    ```
  </Tab>

  <Tab title="Python SDK">
    ```python theme={null}
    import os
    from dodopayments import DodoPayments

    client = DodoPayments(
        bearer_token=os.environ.get("DODO_PAYMENTS_API_KEY"),
        environment="test_mode",  # defaults to "live_mode"
    )

    session = client.checkout_sessions.create(
        product_cart=[
            {"product_id": "prod_subscription_monthly", "quantity": 1}
        ],
        subscription_data={"trial_period_days": 14},  # optional
        customer={
            "email": "subscriber@example.com",
            "name": "Jane Doe",
        },
        return_url="https://example.com/success",
    )

    print(session.checkout_url)
    ```
  </Tab>

  <Tab title="REST API">
    ```javascript theme={null}
    const response = await fetch('https://test.dodopayments.com/checkouts', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${process.env.DODO_PAYMENTS_API_KEY}`
      },
      body: JSON.stringify({
        product_cart: [
          { product_id: 'prod_subscription_monthly', quantity: 1 }
        ],
        subscription_data: { trial_period_days: 14 }, // optional
        customer: {
          email: 'subscriber@example.com',
          name: 'Jane Doe'
        },
        return_url: 'https://example.com/success'
      })
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const session = await response.json();
    console.log(session.checkout_url);
    ```
  </Tab>
</Tabs>

### Phản Hồi API

Dưới đây là một ví dụ về phản hồi:

```json theme={null}
{
  "session_id": "cks_Gi6KGJ2zFJo9rq9Ukifwa",
  "checkout_url": "https://test.checkout.dodopayments.com/session/cks_Gi6KGJ2zFJo9rq9Ukifwa"
}
```

Chuyển hướng khách hàng đến `checkout_url`.

### Webhooks

Khi tích hợp các gói đăng ký, bạn sẽ nhận được webhooks để theo dõi vòng đời của gói thuê bao. Các webhooks này giúp bạn quản lý trạng thái thuê bao và các kịch bản thanh toán một cách hiệu quả.

Để thiết lập endpoint webhook của bạn, vui lòng làm theo [Hướng dẫn Tích hợp Chi tiết](/developer-resources/integration-guide#implementing-webhooks).

#### Các Loại Sự Kiện Thuê Bao

Các sự kiện webhook sau theo dõi các thay đổi trạng thái thuê bao:

1. **`subscription.active`** - Thuê bao được kích hoạt thành công.
2. **`subscription.updated`** - Đối tượng thuê bao đã được cập nhật (khởi động khi có bất kỳ thay đổi nào).
3. **`subscription.on_hold`** - Thuê bao bị tạm dừng do gia hạn thất bại.
4. **`subscription.failed`** - Tạo thuê bao thất bại trong quá trình tạo ủy quyền.
5. **`subscription.renewed`** - Thuê bao được gia hạn cho chu kỳ thanh toán tiếp theo.

Để quản lý vòng đời thuê bao đáng tin cậy, chúng tôi khuyến nghị theo dõi các sự kiện thuê bao này.

<Tip>
  Sử dụng `subscription.updated` để nhận thông báo theo thời gian thực về bất kỳ thay đổi nào của thuê bao, giữ cho trạng thái ứng dụng của bạn đồng bộ mà không cần phải hỏi API.
</Tip>

#### Kịch Bản Thanh Toán

**Luồng Thanh Toán Thành Công**

Khi một khoản thanh toán thành công, bạn sẽ nhận được các webhooks sau:

1. `subscription.active` - Chỉ định kích hoạt thuê bao
2. `payment.succeeded` - Xác nhận thanh toán ban đầu:
   * Đối với thanh toán tức thì (không có ngày dùng thử): Mong đợi trong vòng 2-10 phút
   * Đối với thử nghiệm: bất cứ khi nào kết thúc
3. `subscription.renewed` - Chỉ định khấu trừ thanh toán và gia hạn cho chu kỳ tiếp theo. (Về cơ bản, bất cứ khi nào thanh toán được khấu trừ cho các sản phẩm thuê bao, bạn sẽ nhận được webhook `subscription.renewed` cùng với `payment.succeeded`)

**Kịch Bản Thất Bại Thanh Toán**

1. Thất Bại Thuê Bao

* `subscription.failed` - Tạo thuê bao thất bại do không thể tạo ủy quyền.
* `payment.failed` - Chỉ ra thanh toán thất bại.

2. Thuê Bao Bị Tạm Dừng

* `subscription.on_hold` - Thuê bao bị tạm giữ do gia hạn thất bại hoặc thay đổi kế hoạch thất bại.
* Khi một thuê bao bị tạm dừng, nó sẽ không tự động gia hạn cho đến khi phương thức thanh toán được cập nhật.

<Info>**Thực Hành Tốt Nhất**: Để đơn giản hóa việc triển khai, chúng tôi khuyến nghị chủ yếu theo dõi các sự kiện thuê bao để quản lý vòng đời thuê bao.</Info>

<Tip>
  Để xem xét đầy đủ việc đọc `error_code`/`error_message`, quyết định khi nào nên thử lại và hiển thị sự thất bại cho khách hàng, xem [Xử Lý Thanh Toán Thất Bại](/developer-resources/handle-payment-failures).
</Tip>

#### `subscription.failed` vs. `subscription.on_hold`

Hai sự kiện này dễ gây nhầm lẫn, nhưng chúng yêu cầu xử lý rất khác nhau:

| Sự kiện                | Khi nào nó kích hoạt                                                                           | Trạng thái | Có thể phục hồi?     | Phải làm gì                                                                                                                    |
| ---------------------- | ---------------------------------------------------------------------------------------------- | ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `subscription.failed`  | ủy quyền **ban đầu** không thể được tạo khi tạo **thuê bao**                                   | `failed`   | **Không — kết thúc** | Không cấp quyền truy cập. Yêu cầu khách hàng bắt đầu một thuê bao **mới** với phương thức thanh toán khác.                     |
| `subscription.on_hold` | Thanh toán **gia hạn** (hoặc phí thay đổi kế hoạch) thất bại đối với một thuê bao đã hoạt động | `on_hold`  | **Có**               | Phục hồi bằng cách cập nhật phương thức thanh toán; xem [Xử Lý Thuê Bao Bị Tạm Dừng](#handling-subscription-on-hold) dưới đây. |

<Warning>
  `subscription.failed` là kết thúc — thuê bao không thể được kích hoạt lại. Khách hàng phải tạo một thuê bao mới. Không bao giờ cấp quyền khi sự kiện này kích hoạt.
</Warning>

### Xử Lý Thuê Bao Bị Tạm Dừng

Khi thuê bao vào trạng thái `on_hold`, bạn cần cập nhật phương thức thanh toán để kích hoạt lại. Phần này giải thích khi nào thuê bao bị tạm dừng và cách xử lý chúng.

#### Khi Nào Thuê Bao Bị Tạm Dừng

Thuê bao bị tạm dừng khi:

* **Gia hạn thanh toán thất bại**: Thanh toán tự động thất bại do thiếu tiền, thẻ hết hạn, hoặc bị ngân hàng từ chối
* **Thay đổi kế hoạch thất bại**: Thanh toán tức thì trong quá trình nâng cấp/giảm cấp kế hoạch thất bại
* **Ủy quyền phương thức thanh toán thất bại**: Không thể ủy quyền phương thức thanh toán cho các khoản phí định kỳ

<Warning>
  Các thuê bao trong trạng thái `on_hold` sẽ không tự động gia hạn. Bạn phải cập nhật phương thức thanh toán để kích hoạt lại thuê bao.
</Warning>

#### Kích Hoạt Lại Thuê Bao Từ Trạng Thái Tạm Dừng

Để kích hoạt lại thuê bao từ trạng thái `on_hold`, sử dụng API Cập Nhật Phương Thức Thanh Toán. Điều này tự động:

1. Tạo một phí cho các khoản ủng hộ còn lại
2. Tạo hóa đơn cho khoản phí
3. Xử lý thanh toán bằng phương thức thanh toán mới
4. Kích hoạt lại thuê bao về trạng thái `active` khi thanh toán thành công

<Steps>
  <Step title="Handle subscription.on_hold webhook">
    Khi bạn nhận được webhook `subscription.on_hold`, cập nhật trạng thái ứng dụng của bạn và thông báo cho khách hàng:

    ```javascript theme={null}
    // Webhook handler
    app.post('/webhooks/dodo', async (req, res) => {
      const event = req.body;
      
      if (event.type === 'subscription.on_hold') {
        const subscription = event.data;
        
        // Update subscription status in your database
        await updateSubscriptionStatus(subscription.subscription_id, 'on_hold');
        
        // Notify customer to update payment method
        await sendEmailToCustomer(subscription.customer_id, {
          subject: 'Payment Required - Subscription On Hold',
          message: 'Your subscription is on hold. Please update your payment method to continue service.'
        });
      }
      
      res.json({ received: true });
    });
    ```
  </Step>

  <Step title="Update payment method">
    Khi khách hàng sẵn sàng cập nhật phương thức thanh toán của họ, hãy gọi API Cập Nhật Phương Thức Thanh Toán:

    <CodeGroup>
      ```javascript Node.js theme={null}
      // Update with new payment method
      const response = await client.subscriptions.updatePaymentMethod(subscriptionId, {
        type: 'new',
        return_url: 'https://example.com/return'
      });

      // For on_hold subscriptions, a charge is automatically created
      if (response.payment_id) {
        console.log('Charge created for remaining dues:', response.payment_id);
        // Redirect customer to response.payment_link to complete payment
      }
      ```

      ```python Python theme={null}
      # Update with new payment method
      response = client.subscriptions.update_payment_method(
          subscription_id=subscription_id,
          type="new",
          return_url="https://example.com/return"
      )

      # For on_hold subscriptions, a charge is automatically created
      if response.payment_id:
          print("Charge created for remaining dues:", response.payment_id)
          # Redirect customer to response.payment_link to complete payment
      ```
    </CodeGroup>

    <Info>
      Bạn cũng có thể sử dụng ID phương thức thanh toán hiện có nếu khách hàng đã lưu phương thức thanh toán:

      ```javascript theme={null}
      await client.subscriptions.updatePaymentMethod(subscriptionId, {
        type: 'existing',
        payment_method_id: 'pm_abc123'
      });
      ```

      ```javascript theme={null}
      await client.subscriptions.updatePaymentMethod(subscriptionId, {
        type: 'existing',
        payment_method_id: 'pm_abc123'
      });
      ```
    </Info>
  </Step>

  <Step title="Monitor webhook events">
    Sau khi cập nhật phương thức thanh toán, giám sát các sự kiện webhook này:

    1. **`payment.succeeded`** - Phí cho các khoản ủng hộ còn lại đã thành công
    2. **`subscription.active`** - Thuê bao đã được kích hoạt lại

    ```javascript theme={null}
    if (event.type === 'payment.succeeded') {
      const payment = event.data;
      
      // Check if this payment is for an on_hold subscription
      if (payment.subscription_id) {
        // Wait for subscription.active webhook to confirm reactivation
      }
    }

    if (event.type === 'subscription.active') {
      const subscription = event.data;
      
      // Update subscription status in your database
      await updateSubscriptionStatus(subscription.subscription_id, 'active');
      
      // Restore customer access
      await restoreCustomerAccess(subscription.customer_id);
      
      // Notify customer of successful reactivation
      await sendEmailToCustomer(subscription.customer_id, {
        subject: 'Subscription Reactivated',
        message: 'Your subscription has been reactivated successfully.'
      });
    }
    ```
  </Step>
</Steps>

### Mẫu Payload sự kiện Thuê Bao

***

| Thuộc tính    | Loại   | Bắt buộc | Mô tả                                                                      |
| ------------- | ------ | -------- | -------------------------------------------------------------------------- |
| `business_id` | string | Có       | Mã định danh duy nhất cho doanh nghiệp                                     |
| `timestamp`   | string | Có       | Dấu thời gian của khi sự kiện xảy ra (không nhất thiết giống khi được gửi) |
| `type`        | string | Có       | Loại sự kiện. Xem [Loại Sự Kiện](#event-types)                             |
| `data`        | object | Có       | Payload dữ liệu chính. Xem [Đối Tượng Dữ Liệu](#data-object)               |

## Thay Đổi Kế Hoạch Thuê Bao

Bạn có thể nâng cấp hoặc hạ cấp kế hoạch thuê bao sử dụng endpoint API thay đổi kế hoạch. Điều này cho phép bạn thay đổi sản phẩm thuê bao, số lượng, và xử lý phân bổ chi phí.

<Card title="Change Plan API Reference" icon="arrows-rotate" href="/api-reference/subscriptions/change-plan">
  Để biết thông tin chi tiết về thay đổi kế hoạch thuê bao, vui lòng tham khảo tài liệu API Thay Đổi Kế Hoạch của chúng tôi.
</Card>

### Tùy Chọn Phân Bổ Chi Phí

Khi thay đổi kế hoạch thuê bao, bạn có hai tùy chọn để xử lý phí tức thì:

#### 1. `prorated_immediately`

* Tính toán số tiền phân bổ dựa trên thời gian còn lại trong chu kỳ thanh toán hiện tại
* Tính phí khách hàng chỉ cho sự khác biệt giữa kế hoạch cũ và mới
* Trong thời gian thử nghiệm, điều này sẽ chuyển người dùng ngay lập tức sang kế hoạch mới, tính phí khách hàng ngay lập tức

#### 2. `full_immediately`

* Tính phí khách hàng toàn bộ số tiền thuê bao cho kế hoạch mới
* Bỏ qua thời gian còn lại hoặc các khoản tín dụng từ kế hoạch trước đó
* Hữu ích khi bạn muốn đặt lại chu kỳ thanh toán hoặc tính phí toàn bộ số lượng bất kể phân bổ

#### 3. `difference_immediately`

* Khi nâng cấp, khách hàng ngay lập tức bị tính phí số tiền chênh lệch giữa hai kế hoạch.
* Ví dụ, nếu kế hoạch hiện tại là 30 Đô la và khách hàng nâng cấp lên 80 Đô la, họ sẽ bị tính phí 50 đô la ngay lập tức.
* Khi hạ cấp, số tiền còn lại không sử dụng từ kế hoạch hiện tại được thêm vào dưới dạng tín dụng nội bộ và tự động áp dụng cho các lần gia hạn thuê bao trong tương lai.
* Ví dụ, nếu kế hoạch hiện tại là 50 Đô la và khách hàng chuyển sang kế hoạch 20 Đô la, số còn lại 30 đô la sẽ được ghi nhận và sử dụng cho chu kỳ thanh toán tiếp theo.

### Hành Vi

* Khi bạn gọi API này, Dodo Payments sẽ ngay lập tức khởi tạo một khoản phí dựa trên tùy chọn phân bổ mà bạn đã chọn
* Nếu thay đổi kế hoạch là hạ cấp và bạn sử dụng `prorated_immediately`, các khoản tín dụng sẽ được tự động tính toán và thêm vào số dư tín dụng của thuê bao. Các khoản tín dụng này sẽ chỉ được sử dụng để giảm các khoản thanh toán định kỳ trong tương lai của cùng một thuê bao
* Tùy chọn `full_immediately` sẽ bỏ qua các tính toán tín dụng và tính phí toàn bộ số lượng kế hoạch mới

<Tip>
  **Chọn tùy chọn phân bổ của bạn một cách cẩn thận**: Sử dụng `prorated_immediately` để tính toán công bằng, tính phí thời gian chưa sử dụng, hoặc `full_immediately` khi bạn muốn tính toàn bộ số lượng kế hoạch mới bất kể chu kỳ thanh toán hiện tại.
</Tip>

### Xử Lý Thanh Toán

* Phí tức thì khởi tạo khi thay đổi kế hoạch thường sẽ hoàn tất xử lý trong vòng chưa đến 2 phút
* Nếu phí tức thì này thất bại vì bất kỳ lý do nào, thuê bao sẽ tự động bị tạm dừng cho đến khi vấn đề được giải quyết

## Thuê Bao Theo Nhu Cầu

<Info>
  Thuê bao theo nhu cầu cho phép bạn tính phí khách hàng một cách linh hoạt, không chỉ theo lịch cố định. Tính năng này có sẵn cho tất cả các tài khoản.
</Info>

**Để tạo thuê bao theo nhu cầu:**

Để tạo thuê bao theo nhu cầu, sử dụng endpoint API [POST /subscriptions](/api-reference/subscriptions/post-subscriptions) và bao gồm trường `on_demand` trong yêu cầu của bạn. Điều này cho phép bạn ủy quyền một phương thức thanh toán mà không cần tính phí ngay lập tức hoặc đặt giá ban đầu tùy chỉnh.

**Để tính phí thuê bao theo nhu cầu:**

Đối với các khoản phí tiếp theo, sử dụng endpoint [POST /subscriptions/{subscription_id}/charge](/api-reference/subscriptions/create-charge) và chỉ định số tiền để tính phí cho khách hàng cho giao dịch đó.

<Note>
  Để có hướng dẫn từng bước hoàn chỉnh—bao gồm ví dụ yêu cầu/phản hồi, chính sách thử lại an toàn, và xử lý webhook—hãy xem <a href="/developer-resources/ondemand-subscriptions">Hướng Dẫn Thuê Bao Theo Nhu Cầu</a>.
</Note>

## Tham Khảo API Liên Quan

<CardGroup cols={2}>
  <Card title="Create Subscription" icon="code" href="/api-reference/subscriptions/post-subscriptions">
    Tham khảo API tạo sản phẩm thuê bao và quản lý vòng đời thuê bao
  </Card>

  <Card title="Change Subscription Plan" icon="arrows-rotate" href="/api-reference/subscriptions/change-plan">
    Tham khảo API nâng cấp, hạ cấp, hoặc thay đổi kế hoạch thuê bao với các tùy chọn phân bổ
  </Card>

  <Card title="Update Payment Method" icon="credit-card" href="/api-reference/subscriptions/update-payment-method">
    Tham khảo API cập nhật phương thức thanh toán và kích hoạt lại thuê bao bị tạm dừng
  </Card>

  <Card title="Patch Subscription" icon="pen" href="/api-reference/subscriptions/patch-subscriptions">
    Tham khảo API cập nhật chi tiết và cấu hình thuê bao
  </Card>
</CardGroup>
