# Node.js SDK (/vi/online/integration/node_sdk)



`@tapayadot/checkout` là SDK TypeScript phía máy chủ cho Tapaya Checkout. Tài liệu tham khảo này mô tả phiên bản **0.3.0**.

| Chi tiết               | Giá trị                                                                                  |
| ---------------------- | ---------------------------------------------------------------------------------------- |
| **Gói**                | `@tapayadot/checkout`                                                                    |
| **Phiên bản**          | 0.3.0                                                                                    |
| **Môi trường chạy**    | Node.js 22.13.0 trở lên, ES modules, đã kèm khai báo TypeScript                          |
| **Script trình duyệt** | Tapaya.js 0.2.0, được tải cho [Embedded Checkout](/online/integration/embedded_checkout) |
| **Mã nguồn**           | [tapayadot/checkout-node](https://github.com/tapayadot/checkout-node)                    |

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      Bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```sh
    npm install @tapayadot/checkout
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```sh
    pnpm add @tapayadot/checkout
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```sh
    bun add @tapayadot/checkout
    ```
  </CodeBlockTab>
</CodeBlockTabs>

```ts
import { Tapaya } from '@tapayadot/checkout';

const tapaya = new Tapaya();
```

Client đọc `TAPAYA_SECRET_KEY` từ môi trường máy chủ. Gói này cũng chứa trình tải cho trình duyệt (browser loader) và React hook cho [Embedded Checkout](/online/integration/embedded_checkout). Trình tải lấy Tapaya.js **0.2.0** từ `https://js.tapaya.com/checkout/0.2.0/checkout.js`; phiên bản script trình duyệt độc lập với phiên bản của gói này. Gói không tải tệp `.env`. Hãy giữ key và client trong mã chỉ chạy trên máy chủ. [Hướng dẫn Hosted Checkout](/online/integration/hosted_checkout) trình bày cách tạo đơn hàng, chuyển hướng khách hàng và xác minh thanh toán.

## Cấu hình [#configuration]

```ts
const tapaya = new Tapaya(apiKey, {
	environment: 'sandbox',
	timeoutMs: 30_000,
	maxRetries: 2,
})
```

| Tùy chọn                   | Mặc định                                                                                                   | Mô tả                                                                                                                                                                                                  |
| -------------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `apiKey` (đối số đầu tiên) | `TAPAYA_SECRET_KEY`                                                                                        | Checkout key của merchant, hoặc Secret key của tổ chức ở chế độ nền tảng.                                                                                                                              |
| `environment`              | `TAPAYA_ENVIRONMENT`, sau đó là `production` với key `tapaya_live_` và `sandbox` trong các trường hợp khác | `sandbox` (`https://api.sandbox.tapaya.com`) hoặc `production` (`https://api.tapaya.com`). Key chính thức dùng ngoài production, hoặc key thử nghiệm dùng trong production, sẽ gây lỗi khi tạo client. |
| `baseUrl`                  | `TAPAYA_BASE_URL`, sau đó là URL được chọn theo `environment`                                              | Ghi đè URL API cho các lệnh gọi Hosted Checkout và thanh toán nhúng.                                                                                                                                   |
| `timeoutMs`                | `30000`                                                                                                    | Thời gian chờ cho mỗi lần thử, tính bằng mili giây.                                                                                                                                                    |
| `merchantToken`            | Không có                                                                                                   | Merchant token, được gửi dưới dạng `X-Tapaya-Merchant-Token`. Bắt buộc ở chế độ nền tảng.                                                                                                              |
| `maxRetries`               | `2`                                                                                                        | Số lần thử lại sau lỗi mạng, hết thời gian chờ, HTTP 429, 502, 503 hoặc 504, hoặc khi một yêu cầu tạo phiên vẫn đang được xử lý.                                                                       |
| `fetch`                    | `fetch` toàn cục                                                                                           | Triển khai `fetch` tùy chỉnh, ví dụ để thêm proxy hoặc công cụ giám sát.                                                                                                                               |

Để dùng một endpoint API tùy chỉnh, hãy đặt `TAPAYA_BASE_URL=https://api.example.com` trên máy chủ của bạn.
`baseUrl` được chỉ định tường minh sẽ được ưu tiên hơn `TAPAYA_BASE_URL`; cả hai đều ghi đè lựa chọn môi trường.

Client xác định API key và URL API một lần, khi được khởi tạo. Hãy dùng key khớp với
môi trường đã chọn. URL API, URL trả về và URL Hosted Checkout phải dùng HTTPS, ngoại trừ việc HTTP được phép cho
`localhost`, `127.0.0.1` và `[::1]` khi không ở `NODE_ENV=production`. SDK kiểm tra `NODE_ENV` hiện tại mỗi
lần kiểm tra tính hợp lệ của một URL HTTP cục bộ.

`timeoutMs` phải là số nguyên từ 1 đến 2.147.483.647. `maxRetries` phải là một số nguyên an toàn không âm. Số lần thử lại mặc định cho phép tối đa ba lần thử cho mỗi lệnh gọi, mỗi lần có thời gian chờ riêng.

Mỗi phương thức cũng chấp nhận `signal`, `timeoutMs` và `maxRetries` cho một yêu cầu riêng lẻ:

```ts
const current = await tapaya.checkout.sessions.get(sessionId, {
	signal: request.signal,
	timeoutMs: 5_000,
})
```

Khi signal bị hủy (abort), phương thức dừng ngay lập tức, không thử lại, và bị từ chối (reject) với lý do của signal.

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

### Chọn merchant [#select-a-merchant]

Ở chế độ merchant, checkout key của merchant tự xác định merchant của nó. Dùng `new Tapaya()` mà không cần `merchantToken`.

Ở chế độ nền tảng, một Secret key của tổ chức phục vụ nhiều merchant. Mọi yêu cầu đều cần merchant token; không có
merchant mặc định, và yêu cầu không có token sẽ thất bại với `TapayaAuthenticationError`. Sao chép token từ
bảng merchant trong [Cài đặt Checkout](/online/integration/checkout_settings) và truyền token của đơn hàng khi
tạo cũng như khi truy xuất phiên:

```ts
const tapaya = new Tapaya(process.env.TAPAYA_SECRET_KEY)
const session = await tapaya.checkout.sessions.create(params, {
	merchantToken: order.merchantToken,
	idempotencyKey: order.checkoutAttemptKey,
})
const current = await tapaya.checkout.sessions.get(session.id, {
	merchantToken: order.merchantToken,
})
```

Bạn cũng có thể đặt giá trị mặc định bằng `new Tapaya(key, { merchantToken })`. Token áp dụng cho các lệnh gọi Hosted Checkout và
thanh toán nhúng. Tapaya từ chối các token không thuộc tổ chức của key. Với key của merchant, token là tùy chọn và phải
chỉ đúng merchant của chính key đó. Xem [Xác thực Checkout](/online/integration/api_integration#authentication).

### Tạo một phiên [#create-a-session]

`tapaya.checkout.sessions.create(params, options?)` yêu cầu `merchantOrderId`, `amount` và `currency`.
`merchantOrderId` là mã tham chiếu đơn hàng của bạn, dài từ 1 đến 200 ký tự. Một đơn hàng chỉ có thể được thanh toán một lần; một phiên mới cho
đơn hàng chưa thanh toán sẽ thay thế phiên trước đó. Xem [Một khoản thanh toán cho mỗi đơn hàng](/online/integration/api_integration#one-payment-per-order). `amount` là tổng tiền cuối cùng,
bao gồm thuế và phí giao hàng, là một số nguyên từ 1 đến 2.147.483.647 đơn vị nhỏ nhất của tiền tệ. `currency` là mã ISO 4217
gồm ba chữ cái. Tapaya lưu và trả về mã này ở dạng chữ hoa.

Các trường tùy chọn:

| Trường                    | Mô tả                                                                                                                                                                                        |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `locale`                  | Ngôn ngữ của trang Hosted Checkout, ví dụ `en`. Mặc định là `en`.                                                                                                                            |
| `successUrl`, `cancelUrl` | URL trả về. Mặc định là các URL trong Cài đặt Checkout của bạn.                                                                                                                              |
| `tax`, `shipping`         | Thuế và phí giao hàng đã bao gồm trong `amount`, tính bằng đơn vị nhỏ nhất của tiền tệ.                                                                                                      |
| `taxRate`                 | Một mức thuế suất duy nhất tính theo điểm phần trăm, ví dụ `21`.                                                                                                                             |
| `taxBreakdown`            | Thuế theo từng mức thuế suất, `[{ taxRate, tax }]`, cho đơn hàng có nhiều mức thuế suất. Dùng thay cho `taxRate`: các kiểu dữ liệu không chấp nhận dùng cả hai cùng lúc.                     |
| `items`                   | Sản phẩm hiển thị trên trang Hosted Checkout: `reference`, `name`, `description`, `imageUrl`, `quantity` và `unitAmount`. Các mặt hàng chỉ để hiển thị và không làm thay đổi số tiền bị trừ. |
| `customer`                | Các trường tùy chọn `email`, `billingAddress` và `shippingAddress`. Chỉ gửi những thông tin bạn thu thập.                                                                                    |

Trước khi gửi, SDK kiểm tra kiểu dữ liệu, các trường bắt buộc, số tiền là số nguyên, định dạng loại tiền tệ và độ dài email
tối đa 254 ký tự. URL trả về phải là URL HTTPS tuyệt đối, với ngoại lệ HTTP cục bộ được mô tả trong phần
[Cấu hình](#configuration). Tapaya kiểm tra các quy tắc nghiệp vụ như định dạng email, giới hạn địa chỉ và mã
quốc gia, và báo lỗi dưới dạng `TapayaInvalidRequestError`. Xem
[Phiên Checkout](/online/integration/api_integration#checkout-sessions) để biết quy tắc của các trường,
tổng tiền thuế và giới hạn địa chỉ khách hàng.

### Truy xuất một phiên [#get-a-session]

`tapaya.checkout.sessions.get(id, options?)` trả về trạng thái hiện tại của một phiên. ID phải là chuỗi không rỗng, dài tối đa 200 ký tự; `.` và `..` không hợp lệ. SDK mã hóa URL cho ID.

Việc tạo và truy xuất phiên Hosted Checkout trả về một `HostedCheckoutSession`. Với các phương thức nhúng `prepare()`, `verify()` và `configuration()`, xem [Embedded Checkout](/online/integration/embedded_checkout).

Các trường của phiên Hosted Checkout:

| Trường                                       | Mô tả                                                                                                        |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `id`                                         | ID phiên, ví dụ `cs_Y7u2d`.                                                                                  |
| `url`                                        | Trang thanh toán Hosted Checkout. Chuyển hướng đến URL này mà không thay đổi.                                |
| `merchantOrderId`, `amount`, `currency`      | Thông tin đơn hàng để so sánh với đơn hàng bạn đã lưu.                                                       |
| `status`                                     | `open`, `completed` hoặc `expired`. Các phiên nhận thanh toán mới trong 30 phút. Có thể có thêm giá trị mới. |
| `paymentStatus`                              | `unpaid`, `successful`, `failed`, `cancelled` hoặc `action_needed`. Có thể có thêm giá trị mới.              |
| `paymentReference`                           | Token của lần thử thanh toán gần nhất, hoặc `null` trước lần thử đầu tiên.                                   |
| `tax`, `shipping`, `taxRate`, `taxBreakdown` | Các tổng đã gửi khi tạo phiên. Mặc định lần lượt là `0`, `0`, `null` và `[]`.                                |
| `items`                                      | Các mặt hàng đã gửi khi tạo phiên, hoặc một mảng rỗng.                                                       |
| `createdAt`, `expiresAt`, `completedAt`      | Dấu thời gian ISO 8601. `completedAt` là `null` cho đến khi thanh toán thành công.                           |

Hãy xử lý các giá trị `status` và `paymentStatus` không xác định, ví dụ bằng cách coi chúng là chưa thanh toán. Chỉ có
`paymentStatus: 'successful'` mới chứng minh việc thanh toán.

Thẻ bị từ chối là một kết quả thanh toán, không phải lỗi. Phiên vẫn ở trạng thái `open` với `paymentStatus: 'failed'`, và
khách hàng có thể thử lại. Trong khi Tapaya xác nhận một kết quả chưa chắc chắn, phiên vẫn ở trạng thái `open` và `unpaid`.
Hiện chưa có webhook; hãy thăm dò `get()` từ một tác vụ đối soát.

## Idempotency và thử lại [#idempotency-and-retries]

Mọi yêu cầu tạo phiên đều gửi một `Idempotency-Key`. Key được chỉ định tường minh phải là chuỗi không rỗng gồm các ký tự ASCII in được. Tapaya chấp nhận tối đa 250 ký tự. Nếu bạn bỏ qua `idempotencyKey`, SDK sẽ tạo một key ngẫu nhiên cho
mỗi lệnh gọi. Trong cùng lệnh gọi đó, các lần thử lại dùng lại key này, nên một lần thử lại không bao giờ tạo phiên thứ hai.

Để an toàn giữa các lệnh gọi riêng biệt và khi tiến trình khởi động lại, hãy lưu một key cùng với đơn hàng và truyền key đó mỗi khi bạn
tạo phiên cho lần thử đó, với cùng các tham số. Tapaya sẽ trả về phiên ban đầu thay vì
tạo phiên mới. Chỉ dùng key mới cho một lần thử checkout mới, không bao giờ dùng để thử lại khi kết quả chưa chắc chắn.

Một yêu cầu tạo phiên Hosted Checkout lặp lại sẽ trả về cùng phiên đó với `status` và `paymentStatus` hiện tại.
Hãy gọi `tapaya.checkout.sessions.get(session.id)` để kiểm tra lại trạng thái trước khi hoàn tất đơn hàng.

Với cùng một merchant, các lần thử lại Hosted Checkout phải dùng cùng phần thân yêu cầu và cùng chế độ thông tin xác thực: checkout key của merchant
hoặc Secret key của tổ chức. Thay đổi một trong hai sẽ trả về HTTP 409 với mã `API-0025`. Việc chuyển đổi chế độ thông tin xác thực
gây xung đột ngay cả khi phần thân giống hệt, vì mỗi chế độ dùng cài đặt chuyển hướng riêng.

SDK thử lại cả hai phương thức sau lỗi mạng, hết thời gian chờ và HTTP 429, 502, 503 và 504. SDK cũng thử lại một
yêu cầu tạo phiên thất bại với HTTP 409 `API-0024`. Các lần thử lại gửi cùng key và phần thân, nên chúng trả về phiên ban đầu. SDK
chờ giữa các lần thử theo cơ chế exponential backoff có jitter, và tuân theo `Retry-After` tối đa 10 giây. Khi Tapaya
yêu cầu chờ lâu hơn, SDK ngừng thử lại và ném lỗi tương ứng với mã trạng thái HTTP đó: `TapayaRateLimitError`
cho 429, `TapayaServerError` cho 502, 503 hoặc 504, hoặc `TapayaConflictError` cho 409. Chỉ `TapayaRateLimitError`
cung cấp khoảng chờ được yêu cầu qua `retryAfterMs`; các lỗi HTTP khác giữ header `Retry-After` trong `headers`.
Mọi lần thử lại dùng chung hạn mức `maxRetries`. Đặt `maxRetries: 0` để tắt thử lại.

Không bao giờ chuyển sang key mới để vượt qua một kết quả chưa chắc chắn. Yêu cầu ban đầu có thể đã tạo một
phiên; với key mới và cùng đơn hàng chưa thanh toán, phiên mới sẽ thay thế phiên đó.

## Lỗi [#errors]

Các lớp lỗi của SDK kế thừa `TapayaError`. Một yêu cầu bị hủy sẽ bị từ chối (reject) với lý do của signal.

| Lỗi                         | Khi nào                                                                                                                                                                                            |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TapayaError`               | Thiếu API key hoặc cấu hình client không hợp lệ; lớp cơ sở của các lỗi SDK.                                                                                                                        |
| `TapayaValidationError`     | Tham số không vượt qua bước kiểm tra tính hợp lệ trước khi yêu cầu được gửi.                                                                                                                       |
| `TapayaAuthenticationError` | HTTP 401: key bị thiếu hoặc không hợp lệ, thiếu merchant token ở chế độ nền tảng, hoặc token không khớp với một merchant đang hoạt động.                                                           |
| `TapayaInvalidRequestError` | HTTP 400 hoặc 422: Tapaya từ chối các tham số.                                                                                                                                                     |
| `TapayaPermissionError`     | HTTP 403.                                                                                                                                                                                          |
| `TapayaNotFoundError`       | HTTP 404: phiên không tồn tại đối với merchant này.                                                                                                                                                |
| `TapayaConflictError`       | HTTP 409. `code` là `API-0025` khi idempotency key đã được dùng với các tham số khác. 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ý cũng trả về 409. |
| `TapayaRateLimitError`      | HTTP 429 sau các lần thử lại, hoặc ngay lập tức khi `Retry-After` dài hơn 10 giây. `retryAfterMs` chứa khoảng chờ được yêu cầu, khi Tapaya gửi giá trị này.                                        |
| `TapayaServerError`         | HTTP 5xx. Chỉ 502, 503 và 504 được thử lại, trừ khi thử lại bị tắt hoặc `Retry-After` vượt quá 10 giây. Các mã trạng thái 5xx khác thất bại ngay lập tức.                                          |
| `TapayaApiError`            | Lớp cơ sở của các lỗi HTTP ở trên và mọi mã trạng thái lỗi HTTP khác.                                                                                                                              |
| `TapayaConnectionError`     | Yêu cầu thất bại hoặc phản hồi bị mất sau các lần thử lại. Lỗi gốc nằm trong `cause`.                                                                                                              |
| `TapayaTimeoutError`        | Một `TapayaConnectionError` cho lần thử vượt quá `timeoutMs`.                                                                                                                                      |
| `TapayaResponseError`       | Tapaya trả về một phản hồi mà SDK không đọc được.                                                                                                                                                  |

Các lỗi HTTP mang `status`, `code` lỗi của Tapaya, `fieldErrors` (`{ field, message, code? }[]`), các
`headers` của phản hồi và `requestId` từ header `X-Request-Id`, nếu có. Backend hiện chưa
đặt header này. Hãy đưa ID phiên hoặc mã tham chiếu đơn hàng và thời điểm gửi yêu cầu vào yêu cầu hỗ trợ, kèm theo
`requestId` nếu có. `TapayaResponseError` cũng mang `status`, `headers` và `requestId`.
`TapayaValidationError` cũng mang `fieldErrors`, với `field` được đặt thành đường dẫn tham số, ví dụ `customer.email`.

```ts
import { TapayaConnectionError, TapayaError, TapayaValidationError } from '@tapayadot/checkout'

try {
	const session = await tapaya.checkout.sessions.create(params, { idempotencyKey })
	return Response.redirect(session.url, 303)
} catch (error) {
	if (error instanceof TapayaValidationError) {
		console.error(error.fieldErrors)
	} else if (error instanceof TapayaConnectionError) {
		// The outcome is unknown. Retry later with the same idempotency key.
	} else if (error instanceof TapayaError) {
		console.error(error.message)
	}
	throw error
}
```

## Các kiểu được export [#exported-types]

Gói export `TapayaEnvironment`, `TapayaOptions`, `RequestOptions`, `CreateSessionOptions`, `CreateCheckoutSessionParams`, `CheckoutSessions`, `CheckoutSession`, `CheckoutSessionStatus`, `CheckoutPaymentStatus`, `CheckoutItem`, `TaxBreakdownEntry`, `CheckoutCustomer`, `CheckoutContactAddress` và `TapayaFieldError`.

`VERSION` chứa phiên bản SDK. Các kiểu dùng named import với `import type`:

```ts
import type { CheckoutSession, CreateCheckoutSessionParams } from '@tapayadot/checkout';
```

## Nâng cấp từ 0.2.x [#upgrade-from-02x]

* Trình tải cho trình duyệt tải Tapaya.js 0.2.0, phiên bản hỗ trợ Apple Pay và Google Pay. Trình tải từ chối một script 0.1.x đã được tải trước đó. Hãy cập nhật mọi thẻ script do bạn quản lý thành `https://js.tapaya.com/checkout/0.2.0/checkout.js`.
* `environment: 'production'` hỗ trợ cả Hosted Checkout lẫn thanh toán nhúng. `configuration()` trả về `environment: 'production'` cùng các key của cổng thanh toán chính thức.
* Khi không chỉ định môi trường tường minh hoặc `TAPAYA_ENVIRONMENT`, key `tapaya_live_` sẽ chọn production. Thông tin xác thực không khớp với môi trường đã chọn sẽ gây lỗi khi khởi tạo client. `baseUrl` vẫn ghi đè URL API.
