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

# Giá dựa trên số ghế với các tiện ích bổ sung và liên kết thanh toán

> Tìm hiểu cách tạo các tiện ích bổ sung cho giá dựa trên số ghế và tạo liên kết thanh toán với số lượng tiện ích bổ sung tùy chỉnh bằng Dodo Payments

Trong hướng dẫn này, bạn sẽ học cách triển khai giá dựa trên số ghế bằng cách sử dụng các tiện ích bổ sung của Dodo Payments. Chúng tôi sẽ tạo một sản phẩm đăng ký với các tiện ích bổ sung cho các ghế bổ sung và chỉ cho bạn cách tạo liên kết thanh toán với số lượng tiện ích bổ sung tùy chỉnh.

<Note>
  Hướng dẫn này cung cấp mã triển khai mẫu cho một ứng dụng Node.js/Express. Bạn có thể chỉnh sửa mã này theo khung công tác cụ thể của mình (Next.js, React, Vue, v.v.) và tùy chỉnh giao diện người dùng theo nhu cầu ứng dụng của bạn.
</Note>

Cuối cùng của hướng dẫn này, bạn sẽ biết cách:

* Tạo các sản phẩm đăng ký với giá dựa trên số ghế
* Thiết lập các tiện ích bổ sung cho các ghế bổ sung
* Tạo liên kết thanh toán với số lượng tiện ích bổ sung tùy chỉnh
* Xử lý các phiên thanh toán với số lượng ghế động

## Những gì chúng ta đang xây dựng

Hãy tạo một mô hình giá dựa trên số ghế:

* **Gói cơ bản**: 49 đô la/tháng cho tối đa 5 thành viên trong nhóm
* **Tiện ích bổ sung ghế**: 2 đô la/tháng cho mỗi ghế bổ sung
* **Liên kết thanh toán**: Thanh toán động với số lượng ghế tùy chỉnh

<Info>
  Trước khi bắt đầu, hãy đảm bảo bạn có:

  * Một tài khoản Dodo Payments
  * Sự quen thuộc cơ bản với TypeScript/Node.js
</Info>

## Bước 1: Tạo Tiện Ích Bổ Sung Ghế Của Bạn

Bây giờ chúng ta cần tạo một tiện ích bổ sung đại diện cho các ghế bổ sung. Tiện ích bổ sung này sẽ được gắn vào đăng ký cơ bản của chúng tôi và cho phép khách hàng mua thêm ghế.

<Frame>
  <img src="https://mintcdn.com/dodopayments/ajOhO6Du1yNsg0hy/images/cookbooks/seat-based/seat-based-addons-creation.png?fit=max&auto=format&n=ajOhO6Du1yNsg0hy&q=85&s=d2c17b4a9f2f9a1b19f537f0507656a4" alt="Tạo sản phẩm đăng ký cơ bản" style={{ maxHeight: '500px', width: 'auto' }} width="2348" height="1606" data-path="images/cookbooks/seat-based/seat-based-addons-creation.png" />
</Frame>

<Tip>
  **Những gì chúng ta đang xây dựng**: Một add-on có giá 2 đô la mỗi tháng cho mỗi chỗ ngồi và có thể thêm vào bất kỳ gói đăng ký cơ bản nào.
</Tip>

<Steps>
  <Step title="Navigate to Add-Ons">
    1. Trên bảng điều khiển Dodo Payments của bạn, ở lại phần **Products**
    2. Nhấp vào tab **Add-Ons**
    3. Nhấp **Create Add-On**

    Điều này sẽ mở biểu mẫu tạo add-on.
  </Step>

  <Step title="Enter add-on details">
    Điền các giá trị sau cho add-on chỗ ngồi của chúng ta:

    **Tên Add-On**: `Additional Team Seat`

    **Mô tả**: `Add extra team members to your workspace with full access to all features`

    **Giá**: Enter → `2.00`

    **Tiền tệ**: Phải khớp với tiền tệ của đăng ký cơ bản của bạn

    **Danh mục thuế**: Chọn danh mục phù hợp cho sản phẩm của bạn.
  </Step>

  <Step title="Save your add-on">
    1. Xem lại tất cả cài đặt của bạn:
       * Tên: Additional Team Seat
       * Giá: \$2.00/month
    2. Nhấp **Create Add-On**

    <Check>
      **Add-on đã được tạo!** Add-on chỗ ngồi của bạn giờ có thể được gắn vào các gói đăng ký.
    </Check>
  </Step>
</Steps>

## Bước 2: Tạo Sản Phẩm Đăng Ký Cơ Bản Của Bạn

Chúng ta sẽ bắt đầu bằng cách tạo một sản phẩm đăng ký cơ bản bao gồm 5 thành viên trong nhóm. Đây sẽ là nền tảng cho mô hình giá dựa trên số ghế của chúng ta.

<Frame>
  <img src="https://mintcdn.com/dodopayments/ajOhO6Du1yNsg0hy/images/cookbooks/seat-based/seat-based-subscription.png?fit=max&auto=format&n=ajOhO6Du1yNsg0hy&q=85&s=f17e96f3d106b88b6dc32a00be535620" alt="Tạo sản phẩm đăng ký cơ bản" style={{ maxHeight: '500px', width: 'auto' }} width="2118" height="1580" data-path="images/cookbooks/seat-based/seat-based-subscription.png" />
</Frame>

<Steps>
  <Step title="Navigate to Products">
    1. Đăng nhập vào bảng điều khiển Dodo Payments của bạn
    2. Nhấp vào **Products** trong thanh bên trái
    3. Nhấp vào nút **Create Product**
    4. Chọn **Subscription** làm loại sản phẩm

    Bạn sẽ thấy một biểu mẫu nơi chúng ta sẽ cấu hình gói đăng ký cơ bản.
  </Step>

  <Step title="Fill in the subscription details">
    Bây giờ chúng ta sẽ nhập các chi tiết cụ thể cho gói cơ bản của mình:

    **Tên sản phẩm**: `Motion`

    **Mô tả**: `Where your team's documentation lives.`

    **Giá định kỳ**: Enter → `49.00`

    **Chu kỳ thanh toán**: Select → `Monthly`

    **Đơn vị tiền tệ**: Chọn đơn vị bạn ưa thích (ví dụ, `USD`)
  </Step>
</Steps>

## Bước 3: Kết Nối Tiện Ích Bổ Sung Với Đăng Ký

Bây giờ chúng ta cần liên kết tiện ích bổ sung ghế của mình với đăng ký cơ bản để khách hàng có thể mua thêm ghế trong quá trình thanh toán.

<Steps>
  <Step title="Attach the seat add-on">
    <Frame>
      <img src="https://mintcdn.com/dodopayments/ajOhO6Du1yNsg0hy/images/cookbooks/seat-based/seat-based-addons.png?fit=max&auto=format&n=ajOhO6Du1yNsg0hy&q=85&s=ea4ce0d437201c92590eea6c31a14e80" alt="Đính kèm phần bổ sung vào đăng ký" style={{ maxHeight: '500px', width: 'auto' }} width="2338" height="1196" data-path="images/cookbooks/seat-based/seat-based-addons.png" />
    </Frame>

    1. Cuộn xuống phần **Tiện Ích Bổ Sung**
    2. Nhấp **Thêm Tiện Ích Bổ Sung**
    3. Từ menu thả xuống, chọn tiện ích bổ sung ghế của bạn
    4. Xác nhận rằng nó xuất hiện trong cấu hình đăng ký của bạn
  </Step>

  <Step title="Save subscription changes">
    1. Xem lại toàn bộ thiết lập đăng ký của bạn:
       * Gói cơ bản: \$49/month cho 5 chỗ ngồi
       * Add-on: \$2/month cho mỗi chỗ ngồi bổ sung
       * Dùng thử miễn phí: 14 ngày
    2. Nhấp **Save Changes**

    <Check>
      **Định giá theo chỗ ngồi đã được cấu hình!** Khách hàng giờ có thể mua gói cơ bản và thêm chỗ ngồi tùy ý.
    </Check>
  </Step>
</Steps>

## Bước 4: Tạo Liên Kết Thanh Toán Với Số Lượng Tiện Ích Bổ Sung Tùy Chỉnh

Bây giờ hãy tạo một ứng dụng Express.js để tạo liên kết thanh toán với số lượng tiện ích bổ sung tùy chỉnh. Đây là nơi sức mạnh thực sự của giá dựa trên số ghế xuất hiện - bạn có thể tạo các phiên thanh toán động với bất kỳ số lượng ghế bổ sung nào.

<Steps>
  <Step title="Set up your project">
    Tạo một dự án Node.js mới và cài đặt các phụ thuộc cần thiết:

    ```bash theme={null}
    mkdir seat-based-pricing
    cd seat-based-pricing
    npm init -y
    npm install dodopayments express dotenv
    npm install -D @types/node @types/express typescript ts-node
    ```

    Tạo một file `tsconfig.json`:

    ```json theme={null}
    {
      "compilerOptions": {
        "target": "ES2020",
        "module": "commonjs",
        "outDir": "./dist",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
      }
    }
    ```
  </Step>

  <Step title="Create your environment file">
    Tạo một file `.env` với API key Dodo Payments của bạn:

    ```bash theme={null}
    DODO_PAYMENTS_API_KEY=your_actual_dodo_api_key_here
    ```

    <Warning>
      Không bao giờ commit API key của bạn vào hệ thống điều khiển phiên bản. Thêm `.env` vào file `.gitignore` của bạn.
    </Warning>
  </Step>

  <Step title="Implement the checkout session creation">
    Tạo một file `src/server.ts` với mã sau:

    <CodeGroup>
      ```typescript server.ts expandable theme={null}
      // Add this new endpoint for dynamic seat quantities
      import 'dotenv/config';
      import DodoPayments from 'dodopayments';
      import express, { Request, Response } from 'express';

      const app = express();

      // Initialize the Dodo Payments client
      const client = new DodoPayments({
        bearerToken: process.env.DODO_PAYMENTS_API_KEY,
        environment: 'test_mode'
      });

      async function createCheckoutSession(seatCount: number) {
        try {
          const session = await client.checkoutSessions.create({
            // Products to sell - use IDs from your Dodo Payments dashboard
            product_cart: [
              {
                product_id: 'pdt_7Rl9OWT2Mz4wwUTKz74iZ', // Replace with your actual product ID
                quantity: 1,
                addons: [
                  {
                    addon_id: 'adn_eKQbNakKrivDpaxmI8wKI', // Replace with your actual addon ID
                    quantity: seatCount
                  }
                ]
              }
            ],
            
            // Pre-fill customer information to reduce friction
            customer: {
              email: 'steve@example.com',
              name: 'Steve Irwin',
            },
            // Where to redirect after successful payment
            return_url: 'https://example.com/checkout/success',
          });

          // Redirect your customer to this URL to complete payment
          console.log('Checkout URL:', session.checkout_url);
          console.log('Session ID:', session.session_id);
          
          return session;
          
        } catch (error) {
          console.error('Failed to create checkout session:', error);
          throw error;
        }
      }

      // Example usage in an Express.js route
      app.post('/create-checkout/:seatCount', async (req: Request, res: Response) => {
        try {
          const seatCount = parseInt(req.params.seatCount);
          const session = await createCheckoutSession(seatCount);
          res.json({ checkout_url: session.checkout_url });
        } catch (error) {
          res.status(500).json({ error: 'Failed to create checkout session' });
        }
      });

      // Add this line after your other middleware
      app.use(express.static('public'));

      // Add this route to serve the demo page
      app.get('/', (req, res) => {
        res.sendFile(__dirname + '/../public/index.html');
      });

      app.listen(3000, () => {
        console.log('Server is running on port 3000');
      });
      ```
    </CodeGroup>
  </Step>

  <Step title="Add a simple web interface">
    Tạo một file `public/index.html` để dễ kiểm tra:

    <CodeGroup>
      ```html index.html expandable theme={null}
      <!DOCTYPE html>
      <html>
      <head>
          <title>Seat-Based Pricing Demo</title>
          <style>
              body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }
              .form-group { margin: 20px 0; }
              label { display: block; margin-bottom: 5px; font-weight: bold; }
              input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }
              button { background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
              button:hover { background: #0056b3; }
              .result { margin-top: 20px; padding: 15px; background: #f8f9fa; border-radius: 4px; }
          </style>
      </head>
      <body>
          <h1>Seat-Based Pricing Demo</h1>
          <p>Generate checkout links with custom seat quantities:</p>
          
          <div class="form-group">
              <label for="seatCount">Number of Additional Seats:</label>
              <input type="number" id="seatCount" value="3" min="0" max="50">
          </div>
          
          <button onclick="createCheckout()">Generate Checkout Link</button>
          
          <div id="result" class="result" style="display: none;">
              <h3>Checkout Link Generated!</h3>
              <p><strong>Seat Count:</strong> <span id="seatCountDisplay"></span></p>
              <p><strong>Total Cost:</strong> $<span id="totalCost"></span>/month</p>
              <p><strong>Checkout URL:</strong></p>
              <a id="checkoutUrl" href="#" target="_blank">Click here to checkout</a>
          </div>

          <script>
              async function createCheckout() {
                  const seatCount = document.getElementById('seatCount').value;
                  
                  try {
                      const response = await fetch(`/create-checkout/${seatCount}`, {
                          method: 'POST'
                      });
                      
                      const data = await response.json();
                      
                      if (response.ok) {
                          document.getElementById('seatCountDisplay').textContent = seatCount;
                          document.getElementById('totalCost').textContent = data.total_cost;
                          document.getElementById('checkoutUrl').href = data.checkout_url;
                          document.getElementById('result').style.display = 'block';
                      } else {
                          alert('Error: ' + data.error);
                      }
                  } catch (error) {
                      alert('Error creating checkout session');
                  }
              }
          </script>
      </body>
      </html>
      ```
    </CodeGroup>

    <Check>
      **Giao diện web đã được tạo!** Bạn giờ có một giao diện đơn giản để thử các số lượng chỗ ngồi khác nhau.
    </Check>
  </Step>

  <Step title="Serve static files">
    Thêm đoạn này vào `src/server.ts` của bạn để phục vụ file HTML:

    ```typescript theme={null}
    // Add this line after your other middleware
    app.use(express.static('public'));

    // Add this route to serve the demo page
    app.get('/', (req, res) => {
      res.sendFile(__dirname + '/../public/index.html');
    });
    ```

    <Check>
      **Các tệp tĩnh đã được cấu hình!** Truy cập `http://localhost:3000` để xem giao diện demo của bạn.
    </Check>
  </Step>
</Steps>

## Bước 5: Kiểm Tra Triển Khai Của Bạn

Hãy kiểm tra triển khai giá dựa trên số ghế của chúng ta để đảm bảo mọi thứ hoạt động chính xác.

<Steps>
  <Step title="Start your server">
    1. Đảm bảo bạn có file `.env` chứa API key chính xác
    2. Cập nhật ID sản phẩm và add-on trong mã của bạn bằng các giá trị thực từ bảng điều khiển Dodo Payments
    3. Khởi động server của bạn:

    ```bash theme={null}
    npm run dev
    ```

    <Check>
      Server của bạn nên khởi động thành công và hiển thị "Server running on [http://localhost:3000](http://localhost:3000)"
    </Check>
  </Step>

  <Step title="Test the web interface">
    <Frame>
      <img src="https://mintcdn.com/dodopayments/ajOhO6Du1yNsg0hy/images/cookbooks/seat-based/seat-based-billing-ui.png?fit=max&auto=format&n=ajOhO6Du1yNsg0hy&q=85&s=37d166d8e76b0bfbb4959bc9a3eff1f8" alt="Tạo sản phẩm đăng ký cơ bản" style={{ maxHeight: '500px', width: 'auto' }} width="1806" height="1416" data-path="images/cookbooks/seat-based/seat-based-billing-ui.png" />
    </Frame>

    1. Mở trình duyệt của bạn và truy cập `http://localhost:3000`
    2. Bạn sẽ thấy giao diện demo định giá theo chỗ ngồi
    3. Thử các số lượng chỗ ngồi khác nhau (0, 3, 10, v.v.)
    4. Nhấp "Generate Checkout Link" cho mỗi số lượng
    5. Xác nhận rằng các URL thanh toán được tạo đúng cách
  </Step>

  <Step title="Test a checkout session">
    1. Tạo một liên kết thanh toán với 3 chỗ ngồi bổ sung
    2. Nhấp vào URL thanh toán để mở trang checkout của Dodo Payments
    3. Xác nhận rằng trang thanh toán hiển thị:
       * Gói cơ bản: \$49/month
       * Chỗ ngồi bổ sung: 3 × 2 dollars = \$6/month
    4. Hoàn tất mua hàng thử nghiệm

    <Check>
      Trang thanh toán nên hiển thị chi tiết giá đúng và cho phép bạn hoàn tất giao dịch.
    </Check>
  </Step>

  <Step title="Listen for webhooks and update your DB">
    Để giữ cho cơ sở dữ liệu của bạn đồng bộ với các thay đổi đăng ký và chỗ ngồi, bạn cần lắng nghe các sự kiện webhook từ Dodo Payments. Webhook thông báo cho backend khi khách hàng hoàn tất thanh toán, cập nhật đăng ký hoặc thay đổi số lượng chỗ ngồi.

    Theo hướng dẫn webhook chính thức của Dodo Payments để có hướng dẫn từng bước về cách thiết lập các điểm cuối webhook và xử lý các sự kiện:

    <Card title="Dodo Payments Webhooks Documentation" icon="bell" href="/developer-resources/webhooks">
      Tìm hiểu cách nhận và xử lý an toàn các sự kiện webhook cho việc quản lý đăng ký và chỗ ngồi.
    </Card>
  </Step>
</Steps>

## Khắc Phục Sự Cố

Các vấn đề phổ biến và giải pháp của chúng:

<AccordionGroup>
  <Accordion title="Checkout creation failing">
    **Nguyên nhân có thể:**

    * ID sản phẩm hoặc ID add-on không hợp lệ
    * API key không có quyền hạn đầy đủ
    * Add-on chưa được liên kết đúng với đăng ký
    * Sự cố kết nối mạng

    **Giải pháp:**

    1. Xác nhận ID sản phẩm và add-on tồn tại trong bảng điều khiển Dodo Payments
    2. Kiểm tra xem add-on đã được gắn đúng vào đăng ký chưa
    3. Đảm bảo API key có quyền tạo phiên thanh toán
    4. Kiểm tra kết nối API bằng một yêu cầu GET đơn giản
  </Accordion>
</AccordionGroup>

## Chúc mừng! Bạn đã Triển Khai Giá Dựa Trên Số Ghế

Bạn đã thành công trong việc tạo một hệ thống giá dựa trên số ghế với Dodo Payments! Đây là những gì bạn đã hoàn thành:

<CardGroup cols={2}>
  <Card title="Base Subscription">
    Đã tạo một sản phẩm đăng ký với 5 chỗ ngồi bao gồm và giá 49 đô la/tháng
  </Card>

  <Card title="Seat Add-ons">
    Đã cấu hình các add-on cho chỗ ngồi bổ sung với giá 2 đô la/tháng mỗi chỗ
  </Card>

  <Card title="Checkout">
    Đã xây dựng một API tạo các phiên thanh toán với số lượng chỗ ngồi tùy chỉnh
  </Card>

  <Card title="Web Interface">
    Đã tạo một giao diện web đơn giản để thử các số lượng chỗ ngồi khác nhau
  </Card>
</CardGroup>

<Info>
  Ví dụ này chỉ minh họa một triển khai tối thiểu của định giá theo chỗ ngồi. Để sử dụng trong môi trường sản xuất, bạn nên bổ sung xử lý lỗi chắc chắn, xác thực, kiểm tra dữ liệu, biện pháp bảo mật và điều chỉnh logic sao cho phù hợp với yêu cầu của ứng dụng.
</Info>
