Bill&Go Shop API។

ភ្ជាប់គេហទំព័រ ប្រព័ន្ធគណនេយ្យ ឬដៃគូដឹកជញ្ជូនរបស់អ្នកទៅហាង។ អានម៉ឺនុយ ការកម្ម៉ង់ ការលក់ និងស្តុក ផ្ញើការកម្ម៉ង់ទៅ POS និងកំណត់ថាបានបង់ សម្រាប់ការកម្ម៉ង់ដែលបង់នៅកន្លែងផ្សេង។

សំណើដំបូងរបស់អ្នក
curl https://api.billandgo.app/v1/stores \
  -H "Authorization: Bearer bg_live_…"
Bill&Go Shop API នៅកណ្តាល ភ្ជាប់ទៅគេហទំព័រ កម្មវិធីទូរសព្ទ ដៃគូដឹកជញ្ជូន របាយការណ៍ អ្នកអភិវឌ្ឍន៍ និង POS។

ចាប់ផ្តើម

  1. ប្រើគម្រោង Starter ឬខ្ពស់ជាងនេះShop API ជាការតភ្ជាប់មួយ ដូច Telegram និង BasilBook។ ប្រសិនបើហាងរបស់អ្នកប្តូរទៅ Free នៅពេលក្រោយ សោដែលបានបង្កើតរួច នៅតែប្រើបាន។
  2. បន្ថែមសោ APIម្ចាស់ហាងបើក ការកំណត់ រួច សោ API ក្នុងផ្ទាំងគ្រប់គ្រង។ ដាក់ឈ្មោះសោ («គេហទំព័រ») ជ្រើសអ្វីដែលវាអាចធ្វើ និងសាខាណាខ្លះ។ សោពេញលេញបង្ហាញតែម្តងគត់៖ សូមចម្លងទុកពេលនោះ។
  3. ផ្ញើវាជាមួយគ្រប់សំណើក្នុង 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សាខាមួយ (ដែលសោនេះចូលបាន)។
statusplaced, confirmed, preparing, ready, out_for_delivery, delivered, completed, rejected ឬ cancelled។
updatedSinceពេលវេលា UTC មួយ៖ ការកម្ម៉ង់ដែលផ្លាស់ប្តូរនៅ ឬក្រោយពេលនោះ។
cursornextCursor នៃទំព័រមុន។
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សាខាដែលត្រូវផ្ញើទៅ។
orderTypetakeaway ឬ 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-Keyheader មិនចាំបាច់។ សោដដែលម្តងទៀត ឆ្លើយជាការផ្ញើម្តងទៀត។

ចម្លើយគឺ 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ពេលណា
400VALIDATION_ERRORវាល ឬ query ដែលមិនត្រូវ ផលិតផលដែលសាខានេះមិនលក់ វិធីបង់ប្រាក់ដែលហាងមិនទទួល ឬសាច់ប្រាក់ដែលផ្ញើជាការបង់ប្រាក់។ details.issues បង្ហាញឈ្មោះវាល។
401UNAUTHORIZEDគ្មានសោ សោខុស ឬសោដែលម្ចាស់បានដកចេញ។
403FORBIDDENសោអានតែប៉ុណ្ណោះ ផ្ញើការកម្ម៉ង់ ឬការបង់ប្រាក់ ឬសាខាដែលសោនេះមិនមានសិទ្ធិប្រើ។
404NOT_FOUNDសាខា ការកម្ម៉ង់ ឬផលិតផលរបស់អាជីវកម្មផ្សេង ឬការកម្ម៉ង់នៅសាខាដែលសោនេះមិនអាចប្រើ។
409CONFLICTការកំណត់ថាបានបង់ សម្រាប់ការកម្ម៉ង់ដែលបានបង់ដោយវិធីផ្សេងរួចហើយ បានសងវិញ បានលុបចោល ឬត្រូវបានបដិសេធ។
422INVALID_MODIFIER_SELECTIONជម្រើសដែលល្មើសច្បាប់របស់ក្រុម៖ ខ្វះជម្រើសចាំបាច់ ឬជ្រើសច្រើនពេក។
429TOO_MANY_REQUESTSសំណើច្រើនពេកក្នុងមួយនាទី។ រង់ចាំចំនួនវិនាទីក្នុង Retry-After។

ដែនកំណត់

  • 60 សំណើក្នុងមួយនាទី សម្រាប់សោនីមួយៗ។ មួយទៀត ហើយចម្លើយគឺ 429 TOO_MANY_REQUESTS ជាមួយ header Retry-After ប្រាប់ចំនួនវិនាទីដែលត្រូវរង់ចាំ។
  • ការព្យាយាមបរាជ័យ 20 ដងក្នុងមួយនាទី ពីអាសយដ្ឋានមួយ (គ្មានសោ សោខុស ឬសោដែលបានដកចេញ) បន្ទាប់មក 429 ដែរ។
  • នាទីចាប់ផ្តើមពីសំណើទីមួយ ហើយរាប់លើគ្រប់ម៉ាស៊ីនមេរបស់ Bill&Go ដូច្នេះវានៅតែមានសុពលភាព ពេលយើងធ្វើបច្ចុប្បន្នភាព។

កំពុងធ្វើសមកាលកម្មការកម្ម៉ង់? សួរ GET /orders ជាមួយ cursor ចុងក្រោយរបស់អ្នក រៀងរាល់ប្រហែល 30 វិនាទី ដែលនៅក្នុងដែនកំណត់យ៉ាងស្រួល។

កំពុងភ្ជាប់អ្វីដែលយើងមិនបានគិតដល់?

ប្រាប់យើងពីអ្វីដែលអ្នកចង់ភ្ជាប់ទៅហាងរបស់អ្នក។ យើងនឹងជួយរៀបចំសោ និងសំណើដំបូង។

ត្រៀមចាប់ផ្តើមហើយ? បង្កើតហាងរបស់អ្នក

ប្រើ Bill&Go រួចហើយ? ចូលគណនី

និយាយជាមួយយើង

ចំណាយពេលតែមួយនាទី។ យើងឆ្លើយតបក្នុងរយៈពេលមួយថ្ងៃធ្វើការ។

អ្នកមានប៉ុន្មានហាង?

យើងប្រើព័ត៌មាននេះ សម្រាប់តែទាក់ទងអ្នកវិញប៉ុណ្ណោះ។