# Tích hợp API (/vi/online/integration/api_integration)





Backend của bạn tạo một phiên Checkout cho mỗi đơn hàng và truy xuất phiên đó để xác minh thanh toán.

Với Node.js, [SDK `@tapayadot/checkout`](/online/integration/node_sdk) cung cấp các phương thức có kiểu `create()` và `get()` cho các endpoint này. SDK mặc định đọc `TAPAYA_SECRET_KEY`. Các ví dụ HTTP trên trang này truyền `TAPAYA_CHECKOUT_API_KEY` một cách tường minh.

## Xác thực [#authentication]

Mọi lệnh gọi Checkout đều là giao tiếp giữa máy chủ với máy chủ và gửi key dưới dạng bearer token:

```http
Authorization: Bearer <key>
```

Chọn chế độ phù hợp với bên vận hành backend. Dù dùng key nào, hãy giữ nó trên máy chủ; xem [Bảo mật Checkout](/online/security/checkout_security).

<Cards>
  <VisualCard title="Chế độ merchant" visual="<Store />" href="#merchant-mode" description="Một cửa hàng. Checkout key của merchant tự xác định merchant của nó, không cần header bổ sung." />

  <VisualCard title="Chế độ nền tảng" visual="<Building2 />" href="#platform-mode" description="Nhiều merchant. Secret key của tổ chức, kèm một merchant token trong mỗi yêu cầu." />
</Cards>

### Chế độ merchant [#merchant-mode]

Dùng chế độ này khi backend phục vụ một cửa hàng. Tạo một **checkout key của merchant** trong [Cài đặt Checkout](/online/integration/checkout_settings#create-merchant-checkout-keys): mở merchant và chọn **Create merchant key**. Key bắt đầu bằng `tapaya_test_` trong sandbox hoặc `tapaya_live_` trong production và chỉ ứng với đúng merchant đó.

```http
Authorization: Bearer <merchant-checkout-key>
```

`X-Tapaya-Merchant-Token` là tùy chọn ở chế độ này. Nếu bạn gửi header này, nó phải chỉ đúng merchant đó.

Platform chỉ hiển thị key một lần và chỉ lưu bản hash. Để xoay vòng key, hãy tạo key thay thế trước khi thu hồi key cũ. Việc thu hồi một key không ảnh hưởng đến các key khác của merchant, các key của tổ chức hoặc các client secret đã được cấp cho trình duyệt.

### Chế độ nền tảng [#platform-mode]

Dùng chế độ này khi một backend hoạt động thay mặt nhiều merchant, chẳng hạn một sàn thương mại điện tử hoặc nền tảng giao đồ ăn. Gửi Secret key của tổ chức từ [trang API Keys](https://platform.tapaya.com/api-keys/) và token của merchant trong **mọi** yêu cầu:

```http
Authorization: Bearer <secret-key>
X-Tapaya-Merchant-Token: <merchant-token>
```

Không có merchant mặc định. Yêu cầu không có header này sẽ trả về HTTP 401. Merchant phải thuộc tổ chức của key. Sao chép merchant token từ bảng merchant trong [Cài đặt Checkout](/online/integration/checkout_settings). Token dùng để xác định merchant; nó không phải là thông tin bí mật.

Secret key của tổ chức chính là thông tin xác thực được gọi là Server Secret Token trong [tài liệu Platform API](/platform-api#authentication). Checkout yêu cầu tiền tố `Bearer`. Các endpoint khác của Platform API nhận token trực tiếp. Onboarding merchant và quản lý tổ chức dùng [Platform API](/platform-api).

Phần thân yêu cầu không chấp nhận `merchantId`.

## Môi trường [#environments]

| Môi trường | Origin API                       | Origin Hosted Checkout                | Thông tin xác thực                                                 |
| ---------- | -------------------------------- | ------------------------------------- | ------------------------------------------------------------------ |
| Sandbox    | `https://api.sandbox.tapaya.com` | `https://checkout.sandbox.tapaya.com` | Key sandbox, với [thẻ thử nghiệm](/online/getting-started/testing) |
| Production | `https://api.tapaya.com`         | `https://checkout.tapaya.com`         | Key chính thức, với phương thức thanh toán thật                    |

Đặt `TAPAYA_CHECKOUT_API_URL` thành origin API của môi trường bạn dùng. Cả Hosted Checkout lẫn thanh toán nhúng đều khả dụng trên production. Mỗi merchant phải hoàn tất [onboarding](/merchants/onboarding), được đơn vị xử lý thanh toán trực tuyến phê duyệt và có cổng thanh toán đã được cấu hình. Kiểm tra trạng thái sẵn sàng của merchant trong [Cài đặt Checkout](/online/integration/checkout_settings). Dùng nguyên URL phiên Hosted Checkout được trả về.

## Phiên Checkout [#checkout-sessions]

<div className="fd-steps [&_h3]:fd-step">
  ### Tạo phiên Checkout [#create-a-checkout-session]

  Tạo một phiên khi khách hàng nhấn **Checkout** trong cửa hàng của bạn, sau đó chuyển hướng họ đến `url` được trả về.
  Trước tiên, hãy tính tổng tiền cuối cùng trên máy chủ và lưu đơn hàng cùng một idempotency key duy nhất.

  **Endpoint:** `POST /merchant/checkout-sessions`

  <CodeBlockTabs defaultValue="curl" groupId="http-client">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="curl">
        cURL
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="curl">
      ```bash
      curl -X 'POST' "$TAPAYA_CHECKOUT_API_URL/merchant/checkout-sessions" \
      -H 'Content-Type: application/json' \
      -H "Authorization: Bearer $TAPAYA_CHECKOUT_API_KEY" \
      -H "X-Tapaya-Merchant-Token: $TAPAYA_MERCHANT_TOKEN" \
      -H 'Idempotency-Key: order-1001-checkout-1' \
      -d '{
              "merchantOrderId": "order-1001",
              "amount": 12100,
              "currency": "EUR",
              "tax": 2100,
              "shipping": 605,
              "taxRate": 21,
              "successUrl": "https://shop.example/checkout/return",
              "cancelUrl": "https://shop.example/checkout/cancel"
          }'
      ```
    </CodeBlockTab>
  </CodeBlockTabs>

  **Các header:**

  | Header                    | Bắt buộc        | Mô tả                                                                              |
  | ------------------------- | --------------- | ---------------------------------------------------------------------------------- |
  | `Authorization`           | ✓               | `Bearer <key>`: checkout key của merchant hoặc Secret key của tổ chức.             |
  | `X-Tapaya-Merchant-Token` | Chế độ nền tảng | Merchant token thuộc tổ chức của key. Tùy chọn khi dùng checkout key của merchant. |
  | `Idempotency-Key`         | ✓               | Không rỗng, tối đa 250 ký tự. Xem [Idempotency và lỗi](#idempotency-and-errors).   |

  **Tham số:**

  | Trường            | Kiểu      | Bắt buộc | Mô tả                                                                                                                             |
  | ----------------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
  | `merchantOrderId` | `string`  | ✓        | Mã tham chiếu đơn hàng của bạn, từ 1 đến 200 ký tự. Xem [Một khoản thanh toán cho mỗi đơn hàng](#one-payment-per-order).          |
  | `amount`          | `integer` | ✓        | Tổng số tiền cuối cùng sẽ được trừ, tính bằng đơn vị nhỏ nhất của tiền tệ, bao gồm thuế và phí giao hàng. Từ 1 đến 2.147.483.647. |
  | `currency`        | `string`  | ✓        | Mã ISO 4217 gồm ba chữ cái được backend hỗ trợ, viết hoa, ví dụ `EUR`.                                                            |
  | `locale`          | `string`  |          | Mặc định là `en`.                                                                                                                 |
  | `successUrl`      | `string`  |          | Ghi đè URL thành công mặc định cho phiên này. Phải khớp với một website được phép của thông tin xác thực gọi API.                 |
  | `cancelUrl`       | `string`  |          | Ghi đè URL hủy mặc định cho phiên này. Phải khớp với một website được phép của thông tin xác thực gọi API.                        |
  | `tax`             | `integer` |          | Thuế VAT đã bao gồm trong `amount`, từ 0 đến `amount`. Mặc định là `0`.                                                           |
  | `shipping`        | `integer` |          | Phí giao hàng đã bao gồm trong `amount`, từ 0 đến `amount`. Mặc định là `0`.                                                      |
  | `taxRate`         | `number`  |          | Phần trăm VAT áp dụng cho toàn đơn hàng, ví dụ `21` hoặc `5.5`. Không âm.                                                         |
  | `taxBreakdown`    | `array`   |          | VAT được nhóm theo thuế suất. Một mảng không rỗng không thể đi kèm `taxRate`.                                                     |
  | `items`           | `array`   |          | Các mặt hàng của đơn hàng, chỉ để hiển thị. Không quyết định tổng số tiền được trừ.                                               |
  | `customer`        | `object`  |          | Email, địa chỉ thanh toán và địa chỉ giao hàng của khách hàng.                                                                    |

  Phiên trừ tiền bằng loại tiền tệ `currency` mà bạn đặt. [Loại tiền tệ được hỗ trợ](/merchants/supported_currencies) liệt kê mọi
  loại tiền tệ mà Tapaya hỗ trợ, nhưng một merchant thường chỉ được kích hoạt cho một số loại trong đó. Việc tạo phiên bằng một
  loại tiền tệ mà merchant hoặc backend không hỗ trợ sẽ bị từ chối, nên không có trang Hosted Checkout nào được tạo.

  API trừ đúng `amount` như được cung cấp. API không cộng thêm phí giao hàng hay thuế, không tính VAT từ thuế suất, và không tính
  tổng tiền từ các mặt hàng.

  <Accordions>
    <Accordion title="Các mục taxBreakdown">
      Mỗi mục chứa một `taxRate` dạng số và một `tax` dạng số nguyên, cả hai đều không âm. Các thuế suất là duy nhất và
      tổng số thuế của các mục bằng `tax` ở cấp cao nhất.

      ```json
      {
          "amount": 22650,
          "currency": "EUR",
          "tax": 2650,
          "shipping": 605,
          "taxBreakdown": [
              { "taxRate": 21, "tax": 2100 },
              { "taxRate": 5.5, "tax": 550 }
          ]
      }
      ```
    </Accordion>

    <Accordion title="Các mục items">
      Mỗi mặt hàng yêu cầu `reference`, `name`, `description`, `imageUrl`, `quantity` và `unitAmount`. Các trường
      văn bản không được rỗng, `imageUrl` là một URL, `quantity` là số nguyên dương, và `unitAmount` là
      số nguyên không âm tính bằng đơn vị nhỏ nhất của tiền tệ.
    </Accordion>

    <Accordion title="Các trường customer">
      Đối tượng `customer` chấp nhận `email`, `billingAddress` và `shippingAddress`. Mọi trường đều là tùy chọn,
      mọi trường địa chỉ cũng vậy, nên một địa chỉ thanh toán có thể chỉ chứa mã bưu chính và quốc gia.

      | Trường                   | Giới hạn hoặc định dạng                               |
      | ------------------------ | ----------------------------------------------------- |
      | `email`                  | Địa chỉ email, tối đa 254 ký tự.                      |
      | `name`, `line1`, `line2` | Mỗi trường tối đa 200 ký tự.                          |
      | `phone`                  | Tối đa 30 ký tự.                                      |
      | `city`, `state`          | Mỗi trường tối đa 100 ký tự.                          |
      | `postalCode`             | Tối đa 20 ký tự.                                      |
      | `country`                | Mã quốc gia ISO gồm hai chữ cái viết hoa, ví dụ `CZ`. |

      Văn bản địa chỉ được cung cấp không được rỗng. Có thể bỏ qua các trường mà bạn không có thông tin. Backend lưu lại (snapshot) thông tin
      khách hàng được cung cấp để làm thông tin đánh giá rủi ro cho cổng thanh toán. Thông tin khách hàng không có trong các phản hồi
      của phiên.
    </Accordion>
  </Accordions>

  **Phản hồi:**

  | Mã    | Mô tả                                                                                                                                                                       |
  | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `200` | Phiên đã được tạo, hoặc cùng phiên đó được trả về với trạng thái hiện tại khi key, phần thân và chế độ thông tin xác thực được lặp lại                                      |
  | `409` | Xung đột: key được dùng lại với phần thân hoặc chế độ thông tin xác thực đã thay đổi (`API-0025`), hoặc đơn hàng đã được thanh toán hay đang có khoản thanh toán được xử lý |

  **Phần thân phản hồi:**

  ```json
  {
      "id": "cs_7Hq2x9LmR4",
      "merchantOrderId": "order-1001",
      "url": "https://checkout.sandbox.tapaya.com/c/cs_7Hq2x9LmR4",
      "status": "open",
      "paymentStatus": "unpaid",
      "amount": 12100,
      "currency": "EUR",
      "tax": 2100,
      "shipping": 605,
      "taxRate": 21,
      "taxBreakdown": [],
      "items": [],
      "expiresAt": "2026-07-15T12:30:00Z",
      "createdAt": "2026-07-15T12:00:00Z"
  }
  ```

  `taxRate` chỉ có mặt khi được cung cấp. Lưu `id` cùng với đơn hàng, sau đó chuyển trình duyệt đến `url` bằng
  HTTP 303. Một phiên nhận thanh toán mới trong 30 phút kể từ khi tạo; một khoản thanh toán đã được gửi vẫn được
  hoàn tất sau thời điểm đó. URL trả về được chọn và kiểm tra khi tạo phiên, dựa trên giá trị trong yêu cầu hoặc
  giá trị mặc định của thông tin xác thực gọi API: [đích trả về](/online/integration/checkout_settings#configure-return-destinations) của tổ chức
  cho Secret key của tổ chức kèm `X-Tapaya-Merchant-Token`, và đích trả về của merchant cho checkout key của merchant. Hai
  cấu hình này không bao giờ được kết hợp.

  ### Truy xuất phiên [#retrieve-a-session]

  Khi khách hàng quay lại URL thành công hoặc URL hủy của bạn, hoặc từ một tác vụ đối soát, hãy truy xuất phiên và
  so sánh với đơn hàng đã lưu trước khi hoàn tất đơn hàng.

  **Endpoint:** `GET /merchant/checkout-sessions/{id}`

  <CodeBlockTabs defaultValue="curl" groupId="http-client">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="curl">
        cURL
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="curl">
      ```bash
      curl "$TAPAYA_CHECKOUT_API_URL/merchant/checkout-sessions/cs_7Hq2x9LmR4" \
      -H "Authorization: Bearer $TAPAYA_CHECKOUT_API_KEY" \
      -H "X-Tapaya-Merchant-Token: $TAPAYA_MERCHANT_TOKEN"
      ```
    </CodeBlockTab>
  </CodeBlockTabs>

  Không có hậu tố `/status`. Phản hồi chứa định danh phiên, URL, các tổng tiền, các trường thuế, trạng thái và
  dấu thời gian, cùng với `paymentReference` và `completedAt` có thể là null. `paymentReference` xác định lần thử
  xác nhận gần nhất và là null khi chưa có lần thử nào. Một tham chiếu khác null không chứng minh thanh toán thành công.
  Dấu thời gian là chuỗi ISO 8601.

  **Trạng thái phiên:**

  | `status`    | Ý nghĩa                                                                                                                       |
  | ----------- | ----------------------------------------------------------------------------------------------------------------------------- |
  | `open`      | Phiên sẵn sàng để thanh toán, hoặc kết quả của một khoản thanh toán đã gửi vẫn đang được xác nhận.                            |
  | `completed` | Phiên đã hoàn tất. Kiểm tra `paymentStatus` để biết kết quả thanh toán.                                                       |
  | `expired`   | Phiên không còn khả dụng cho khoản thanh toán mới: phiên đã hết hạn, hoặc một phiên mới hơn cho cùng đơn hàng đã thay thế nó. |

  <a id="payment-status" />

  **Trạng thái thanh toán:**

  | `paymentStatus` | Ý nghĩa đối với đơn hàng                                                                                     |
  | --------------- | ------------------------------------------------------------------------------------------------------------ |
  | `unpaid`        | Chưa có khoản thanh toán thành công nào được xác nhận. Kết quả có thể vẫn đang được xác nhận.                |
  | `successful`    | Thanh toán thành công. Đối soát mã tham chiếu đơn hàng, số tiền và loại tiền tệ trước khi hoàn tất đơn hàng. |
  | `failed`        | Thanh toán bị từ chối. Khách hàng có thể thử thẻ hoặc ví khác khi phiên còn mở.                              |
  | `cancelled`     | Thanh toán đã bị hủy.                                                                                        |
  | `action_needed` | Cần thực hiện thêm thao tác. Không hoàn tất đơn hàng như đã thanh toán.                                      |

  Thẻ bị từ chối khiến `status` vẫn là `open` và `paymentStatus` là `failed`. Trong khi Tapaya xác nhận một kết quả
  chưa chắc chắn ở chế độ nền, phiên vẫn ở trạng thái `open` và `unpaid`; hãy truy xuất lại sau. Việc mở liên kết hủy trên
  trang Hosted Checkout chỉ là điều hướng đến `cancelUrl`; bản thân nó không chứng minh trạng thái thanh toán `cancelled`.

  <Callout title="Xác minh trên máy chủ của bạn" type="warn">
    Chỉ hoàn tất đơn hàng khi phiên được truy xuất khớp với mã tham chiếu, số tiền và loại tiền tệ của đơn hàng đã lưu, và
    `paymentStatus` là `successful`. Chỉ riêng một lần chuyển hướng trình duyệt không bao giờ chứng minh việc thanh toán.
  </Callout>
</div>

## Quyền quản lý đơn hàng và thanh toán [#order-and-payment-ownership]

| Máy chủ của bạn quản lý                                                   | Tapaya quản lý                                          |
| ------------------------------------------------------------------------- | ------------------------------------------------------- |
| Mã tham chiếu đơn hàng, giá, loại tiền tệ và trạng thái hoàn tất đơn hàng | Phiên checkout và kết quả thanh toán từ cổng thanh toán |

ID phiên checkout xác định một phiên Hosted Checkout. `merchantOrderId` của phiên liên kết nó với đơn hàng của bạn, còn
`paymentReference` xác định lần thử xác nhận gần nhất. Hãy lưu ID phiên cùng với đơn hàng để bạn có thể
truy xuất kết quả ngay cả khi khách hàng đóng trình duyệt.

Trang Hosted Checkout dùng các endpoint công khai riêng để tải và xác nhận phiên. ID phiên của trang là thông tin xác thực truy cập
cho các endpoint đó. Một tích hợp của merchant xác minh thanh toán qua endpoint merchant yêu cầu đăng nhập.

### Một khoản thanh toán cho mỗi đơn hàng [#one-payment-per-order]

Hosted Checkout và [Embedded Checkout](/online/integration/embedded_checkout) dùng chung một công cụ xử lý thanh toán. Một merchant có thể
có tối đa một khoản thanh toán thành công cho mỗi `merchantOrderId`. ID đơn hàng có phạm vi theo merchant, nên các
merchant khác nhau, kể cả khi thuộc cùng một tổ chức, có thể dùng cùng ID.

* Việc tạo phiên cho một đơn hàng chưa thanh toán sẽ thay thế phiên chưa thanh toán trước đó. URL cũ báo `expired` và
  không thể nhận thanh toán nữa.
* Việc tạo phiên cho một đơn hàng đã được thanh toán, hoặc đang có khoản thanh toán được xử lý, sẽ trả về HTTP 409.
* Sau khi bị từ chối, khách hàng có thể thử thẻ hoặc ví khác trong cùng phiên. Việc gửi lại cùng một token thẻ
  không bao giờ trừ tiền hai lần.
* Tapaya thiết lập chính sách 3-D Secure và luôn cố gắng thực hiện xác thực.

## Idempotency và lỗi [#idempotency-and-errors]

Việc tạo phiên yêu cầu một `Idempotency-Key` không rỗng, dài tối đa 250 ký tự. Với cùng một merchant:

| Bạn gửi                                                                                                                                                           | Tapaya trả về                                                             |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| Cùng key, phần thân và chế độ thông tin xác thực                                                                                                                  | Cùng phiên đó, với `status` và `paymentStatus` hiện tại                   |
| Cùng key với phần thân đã thay đổi                                                                                                                                | HTTP 409 với mã `API-0025`                                                |
| Cùng key và phần thân nhưng với chế độ thông tin xác thực còn lại                                                                                                 | HTTP 409 với mã `API-0025`, vì mỗi chế độ dùng cài đặt chuyển hướng riêng |
| Tổng tiền không hợp lệ, loại tiền tệ không được hỗ trợ, đích chuyển hướng không hợp lệ, thiếu cấu hình cổng thanh toán, hoặc merchant chưa được thêm vào Checkout | Yêu cầu tạo phiên bị từ chối                                              |

Thẻ bị từ chối là một kết quả thanh toán, không phải bằng chứng cho thấy yêu cầu HTTP đã thất bại.

<Callout title="Kết quả chưa xác định" type="warn">
  Lỗi mạng hoặc hết thời gian chờ khiến kết quả thanh toán chưa được xác định. Hiện chưa có webhook cho Checkout: hãy đối soát bằng cách
  truy xuất phiên từ máy chủ của bạn, và chỉ thử lại việc tạo phiên với cùng key và phần thân.
</Callout>
