Nhảy tới nội dung
Phiên bản: Lastest

API Nạp tiền điện thoại bất đồng bộ

Endpoint: /api/v2/service/topup/charging-async

Method: POST

Header: Cách tạo JWT_TOKEN

Language: en | vi

{
"X-APPOTAPAY-AUTH": Bearer JWT_TOKEN,
"Content-Type": "application/json",
"Language": LANGUAGE
}
Điều kiện sử dụng
  • Đối tác phải đăng ký URL nhận IPN với AppotaPay trước khi sử dụng API này. Nếu chưa có URL nhận IPN, AppotaPay sẽ không gửi được kết quả giao dịch cho đối tác.
  • API chỉ trả về xác nhận đã tiếp nhận giao dịch, không trả về kết quả nạp tiền. Kết quả cuối được gửi qua IPN.
  • Xem thêm tổng quan luồng bất đồng bộ.

Tham số​

Tham sốYêu cầuKiểu dữ liệuMô tảGhi chú
partnerRefId√StringMã giao dịch phía đối tác, duy nhất cho mỗi giao dịchmax_length: 50
telcoStringTên nhà mạng (xem thêm phần Phụ Lục bảng nhà mạng)
telcoServiceType√StringLoại dịch vụ

prepaid: Nạp tiền điện thoại trả trước

postpaid: Nạp tiền điện thoại trả sau

productCode√StringMã sản phẩm (xem thêm API lấy bảng mã sản phẩm)
phoneNumber√StringSố điện thoại nạp tiền (truyền dạng: 09x, 08x,..)
signature√StringChữ ký các tham số truyền lên API, các tham số được đưa vào chữ ký theo thứ tự bao gồm: partnerRefId + phoneNumber + productCode + telco + telcoServiceType (xem thêm phần cách tạo signature)

Chú ý:

- APPOTAPAY đã hỗ trợ Partner trong việc chủ động kiểm tra nhà mạng của SĐT truyền sang.

- Partner có thể sử dụng bảng mã sản phẩm chung để truyền vào API mà không cần định nghĩa rõ product_code theo từng nhà mạng của SĐT (Bảng mã này dùng với nạp topup thường, không dùng cho topup data).

Dữ liệu trả về​

Tham sốKiểu dữ liệuMô tả
errorCodeIntegerMã lỗi trả về.

35: giao dịch đã được tiếp nhận và đang chờ xử lý

Các mã khác: giao dịch không được tiếp nhận (xem bảng mã lỗi)

messageStringMô tả chi tiết mã lỗi

Chú ý: errorCode = 35 không phải kết quả cuối của giao dịch. Đối tác chỉ được cập nhật đơn hàng thành công/thất bại khi nhận được IPN có trạng thái cuối hoặc khi API kiểm tra trạng thái giao dịch trả về trạng thái cuối.

Ví dụ​

Request​

{
"partnerRefId": "AB123",
"telco": "viettel",
"telcoServiceType": "prepaid",
"phoneNumber": "0866123456",
"productCode": "viettel_10",
"signature": "5a2774918a29cf4d2bdb78cccceb956f4c27837fad09a03a56e1df68b1bf29dd"
}

Response​

Tiếp nhận giao dịch thành công

{
"errorCode": 35,
"message": "Giao dịch đang chờ xử lý vui lòng kiểm tra lại sau"
}

Không tiếp nhận được giao dịch

{
"errorCode": 31,
"message": "Mã giao dịch bị trùng"
}

IPN (Instant Payment Notification)​

Khi giao dịch có kết quả cuối, AppotaPay gửi thông báo tới URL nhận IPN mà đối tác đã đăng ký. Đối tác kiểm tra tính toàn vẹn dữ liệu qua tham số signature, sau đó cập nhật trạng thái đơn hàng.

Method: POST

{
"Content-Type": "application/json"
}

Tham số AppotaPay gửi sang​

Tham sốKiểu dữ liệuMô tả
errorCodeIntegerMã lỗi giao dịch. 0 khi nạp tiền thành công, khác 0 khi thất bại (xem bảng mã lỗi)
statusStringTrạng thái cuối của giao dịch: success hoặc error
appotapayTransIdStringMã giao dịch phía AppotaPay
partnerRefIdStringMã giao dịch phía đối tác
phoneNumberStringSố điện thoại nạp
productCodeStringMã sản phẩm
amountIntegerSố tiền giao dịch
topupAmountIntegerSố tiền nạp cho thuê bao
telcoStringTên nhà mạng

Thông tin trả về có thể khác với request đầu vào do ghi nhận thực tế từ nhà mạng

telcoServiceTypeStringLoại dịch vụ (giá trị có thể thay đổi theo thông tin thực tế của thuê bao do nhà mạng trả về)
timeStringThời gian nạp tiền (định dạng chuẩn RFC-3339)
signatureStringChữ ký dữ liệu IPN, các trường được ký bao gồm (amount + appotapayTransId + errorCode + partnerRefId + phoneNumber + productCode + status + telco + telcoServiceType + time + topupAmount)

Cách kiểm tra signature​

Thứ tự các tham số để tạo ra signature được sort theo thứ tự alphabet, ghép thành chuỗi key=value nối bằng &, sau đó ký bằng HMAC_SHA256 với SECRET_KEY của đối tác.

signature = HMAC_SHA256("amount=10000&appotapayTransId=01J7G2DYZTPCGHM3AAF8ANZC7J&errorCode=0&partnerRefId=AB123&phoneNumber=0866123456&productCode=viettel_10&status=success&telco=viettel&telcoServiceType=prepaid&time=2026-08-17T10:15:30+07:00&topupAmount=10000", YOUR_SECRET_KEY)

Đối tác tự tạo lại signature từ dữ liệu nhận được rồi so sánh với signature AppotaPay gửi sang. Nếu không khớp, không được cập nhật đơn hàng.

Ví dụ IPN​

Giao dịch thành công

{
"errorCode": 0,
"status": "success",
"appotapayTransId": "01J7G2DYZTPCGHM3AAF8ANZC7J",
"partnerRefId": "AB123",
"phoneNumber": "0866123456",
"productCode": "viettel_10",
"amount": 10000,
"topupAmount": 10000,
"telco": "viettel",
"telcoServiceType": "prepaid",
"time": "2026-08-17T10:15:30+07:00",
"signature": "44b2e44eaaecfbbe5d9512c323affad5a9515a7f606afc486de80b7c2f771f2f"
}

Giao dịch thất bại

{
"errorCode": 33,
"status": "error",
"appotapayTransId": "01J7G2DYZTPCGHM3AAF8ANZC7J",
"partnerRefId": "AB124",
"phoneNumber": "0866123456",
"productCode": "viettel_10",
"amount": 10000,
"topupAmount": 10000,
"telco": "viettel",
"telcoServiceType": "prepaid",
"time": "2026-08-17T10:15:30+07:00",
"signature": "046ae200d3210f0a5417ac2b3a8ccc83edf6452f00c8655fd31a13e73e1f84d8"
}

Phản hồi đối tác cần trả về​

Đối tác trả về mã HTTP 200 để xác nhận đã nhận IPN thành công. Mọi mã HTTP khác 200 đều được AppotaPay coi là gửi IPN thất bại.

Tiêu chíGiá trị
Mã xác nhận thành côngHTTP 200
Thời gian chờ kết nối5 giây
Thời gian chờ phản hồi10 giây
Số lần gửi tối đa4 lần (bao gồm lần gửi đầu tiên)
Khoảng cách giữa các lần gửi lại5 phút
Lưu ý
  • AppotaPay chỉ gửi IPN khi giao dịch đã có kết quả cuối (status là success hoặc error), không gửi IPN cho giao dịch còn đang xử lý.
  • Cùng một giao dịch có thể nhận IPN nhiều lần. Hệ thống đối tác cần chống xử lý trùng theo partnerRefId: nếu đơn đã được cập nhật kết quả trước đó thì bỏ qua và vẫn trả về HTTP 200.
  • Nếu sau 4 lần gửi vẫn thất bại, AppotaPay dừng gửi lại. Đối tác cần chủ động gọi API kiểm tra trạng thái giao dịch để đối soát, hoặc liên hệ AppotaPay để gửi lại IPN thủ công.
  • Chỉ xác nhận giao dịch thành công khi status = success, errorCode = 0 và signature hợp lệ.

Ví dụ Code​

curl --location 'https://gateway.dev.appotapay.com/api/v2/service/topup/charging-async' \
--header 'X-APPOTAPAY-AUTH: JWT_TOKEN' \
--header 'Content-Type: application/json' \
-d '{
"partnerRefId": "AB123",
"telco": "viettel",
"telcoServiceType": "prepaid",
"phoneNumber": "0866123456",
"productCode": "viettel_10",
"signature": "5a2774918a29cf4d2bdb78cccceb956f4c27837fad09a03a56e1df68b1bf29dd"
}'

Request​

REQUEST

Development server
Example (from schema)