API mua thẻ
cardws
Mua mã thẻ#
Mua mã thẻ điện tử, tải lại đơn đã mua, kiểm tra tồn kho, lấy số dư và danh sách sản phẩm.
Tổng quan#
Mọi lệnh gửi tới một endpoint, phân biệt bằng tham số command. Gửi POST với thân JSON (form urlencoded cũng được nhận). Danh sách sản phẩm lấy qua một endpoint GET riêng.
- Endpoint
- https://thepay1s.com/api/cardws
- Danh sách sản phẩm
- https://thepay1s.com/api/cardws/products?partner_id=YOUR_PARTNER_ID
- Phương thức
- POST (lệnh), GET (danh sách sản phẩm)
- Kiểu dữ liệu
- application/json
- Lệnh
- buycard, redownload, checkavailable, getbalance
Điều kiện
- Kết nối API có loại Mua mã thẻ và đang hoạt động; tài khoản sở hữu và ví không bị khoá.
- Kết nối Mua mã thẻ bắt buộc khai báo Địa chỉ IP. Yêu cầu từ IP khác, hoặc khi danh sách trống, bị từ chối với status=114.
- wallet_number là số ví của tài khoản sở hữu kết nối, xem ở trang Kết nối API.
- request_id của buycard là duy nhất trong tài khoản; trùng trả status=109. Dùng lại request_id đó cho redownload.
- API không gọi callback. Đơn trả status=2 thì gọi redownload sau ít phút để nhận mã thẻ.
Ghi chú tương thích
- status trả về là chuỗi số (1, 2, 116…); nên ép sang số trước khi so sánh.
- Số dư không đủ trả 121, không tìm thấy đơn trả 103, hết hàng hoặc bảo trì trả 118.
- Hạn dùng của thẻ nằm ở trường expire.
- API sản phẩm trả thêm discount theo từng mệnh giá, imgurl đầy đủ và mô tả sản phẩm.
Chữ ký#
sign là MD5 của Partner Key ghép với Partner ID, tên lệnh và (với buycard, redownload) request_id. API danh sách sản phẩm không cần chữ ký.
| Công thức | Lệnh |
|---|---|
| md5(partner_key + partner_id + command + request_id) | buycard, redownload |
| md5(partner_key + partner_id + command) | checkavailable, getbalance |
Ví dụ tính chữ ký
Với Partner Key giả định YOUR_PARTNER_KEY và Partner ID ở các ví dụ:
buycard#
Mua mã thẻ điện tử. Tiền được trừ khỏi ví khi tạo đơn; thẻ có sẵn được trả ngay trong phản hồi.
- commandchuỗiBắt buộcTên lệnh, đúng như tên mục.
- partner_idchuỗiBắt buộcPartner ID của kết nối.
- request_idchuỗiBắt buộcMã đơn phía bạn. Lệnh buycard: mã mới, duy nhất trong tài khoản. Lệnh redownload: request_id đã dùng khi mua.
- service_codechuỗiBắt buộcMã sản phẩm, lấy từ API danh sách sản phẩm.
- valuesố nguyênBắt buộcMệnh giá thẻ (VND), là một value trong cardvalue của sản phẩm.
- qtysố nguyênBắt buộcSố lượng thẻ, số nguyên dương.
- wallet_numberchuỗiBắt buộcSố ví thanh toán của tài khoản sở hữu kết nối.
- signchuỗiBắt buộcChữ ký md5(partner_key + partner_id + command + request_id), xem mục Chữ ký.
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
| command | chuỗi | Có | Tên lệnh, đúng như tên mục. |
| partner_id | chuỗi | Có | Partner ID của kết nối. |
| request_id | chuỗi | Có | Mã đơn phía bạn. Lệnh buycard: mã mới, duy nhất trong tài khoản. Lệnh redownload: request_id đã dùng khi mua. |
| service_code | chuỗi | Có | Mã sản phẩm, lấy từ API danh sách sản phẩm. |
| value | số nguyên | Có | Mệnh giá thẻ (VND), là một value trong cardvalue của sản phẩm. |
| qty | số nguyên | Có | Số lượng thẻ, số nguyên dương. |
| wallet_number | chuỗi | Có | Số ví thanh toán của tài khoản sở hữu kết nối. |
| sign | chuỗi | Có | Chữ ký md5(partner_key + partner_id + command + request_id), xem mục Chữ ký. |
{ "command": "buycard", "partner_id": "YOUR_PARTNER_ID", "request_id": "CARD20261024001", "service_code": "VIETTEL", "value": 100000, "qty": 2, "wallet_number": "4821061234", "sign": "32c58a3753c5d13822ee35ead1c9dfc4" }
- Số tiền trừ ví: total_price = value × (100 − discount) / 100 × qty, với discount theo nhóm của tài khoản (xem API danh sách sản phẩm).
- Thẻ trong kho được trả ngay với status=1. Nếu phải lấy từ nhà cung cấp mà chưa có thẻ, đơn trả status=2: tiền đã trừ, gọi redownload sau để nhận thẻ.
- qty tối đa theo cấu hình của hệ thống (mặc định 10 thẻ mỗi đơn); vượt mức trả status=125.
- Gửi lại cùng request_id trả status=109 và không trừ tiền lần hai; dùng redownload để lấy đơn đã mua.
redownload#
Lấy lại mã thẻ của một đơn đã mua, hoặc nhận thẻ của đơn đang chờ.
- commandchuỗiBắt buộcTên lệnh, đúng như tên mục.
- partner_idchuỗiBắt buộcPartner ID của kết nối.
- request_idchuỗiBắt buộcMã đơn phía bạn. Lệnh buycard: mã mới, duy nhất trong tài khoản. Lệnh redownload: request_id đã dùng khi mua.
- signchuỗiBắt buộcChữ ký md5(partner_key + partner_id + command + request_id), xem mục Chữ ký.
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
| command | chuỗi | Có | Tên lệnh, đúng như tên mục. |
| partner_id | chuỗi | Có | Partner ID của kết nối. |
| request_id | chuỗi | Có | Mã đơn phía bạn. Lệnh buycard: mã mới, duy nhất trong tài khoản. Lệnh redownload: request_id đã dùng khi mua. |
| sign | chuỗi | Có | Chữ ký md5(partner_key + partner_id + command + request_id), xem mục Chữ ký. |
{ "command": "redownload", "partner_id": "YOUR_PARTNER_ID", "request_id": "CARD20261024001", "sign": "068d7702f03666aae248ca16b0b072a7" }
- Dùng khi mất kết nối lúc mua, khi đơn trả status=2, hoặc khi cần xem lại mã thẻ.
- Đơn đã có thẻ trả status=1; đang chờ trả status=2; đã huỷ và hoàn tiền trả status=3; thất bại chưa hoàn tiền trả status=102.
checkavailable#
Kiểm tra tồn kho của một mệnh giá trước khi mua.
- commandchuỗiBắt buộcTên lệnh, đúng như tên mục.
- partner_idchuỗiBắt buộcPartner ID của kết nối.
- service_codechuỗiBắt buộcMã sản phẩm, lấy từ API danh sách sản phẩm.
- valuesố nguyênBắt buộcMệnh giá thẻ (VND), là một value trong cardvalue của sản phẩm.
- qtysố nguyênBắt buộcSố lượng thẻ, số nguyên dương.
- signchuỗiBắt buộcChữ ký md5(partner_key + partner_id + command), xem mục Chữ ký.
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
| command | chuỗi | Có | Tên lệnh, đúng như tên mục. |
| partner_id | chuỗi | Có | Partner ID của kết nối. |
| service_code | chuỗi | Có | Mã sản phẩm, lấy từ API danh sách sản phẩm. |
| value | số nguyên | Có | Mệnh giá thẻ (VND), là một value trong cardvalue của sản phẩm. |
| qty | số nguyên | Có | Số lượng thẻ, số nguyên dương. |
| sign | chuỗi | Có | Chữ ký md5(partner_key + partner_id + command), xem mục Chữ ký. |
{ "command": "checkavailable", "partner_id": "YOUR_PARTNER_ID", "service_code": "VIETTEL", "value": 100000, "qty": 2, "sign": "5cdd77a9a2175621c73c6c0cf25e21ab" }
- Không giữ hàng: tồn kho có thể thay đổi trước khi bạn gọi buycard. Phản hồi là JSON trực tiếp, không có khung status.
getbalance#
Lấy số dư ví dùng để mua thẻ.
- commandchuỗiBắt buộcTên lệnh, đúng như tên mục.
- partner_idchuỗiBắt buộcPartner ID của kết nối.
- wallet_numberchuỗiBắt buộcSố ví thanh toán của tài khoản sở hữu kết nối.
- signchuỗiBắt buộcChữ ký md5(partner_key + partner_id + command), xem mục Chữ ký.
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
| command | chuỗi | Có | Tên lệnh, đúng như tên mục. |
| partner_id | chuỗi | Có | Partner ID của kết nối. |
| wallet_number | chuỗi | Có | Số ví thanh toán của tài khoản sở hữu kết nối. |
| sign | chuỗi | Có | Chữ ký md5(partner_key + partner_id + command), xem mục Chữ ký. |
{ "command": "getbalance", "partner_id": "YOUR_PARTNER_ID", "wallet_number": "4821061234", "sign": "1a4651ad1fc33b21b9a5dabf410c7f79" }
- wallet_number phải là số ví của tài khoản sở hữu kết nối. Phản hồi là JSON trực tiếp, không có khung status.
Danh sách sản phẩm#
Lấy danh sách sản phẩm đang bán và mệnh giá. Gọi GET, không cần chữ ký, chỉ cần partner_id trên query string. IP gọi phải nằm trong danh sách IP của kết nối.
- partner_idchuỗiBắt buộcPartner ID của kết nối có loại Mua mã thẻ.Ví dụ: YOUR_PARTNER_ID
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
| partner_id | chuỗi | Có | Partner ID của kết nối có loại Mua mã thẻ.Ví dụ: YOUR_PARTNER_ID |
curl 'https://thepay1s.com/api/cardws/products?partner_id=YOUR_PARTNER_ID'
[ { "name": "Viettel", "slug": "viettel", "service_code": "VIETTEL", "image": "/uploads/softcard/viettel.png", "short_description": null, "description": null, "imgurl": "https://thepay1s.com/uploads/softcard/viettel.png", "cardvalue": [ { "service_code": "VIETTEL", "value": 50000, "id": 10, "discount": "2.5" }, { "service_code": "VIETTEL", "value": 100000, "id": 11, "discount": "2.5" } ] } ]
Các trường
- nameKiểuchuỗiMô tảTên sản phẩm.
- slugKiểuchuỗiMô tảĐường dẫn rút gọn của sản phẩm.
- service_codeKiểuchuỗiMô tảMã sản phẩm, gửi trong service_code của buycard và checkavailable.
- imageKiểuchuỗiMô tảĐường dẫn ảnh tương đối.
- imgurlKiểuchuỗiMô tảĐường dẫn ảnh đầy đủ.
- short_description, descriptionKiểuchuỗiMô tảMô tả ngắn và mô tả chi tiết, có thể là null.
- cardvalueKiểumảngMô tảMệnh giá đang bán: service_code, value, id và discount (phần trăm, dạng chuỗi).
Phản hồi lỗi
- 104messageDu lieu gui len khong dungNguyên nhân và cách xử lýThiếu partner_id.
- 108messageKhong ton tai tai khoan merchantNguyên nhân và cách xử lýKhông có kết nối loại Mua mã thẻ với partner_id này, kết nối chưa khai IP hoặc IP gọi không nằm trong danh sách IP của kết nối.
- 115messageAPI KEY CHƯA ĐƯỢC BẬTNguyên nhân và cách xử lýKết nối đang tắt.
- 429messageGui qua nhieu yeu cau, vui long thu lai sauNguyên nhân và cách xử lýMột IP gọi quá nhiều lần trong một phút, chờ rồi gọi lại.
- Thành công trả mảng JSON; lỗi trả object có status là số, message và data bằng false.
- Danh sách thay đổi khi quản trị viên bật, tắt sản phẩm; nên lấy lại định kỳ.
Phản hồi và trạng thái đơn#
buycard và redownload trả khung status, message, data. checkavailable và getbalance trả JSON trực tiếp. Lỗi của mọi lệnh chỉ gồm status và message. Luôn trả HTTP 200.
Các trường
- data.cardsKiểumảngLệnhbuycard, redownloadMô tảDanh sách thẻ của đơn, rỗng khi đơn đang chờ hoặc đã huỷ.
- data.cards[].nameKiểuchuỗiLệnhbuycard, redownloadMô tảTên thẻ.
- data.cards[].serialKiểuchuỗiLệnhbuycard, redownloadMô tảSerial thẻ.
- data.cards[].codeKiểuchuỗiLệnhbuycard, redownloadMô tảMã thẻ.
- data.cards[].expireKiểuchuỗiLệnhbuycard, redownloadMô tảHạn dùng của thẻ, có thể là null.
- data.timeKiểuchuỗiLệnhbuycard, redownloadMô tảThời điểm phản hồi, dạng YYYY-MM-DD HH:mm:ss giờ Việt Nam.
- data.request_idKiểuchuỗiLệnhbuycard, redownloadMô tảrequest_id bạn đã gửi.
- data.order_codeKiểuchuỗiLệnhbuycard, redownloadMô tảMã đơn của hệ thống.
- data.total_priceKiểusố nguyênLệnhbuycard, redownloadMô tảTổng tiền đã trừ ví.
- stock_availableKiểubooleanLệnhcheckavailableMô tảtrue nếu kho đủ qty thẻ.
- SERVICE-VALUEKiểuchuỗiLệnhcheckavailableMô tảKhoá dạng SERVICE_CODE-VALUE, giá trị là Còn hàng, Hết hàng hoặc Sản phẩm không tồn tại.
- balanceKiểusố nguyênLệnhgetbalanceMô tảSố dư ví (VND).
- currency_codeKiểuchuỗiLệnhgetbalanceMô tảLuôn là VND.
Trạng thái đơn
- 1messageMua thẻ thành côngÝ nghĩaĐơn hoàn tất, data.cards có đủ thẻ.
- 2messageĐơn hàng đang chờ xử lýÝ nghĩaTiền đã trừ, đang chờ nhà cung cấp trả thẻ. Gọi redownload sau ít phút.
- 2messageĐơn hàng đã hủy và hoàn tiềnÝ nghĩaĐơn bị huỷ, tiền đã hoàn về ví.
Mã lỗi#
Thông báo được giữ nguyên văn để bạn so khớp, nhưng nên xử lý theo status.
- 100messageThiếu dữ liệu bắt buộcNguyên nhân và cách xử lýThiếu partner_id, command hoặc sign, hoặc thiếu request_id, wallet_number của lệnh cần chúng.
- 100messageTài khoản bị khóaNguyên nhân và cách xử lýTài khoản sở hữu kết nối đang bị khoá.
- 100messageĐịa chỉ ví bị khóaNguyên nhân và cách xử lýVí của tài khoản đang bị khoá.
- 100messageDữ liệu mua thẻ không hợp lệNguyên nhân và cách xử lýbuycard thiếu service_code, hoặc value, qty không phải số dương.
- 100messageDữ liệu kiểm tra tồn kho không hợp lệNguyên nhân và cách xử lýcheckavailable thiếu service_code, hoặc value, qty không phải số dương.
- 100messageSản phẩm không tồn tại hoặc bị tắtNguyên nhân và cách xử lýKhông có mệnh giá value đang bán của service_code này.
- 100messageLệnh không hợp lệNguyên nhân và cách xử lýcommand không thuộc danh sách lệnh hỗ trợ.
- 102messageĐơn chưa có mã thẻNguyên nhân và cách xử lýredownload: đơn thất bại và chưa được hoàn tiền; liên hệ hỗ trợ.
- 103messageKhông tìm thấy giao dịchNguyên nhân và cách xử lýredownload: không có đơn nào của tài khoản với request_id này.
- 107messageLỗi tạo đơn hàng, vui lòng báo kỹ thuậtNguyên nhân và cách xử lýKhông tạo được đơn; tiền không bị trừ.
- 108messageApi key không hợp lệNguyên nhân và cách xử lýpartner_id không tồn tại hoặc kết nối không có loại Mua mã thẻ.
- 109messagerequest_id đã tồn tạiNguyên nhân và cách xử lýrequest_id đã được dùng cho một đơn khác trong tài khoản.
- 114messageĐịa chỉ IP không được phép, vui lòng gửi IP này cho adminNguyên nhân và cách xử lýIP gọi API không nằm trong danh sách Địa chỉ IP của kết nối, hoặc kết nối chưa khai báo IP.
- 115messageApi key chưa được bậtNguyên nhân và cách xử lýKết nối đang tắt.
- 116messageChữ ký không hợp lệNguyên nhân và cách xử lýChữ ký không khớp. Kiểm tra Partner Key, thứ tự ghép và lệnh có ghép request_id hay không.
- 118messageWebsite đang bảo trìNguyên nhân và cách xử lýChức năng mua thẻ đang tạm dừng.
- 118messageSản phẩm đã hếtNguyên nhân và cách xử lýKho và nhà cung cấp đều không đủ thẻ cho mệnh giá này.
- 118messageNhà cung cấp tạm hết số dưNguyên nhân và cách xử lýNhà cung cấp tạm không bán được; thử lại sau.
- 121messageSố dư không đủNguyên nhân và cách xử lýSố dư ví không đủ cho total_price.
- 125messageĐơn hàng vượt quá số lượng cho phépNguyên nhân và cách xử lýqty vượt số thẻ tối đa mỗi đơn (mặc định 10).
Thử bằng key sandbox
Dùng key sandbox (Partner ID bắt đầu bằng sandbox_) với đúng URL này. Sản phẩm, mệnh giá, tài khoản và giá vẫn kiểm tra trên danh mục thật; kết quả chọn bằng đuôi request_id, đọc từ dấu _ cuối cùng, không phân biệt hoa thường.
- (không có)Kết quảstatus 1 kèm mã thẻ giả có prefix SANDBOX, trừ tiền thử
- _pendingKết quảstatus 2 chưa có thẻ, vài giây sau redownload trả thẻ
- _canceledKết quảstatus 2 đơn huỷ, trừ rồi hoàn tiền thử
- _nocardsKết quảstatus 2 chờ mãi, redownload trả lỗi 102 chưa có mã thẻ
- _soldoutKết quảTrả lỗi 118 hết hàng, không trừ tiền
- _providerKết quảTrả lỗi 118 nhà cung cấp hết số dư, không trừ tiền
- _maintenanceKết quảTrả lỗi 118 bảo trì, không trừ tiền
- _failedKết quảTrả lỗi 107 lỗi tạo đơn, không trừ tiền
- _lockedKết quảTrả lỗi 100 tài khoản bị khoá, không trừ tiền
- _walletlockedKết quảTrả lỗi 100 ví bị khoá, không trừ tiền
Code mẫu#
Hàm mẫu tự tính chữ ký theo từng lệnh rồi gửi yêu cầu; ví dụ mua thẻ rồi tải lại đơn. Tab cURL gửi lệnh buycard.
PARTNER_ID='YOUR_PARTNER_ID' PARTNER_KEY='YOUR_PARTNER_KEY' REQUEST_ID='CARD20261024001' SIGN=$(printf '%s' "${PARTNER_KEY}${PARTNER_ID}buycard${REQUEST_ID}" | md5sum | cut -d ' ' -f 1) curl -X POST 'https://thepay1s.com/api/cardws' \ -H 'Content-Type: application/json' \ --data-binary @- <<JSON { "command": "buycard", "partner_id": "$PARTNER_ID", "request_id": "$REQUEST_ID", "service_code": "VIETTEL", "value": 100000, "qty": 2, "wallet_number": "4821061234", "sign": "$SIGN" } JSON