iKKM Bluetooth и USBserial API
Описание протокола
October 19 2020
Вступление
Протокол работы с iKKM по Bluetooth и USB совместим и повторяет Web API. Подробное описание Web API доступно по адресу http://www.ikkm.kz/apidoc .
Процесс обмена данными по Bluetooth или по USB Serial одинаков, поэтому далее по тексту, будет упомянут только Bluetooth
, для USB
будут указаны только особенности.
Данная документация подразумевает, что вы уже ознакомились с форматом запросов Web API и приступили к интеграции Bluetooth.
Поддержка Bluetooth API реализована параллельно с Web API. Если ваш iKKM поддерживает Bluetooth, вы можете включить Bluetooth API вместе с Web API, конфликтов в одновременном использовании не возникнет.
Для корректной работы с iKKM, рекомендуем не блокировать основной поток вашей программы, а использовать несколько потоков для обмена сообщениями по Bluetooth с iKKM.
Передача данных
Для подключения по Bluetooth: Отправка данных осуществляется по Bluetooth Socket RFCOMM. RFCOMM является потоковыми транспортом и еще известен как Serial Port Profile (SPP).
Для подключения по USB: используется режим CDC (USB Serial), специальных драйверов для USB не требуется, VID/PID:"067b:2303"
Определяется iKKM как Prolific USB-to-Serial Comm Port
Обрамление передаваемых пакетов отсутствует, однако есть важная особенность - любое сообщение должно заканчиваться символом новой строки (\n
или в HEX:0x0a
), ответы от iKKM приходят так же - в конце 0x0a
.
Размер пакета сообщения не имеет значения, так как iKKM будет
накапливать все входящие пакеты до символа 0x0a
. Однако рекомендуем не превышать размер каждого отдельного пакета 2048 байт. В большинстве случаев вам не потребуется проводить такого тюнинга, например в случае Android, система сама позаботится правильной длине пакетов.
Формат передаваемых сообщений JSON с кодировкой UTF-8.
Особенности API
Отправка запросов
Поддерживаются следующие методы: api
, apicheck
, apiprint
, apibank
, apideposit
, apiwithdraw
, apizreport
, apixreport
.
Методы получения справочников и отчетов, все что начинается с /dump/...
не поддерживается, и ввиду дизайна системы не планируется в ближайшее время.
Формирование строки запроса аналогично с Web API если бы вы использовали /v2.. и передавали JSON в POST запросе. Метод указывается не в URL, а в самом JSON, в параметре method
Пример Web API:
GET /api/?key=12345678&sale=800&cash=1000 HTTP/1.1
Тоже самое для Bluetooth API:
{"method":"/api","key":"12345678","sale":"800","cash":"1000"}/n
Обратите внимание на наличие /
в начале метода.
Далее, для ясности, все примеры будут приведены на Javascript. Рассмотрим пример отправки фискального чека:
function saleprepare(_sum,_cash,_itemdata){
var obj = {
key: AppSettings.apikey,
method: "/api",
sale: _sum,
cash: _cash,
itemdata:_itemdata
};
var myJSON = JSON.stringify(obj);
return myJSON;
}
property var _itemdata: [{
"name": "Чай Green Frencho",
"price": "140",
"qty": "2",
"sum": "280",
"taxid":"2"
}, {
"name": "Стейк",
"price": "8000",
"qty": "2",
"sum": "16000"
}]
При передаче itemdata
, передавайте параметр как часть основного JSON, как вложенную структуру, без каких-либо эскейпов.
Пример запроса оплаты по карте банка:
function bankpurchase(_amount, _showSlotmenu, _customerSlip){
var obj = {
key: AppSettings.apikey,
method: "/apibank",
amount : _amount,
showSlotMenu : _showSlotmenu,
customerSlip : _customerSlip,
message: "purchase",
trackdocument: AppParams.bankTrackDocument,
bankSlot: "1"
}
var myJSON = JSON.stringify(obj);
return myJSON;
}
Параметры _showSlotmenu
, _customerSlip
принимают значение в string
"false" или "true".
Для простоты все значения передаются как String
с обычными кавычками. Например amount
передается как "1000" или с разделителем - точкой "1001.00", никаких пробелов и прочих символов не должно быть. (см типы данных http://ikkm.kz/apidoc/#3)
Роль Web-API ключа
Передавать Web-API ключ в запросах по Bluetooth обязательно, однако в Bluetooth API можно всегда получить актуальный Web-API ключ в методе /apicheck/getinfo
, название параметра activeKey
. При этом в самом запросе /apicheck/getinfo
, Web-API ключ передавать не обязательно.
Причина следующая - при использовании Bluetooth API, отпадает необходимость Web-API ключа как средства безопастности, однако помимо безопастности Web-API ключ решает задачу идемпотентности, что позволяет избежать задвоений чеков и прочих неясностей операций.
Пример запроса получения информации iKKM:
function getinfo(){
var obj = {
method: "/apicheck/getinfo"
};
var myJSON = JSON.stringify(obj);
return myJSON;
}
Получение ответов
Формат ответов JSON. Все ответы идентичны кроме ответа на запрос
/apicheck/getinfo
(см http://ikkm.kz/apidoc/#7-1-apicheck)
Стандартный ответ имеет вид:
{"body":"0", "result":"200", "resultText":"z-report-started", "replyMethod":"/apizreport"}
Подробное описание ошибок и результатов можно найти на http://ikkm.kz/apidoc/#11-1
Исключение составляет только поле replyMethod
, которое содержит ваш запрос на который ответил iKKM.
При разборе ответа, необходимо проверить что ответ JSON формата, затем проверить поле result
, коды аналогичны HTTP кодам ответа.
Пример проверки ответа и списка основных ошибок:
function errorApiHandler(obj){
// returns true if ok!
if (obj.result === "200" || obj.result === "203"){
// here possibly add some action on 203', since it is a repeated operation
return true;
}
var err = ikkmErrorList[obj.body]
if (err !== undefined){
pops.error(err)
}
else {
pops.error("Неизвестная ошибка: "+obj.body)
}
return false;
}
Component.onCompleted: {
ikkmErrorList = {
"-1" : "неверный web-api ключ, настроить новый ключ на iKKM",
"-2" : "происходит регистрация или банковская операция, повторите запрос позже",
"-3" : "смена превысила 24 часа, необходимо закрыть смену",
"-4" : "оффлайн период более 72 часов, решить проблему связи и разблокировать ккм",
"-5" : "низкий заряд батареи ккм",
"-6" : "принтер не готов, проверить бумагу",
"-7" : "ошибка запроса, проверьте передаваемые параметры",
"-8" : "ошибка метода",
"-9" : "ошибка значений параметров передавать только цифры (кроме параметра print)",
"-10": "сумма cash (bank, tara, credit) меньше sale (buy…), проверьте логику работы с API",
"-11": "смена открыта другим кассиром, закрыть смену",
"-12": "значение cash меньше чем расчетная итоговая сумма, может возникнуть если налог насчитывается поверх суммы",
"-13": "банк или тара или кредит больше чем итоговая сумма (или чек уже оплачен наличностью) проверьте логику работы с API",
"-14": "ошибка налога, указанный налог не найден",
"-15": "нет наличности в кассе (возврат продажи, покупка), проверьте передаваемые параметры",
"-16": "операцию может проводить только старший кассир, проверить пользователя",
"-17": "ошибка обработки JSON запроса в itemdata, проверьте логику работы с API",
"-18": "отклонен IP адрес хоста с которого выполнен запрос",
"-19": "itemdata errors",
"-20": "происходит регистрация или банковская операция, повторите запрос позже"
}
}
Push сообщения
Учитывая особенность Bluetooth соединения - способность начинать передавать данные в обе стороны, в iKKM добавлены Push сообщения.
Постоянно опрашивать iKKM не нужно. Теперь вы можете получить оповещение об отправке чека в ОФД, iKKM закончил выполнение отчета, результат банковской операции итд.
Оповещения от iKKM начинается с @...
в поле replyMethod
Пример:
{"replyMethod":"@ZREPORTdone"}
Описание Push сообщений:
-
"@BANKdone" - закончена банковская операция или сверка с банком, других параметров не содержит.
-
"@ZREPORTdone" - закончен Z или X отчет, других параметров не содержит.
-
"@OFDdone" - чек отправлен в ОФД или чеку присвоен автономный признак. Содержит два дополнительных поля:
ofdCode
фискальный признак ОФД, номер чекаcheqId
. Следует ожидать данное событие только если ответ на запрос содержитresult=200
. Ошибки400
, а также повторения203
не вызовут появления данного события. -
"@BANKresult" - результат платежа,
transactionResult
со значениемtrue/false
и номером чека в полеchequeId
В режиме USB режиме, `transactionResult` возвращается только если операция `true`
-
"@PRINTdata" - распечатанные данные в HTML формате, поле
print
. Новая строка вprint
заменена на<br>
итдОтправка только если выбран внутренний или виртуальный принтер.
В хронологическом порядке оповещения типа @...done
приходят в самом конце обработки уже после @BANKresult
или @PRINTdata
Пример обработчика входящих оповещений:
function handle_live_msg(obj){
var r = obj.replyMethod
switch (r){
case "@BANKdone":
case "@ZREPORTdone":
pops.close()
pops.info("Оповещение","Операция закончена")
break;
case "@OFDdone":
pops.close()
pops.info("Чек отправлен","Код ОФД: "+obj.ofdCode+", чек №"+obj.cheqId)
break;
case "@BANKresult":
pops.close()
pops.info("Результат платежа:",obj.transactionResult + " чек №"+obj.chequeId)
break;
case "@PRINTdata":
sigGotCheque(obj.print)
break;
}
}
Вы можете игнорировать данные оповещения если они вам не нужны, например "@PRINTdata". Или сохранять в вашем приложении чек и отправлять клиенту.
Возможно также блокировать интерфейс ввода данных или показывать экран загрузки до тех пор пока не получены оповещения @ZREPORTdone
, @OFDdone
, @BANKdone
.
Примеры
USB Python: http://www.ikkm.kz/assets/docs/python-ikkm-usb.zip
-THE END-