LIFE POS Checkout

Документация по интеграции с приложением LIFE POS Checkout. Все примеры кода приведены на языке программирования Kotlin.

Исходники приложения для демонстрации интеграции

1. Интеграция с LIFE POS Checkout

1.1. Настройка проекта

В build.gradle файле проекта прописать maven репозиторий:

allprojects {
    repositories {
        maven { 
            url 'https://lifepaycloudplatform.pkgs.visualstudio.com/Common/_packaging/external/maven/v1' 
        }
    }
}

В build.gradle файле модуля приложения подключить зависимости:

implementation "ru.lifepay:periphery-models:$lifepayPeripheryVersion"

Последняя версия моделей:

ext {
    lifepayPeripheryVersion = '1.0.4' 
}

1.2. Запуск LIFE POS Checkout

Чтобы отправить запрос в приложение LIFE POS Checkout необходимо создать в своем приложении экземпляр класса Intent с packageName = "ru.lifepay.checkout" и сбросить все флаги через setFlags(0). Необходимо указать действие ( action ) в зависимости от типа запроса. Список поддерживаемых действий перечислен в таблице 1.1.

val lifepayIntent = (getPackageManager()
    ?.getLaunchIntentForPackage("ru.lifepay.checkout") 
    ?: throw IllegalStateException("Приложение LIFE POS Checkout не установлено."))
    .setFlags(0)
    .setAction(action)

Таблица 1.1. Поддерживаемые действия

Действие Описание
fiscal Действие для запроса фискализации на фискальном регистраторе. Смотрите раздел 2. Фискализация.
transaction Действие для запроса платежных транзакций на платежном терминале. Смотрите раздел 3. Платежные транзакции.
print_receipt Действие для запроса печати текста на принтере чеков. Смотрите раздел 4. Печать чека (текста).

Для отправки запроса в приложение LIFE POS Checkout необходимо использовать метод startActivityForResult(...) класса Activity:

startActivityForResult(lifepayIntent, 5555)

Для получения результата операции необходимо перегрузить метод onActivityResult(...) класса Activity:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    //обработка
}

Параметры запроса необходимо передавать при помощи метода putExtra(...) класса Intent. Например:

lifepayIntent.putExtra("document_type", "receipt")

2. Фискализация

2.1. Параметры входящего запроса

2.1.1. Базовые поля

В таблице 2.1 приводится список базовых полей любого запроса на фискализацию.

Таблица 2.1. Базовые поля

Название Тип Обязательность Описание
document_type String да Тип документа: обычный чек, чек коррекции, …
data String в зависимости от типа документа Сериализованные в json строку параметры документа.
request_id String нет Уникальный идентификатор запроса. Возвращается в ответе. Для определенных фискальных регистраторов (не всех) используется для механизма защиты от дублирования, смотрите раздел 2.3. Защита от дублирования.
metadata HashMap<String, String> нет Дополнительные пользовательские данные. Смотрите раздел 2.4. Передача дополнительных данных.

Поле data заполняется в зависимости от типа документа document_type. Подключенный модуль periphery-models содержит набор классов (моделей) параметров фискальных чеков. Для каждого типа документа используется своя модель. В разделе 2.1.2. Полный список полей для каждого типа документа указана соответствующая ему модель. Чтобы сериализовать модель в json строку для передачи параметром запроса, необходимо использовать метод toJSON().

В зависимости от типа документа и используемого типа фискального регистратора (ФР) в запросе могут быть дополнительные поля. Тип используемого ФР выбирается вручную из приложения LIFE POS Checkout. В первой версии приложения доступен только только один тип ФР - облачная касса. Он же установлен по умолчанию, выбирать вручную его не нужно. Для использования облачной кассы необходимо передавать дополнительные поля приведенные в таблице 2.2. Также облачная касса поддерживает механизм защиты от дублирования. Смотрите раздел 2.3. Защита от дублирования.

Таблица 2.2. Дополнительные поля для облачной кассы

Название Тип Обязательность Описание
apikey String да АПИ-ключ компании в системе Lifepay. Узнать свой АПИ-ключ можно в личном кабинете Lifepay.
target_serial String нет Серийный номер принтера, на котором необходимо фискализировать данные. Если не задан, чек будет фискализирован на одном из подключенных (активных) фискальных принтеров.

2.1.2. Полный список полей

Таблица 2.3. Кассовый чек (облачная касса)

Название Тип Обязательность Значение/описание
document_type String да receipt
data String да Экземпляр класса FCRegisterReceiptParams сериализованный в json.
request_id String нет Уникальный идентификатор запроса. Возвращается в ответе. Для облачной кассы поддерживается механизм защиты от дублирования на основе request_id, смотрите раздел 2.3. Защита от дублирования.
metadata HashMap<String, String> нет Дополнительные пользовательские данные. Смотрите раздел 2.4. Передача дополнительных данных.
print Boolean нет true/false - необходимо ли печатать чек на фискальном регистраторе. По умолчанию - true. В случае false, необходимо заполнять поле customerContacts модели FCRegisterReceiptParams (поле data).
apikey String да АПИ-ключ компании в системе Lifepay. Узнать свой АПИ-ключ можно в личном кабинете Lifepay.
target_serial String нет Серийный номер принтера, на котором необходимо фискализировать данные. Если не задан, чек будет фискализирован на одном из подключенных (активных) фискальных принтеров.

Таблица 2.4. Кассовый чек коррекции (облачная касса)

Название Тип Обязательность Значение/описание
document_type String да receipt_of_correction
data String да Экземпляр класса FCRegisterReceiptOfCorrectionParams сериализованный в json.
request_id String нет Уникальный идентификатор запроса. Возвращается в ответе. Для облачной кассы поддерживается механизм защиты от дублирования на основе request_id, смотрите раздел 2.3. Защита от дублирования.
metadata HashMap<String, String> нет Дополнительные пользовательские данные. Смотрите раздел 2.4. Передача дополнительных данных.
apikey String да АПИ-ключ компании в системе Lifepay. Узнать свой АПИ-ключ можно в личном кабинете Lifepay.
target_serial String нет Серийный номер принтера, на котором необходимо фискализировать данные. Если не задан, чек будет фискализирован на одном из подключенных (активных) фискальных принтеров.

2.2. Параметры результата запроса

В случае успешной обработки запроса приложение LIFE POS Checkout возвращает resultCode = Activity.RESULT_OK (-1) и экземпляр класса Intent с action = "fiscal". Если resultCode отличается от Activity.RESULT_OK или intent == null, то следует рассматривать данную ситуацию как ошибку. Для фискальных регистраторов с поддержкой защиты от дублирования (например облачная касса) данную ошибку стоит расценировать, как error_type = unknown_result_error.
(Смотрите таблицу 2.7. Поля ошибки и раздел 2.3. Защита от дублирования.)

Успешная обработка запроса еще не означает успешной фискализации документа. Для определения факта успешной/неуспешной фискализации следует ориентироваться на поле code. В таблице 2.5 представлен список полей в случае успешной фискализации.

Таблица 2.5. Поля результата запроса в случае успешной фискализации

Название Тип Обязательность Значение/описание
code Int да 0 - успешная фискализация документа.
document_type String да Значение document_type, которое было передано в запросе.
data String да Сериализованные в json строку данные фискализации.
request_id String да Значение request_id, которое было передано в запросе. Если значение не было передано в запросе, то генерируется автоматически.
metadata HashMap<String, String> нет Значение metadata, которое было передано в запросе. Извлекается через extras.getSerializable(“metadata”).

Значения полей можно извлекать из экземпляра класса Bundle получаемого из свойства extra класса Intent при помощи методов extras.getInt(...) и extras.getString(...). Значение поля data зависит от типа документа document_type. Для результатов запроса (также как и для входящего запроса) имеется набор классов (моделей) для каждого типа документа в модуле periphery-models. В таблице 2.6 представлен список соотвествия типов документов и моделей из библиотеки.

Таблица 2.6. Соответствие типов документов и моделей

Название типа документа Тип документа Входящая модель Исходящая модель
Кассовый чек receipt FCRegisterReceiptParams FDReceipt
Кассовый чек коррекции receipt_of_correction FCRegisterReceiptOfCorrectionParams FDReceiptOfCorrection

Для десериализации строкового значения поля data в соответствующую модель следует использовать следующий метод:

GSONConverter.fromJSON(data, FDReceipt::class.java)

В таблице 2.7 представлен список полей в случае завершения запроса с ошибкой.

Таблица 2.7. Поля ошибки

Название Тип Обязательность Значение/описание
code Int да Отличное от нуля значение. Означает, что произошла ошибка.
document_type String да Значение document_type, которое было передано в запросе.
request_id String да Значение request_id, которое было передано в запросе. Если значение не было передано в запросе, то генерируется автоматически.
metadata HashMap<String, String> нет Значение metadata, которое было передано в запросе. Извлекается через extras.getSerializable(“metadata”).
error_type String да Тип ошибки. Каждый тип имеет свою собственную логику обработки. Смотрите таблицу 2.8. Типы ошибок.
message String да Текст ошибки для вывода пользователю.
log String нет Дополнительная информация об ошибке для разработчиков. Для пользователей ее выводить не нужно. Может быть полезной на этапе интеграции и тестирования. Может иметь большой размер.

Таблицу 2.8. Типы ошибок

Тип ошибки (error_type) Описание
default_error Обычная ошибка.
unknown_result_error Результат запроса неизвестен. Выдается только для фискальных регистраторов и операций с поддержкой защиты от дублирования. Логику обработки смотрите в разделе 2.3. Защита от дублирования.

2.3. Защита от дублирования

Данный раздел актуален только для фискальных регистраторов и операций с поддержкой защиты от дублирования. ФР облачная касса поддерживает механизм защиты от дублирования.

Для каждого запроса в приложение LIFE POS Checkout можно передавать параметр request_id. В случае если его не передать, то он будет сгенерирован автоматически и вернется в успешном/неуспешном ответе. request_id является уникальным идентификатором запроса и используется локально в приложении LIFE POS Checkout для работы механизма защиты от дублирования. В случае если фискальный регистратор и вызываемая операция поддерживает этот механизм.

Логика работы следующая: У стороннего приложения имеются данные кассового чека, которые необходимо зафискализировать. Стороннее приложение локально генерирует request_id и отсылает запрос на фискализацию в LIFE POS Checkout вместе с этим request_id.

  1. Если в результате выполнения запроса происходит успешная фискализация (code == 0), то повторный запрос в LIFE POS Checkout с тем же request_id будет выдавать успешный результат с теми же данными, без выполнения фискализации. При этом нужно понимать, что этот механизм будет работать только локально на этом android устройстве. Если отправить запрос с тем же request_id с другого android устройства, то такой чек будет зафискализирован. Данный механизм нужен, чтобы защититься от дублей локально, в определенное ограниченное время. К примеру для облачной кассы запоминается локально 50 последних запросов.
  2. Если в результате выполнения запроса происходит ошибка с error_type == unknown_result_error, то это означает что результат запроса неизвестен, он может быть как успешным, так и нет. В этом случае при следующей попытке фискализации этого чека необходимо отсылать запрос с тем же request_id, что и первоначальный. Это необходимо делать либо до получения успешного результата, либо до получения ошибки с другим error_type.
  3. Если в результате выполнения запроса происходит ошибка с error_type != unknown_result_error, то для следующей попытки фискализации этого чека необходимо сгенерировать новый request_id. Т.к. старый request_id с большой долей вероятности в этом случае выдаст ошибку.

Не используйте для этих целей какие либо внутренние глобальные идентификаторы заказа из своей системы. Т.к во первых как описано выше для одного и того же чека может потребоваться изменять request_id. И также иногда может требоваться для одного заказа фискализация нескольких разных чеков.

Если вам требуется передавать внутренний глобальный идентификатор заказа вместе с чеком смотрите раздел 2.4. Передача дополнительных данных

2.4. Передача дополнительных данных

Приложение LIFE POS Checkout позволяет передавать дополнительные данные в запросах фискализации. Например это может быть уникальный идентификатор заказа из вашей системы.

При использовании облачной кассы это дает возможность находить чеки в личном кабинете Lifepay по переданным данным. Также эти данные можно получать в уведомлении об обработке документа, если в ЛК Lifepay указан URL для уведомлений.

Для реализации этого механизма в запросе фискализации необходимо передавать параметр metadata. Пример заполнения параметра metadata:

val metadata = HashMap<String, String>().apply {
            put("OrderId", "12345")
        }
lifeposCheckoutIntent.putExtra("metadata", metadata)

Никаких проверок на дублирование для данного параметра не производится.

3. Платежные транзакции

3.1. Параметры входящего запроса

3.1.1. Базовые поля

В таблице 3.1 приводится список базовых полей любого запроса на проведение платежной транзакции.

Таблица 3.1. Базовые поля

Название Тип Обязательность Описание
request_type String да Тип запроса: проведение транзакции, запрос результата транзакции
transaction_type String да Тип транзакции: оплата, возврат, отмена, сверка итогов.
request_id String нет Уникальный идентификатор запроса. Возвращается в ответе. Некоторые виды терминалов поддерживают запрос результата транзакции для определенных типов транзакций по request_id. Логику работы смотрите в разделе 3.3. Запрос результата транзакции.
metadata HashMap<String, String> нет Дополнительные пользовательские данные.

В зависимости от типа транзакции могут требоваться дополнительные параметры. Некоторые параметры могут быть классами(моделями) из подключенного модуля periphery-models. Для передачи модели параметром запроса необходимо сереализовать ее при помощи метода toJSON().

3.1.2. Полный список полей для проведения транзакции

Таблица 3.2. Оплата

Название Тип Обязательность Значение/описание
request_type String да start_transaction
transaction_type String да payment
request_id String нет Уникальный идентификатор запроса. Возвращается в ответе. Некоторые виды терминалов поддерживают запрос результата транзакции для определенных типов транзакций по request_id. Логику работы смотрите в разделе 3.3. Запрос результата транзакции.
metadata HashMap<String, String> нет Дополнительные пользовательские данные. Возвращаются в ответе.
amount Long да Сумма оплаты в копейках.

Таблица 3.3. Возврат

Название Тип Обязательность Значение/описание
request_type String да start_transaction
transaction_type String да refund
request_id String нет Уникальный идентификатор запроса. Возвращается в ответе. Некоторые виды терминалов поддерживают запрос результата транзакции для определенных типов транзакций по request_id. Логику работы смотрите в разделе 3.3. Запрос результата транзакции.
metadata HashMap<String, String> нет Дополнительные пользовательские данные. Возвращаются в ответе.
amount Long да Сумма возврата в копейках.
primary_transaction String да Сериализованный в json строку объект PTTransaction полученый в результатах оплаты.

Таблица 3.4. Отмена

Название Тип Обязательность Значение/описание
request_type String да start_transaction
transaction_type String да cancellation
request_id String нет Уникальный идентификатор запроса. Возвращается в ответе. Некоторые виды терминалов поддерживают запрос результата транзакции для определенных типов транзакций по request_id. Логику работы смотрите в разделе 3.3. Запрос результата транзакции.
metadata HashMap<String, String> нет Дополнительные пользовательские данные. Возвращаются в ответе.
amount Long да Сумма отмены в копейках.
primary_transaction String да Сериализованный в json строку объект PTTransaction полученый в результатах оплаты.

Таблица 3.5. Сверка итогов

Название Тип Обязательность Значение/описание
request_type String да start_transaction
transaction_type String да close_shift
request_id String нет Уникальный идентификатор запроса. Возвращается в ответе.
metadata HashMap<String, String> нет Дополнительные пользовательские данные. Возвращаются в ответе.

3.2. Параметры результата запроса

В случае успешной обработки запроса приложение LIFE POS Checkout возвращает resultCode = Activity.RESULT_OK (-1) и экземпляр класса Intent с action = "transaction". Если resultCode отличается от Activity.RESULT_OK или intent == null, то следует рассматривать данную ситуацию как ошибку.

Успешная обработка запроса еще не означает, что транзакция проведена успешно. Для определения факта успешной/неуспешной транзакции следует ориентироваться на поле code. В таблице 3.6 представлен список полей в случае успешной транзакции.

Таблица 3.6. Поля результата запроса в случае успешной транзакции

Название Тип Обязательность Значение/описание
code Int да 0 - транзакция проведена успешно.
transaction_type String да Значение transaction_type, которое было передано в запросе.
data String да Сериализованный в json строку результат транзакции.
request_id String да Значение request_id, которое было передано в запросе. Если значение не было передано в запросе, то генерируется автоматически.
metadata HashMap<String, String> нет Значение metadata, которое было передано в запросе. Извлекается через extras.getSerializable(“metadata”).

Значения полей можно извлекать из экземпляра класса Bundle получаемого из свойства extra класса Intent при помощи методов extras.getInt(...) и extras.getString(...). Значение поля data зависит от типа транзакции transaction_type. Для результатов запроса имеется набор классов (моделей) для каждого типа транзакции в модуле periphery-models. В таблице 3.7 представлен список соотвествия типов транзакций и моделей из библиотеки.

Таблица 3.7. Соответствие типов транзакций и моделей

Название типа транзакции Тип транзакции Результат (поле data)
Оплата payment PTTransaction
Возврат refund PTTransaction
Отмена cancellation PTTransaction
Сверка итогов close_shift PTSummaryShiftReport

Для десериализации строкового значения поля data в соответствующую модель следует использовать следующий метод:

GSONConverter.fromJSON(data, PTTransaction::class.java)

В таблице 3.8 представлен список полей в случае завершения запроса с ошибкой.

Таблица 3.8. Поля ошибки

Название Тип Обязательность Значение/описание
code Int да Отличное от нуля значение. Означает, что произошла ошибка.
transaction_type String да Значение transaction_type, которое было передано в запросе.
request_id String да Значение request_id, которое было передано в запросе. Если значение не было передано в запросе, то генерируется автоматически.
metadata HashMap<String, String> нет Значение metadata, которое было передано в запросе. Извлекается через extras.getSerializable(“metadata”).
error_type String да Тип ошибки. Каждый тип имеет свою собственную логику обработки. Смотрите таблицу 3.9. Типы ошибок.
message String да Текст ошибки для вывода пользователю.
log String нет Дополнительная информация об ошибке для разработчиков. Для пользователей ее выводить не нужно. Может быть полезной на этапе интеграции и тестирования. Может иметь большой размер.

Таблицу 3.9. Типы ошибок

Тип ошибки (error_type) Описание
default_error Обычная ошибка.
unknown_result_error Результат запроса неизвестен. Выдается только для терминалов и типов транзакций поддерживающих запрос результата транзакции. Логику обработки смотрите в разделе 3.3. Запрос результата транзакции.
duplicate_request_error Повторный запрос с одним и тем же request_id. Выдается только для терминалов и типов транзакций поддерживающих запрос результата транзакции. Логику обработки смотрите в разделе 3.3. Запрос результата транзакции.
result_not_found_error Результат транзакции не найден. Выдается только на запрос результата операции. Подробнее в разделе 3.3. Запрос результата транзакции.

3.3. Запрос результата транзакции

Запрос результата транзакции поддерживается некоторыми видами терминалов для определенных типов транзакций. Терминал СБЕРБАНК ICMP UPOS поддерживает получение результата транзакции для оплаты (transaction_type == payment).

Для каждого запроса в приложение LIFE POS Checkout можно передавать параметр request_id. В случае если его не передать, то он будет сгенерирован автоматически и вернется в успешном/неуспешном ответе. request_id является уникальным идентификатором запроса и используется локально в приложении LIFE POS Checkout для получения результата транзакции. Для этого необходимо отправить запрос в LIFE POS Checkout с action = transaction и полями из Таблицы 3.10. Запрос результата транзакции.

Таблица 3.10. Запрос результата транзакции

Название Тип Обязательность Значение/описание
request_type String да get_result
transaction_type String да Значение transaction_type, которое было передано при первоначальном проведении транзакции.
request_id String да Значение request_id, которое было передано при первоначальном проведении транзакции.
metadata HashMap<String, String> нет Значение metadata, которое было передано при первоначальном проведении транзакции.

В результате выполнения запроса (успешного/неуспешного) выдается такой же ответ как и в случае запроса проведения транзакции. Смотрите раздел 3.2. Параметры результата запроса.

Далее приводится алгоритм обработки ошибок с типами error_type == unknown_result_error и error_type == duplicate_request_error. Данные ошибки могут выдаваться для терминалов и типов транзакций, которые поддерживают запрос результата транзакции. Если терминал или тип транзакции на конкретном терминале не поддерживает запрос результата транзакции, то ошибки unknown_result_error и duplicate_request_error для них не выдаются. Запрос результата транзакции работает определенное ограниченное время после проведения транзакции. К примеру терминал СБЕРБАНК ICMP UPOS стирает результаты транзакций после проведения сверки итогов.

  1. Если в результате проведения транзакции происходит ошибка с error_type == unknown_result_error, то это означает что результат транзакции неизвестен, он может быть как успешным, так и нет. В этом случае можно проверить результат выполнения транзакции, отправив запрос результата транзакции в LIFE POS Checkout. Не нужно отправлять этот запрос автоматически после получения ошибки, т.к. данная ошибка может произойти из-за того, что терминал потерял связь со смартфоном или сел. Рекомендуется предупредить кассира и предложить ему запросить результат транзакции нажав на кнопку. Пример реализации данного алгоритма смотрите в демо приложении в методах errorHandling(...) и showErrorWithGetResult(...) в классе BaseIntentActivity.
  2. Запрос получения результата транзакции также может закончиться ошибкой с типом error_type == unknown_result_error. Например если не удалось соединиться с терминалом. В этом случае можно повторить алгоритм из пункта 1.
  3. Если запрос получения результата транзакции заканчивается ошибкой с типом error_type != unknown_result_error, тогда обновляем request_id для проведения следующей транзакции.
  4. Если в результате проведения транзакции происходит ошибка с error_type == duplicate_request_error, то переходим к пункту 1 алгоритма. Данная ошибка возникает в случае попытки провести транзакцию с тем же request_id, что и ранее. А это означает, что по предыдущей транзакции не был получен результат и request_id не был изменен.

Не используйте в качестве request_id какие либо внутренние глобальные идентификаторы заказа из своей системы. Как описано выше для одной и той же транзакции может потребоваться изменять request_id.

4. Печать чека (текста)

Перед печатью чека необходимо подключить принтер чеков вручную из приложения LIFE POS Checkout. На данный момент для подключения доступен принтер чеков MiNi Thermal Printer. Подключение производится по bluetooth. Данный принтер выводит 32 символа в строке.

4.1. Параметры входящего запроса

Таблица 4.1. Поля запроса для печати чека (текста)

Название Тип Обязательность Значение/описание
body String да Тело чека. Отдельные строки должны быть разделены символом \n. Можно использовать поле printView класса FDReceipt, после фискализации.
qr_code String нет QR код. Печатается после тела чека, в случае наличия. Можно использовать поле qrCode класса FDReceipt, после фискализации.
footer String нет Завершающая часть чека. Печатается после QR кода, в случае наличия. Отдельные строки должны быть разделены символом \n.

В случае необходимости можно передавать поля request_id: String и metadata: HashMap<String, String>, они будут возвращаться в ответе. (Успешном/неуспешном) Никакой логики на них не завязано.

4.2. Параметры результата запроса

В случае успешной обработки запроса приложение LIFE POS Checkout возвращает resultCode = Activity.RESULT_OK (-1) и экземпляр класса Intent с action = "print_receipt". Если resultCode отличается от Activity.RESULT_OK или intent == null, то следует рассматривать данную ситуацию как ошибку.

Для определения факта успешной/неуспешной печати чека (текста) следует ориентироваться на поле code.

Таблица 4.2. Поля результата запроса в случае успешной печати чека (текста)

Название Тип Обязательность Значение/описание
code Int да 0 - успешная печать чека (текста).

Значения полей можно извлекать из экземпляра класса Bundle получаемого из свойства extra класса Intent при помощи методов extras.getInt(...) и extras.getString(...).

Таблица 4.3. Поля результата запроса в случае ошибки

Название Тип Обязательность Значение/описание
code Int да Отличное от нуля значение. Означает, что произошла ошибка.
message String да Текст ошибки для вывода пользователю.
log String нет Дополнительная информация об ошибке для разработчиков. Для пользователей ее выводить не нужно. Может быть полезной на этапе интеграции и тестирования. Может иметь большой размер.