ចាប់ផ្តើម
- ប្រើគម្រោង Starter ឬខ្ពស់ជាងនេះShop API ជាការតភ្ជាប់មួយ ដូច Telegram និង BasilBook។ ប្រសិនបើហាងរបស់អ្នកប្តូរទៅ Free នៅពេលក្រោយ សោដែលបានបង្កើតរួច នៅតែប្រើបាន។
- បន្ថែមសោ APIម្ចាស់ហាងបើក ការកំណត់ រួច សោ API ក្នុងផ្ទាំងគ្រប់គ្រង។ ដាក់ឈ្មោះសោ («គេហទំព័រ») ជ្រើសអ្វីដែលវាអាចធ្វើ និងសាខាណាខ្លះ។ សោពេញលេញបង្ហាញតែម្តងគត់៖ សូមចម្លងទុកពេលនោះ។
- ផ្ញើវាជាមួយគ្រប់សំណើក្នុង header
Authorizationដូចខាងក្រោម។ គ្រប់អាសយដ្ឋាននៅលើទំព័រនេះ ចាប់ផ្តើមដោយhttps://api.billandgo.app/v1។
Authorization: Bearer bg_live_…រក្សាសោនៅលើម៉ាស៊ីនមេរបស់អ្នក កុំដាក់ក្នុងគេហទំព័រ ឬកម្មវិធីទូរសព្ទ៖ អ្នកណាមានវា អាចអានទិន្នន័យហាងរបស់អ្នក។ ប្រសិនបើវាលេចធ្លាយ សូមដកវាចេញក្នុងផ្ទាំងគ្រប់គ្រង ហើយបង្កើតសោថ្មី។
សោ និងសិទ្ធិ
| ក្នុងផ្ទាំងគ្រប់គ្រង | សោអាច |
|---|---|
| អាន | គ្រប់ GET នៅលើទំព័រនេះ។ |
| អាន និងដាក់ការកម្ម៉ង់ | ព្រមទាំង POST /orders និង POST /orders/:id/payment។ |
- សាខា។ សោដែលមិនបានជ្រើសសាខា អាចចូលគ្រប់សាខា រួមទាំងសាខាដែលអ្នកបើកនៅពេលក្រោយ។ ជ្រើសសាខា ហើយសោនឹងមើលឃើញ និងដាក់ការកម្ម៉ង់បានតែនៅសាខាទាំងនោះ។
- ដកសោចេញ។ ការដកសោចេញ បញ្ឈប់វានៅសំណើបន្ទាប់។ សោដែលបានដកចេញ មិនអាចប្រើបានទៀតទេ។
- សោដែលដំណើរការរហូតដល់ 10 ក្នុងមួយអាជីវកម្ម។ បង្កើតមួយសម្រាប់ប្រព័ន្ធនីមួយៗ ដើម្បីអាចដកមួយចេញ ដោយមិនបញ្ឈប់ប្រព័ន្ធផ្សេងទៀត។
- សម្រាប់តែ API នេះ។ សោដំណើរការតែលើអាសយដ្ឋានក្រោម
/v1ហើយការចូលផ្ទាំងគ្រប់គ្រង មិនដំណើរការនៅទីនោះទេ។
ទម្រង់ចម្លើយ
- JSON ទាំងចូល និងចេញ។ វាលដែល API មិនស្គាល់ ត្រូវបានបដិសេធ ដូច្នេះកំហុសអក្ខរាវិរុទ្ធមិនដែលរំលងទេ។
- ពេលវេលា ជា UTC ដូចជា
2026-10-23T03:04:05.123Z។ ចម្លើយដែលមានពេលវេលា ក៏មានtimezoneរបស់ហាងដែរ ហើយថ្ងៃត្រូវរាប់តាមនាឡិកានោះ។ - ប្រាក់ ជាចំនួនគត់ក្នុងឯកតាតូចបំផុត ក្បែរ
currency៖20900ជាមួយKHRគឺ 20 900 រៀល450ជាមួយUSDគឺ $4.50។ - បញ្ជី មកក្នុងទម្រង់
{ "data": [ … ] }។ - កំហុស មកក្នុងទម្រង់
{ "error": { "code", "message", "details" } }។ មើល កំហុស។
Endpoint
GET/stores
សោណាមួយក៏បាន។
សាខាដែលសោនេះអាចចូលបាន។
{
"data": [
{
"id": "0b6c…", "name": "Riverside", "address": "St. 108, Phnom Penh",
"lat": 11.57, "lng": 104.92, "phone": "+855 12 345 678",
"openingHours": { "mon": [{ "open": "07:00", "close": "21:00" }] },
"active": true, "taxRate": 10, "deliveryFee": 2000, "deliveryRadiusM": 5000,
"currency": "KHR", "timezone": "Asia/Phnom_Penh"
}
],
"timezone": "Asia/Phnom_Penh"
}GET/orders
សោណាមួយក៏បាន។
ការកម្ម៉ង់ ដែលផ្លាស់ប្តូរយូរជាងគេមកមុន ម្តងមួយទំព័រ៖ ប្រើវាដើម្បីធ្វើឱ្យប្រព័ន្ធផ្សេងទៀតទាន់សម័យ។
| វាល | ជាអ្វី |
|---|---|
storeId | សាខាមួយ (ដែលសោនេះចូលបាន)។ |
status | placed, confirmed, preparing, ready, out_for_delivery, delivered, completed, rejected ឬ cancelled។ |
updatedSince | ពេលវេលា UTC មួយ៖ ការកម្ម៉ង់ដែលផ្លាស់ប្តូរនៅ ឬក្រោយពេលនោះ។ |
cursor | nextCursor នៃទំព័រមុន។ |
limit | ពី 1 ដល់ 100។ 50 ពេលមិនបានដាក់។ |
{
"data": [ { "id": "9a1e…", "…": "an order, as below" } ],
"nextCursor": "WyIyMDI2LTEwLTIzVDAzOjA0OjA1LjEyMzQ1NloiLCI3ZjFjLi4uIl0",
"hasMore": false,
"timezone": "Asia/Phnom_Penh"
}រក្សាឱ្យទាន់សម័យ។ តាម nextCursor ដរាបណា hasMore គឺ true។ រក្សា nextCursor ចុងក្រោយទុក ទោះ hasMore គឺ false ហើយសួរម្តងទៀតជាមួយវានៅពេលក្រោយ៖ ការកម្ម៉ង់ដែលផ្លាស់ប្តូរចាប់ពីពេលនោះ នឹងត្រលប់មកវិញ ម្តងសម្រាប់ការផ្លាស់ប្តូរនីមួយៗ។ នាឡិការបស់អ្នកមិនពាក់ព័ន្ធទេ។
GET /orders/:id
ការកម្ម៉ង់មួយ។ បន្ទាត់ និងតម្លៃ គឺដូចពេលលក់ មិនមែនតាមម៉ឺនុយថ្ងៃនេះទេ។ source គឺ app, api, counter ឬ phone។
អាជីវកម្មដែលមានសាខាច្រើន អាចបញ្ជូនការកម្ម៉ង់ទៅសាខាផ្សេង ពេលសាខាទីមួយមិនទទួលទាន់ពេល។ storeId និង storeName គឺជាកន្លែងដែលវានៅឥឡូវនេះ ហើយ callNumber គឺជារបស់សាខានោះ លេខយោង និងតម្លៃនៅដដែលដូចពេលដាក់ ហើយវាផ្លាស់ទៅតែសាខាដែលសោរបស់អ្នកអាចដាក់ការកម្ម៉ង់។ estimatedReadyAt គឺជាការប៉ាន់ស្មានពេលដែលវានឹងរួចរាល់៖ ពេលវេលាថ្មីៗរបស់សាខាក្នុងការធ្វើការកម្ម៉ង់មួយ សម្រាប់ការកម្ម៉ង់នេះ និងនីមួយៗដែលនៅមុនវា។ វាជា null ពេលការកម្ម៉ង់រួចរាល់ និងក្នុងបញ្ជី។
{
"id": "9a1e…", "storeId": "0b6c…", "storeName": "Riverside",
"estimatedReadyAt": "2026-10-23T03:16:05.000Z", "reference": "20261023-0042", "callNumber": 42,
"source": "api", "orderType": "takeaway", "status": "placed",
"paymentStatus": "unpaid", "paymentMethod": null,
"items": [
{
"productId": "p1…", "name": "Iced Latte", "nameKm": "ឡាតេទឹកកក", "qty": 2, "pack": null,
"options": [{ "group": "Sugar", "name": "Extra sweet", "nameKm": null, "priceDelta": 500 }],
"basePrice": 9000, "unitPrice": 9500, "lineTotal": 19000, "note": null
}
],
"subtotal": 19000, "discount": 0, "discounts": [], "deliveryFee": 0, "tax": 1900, "total": 20900,
"currency": "KHR",
"customerName": "Dara", "customerPhone": null, "deliveryAddress": null,
"deliveryLat": null, "deliveryLng": null, "notes": null, "cancelReason": null,
"placedAt": "2026-10-23T03:04:05.123Z", "paidAt": null, "cancelledAt": null, "refundedAt": null,
"createdAt": "2026-10-23T03:04:05.123Z", "updatedAt": "2026-10-23T03:04:05.123Z",
"timezone": "Asia/Phnom_Penh"
}GET/sales/daily?from=YYYY-MM-DD&to=YYYY-MM-DD
សោណាមួយក៏បាន។
ចំណូលតាមថ្ងៃលក់ តាមសាខា និងរូបិយប័ណ្ណ យ៉ាងច្រើន 92 ថ្ងៃ។ បន្ថែម storeId សម្រាប់សាខាមួយ។
{
"from": "2026-10-23", "to": "2026-10-23", "timezone": "Asia/Phnom_Penh",
"data": [
{
"day": "2026-10-23", "storeId": "0b6c…", "currency": "KHR",
"orders": 2, "sales": 15000, "discount": 0, "tax": 909, "deliveryFees": 0,
"refunds": 1, "refunded": 5000, "net": 10000
}
]
}orders,sales,discount,tax,deliveryFees៖ ការកម្ម៉ង់ដែលបានបង់នៅថ្ងៃនោះ តាមពេលដែលប្រាក់ចូល រួមទាំងការកម្ម៉ង់ដែលសងប្រាក់វិញនៅពេលក្រោយ។refunds,refunded៖ ប្រាក់ដែលសងវិញនៅថ្ងៃនោះ មិនថាបានបង់ពេលណាទេ។netគឺsalesដកrefunded។- ថ្ងៃដែលគ្មានអ្វី ត្រូវបានលុបចេញ។ កុំបូកចំនួនប្រាក់ដែលជារូបិយប័ណ្ណខុសគ្នាចូលគ្នា។
GET/stock?storeId=…
សោណាមួយក៏បាន។
អ្វីដែលនៅលើធ្នើ ជាឯកតាទោល សម្រាប់គ្រប់ផលិតផលដែលហាងរាប់។ គ្រប់សាខាដែលសោចូលបាន ពេលមិនបានដាក់ storeId។ វាអាចតិចជាងសូន្យ៖ ការលក់មិនដែលត្រូវបានបដិសេធដោយសារស្តុកទេ។
{
"data": [
{
"storeId": "0b6c…", "productId": "p9…", "name": "Coca-Cola", "nameKm": null, "unitName": "Can",
"onHand": 77, "lowStockAt": 24, "packs": [{ "id": "u1…", "name": "Box", "contains": 24 }]
}
],
"timezone": "Asia/Phnom_Penh"
}POST/orders
ត្រូវការសោដែលអាចដាក់ការកម្ម៉ង់។
ផ្ញើការកម្ម៉ង់ទៅ POS។ ត្រូវមាន header Idempotency-Key (មើល ផ្ញើម្តងទៀតដោយសុវត្ថិភាព)។
curl -X POST https://api.billandgo.app/v1/orders \
-H "Authorization: Bearer bg_live_…" \
-H "Idempotency-Key: web-order-58213" \
-H "Content-Type: application/json" \
-d '{
"storeId": "0b6c…",
"orderType": "takeaway",
"items": [
{ "productId": "p1…", "qty": 2, "modifierIds": ["o2…"], "note": "less ice" },
{ "productId": "p9…", "qty": 1, "unitId": "u1…" }
],
"customerName": "Dara",
"customerPhone": "+855 12 345 678",
"notes": "Pick up at 7"
}'| វាល | ជាអ្វី |
|---|---|
storeId | សាខាដែលត្រូវផ្ញើទៅ។ |
orderType | takeaway ឬ delivery។ ការដឹកជញ្ជូនត្រូវការ customerPhone និង deliveryAddress ហើយអាចមានទីតាំង (deliveryLat និង deliveryLng) ដែលត្រូវបានបដិសេធ ប្រសិនបើហួសចម្ងាយដឹកជញ្ជូនរបស់សាខា។ |
items[] | productId, qty (1 ដល់ 99), modifierIds (id ជម្រើសពីម៉ឺនុយ គ្រប់ក្រុមរួមគ្នា), unitId (កញ្ចប់), grams (ទម្ងន់ដែលថ្លឹង សម្រាប់ផលិតផលដែលមាន perGrams), note (សម្រាប់ផ្ទះបាយ)។ |
customerName, customerPhone, notes | មិនចាំបាច់។ |
មិនចាំបាច់ផ្ញើតម្លៃទេ៖ Bill&Go គិតតម្លៃគ្រប់ការកម្ម៉ង់ ដូច POS ដែរ ជាមួយច្បាប់ជម្រើស ម៉ឺនុយរបស់សាខា ការបញ្ចុះតម្លៃដែលកំពុងដំណើរការ ពន្ធ និងថ្លៃដឹកជញ្ជូន។ ចម្លើយគឺ 201 ជាមួយការកម្ម៉ង់។
វាមកដល់ជាការកម្ម៉ង់អនឡាញ៖ ស្ថានភាព placed មិនទាន់បង់។ បុគ្គលិកទទួលបានដំណឹង ហើយបញ្ជរទទួល ឬបដិសេធវា។ សំបុត្រផ្ទះបាយរបស់វាបោះពុម្ពពេលវាត្រូវបានទទួល ឬភ្លាមៗ ប្រសិនបើច្បាប់បោះពុម្ពរបស់ហាងកំណត់ដូច្នេះ។ តាមដានវាជាមួយ GET /orders។
POST/orders/:id/payment
ត្រូវការសោដែលអាចដាក់ការកម្ម៉ង់។
កំណត់ថាបានបង់ សម្រាប់ការកម្ម៉ង់ដែលបង់នៅកន្លែងផ្សេង៖ ដៃគូដឹកជញ្ជូន ឬគេហទំព័ររបស់អ្នក បានគិតប្រាក់អតិថិជនរួចហើយ។ បន្ទាប់មក POS ប្រគល់ការកម្ម៉ង់ ដោយមិនសុំប្រាក់។
curl -X POST https://api.billandgo.app/v1/orders/9a1e…/payment \
-H "Authorization: Bearer bg_live_…" \
-H "Idempotency-Key: grab-GF-58213" \
-H "Content-Type: application/json" \
-d '{ "method": "grab", "reference": "GF-58213" }'| វាល | ជាអ្វី |
|---|---|
method | កូដនៃវិធីបង់ប្រាក់មួយរបស់ហាង ដែលទទួលនៅសាខានេះ (ការកំណត់ វិធីបង់ប្រាក់ ឧទាហរណ៍ card, khqr ឬវិធីដែលហាងបានបន្ថែម ដូចជា grab)។ មិនមែនវិធីសាច់ប្រាក់ទេ៖ សាច់ប្រាក់រាប់ក្នុងថតប្រាក់នៅ POS។ |
reference | មិនចាំបាច់។ id ផ្ទាល់ខ្លួនរបស់អ្នកសម្រាប់ការបង់ប្រាក់ រហូតដល់ 100 តួអក្សរ ដែលរក្សាទុកជាមួយវា។ |
Idempotency-Key | header មិនចាំបាច់។ សោដដែលម្តងទៀត ឆ្លើយជាការផ្ញើម្តងទៀត។ |
ចម្លើយគឺ 200 ជាមួយការកម្ម៉ង់ ឥឡូវជា "paymentStatus": "paid"។ ស្ថានភាពរបស់វាមិនផ្លាស់ប្តូរទេ៖ ការកម្ម៉ង់ដែលកំពុងរង់ចាំ នៅតែរង់ចាំបញ្ជរទទួល។ វិក្កយបត្របោះពុម្ពនៅហាង។
{
"id": "9a1e…", "status": "placed",
"paymentStatus": "paid", "paymentMethod": "grab",
"total": 20900, "currency": "KHR",
"paidAt": "2026-10-23T03:09:41.002Z",
"…": "the rest of the order"
}- ផ្ញើម្តងទៀត ជាមួយ
Idempotency-Keyដដែល ឬជាមួយវិធី និងលេខយោងដដែល៖ 200 ជាមួយការកម្ម៉ង់ និងIdempotent-Replayed: true។ គ្មានអ្វីបង់ពីរដងទេ។ - បានបង់ដោយវិធីផ្សេងរួចហើយ (នៅ POS ឬជាមួយលេខយោងផ្សេង) បានសងវិញ បានលុបចោល ឬ ត្រូវបានបដិសេធ៖ 409
CONFLICT។ - ការកម្ម៉ង់នៅសាខាដែលសោនេះមិនអាចប្រើ៖ 404។
ផ្ញើម្តងទៀតដោយសុវត្ថិភាព
បណ្តាញអាចដាច់។ ដើម្បីផ្ញើសំណើម្តងទៀត ដោយមិនបង្កើតការកម្ម៉ង់ទីពីរ ឬការបង់ប្រាក់ទីពីរ សូមដាក់ header Idempotency-Key៖ អត្ថបទណាមួយដែលអ្នកបង្កើត ពី 1 ដល់ 255 តួអក្សរដែលអាចបោះពុម្ពបាន គ្មានដកឃ្លា មួយសម្រាប់ការកម្ម៉ង់ ឬការបង់ប្រាក់នីមួយៗ ហើយដដែលរាល់ពេលអ្នកព្យាយាមម្តងទៀត។
- សោដដែលម្តងទៀត ឆ្លើយ 200 ជាមួយអ្វីដែលសំណើទីមួយបានបង្កើត និង
Idempotent-Replayed: trueទោះបីទាំងពីរមកដល់ក្នុងពេលតែមួយក៏ដោយ។ - សោនេះជារបស់សោ API របស់អ្នក៖ ប្រព័ន្ធពីរដែលប្រើអត្ថបទដូចគ្នា មិនដែលជួបគ្នាទេ។
- ការផ្ញើម្តងទៀតត្រូវបានឆ្លើយពីសំណើទីមួយ មិនថាវាមានអ្វីទេ៖ កុំប្រើសោតែមួយសម្រាប់ការកម្ម៉ង់ពីរផ្សេងគ្នា។
- សំណើដែលបរាជ័យ (400, 422…) មិនបានធ្វើអ្វីទេ ដូច្នេះការផ្ញើម្តងទៀត គឺជាការព្យាយាមម្តងទៀតពិតប្រាកដ។
កំហុស
{
"error": {
"code": "VALIDATION_ERROR",
"message": "This shop does not take that payment method",
"details": { "method": "bitcoin" }
}
}សម្រេចតាម code និង details។ message សរសេរសម្រាប់មនុស្ស ហើយអាចផ្លាស់ប្តូរ។
| ស្ថានភាព | code | ពេលណា |
|---|---|---|
| 400 | VALIDATION_ERROR | វាល ឬ query ដែលមិនត្រូវ ផលិតផលដែលសាខានេះមិនលក់ វិធីបង់ប្រាក់ដែលហាងមិនទទួល ឬសាច់ប្រាក់ដែលផ្ញើជាការបង់ប្រាក់។ details.issues បង្ហាញឈ្មោះវាល។ |
| 401 | UNAUTHORIZED | គ្មានសោ សោខុស ឬសោដែលម្ចាស់បានដកចេញ។ |
| 403 | FORBIDDEN | សោអានតែប៉ុណ្ណោះ ផ្ញើការកម្ម៉ង់ ឬការបង់ប្រាក់ ឬសាខាដែលសោនេះមិនមានសិទ្ធិប្រើ។ |
| 404 | NOT_FOUND | សាខា ការកម្ម៉ង់ ឬផលិតផលរបស់អាជីវកម្មផ្សេង ឬការកម្ម៉ង់នៅសាខាដែលសោនេះមិនអាចប្រើ។ |
| 409 | CONFLICT | ការកំណត់ថាបានបង់ សម្រាប់ការកម្ម៉ង់ដែលបានបង់ដោយវិធីផ្សេងរួចហើយ បានសងវិញ បានលុបចោល ឬត្រូវបានបដិសេធ។ |
| 422 | INVALID_MODIFIER_SELECTION | ជម្រើសដែលល្មើសច្បាប់របស់ក្រុម៖ ខ្វះជម្រើសចាំបាច់ ឬជ្រើសច្រើនពេក។ |
| 429 | TOO_MANY_REQUESTS | សំណើច្រើនពេកក្នុងមួយនាទី។ រង់ចាំចំនួនវិនាទីក្នុង Retry-After។ |
ដែនកំណត់
- 60 សំណើក្នុងមួយនាទី សម្រាប់សោនីមួយៗ។ មួយទៀត ហើយចម្លើយគឺ 429
TOO_MANY_REQUESTSជាមួយ headerRetry-Afterប្រាប់ចំនួនវិនាទីដែលត្រូវរង់ចាំ។ - ការព្យាយាមបរាជ័យ 20 ដងក្នុងមួយនាទី ពីអាសយដ្ឋានមួយ (គ្មានសោ សោខុស ឬសោដែលបានដកចេញ) បន្ទាប់មក 429 ដែរ។
- នាទីចាប់ផ្តើមពីសំណើទីមួយ ហើយរាប់លើគ្រប់ម៉ាស៊ីនមេរបស់ Bill&Go ដូច្នេះវានៅតែមានសុពលភាព ពេលយើងធ្វើបច្ចុប្បន្នភាព។
កំពុងធ្វើសមកាលកម្មការកម្ម៉ង់? សួរ GET /orders ជាមួយ cursor ចុងក្រោយរបស់អ្នក រៀងរាល់ប្រហែល 30 វិនាទី ដែលនៅក្នុងដែនកំណត់យ៉ាងស្រួល។
