Внедрение SDK под android в свое приложение

SDK от Lifepay позволяет внедрить в свое приложение функционал эквайринга, доступный в мобильном приложении Lifepay.

Начало работы

SDK распространяется как библиотека с расширением .aar. Для подключения SDK к проекту на Android Studio необходимо нажать правой кнопкой на проект и выбрать New>Module>Import .JAR/.AAR Package.

Прописать в build.gradle файл в dependencies следующие зависимости:

compile 'com.fasterxml.jackson.core:jackson-core:2.4.2'
compile 'com.fasterxml.jackson.core:jackson-annotations:2.4.0'
compile 'com.fasterxml.jackson.core:jackson-databind:2.4.2'

AndroidManifest.xml должен иметь следующие разрешения:

<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Работа с пинпадом RP750x

Исходники:

Взаимодействие с SDK производится при помощи LifePayApi.getInstance().pinPad(), который возвращает ссылку на объект класса PinPadApi со следующими методами:

void startTransaction(Context context, PaymentParams args, PaymentModelCallback callback) throws LifePayException
void stopTransaction()
void removeLinkedDevice(Context context)
boolean hasLinkedDevice(Context context)
List<String> getBoundedDevices()
void selectDevice(int index)
boolean canStop()
void selectApplication(int index)
void cancelSelectApplication()
void setSignature(byte[] signature, Context context) throws LifePayException
void cancelSignature()

Стоит отметить, что обращаться к SDK всегда следует через LifePayApi.getInstance().pinPad(), и не запоминать ссылку в отдельную переменную, т.к. объект пересоздается между сессиями взаимодействия с терминалом.

Для проведения транзакции через SDK необходимо создать объект класса PaymentParams, заполнив его следующими данными:

  • String login, String password – Аккаунт в системе LifePay.
  • String phone, String mail – Если клиенту нужно отправить чек об оплате на телефон или электронную почту, можно заполнить эти поля; в противном случае указать null. Пример заполнения поля phone: “75555555555”.
  • String description, double amount – Описание и сумма (в рублях, 2 разряда после запятой) платежа. Например, description: “Букет цветов” amount: 823.48. Эти поля являются обязательными.
  • double discount – Сумма скидки в рублях, при отсутствии указать 0.0.
  • double tips – Сумма чаевых в рублях, при отсутствии указать 0.0. Например, если amount = 500.00, discount = 100.00, tips = 250.00, то с карты клиента спишется 500.00 – 100.00 + 250.00 = 650 рублей.
  • byte[] signature – Всегда указываем null.
  • OperationType operationType – Тип операции. OperationType.PAYMENT - для платежа, OperationType.REFUND - для возврата.
  • String transactionNumber – Номер транзакции для возврата. При платеже указать null.


Затем необходимо создать экземпляр анонимного класса, унаследованного от интерфейса RP750xPaymentModelIntf.PaymentModelCallback.

interface PaymentModelCallback {
    void connecting();
    void selectDevice();
    void configuring();
    void waitCard();
    void applicationSelection(List<String> appLabels);
    void terminalProcessing();
    void transactionStart();
    void transactionSuccess(TransactionResult result);
    void transactionError(TransactionResult result);
    void authError(String error);
    void stopping();
    void stopped();
    void signatureRequest();
}

Вызовите метод LifePayApi.getInstance().pinPad().startTransaction, передав ему только что созданные объекты и Context. В случае неверного заполнения полей или, если не будут заполнены обязательные поля, будет выброшено LifePayException. Подобного рода случаи должны быть решены еще на стадии внедрения. Дальнейшее общение с SDK осуществляется путем получения промежуточных результатов, запросов дополнительных данных при помощи методов интерфейса PaymentModelCallback и отправки команд в SDK при помощи методов класса PinPadApi через LifePayApi.getInstance().pinPad().

SDK начинает свою работу по взаимодействию с терминалом и проведению транзакции после вызова метода startTransaction и хранит свое состояние на протяжении этого процесса до его успешного/неуспешного завершения или отмены. Поэтому при дальнейших вызовах метода startTransaction (в процессе работы SDK) будет вызван какой-либо из методов PaymentModelCallback, соответствующий текущему состоянию SDK. Если ваше приложение построено так, что в процессе работы Activity может пересоздаваться (например, при повороте экрана), то после его пересоздания, для получения состояния, необходимо будет вызвать метод startTransaction.

Важно отметить, что ответы от SDK, то есть методы интерфейса RP750xPaymentModelIntf .PaymentModelCallback, могут быть вызваны не из главного потока. В случае необходимости (изменение UI) ответственность за проверку и переход в главный поток ложится на пользователя SDK.

Завершением сессии взаимодействия с SDK полностью управляет само SDK. SDK завершает сессию в случае успешного проведения транзакции или в случае ошибки. При этом можно обозначить SDK свое желание отменить сессию вызовом метода stopTransaction, но перед этим нужно проверить возможность отмены методом canStop (см. Тестовый пример). SDK может запретить отмену сессии, если он находится в процессе настройки терминала, либо если уже началось проведения самой транзакции. Если же SDK разрешает отмену сессии, то сначала будет вызван метод stopping интерфейса PaymentModelCallback. Это означает, то SDK начал процесс отмены сессии взаимодействия, поэтому нужно вывести на экран какую- то информацию об этом для пользователя. В обычных условиях отмена выполняется достаточно быстро, меньше секунды, но бывают случаи, когда для этого может потребоваться до 10 секунд. После того, как SDK завершит процесс отмены, будет вызван метод stopped. Только в этом случае можно закрыть окно оплаты (или, в зависимости от реализации, произвести навигацию между активити).

При первом вызове метода startTransaction будет произведена проверка статуса Bluetooth и, если он выключен, будет выдано системное диалоговое окно с предложением его включить. Начинается процесс проведения транзакции. Далее идет описание ответов от SDK и необходимых реакций на ответы:

  • void connecting() – SDK начало попытку подключения к терминалу. Завершится либо ошибкой по истечении 20-секундного таймаута, либо будет вызван другой callback.
  • void selectDevice() – SDK не знает, к какому терминалу нужно подключиться. Вызывается при первом запуске или после очистки связанного терминала (метод removeLinkedDevice класса PinPadApi). Для получения списка сопряженных устройств нужно вызвать метод List<String> getBoundedDevices и продемонстрировать его пользователю. После того, как пользователь выбрал определенный терминал из списка, вызвать метод void selectDevice(int index) с номером терминала из списка. Если нужного терминала нет в списке, необходимо предоставить пользователю возможность перейти в настройки Bluetooth Android’а для сопряжения терминала. При возвращении в приложение необходимо заново запросить список терминалов, вызвав метод List<String> getBoundedDevices и обновив интерфейс. Пример реализации данного функционала смотрите в Тестовом примере.
  • void configuring() – SDK начал настройку терминала, можно вывести информацию об этом пользователю.
  • void waitCard() – Терминал ожидает вставки в него карты. Нужно об этом уведомить пользователя.
  • void applicationSelection(List<String> appLabels) – Карта, которую вставили в терминал, имеет несколько приложений, поэтому необходимо предоставить пользователю возможность выбрать конкретное приложение на экране телефона/планшета. Далее необходимо вызвать метод selectApplication(int index) класса PinPadApi, передав номер выбранного приложения из списка. Также можно отказаться от выбора приложения, вызвав метод cancelSelectApplication(). См. Тестовый пример.
  • void terminalProcessing() – Терминал считал данные с карты. Скорее всего, терминал потребует ввести пин-код карты. Поэтому нужно об этом уведомить пользователя. (см. Тестовый пример)
  • void transactionStart() – SDK получил все необходимые данные и начал общение с сервером процессинга.
  • void transactionSuccess(TransactionResult result) – Успешное завершение транзакции. Нужно перевести пользователя на экран с успешным состоянием (например, на новую Activity). Описание класса TransactionResult смотрите ниже.
  • void transactionError(TransactionResult result) – Ошибка в процессе проведения транзакции. Ошибка может возникнуть как в случае возвращения неуспешного ответа от сервера, так и, например, при неудачном подключении к терминалу. Описание класса TransactionResult смотрите ниже. Важный момент: нужно вызвать метод errorReaded класса TransactionResult перед возвращением на предыдущий экран. В противном случае SDK будет возвращать transactionError при вызове метода startTransaction. Это нужно, чтобы при пересоздании Activity не потерялся статус ошибки, пока пользователь не подтвердил ее прочтение. См. Тестовый пример.
  • void authError(String error) – Ошибка возникает в случае, если у пользователя сменился пароль или пользователя не существует в базе.
  • void stopping() – Начался процесс отмены сессии взаимодействия. Нужно вывести информацию для пользователя.
  • void stopped() – Процесс отмены сессии взаимодействия завершен. Нужно убрать экран с проведением транзакции.
  • void signatureRequest() – SDK запрашивает подпись покупателя для подтверждения операции. Необходимо предоставить пользователю возможность ввести подпись на экране телефона/планшета. Далее необходимо вызвать метод setSignature(byte[] signature, Context context) throws LifePayException класса PinPadApi, передав ему подпись, конвертированную в байтовый массив. Либо можно отказаться от ввода подписи (тем самым отменив транзакцию), вызвав метод cancelSignature(). См. Тестовый пример.

При успешном завершении транзакции у полученного объекта класса TransactionResult необходимо, для получения данных о транзакции, вызвать метод TransactionInfo getTransactionInfo(). При ошибке транзакции, для получения текста ошибки, нужно вызвать метод String getError().

class TransactionInfo {
    String getLifepayTransactionNumber(); // номер транзакции в системе LifePay
    String getAuthorizationCode(); // код авторизации от банка
    String getResponseCode(); // код ответа от банка
    String getCreated(); // UNIX timestamp UTC+0
    String getPan(); // первые 6 и последние 4 цифры номера карты
    String getRrn(); // номер ссылки от банка
    String getCompanyName(); // наименование компании, совершившей платеж
    String getInn(); // ИНН компании, совершившей платеж
    //Следующие поля приходят только при оплате через чип:
    String getCardLabel();
    String getAid();
    String getTvr();
}

После успешного соединения с терминалом, SDK запоминает адрес терминала и, в дальнейшем, подключается к нему напрямую. При первом запуске пользователю будет предложено выбрать терминал из списка сопряженных.

Поэтому в SDK предусмотрены методы, позволяющие узнать, имеется ли запомненный терминал boolean hasLinkedDevice(Context context) и void removeLinkedDevice(Context context), чтобы иметь возможность стереть запомненный терминал. Тогда при проведении транзакции пользователю будет показан список терминалов для выбора, как при первом запуске.

Внедрение и тестирование SDK стоит производить не тестовом стенде. Для этого необходимо вызвать метод LifePayApi.getInstance().changeTestMode(context, true). При боевой эксплуатации продукта необходимо либо закомментировать эту строчку, либо передавать false.