ເອກະສານ API
Base URL: https://pos-v2.luayver.com. ຫົວຂໍ້ທີ່ໝາຍ “ໃຊ້ໄດ້ແລ້ວ” ເອີ້ນໃຊ້ໄດ້ຈິງໃນປັດຈຸບັນ; “ກຳລັງພັດທະນາ” ແມ່ນແຜນງານ ຍັງເອີ້ນໃຊ້ບໍ່ໄດ້.
ຄູ່ມືເລີ່ມຕົ້ນໃຊ້ງານ · ໃຊ້ໄດ້ແລ້ວ
ປະຕິບັດຕາມຂັ້ນຕອນລຸ່ມນີ້ ເພື່ອເຂົ້າລະບົບ, ກວດເບິ່ງຮ້ານ ແລະ API ທີ່ທ່ານໄດ້ຮັບອະນຸຍາດ, ແລະ ກຽມລະບົບຂອງທ່ານໃຫ້ພ້ອມເຊື່ອມຕໍ່. ຕົວຢ່າງໃຊ້ curl; ພາສາອື່ນໃຊ້ຫຼັກການດຽວກັນ.
- ຂໍບັນຊີນັກພັດທະນາ
ບໍ່ມີການສະໝັກເອງ. ຕິດຕໍ່ຜູ້ດູແລລະບົບ SmartPOS ເພື່ອຂໍບັນຊີ ໂດຍແຈ້ງຊື່ບໍລິສັດ, ຮ້ານທີ່ຕ້ອງການເຊື່ອມ ແລະ ຂໍ້ມູນທີ່ລະບົບຂອງທ່ານຕ້ອງໃຊ້ (ເຊັ່ນ ອ່ານສິນຄ້າ, ສົ່ງອໍເດີ). ຜູ້ດູແລລະບົບຈະສົ່ງຊື່ຜູ້ໃຊ້ ແລະ ລະຫັດຜ່ານໃຫ້ທ່ານ.
- ເຂົ້າລະບົບໃນເວັບໄຊນີ້
ເປີດໜ້າ ເຂົ້າລະບົບ ແລ້ວປ້ອນຊື່ຜູ້ໃຊ້ ແລະ ລະຫັດຜ່ານ. ໜ້າ ບັນຊີ ຈະສະແດງຮ້ານທີ່ໄດ້ຮັບອະນຸຍາດ ແລະ ລາຍການ API ທີ່ທ່ານເຊື່ອມໄດ້. ຖ້າຍັງບໍ່ມີລາຍການ ໃຫ້ແຈ້ງຜູ້ດູແລລະບົບ.
- ຂໍ token ຈາກໂປຣແກຣມຂອງທ່ານ
ສົ່ງຊື່ຜູ້ໃຊ້ ແລະ ລະຫັດຜ່ານໄປທີ່ /api/v2/dev/auth/login. ຄຳຕອບມີ token (ຂຶ້ນຕົ້ນດ້ວຍ dvs_) ແລະ expiresAt. token ໃຊ້ໄດ້ 12 ຊົ່ວໂມງ; ເມື່ອໝົດອາຍຸ ໃຫ້ເຂົ້າລະບົບໃໝ່. ເກັບ token ໄວ້ຝັ່ງ server ເທົ່ານັ້ນ ຫ້າມໃສ່ໃນແອັບມືຖື ຫຼື ໜ້າເວັບ.
curl -X POST https://pos-v2.luayver.com/api/v2/dev/auth/login \ -H "content-type: application/json" \ -d '{ "username": "acme.dev", "password": "…" }' # ຄຳຕອບ { "token": "dvs_…", "expiresAt": "2026-11-14T03:00:00.000Z" } - ກວດເບິ່ງບັນຊີ, ຮ້ານ ແລະ API ທີ່ອະນຸຍາດ
ສົ່ງ token ໃນ header Authorization: Bearer <token>. field shop_code ແມ່ນຮ້ານທີ່ທ່ານເຊື່ອມໄດ້; field apis ແມ່ນລາຍການ API ທີ່ອະນຸຍາດ (key, label, method, path). ໃຫ້ລະບົບຂອງທ່ານອ່ານ apis ທຸກຄັ້ງທີ່ເລີ່ມເຮັດວຽກ ເພາະຜູ້ດູແລລະບົບອາດປ່ຽນລາຍການໄດ້ໂດຍບໍ່ຕ້ອງເຂົ້າລະບົບໃໝ່.
curl https://pos-v2.luayver.com/api/v2/dev/auth/me -H "authorization: Bearer dvs_…" # ຄຳຕອບ (ຕົວຢ່າງ) { "username": "acme.dev", "shop_code": "e2e", "shop_name": "ຮ້ານທົດສອບ", "scopes": ["products:read", "orders:write"], "apis": [ { "key": "products:read", "label": "ອ່ານສິນຄ້າ ແລະ ລາຄາ", "method": "GET", "path": "/api/open/v1/products" }, { "key": "orders:write", "label": "ສົ່ງອໍເດີເຂົ້າຮ້ານ (ເປັນບິນພັກ)", "method": "POST", "path": "/api/open/v1/orders" } ] } - ກຽມການສົ່ງຊ້ຳຢ່າງປອດໄພ
ທຸກຄຳຂໍທີ່ປ່ຽນຂໍ້ມູນ ຕ້ອງມີ header Idempotency-Key ທີ່ບໍ່ຊ້ຳກັນຕໍ່ການກະທຳໜຶ່ງ (ເຊັ່ນ UUID ຂອງອໍເດີໃນລະບົບທ່ານ). ຖ້າເຄືອຂ່າຍຂາດ ໃຫ້ສົ່ງໃໝ່ດ້ວຍ key ເດີມ; ຂໍ້ມູນຈະປ່ຽນພຽງຄັ້ງດຽວ. ເບິ່ງລາຍລະອຽດໃນຫົວຂໍ້ Idempotency-Key ລຸ່ມນີ້.
curl -X POST https://pos-v2.luayver.com/api/open/v1/orders \ -H "authorization: Bearer <API key>" \ -H "Idempotency-Key: 7f3c2a90-…" \ -H "content-type: application/json" -d '{ … }' - ອອກຈາກລະບົບ
ເມື່ອບໍ່ໃຊ້ token ແລ້ວ ໃຫ້ຍົກເລີກມັນ. ການປ່ຽນລະຫັດຜ່ານ, ປ່ຽນຮ້ານ ຫຼື ປິດບັນຊີ ໂດຍຜູ້ດູແລລະບົບ ຈະຍົກເລີກທຸກ token ຂອງບັນຊີທັນທີ.
curl -X POST https://pos-v2.luayver.com/api/v2/dev/auth/logout -H "authorization: Bearer dvs_…"
- ເສັ້ນທາງ /api/open/v1 ຍັງເອີ້ນໃຊ້ບໍ່ໄດ້: ຈະເປີດພ້ອມ API key (ເບິ່ງຫົວຂໍ້ ແອັບ ແລະ API key). ຕອນນີ້ ທ່ານສາມາດເຂົ້າລະບົບ, ອ່ານລາຍການ API ທີ່ອະນຸຍາດ ແລະ ຮັບເຫດການຜ່ານ webhook ໄດ້ແລ້ວ.
- ທ່ານເອີ້ນໄດ້ສະເພາະ API ທີ່ຢູ່ໃນ apis ແລະ ສະເພາະຮ້ານ shop_code ຂອງທ່ານເທົ່ານັ້ນ.
- ໜ້າ ສະຖານະ ສະແດງວ່າ API ໃຊ້ງານໄດ້ ແລະ ຕອບໄວເທົ່າໃດ.
ການຢືນຢັນຕົວຕົນ (Session) · ໃຊ້ໄດ້ແລ້ວ
ບັນຊີນັກພັດທະນາ ແຍກຈາກບັນຊີ POS ເພື່ອຄວາມປອດໄພ: ຜູ້ດູແລລະບົບເປັນຜູ້ສ້າງບັນຊີ ແລະ ກຳນົດຮ້ານທີ່ທ່ານເຊື່ອມໄດ້. ເຂົ້າລະບົບດ້ວຍຊື່ຜູ້ໃຊ້ ແລະ ລະຫັດຜ່ານ ແລ້ວສົ່ງ token ໃນ header Authorization: Bearer <token>. token ຂອງ POS ໃຊ້ຢູ່ບ່ອນນີ້ບໍ່ໄດ້ ແລະ token ນັກພັດທະນາ ໃຊ້ກັບ POS ບໍ່ໄດ້.
| Method | Path | Auth | ລາຍລະອຽດ |
|---|---|---|---|
| POST | /api/v2/dev/auth/login{ "username": "acme.dev", "password": "…" } | ບໍ່ຕ້ອງການ | ເຂົ້າລະບົບ; ຕອບກັບ token (ຂຶ້ນຕົ້ນດ້ວຍ dvs_) ແລະ ເວລາໝົດອາຍຸ |
| GET | /api/v2/dev/auth/me | Bearer | ຂໍ້ມູນບັນຊີ ແລະ ຮ້ານທີ່ໄດ້ຮັບອະນຸຍາດ |
| POST | /api/v2/dev/auth/logout | Bearer | ອອກຈາກລະບົບ ແລະ ຍົກເລີກ token |
| GET | /api/v2/health | ບໍ່ຕ້ອງການ | ສະຖານະຂອງ API ແລະ ຖານຂໍ້ມູນ |
ການສົ່ງຊ້ຳຢ່າງປອດໄພ (Idempotency-Key) · ໃຊ້ໄດ້ແລ້ວ
ທຸກຄຳຂໍທີ່ປ່ຽນຂໍ້ມູນ ສົ່ງ header Idempotency-Key (ຄ່າບໍ່ຊ້ຳກັນຕໍ່ການກະທຳໜຶ່ງ). ການສົ່ງຊ້ຳດ້ວຍ key ເດີມ ຈະໄດ້ຄຳຕອບເດີມ ແລະ ຂໍ້ມູນປ່ຽນພຽງຄັ້ງດຽວ.
- 425 — ຄຳຂໍທຳອິດຍັງດຳເນີນການຢູ່: ລໍຖ້າແລ້ວສົ່ງໃໝ່
- 409 idempotency_applied — ການກະທຳນີ້ສຳເລັດແລ້ວ: ຢ່າສົ່ງໃໝ່
- 422 — key ເດີມ ແຕ່ເນື້ອໃນຄຳຂໍຕ່າງກັນ
ລະຫັດຂໍ້ຜິດພາດ · ໃຊ້ໄດ້ແລ້ວ
ເມື່ອຄຳຂໍບໍ່ສຳເລັດ API ຕອບເປັນ JSON { "error": "<ລະຫັດ>" } ພ້ອມ HTTP status ລຸ່ມນີ້.
- 400 invalid_input — ຂໍ້ມູນທີ່ສົ່ງມາບໍ່ຖືກຮູບແບບ; field issues ບອກວ່າຜິດບ່ອນໃດ
- 401 invalid_credentials — ຊື່ຜູ້ໃຊ້ ຫຼື ລະຫັດຜ່ານບໍ່ຖືກ, ຫຼື ບັນຊີຖືກປິດ
- 401 unauthorized — ບໍ່ມີ token, token ໝົດອາຍຸ ຫຼື ຖືກຍົກເລີກ: ໃຫ້ເຂົ້າລະບົບໃໝ່
- 429 too_many_attempts — ປ້ອນລະຫັດຜ່ານຜິດຫຼາຍເທື່ອ: ລໍຖ້າ 15 ນາທີ ແລ້ວລອງໃໝ່
- 5xx — ຂໍ້ຜິດພາດຝັ່ງ server: ລໍຖ້າຈັກໜ່ອຍ ແລ້ວສົ່ງໃໝ່ດ້ວຍ Idempotency-Key ເດີມ
ແອັບ ແລະ API key · ກຳລັງພັດທະນາ
ລົງທະບຽນແອັບ, ຮັບ key ແບບທົດສອບ (sk_test_) ແລະ ແບບໃຊ້ງານຈິງ (sk_live_), ກຳນົດຂອບເຂດສິດ (scope) ແລະ ເຈົ້າຂອງຮ້ານເປັນຜູ້ອະນຸຍາດ. ເສັ້ນທາງ API ສາທາລະນະ: /api/open/v1.
Webhook ແລະ ເຫດການ · ໃຊ້ໄດ້ແລ້ວ
SmartPOS ສົ່ງເຫດການຂອງຮ້ານທ່ານ ໄປຫາ URL (https) ຂອງທ່ານ ທັນທີທີ່ເກີດຂຶ້ນ (ພາຍໃນປະມານ 15 ວິນາທີ). ຕ້ອງມີສິດ webhooks:manage ຈາກຜູ້ດູແລລະບົບ. ຈັດການ webhook ໄດ້ໃນໜ້າ Webhook ຂອງເວັບໄຊນີ້ ຫຼື ຜ່ານ API ລຸ່ມນີ້.
- ເຫດການທີ່ມີ
sale.completed — ຂາຍສຳເລັດ (ບິນໃໝ່, ມີລາຍການສິນຄ້າ ແລະ ຍອດລວມ). sale.voided — ຍົກເລີກບິນ. price.changed — ປ່ຽນລາຄາຂາຍສິນຄ້າ (ລາຄາເກົ່າ ແລະ ໃໝ່). webhook.ping — ເຫດການທົດສອບຈາກປຸ່ມ ທົດສອບ.
POST https://your-app.example/webhook webhook-id: 3f1c… (ລະຫັດເຫດການ: ໃຊ້ກັນການຮັບຊ້ຳ) webhook-timestamp: 1763020800 webhook-signature: v1,K5o…= { "id": "3f1c…", "type": "sale.completed", "occurred_at": "2026-11-13T08:00:00.000Z", "data": { "receipt_no": "T1-000123", "doc_no": "INV-2026-0042", "branch_id": "…", "sold_at": "2026-11-13T08:00:00.000Z", "currency": "LAK", "total": 150000, "items": [{ "product_id": "…", "variant_id": null, "name": "ນ້ຳດື່ມ", "qty": 2, "unit_price": 5000, "discount": 0, "line_total": 10000 }] } } - ກວດລາຍເຊັນທຸກຄັ້ງ
ລາຍເຊັນເປັນແບບ Standard Webhooks: HMAC-SHA256 ຂອງ "webhook-id.webhook-timestamp.body" ດ້ວຍ secret (ສ່ວນຫຼັງ whsec_ ຖອດລະຫັດ base64). ໃຊ້ body ດິບທີ່ໄດ້ຮັບ ກ່ອນແປງເປັນ JSON. ປະຕິເສດຖ້າລາຍເຊັນບໍ່ກົງ ຫຼື timestamp ຫ່າງຈາກເວລາປັດຈຸບັນເກີນ 5 ນາທີ. ຫຼື ໃຊ້ library standardwebhooks ທີ່ມີໃຫ້ຫຼາຍພາສາ.
// Node.js import { createHmac, timingSafeEqual } from "node:crypto"; function verify(secret, headers, rawBody) { const key = Buffer.from(secret.replace("whsec_", ""), "base64"); const id = headers["webhook-id"], ts = headers["webhook-timestamp"]; if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; const want = "v1," + createHmac("sha256", key).update(`${id}.${ts}.${rawBody}`).digest("base64"); const got = headers["webhook-signature"].split(" ").find((s) => s.length === want.length) ?? ""; return got.length === want.length && timingSafeEqual(Buffer.from(got), Buffer.from(want)); } - ຕອບກັບໃຫ້ໄວ ແລະ ຮັບຊ້ຳໄດ້
ຕອບ HTTP 2xx ພາຍໃນ 10 ວິນາທີ ແລ້ວຈຶ່ງປະມວນຜົນພາຍຫຼັງ. ຖ້າບໍ່ໄດ້ 2xx ລະບົບຈະສົ່ງໃໝ່ຫຼັງ 1, 5, 30, 120 ແລະ 720 ນາທີ; ຫຼັງຈາກນັ້ນ ຈະໝາຍວ່າ ສົ່ງບໍ່ສຳເລັດ ແລະ ທ່ານກົດ ສົ່ງໃໝ່ ໄດ້ໃນໜ້າ Webhook. ເຫດການດຽວກັນອາດມາຮອດຫຼາຍກວ່າໜຶ່ງເທື່ອ: ໃຫ້ກວດ webhook-id ແລະ ຂ້າມອັນທີ່ເຄີຍປະມວນຜົນແລ້ວ.
| Method | Path | Auth | ລາຍລະອຽດ |
|---|---|---|---|
| GET | /api/v2/dev/webhooks | Bearer | ລາຍການ webhook, ເຫດການທີ່ມີ ແລະ ປະຫວັດການສົ່ງ 50 ລາຍການລ່າສຸດ |
| POST | /api/v2/dev/webhooks{ "url": "https://your-app.example/webhook", "events": ["sale.completed", "price.changed"] } | Bearer | ເພີ່ມ webhook; ຕອບກັບ secret (whsec_…) ພຽງຄັ້ງດຽວ |
| DELETE | /api/v2/dev/webhooks/:id | Bearer | ລົບ webhook |
| POST | /api/v2/dev/webhooks/:id/ping | Bearer | ສົ່ງເຫດການທົດສອບ webhook.ping ທັນທີ ແລະ ຕອບຜົນການສົ່ງ |
| POST | /api/v2/dev/webhooks/deliveries/:id/resend | Bearer | ສົ່ງລາຍການທີ່ບໍ່ສຳເລັດໃໝ່ (webhook-id ເດີມ) |
- 403 scope_missing — ບັນຊີຍັງບໍ່ມີສິດ webhooks:manage
- 403 no_shop — ຍັງບໍ່ໄດ້ກຳນົດຮ້ານໃຫ້ບັນຊີ
- 409 too_many_webhooks — ຮ້ານໜຶ່ງມີ webhook ໄດ້ສູງສຸດ 5 ອັນ
- ປະຫວັດການສົ່ງ ເກັບໄວ້ 30 ວັນ
ຮ້ານທົດສອບ (Sandbox) · ກຳລັງພັດທະນາ
ນັກພັດທະນາແຕ່ລະຄົນຈະໄດ້ຮ້ານທົດສອບແຍກຕ່າງຫາກ ທີ່ມີສິນຄ້າຕົວຢ່າງ; key ທົດສອບບໍ່ສາມາດເຂົ້າເຖິງຮ້ານຈິງໄດ້.