Cloud Accept API
BetaBắt đầu thanh toán tại cửa hàng trên một thiết bị Tapaya đã ghép nối từ ECR hoặc backend POS của bạn.
Cloud Accept API cho phép máy tính tiền điện tử (ECR) hoặc backend POS của bạn bắt đầu một khoản thanh toán trên thiết bị Tapaya qua đám mây. Hệ thống của bạn không bao giờ giao tiếp trực tiếp với thiết bị: hệ thống gửi một yêu cầu thanh toán tới Tapaya, Tapaya thông báo cho thiết bị đã ghép nối, và thiết bị hiển thị màn hình thanh toán cho khách hàng. Tiến trình được gửi lại cho bạn dưới dạng webhook, hoặc bạn tự thăm dò (poll).
Chọn cách ghép nối thiết bị
Mỗi thiết bị mà bạn gửi thanh toán tới đều cần một externalDeviceId: tên mà hệ thống của bạn dùng cho thiết bị đó, chẳng hạn
till-01. Gán định danh này theo một trong hai cách:
Từ ứng dụng của bạn
Ứng dụng của bạn gán externalDeviceId thông qua Accept SDK trên thiết bị.
Mã QR từ nền tảng
Merchant hiển thị một mã QR trong Tapaya Platform và quét mã đó bằng ứng dụng merchant.
Trước khi bắt đầu
- Lấy Server Secret Token của bạn từ trang API Keys. Bạn đổi token này lấy một access token theo phạm vi merchant ở bên dưới (xem xác thực Platform API).
- Đăng ký merchant và lưu lại
merchantTokencủa merchant đó. Xem Đăng ký merchant mới. - Chuẩn bị từng thiết bị với ứng dụng Tapaya đã đăng nhập cho merchant đó, để thiết bị có thể được ghép nối và nhận yêu cầu.
Một token, một merchant
Mỗi lệnh gọi Cloud Accept API chỉ áp dụng cho đúng một merchant. Để phục vụ nhiều merchant, hãy đổi lấy một access token riêng cho từng merchant.
Ghép nối thiết bị
Việc ghép nối gán một externalDeviceId cho thiết bị Tapaya, để bạn có thể tìm thấy thiết bị khi liệt kê danh sách thiết bị
và chỉ định thiết bị đó khi bắt đầu một khoản thanh toán.
Gán từ ứng dụng của bạn
Ứng dụng của bạn gọi một hàm của Accept SDK trên thiết bị để gán externalDeviceId cho thiết bị. Hãy dùng
cùng một định danh trong mọi lệnh gọi Cloud Accept API cho thiết bị đó.
Quét mã QR
Merchant mở quy trình ghép nối trong Tapaya Platform, nơi hiển thị một mã QR cho
externalDeviceId cần gán. Merchant quét mã bằng ứng dụng merchant của Tapaya trên thiết bị
đích, và ứng dụng sẽ hoàn tất việc ghép nối.
Đổi lấy access token
Đổi Server Secret Token của bạn và merchantToken của merchant lấy một access token bằng
POST /ecr/auth. merchantToken chỉ được gửi ở đây; mọi lệnh gọi sau đó chỉ gửi
Authorization: Bearer <accessToken>.
curl -X 'POST' 'https://api.tapaya.com/ecr/auth' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer REPLACE_ME' \
-d '{
"merchantToken": "unique_merchant_id_from_your_db"
}'{
"accessToken": "...",
"tokenType": "Bearer",
"expiresAt": "2026-01-13T16:30:59Z"
}| Mã | Mô tả |
|---|---|
401 | API token không hợp lệ hoặc đã hết hạn. |
403 | Không được phép truy cập merchant. |
Gia hạn trước expiresAt. Gọi POST /ecr/auth/refresh với accessToken hiện tại, vẫn còn hiệu lực,
trong header Authorization. Endpoint này trả về cùng nội dung phản hồi với một token mới; hãy lưu token đó
và dùng nó từ đó trở đi.
curl -X 'POST' 'https://api.tapaya.com/ecr/auth/refresh' \
-H 'Authorization: Bearer ACCESS_TOKEN'| Mã | Mô tả |
|---|---|
401 | Access token không hợp lệ, đã hết hạn hoặc đã bị thu hồi. Đổi lấy token mới bằng POST /ecr/auth. |
403 | Bạn không còn quyền truy cập merchant. Không có token mới nào được cấp. |
Tìm thiết bị sẵn sàng
Liệt kê các thiết bị của merchant bằng GET /ecr/devices. Thêm ?connected=true để loại bỏ
các thiết bị đã ngắt kết nối, hoặc lấy một thiết bị bằng GET /ecr/devices/{externalDeviceId}.
curl 'https://api.tapaya.com/ecr/devices/till-01' \
-H 'Authorization: Bearer ACCESS_TOKEN'{
"externalDeviceId": "till-01",
"name": "Front counter",
"connected": true,
"availability": "ready",
"lastSeenAt": "2026-01-13T16:30:59Z",
"updatedAt": "2026-01-13T16:30:59Z"
}Chỉ gửi thanh toán tới thiết bị có availability là ready. Danh sách chỉ là bản lưu (snapshot) tại một thời điểm:
tình trạng sẵn sàng được kiểm tra lại khi bạn gửi yêu cầu, nên một thiết bị ready vẫn có thể từ chối
yêu cầu. Xem Tình trạng sẵn sàng của thiết bị.
Gửi yêu cầu thanh toán
Gửi số tiền tới một thiết bị bằng POST /ecr/payment-requests. Tapaya lưu yêu cầu và
thông báo cho thiết bị, thiết bị sẽ hiển thị màn hình thanh toán.
curl -X 'POST' 'https://api.tapaya.com/ecr/payment-requests' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Idempotency-Key: order-1042-attempt-1' \
-d '{
"externalDeviceId": "till-01",
"amount": 12500,
"tipAmount": 500,
"currency": "CZK"
}'Tham số:
| Trường | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
externalDeviceId | string | ✓ | Thiết bị đã ghép nối sẽ hiển thị khoản thanh toán. |
amount | integer | ✓ | Số tiền theo đơn vị nhỏ nhất của loại tiền tệ (12500 là 125,00 Kč). |
tipAmount | integer | Tiền tip theo đơn vị nhỏ nhất của loại tiền tệ. | |
currency | string | ✓ | Mã tiền tệ ISO 4217. |
paymentToken | string | Một khoản thanh toán hiện có để gắn yêu cầu vào. |
{
"requestId": "...",
"status": "pending",
"paymentToken": null,
"expiresAt": "2026-01-13T16:30:59Z"
}202 không phải là kết quả thanh toán
202 Accepted nghĩa là Tapaya đã lưu yêu cầu. Điều đó không có nghĩa là thiết bị đã nhận được yêu cầu
hay khách hàng đã thanh toán. Mọi lỗi xảy ra sau đó sẽ được gửi tới bạn dưới dạng thay đổi trạng thái.
Đảm bảo thử lại an toàn. Header Idempotency-Key là bắt buộc. Dùng lại một key với cùng nội dung yêu cầu
sẽ trả về yêu cầu ban đầu thay vì bắt đầu khoản thanh toán thứ hai; dùng lại key đó với nội dung
khác sẽ trả về 409 idempotency_conflict. Hãy gắn key với giao dịch bán hàng và lần thử, như trong
order-1042-attempt-1.
Theo dõi kết quả
Theo dõi yêu cầu qua các trạng thái của nó bằng webhook, hoặc thăm dò
GET /ecr/payment-requests/{requestId}. Khi yêu cầu đạt payment_created, phản hồi sẽ chứa
paymentToken, và các sự kiện payment.updated báo tiến trình của chính khoản thanh toán.
Để nhận webhook, hãy đăng ký endpoint HTTPS của bạn bằng POST /ecr/webhook-subscriptions, hoặc đặt
URL callback và các loại sự kiện cho merchant trong Tapaya Platform.
curl -X 'POST' 'https://api.tapaya.com/ecr/webhook-subscriptions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-d '{
"url": "https://integrator.example.com/webhooks/tapaya",
"events": ["payment_request.updated", "payment.updated"]
}'{
"subscriptionId": "...",
"url": "https://integrator.example.com/webhooks/tapaya",
"events": ["payment_request.updated", "payment.updated"],
"signingSecret": "..."
}Lưu signing secret
signingSecret chỉ được trả về khi tạo đăng ký (subscription). Hãy lưu trữ nó an toàn; bạn
cần nó để xác minh chữ ký của các webhook gửi đến.
Một đăng ký thuộc về bên tích hợp và merchant, không thuộc về access token, nên nó vẫn được giữ nguyên khi
gia hạn token. Hãy phản hồi mỗi sự kiện bằng mã 2xx khi bạn đã chấp nhận sự kiện đó.
Xác minh trước khi chốt giao dịch bán hàng
Webhook là thông báo, không phải bằng chứng thanh toán, và một webhook có thể đến muộn hoặc hoàn toàn không đến. Không bao giờ chốt giao dịch bán hàng chỉ dựa vào webhook:
- Khi có webhook đến, hãy xác nhận kết quả cuối cùng qua API, ví dụ bằng Truy xuất chi tiết thanh toán.
- Khi không có webhook nào đến trong khoảng thời gian chờ phù hợp với quy trình thanh toán của bạn, và không muộn
hơn
expiresAtcủa yêu cầu, hãy gọiGET /ecr/payment-requests/{requestId}để đọc trạng thái hiện tại, sau đó xác nhận khoản thanh toán qua API như trên.
Tham chiếu
Các endpoint
| Phương thức | Endpoint | Mô tả |
|---|---|---|
POST | /ecr/auth | Đổi API token của bạn và một merchantToken lấy access token. |
POST | /ecr/auth/refresh | Gia hạn một access token vẫn còn hiệu lực. |
GET | /ecr/devices | Liệt kê các thiết bị của merchant. Bộ lọc connected=true tùy chọn. |
GET | /ecr/devices/{externalDeviceId} | Trả về một thiết bị và tình trạng sẵn sàng hiện tại của thiết bị đó. |
POST | /ecr/payment-requests | Lưu một yêu cầu thanh toán và thông báo cho thiết bị. Bắt buộc có header Idempotency-Key. |
GET | /ecr/payment-requests/{requestId} | Trả về trạng thái yêu cầu, lý do từ chối (nếu có) và paymentToken. |
POST | /ecr/webhook-subscriptions | Đăng ký một URL HTTPS và các loại sự kiện. Trả về signing secret một lần duy nhất. |
GET | /ecr/webhook-subscriptions | Liệt kê các đăng ký của bạn cho merchant, không kèm signing secret. |
DELETE | /ecr/webhook-subscriptions/{subscriptionId} | Xóa một đăng ký. |
Tình trạng sẵn sàng của thiết bị
Cả hai endpoint thiết bị đều trả về externalDeviceId, name, connected, availability, lastSeenAt và
updatedAt.
| Giá trị | Mô tả |
|---|---|
ready | Đã kết nối, có thể nhận và hiển thị yêu cầu. |
busy | Đang xử lý một khoản thanh toán hoặc đang giữ một yêu cầu đang hoạt động. |
unavailable | Đã ngắt kết nối, không có phiên hợp lệ hoặc không thể hiển thị màn hình thanh toán. |
Trạng thái yêu cầu
| Trạng thái | Mô tả |
|---|---|
pending | Đã được lưu và đang chờ thiết bị chấp nhận. |
acknowledged | Thiết bị đã nhận được yêu cầu; khoản thanh toán có thể chưa tồn tại. |
payment_created | Đã có khoản thanh toán và paymentToken của nó. Theo dõi tiến trình tiếp theo trên khoản thanh toán. |
rejected | Thiết bị hoặc Tapaya đã từ chối yêu cầu. |
Lỗi
| Mã | Lý do | Mô tả |
|---|---|---|
403 / 404 | Đối tượng không khả dụng | Bạn không có quyền, hoặc đối tượng không tồn tại. |
409 | device_busy | Thiết bị đã có một yêu cầu hoặc khoản thanh toán đang hoạt động. |
409 | device_unavailable | Thiết bị đã kết nối nhưng chưa sẵn sàng. |
409 | idempotency_conflict | Key đã được dùng với một nội dung yêu cầu khác. |
503 | device_offline | Thiết bị không có kết nối. |
Lỗi kiểm tra tính hợp lệ của số tiền và loại tiền tệ tuân theo cùng quy ước như phần còn lại của Platform API.
Sự kiện webhook
| Sự kiện | Được gửi khi | Nội dung data |
|---|---|---|
device.connected | Một thiết bị mở kết nối trực tiếp mới. | externalDeviceId, connected, availability, lastSeenAt |
device.disconnected | Kết nối bị đóng hoặc việc kiểm tra hoạt động (liveness check) hết thời gian chờ. | externalDeviceId, connected, availability, lastSeenAt |
device.availability_changed | Tình trạng sẵn sàng chuyển giữa ready, busy và unavailable. | externalDeviceId, availability, reason tùy chọn |
payment_request.updated | Yêu cầu được lưu, được xác nhận đã nhận, bị từ chối, hết hạn hoặc có khoản thanh toán. | requestId, externalDeviceId, status, paymentToken, reference, reason, expiresAt |
payment.updated | Trạng thái của khoản thanh toán thay đổi. | paymentToken, requestId, externalDeviceId, status, reference, amount, currency, receiptUrl tùy chọn |
Mọi sự kiện đều đến dưới dạng HTTP POST trong cùng một cấu trúc bao (envelope):
{
"eventId": "...",
"type": "payment_request.updated",
"occurredAt": "2026-01-13T16:30:59Z",
"data": {
"requestId": "...",
"externalDeviceId": "till-01",
"status": "acknowledged",
"paymentToken": null,
"reference": "ORDER-1042",
"reason": null,
"expiresAt": "2026-01-13T16:30:59Z"
}
}