Tinkoff Acquiring SDK for iOS

Acquiring SDK позволяет интегрировать Интернет-Эквайринг в мобильные приложения для платформы iOS.

Возможности SDK

  • Прием платежей (в том числе рекуррентных);
  • Сохранение банковских карт клиента;
  • Работа с привязанными картами;
  • Поддержка английского;
  • Интеграция с онлайн-кассами;
  • Оплата с помощью ApplePay;
  • Оплата с помощью Системы Быстрых Платежей;
  • Оплата с помощью TinkoffPay;
  • Настройки окна оплаты;

Требования и ограничения

Для работы Tinkoff Acquiring SDK необходимо:

  • Поддержка iOS 11 и выше;

Подключение

Рекомендуется использовать Cocoa Pods. Для подключения добавьте в файл Podfile зависимости:

pod 'TinkoffASDKCore'
pod 'TinkoffASDKUI'

Если вы не используете Cocoa Pods, необходимо добавить TinkoffASDKUI.xcodeproj в проект.

Подготовка к работе

Важно: Необходимо хранить сильную ссылку на экземпляр AcquiringUISDK

Для начала работы с SDK вам понадобятся:

  • Terminal key - терминал Продавца;
  • Public key – публичный ключ. Используется для шифрования данных. Необходим для интеграции вашего приложения с интернет-эквайрингом Тинькофф.

Данные выдаются в личном кабинете после подключения к Интернет-Эквайрингу.

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

В начале нужно создать конфигурацию, используем объект AcquiringSdkConfiguration, обязательные параметры:

  • credential: AcquiringSdkCredential - учетные данные, полученные в личном кабинете
  • serverEnvironment: AcquiringSdkEnvironment - тестовый или боевой сервер

Дополнительные параметры которые можно установить:

  • logger - Интерфейс для логирование работы, есть реализация по умолчанию LoggerDelegate форматированный вывод данных о работе SDK в консоль
  • language - Язык платежной формы. На каком языке сервер будет присылать тексты ошибок клиенту.
  • showErrorAlert: Bool - Показывать ошибки после выполнения запроса системным UIAlertViewController

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

Для проведения оплаты в модуле TinkoffASDKUI/AcquiringUISDK реализованы методы для сценрия оплаты. Доступны:

  • presentPaymentView(paymentData: PaymentInitData) показать форму оплаты для выброра источника оплаты (сохраненная карта или реквизиты новой) и оплатить
  • presentPaymentView(paymentData: PaymentInitData, parentPatmentId: Int64) быстрая оплата, оплатить рекурентный/ругулярный платеж.
  • presentPaymentSBP(paymentData: PaymentInitData) оплаить использую Систему Быстрых Платежей
  • presentPaymentAcceptanceQR() сгенерировать и показать QR-код для приема платежей
  • paymentApplePay(paymentData: PaymentInitData) оплатить используя ApplePay

Для работы со списком сохраненных карты реализован метод:

  • presentCardList показать список сохраненных карт, и отредактировать список карт - добавить карту, удалить карту.

Проведение оплаты

Инициализация платежа

Для проведения оплаты нужно сформировать информацию о платеже используем струкруту PaymentInitData:

  • amount:Int64. Сумма в копейках. Например, сумма 3руб. 12коп. это число 312. Параметр должен быть равен сумме всех товаров в чеке;
  • orderId:String. Номер заказа в системе Продавца;
  • customerKey:String. Идентификатор клиента в системе продавца. Например, для этого идентификатора будут сохраняться список карт;
  • description:String. Краткое описание покупки;
  • payType:PayType. Тип проведения платежа;
  • savingAsParentPayment:Bool. Если передается и установлен в true, то регистрирует платёж как рекуррентный (родительский);
  • paymentFormData:[String: String]. JSON объект, содержащий дополнительные параметры в виде [Key: Value].Key: String – 20 знаков, Value: String – 100 знаков. Максимальное количество пар параметров не может превышать 20;
  • receipt:Receipt. Данные чека;
  • shops:[Shop]. Информация о магазинах;
  • receipts:[Receipt]. Информация о чеках для онлайн магазинов.
  • redirectDueDate:Date?. Cрок жизни ссылки.

Начало платежа

Далее вызываем метод AcquiringUISDK.presentPaymentView. Перед началом оплаты открывается форма оплаты товара и запускается метод Init результатом которого является регистрация и получение информации о платеже для оплаты PaymentInitResponse:

  • success:Bool - статус обработки запроса;
  • errorCode:Int - номер ошибки в случае неуспешной инициализации платежа, по умолчанию 0;
  • errorMessage:String? - описание ошибки;
  • errorDetails:String? - детальное описание ошибки;
  • terminalKey:String? - идентификатор терминала на котором инициализирован платеж;
  • amount:Int64 - сумма платежа в копейках;
  • orderId:String - номер заказа в системе продавца;
  • paymentId:Int64 - номер для оплаты в системе банка;
  • status:PaymentStatus - статус платежа, [подробнее][https://oplata.tinkoff.ru/landing/develop/documentation/processing_payment]. В случае удачной регистрации платежа на форме оплаты показываются список карт, карточка ввода реквизитов новой карты и кнопка Оплатить. Пользователь выбирает карту, либо вводит реквизиты новой нажимает и нажимает Оплатить.

Завершение платежа

Вызывается метод FinishAuthorize с реквизитами карты или FinishAuthorize с номером ранее сохраненной карты. Во время проведения платежа сервер может запросить подтверждение в этом случае пользователю показывается форма 3D Secure в зависимости от выбранного источника оплаты, это решение принимается сервером.

TinkoffPay

Для работы с TinkoffPay необходимо добавить в Info.plist в массив по ключу LSApplicationQueriesSchemes значение tinkoffbank.

<key>LSApplicationQueriesSchemes</key>
<array>
	***
  <string>tinkoffbank</string>
  ***
</array>

TinkoffPay на экране оплаты

При открытии экрана оплаты SDK проверит наличие возможности оплаты через TinkoffPay и, в зависимости от результата, отобразит кнопку оплаты. Отключить отображение кнопки программно можно с помощью параметра tinkoffPayEnabled в featuresOptions в конфигурации AcquiringViewConfiguration.

Совершить платеж можно через общий экран оплаты, вызвав метод у экземпляра AcquiringUISDK.

func presentPaymentView(on presentingViewController: UIViewController,
                       acquiringPaymentStageConfiguration: AcquiringPaymentStageConfiguration,
                       configuration: AcquiringViewConfiguration,
                       tinkoffPayDelegate: TinkoffPayDelegate?,
                       completionHandler: @escaping PaymentCompletionHandler)

В момент показа экрана оплаты, будет совершен запрос, который проверит, доступна ли оплата через TinkoffPay для вашего терминала и в случае доступности отобразится кнопка для совершения оплаты.

Для определения возможности оплаты через TinkoffPay SDK посылает запрос на https://securepay.tinkoff.ru/v2/TinkoffPay/terminals/$terminalKey/status.

Ответ на запрос кэшируется на некоторое время. Значение по-умолчанию 300 секунд. Его можно сконфигурировать через параметр tinkoffPayStatusCacheLifeTime у объектаAcquiringSdkConfiguration, который используется при инициализации AcquiringUISDK.

Посредством реализации протокола TinkoffPayDelegate можно получит сообщение о том, что оплата через Tinkoff Pay недоступна.

Можно сконфигурировать стиль кнопки TinkoffPay через параметр tinkoffPayButtonStyle у AcquiringViewConfiguration.

TinkoffPay внутри вашего приложения

Для отображения кнопки оплаты через Tinkoff Pay внутри вашего приложения (вне экрана оплаты, предоставляемого SDK) необходимо:

  1. Самостоятельно вызвать метод определения доступности оплаты через Tinkoff Pay. Для этого можно использовать метод
func getTinkoffPayStatus(completion: @escaping (Result<GetTinkoffPayStatusResponse, Error>) -> Void)
  1. При наличии возможности оплаты отобразить кнопку оплаты через Tinkoff Pay в вашем приложении.
  2. По нажатию кнопки вызвать метод(параметр GetTinkoffPayStatusResponse.Status.Version получен на 1 шаге) и показать полученный из метода ViewController.
func tinkoffPayViewController(acquiringPaymentStageConfiguration: AcquiringPaymentStageConfiguration,
                              configuration: AcquiringViewConfiguration,
                              version: GetTinkoffPayStatusResponse.Status.Version,
                              completionHandler: PaymentCompletionHandler? = nil) -> UIViewController

Задача по закрытию ViewController, полученный из метода, ложится на плечи пользователя в момент вызова completionHandler.

API

Если необходимо использовать отдельно методы API для TinkoffPay: У экземпляра AcquiringSdk можно вызвать:

Для проверки доступности TinkoffPay на терминале

func getTinkoffPayStatus(completion: @escaping (Result<GetTinkoffPayStatusResponse, Error>) -> Void)

Для получения deeplink

func getTinkoffPayLink(paymentId: Int64,
                       version: GetTinkoffPayStatusResponse.Status.Version,
                       completion: @escaping (Result<GetTinkoffLinkResponse, Error>) -> Void)
Кнопка TinkoffPay

Для отрисовки кнопки существует класс TinkoffPayButton.

При инициализации можно указать требуемый стиль кнопки.

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

init(style: TinkoffPayButton.Style)

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

init(dynamicStyle: TinkoffPayButton.DynamicStyle)

Подключение сканера

для сканера нужно использовать любое стронее решение, подключение сканера к SDK осуществляется через AcquiringViewConfigration.scaner - это ссылка на объект который реализует протокол CardRequisitesScanerProtocol

protocol CardRequisitesScanerProtocol: class {
  func startScanner(completion: @escaping (_ number: String?, _ yy: Int?, _ mm: Int?) -> Void)
}

пример использования:

extension RootViewController: AcquiringScanerProtocol {
	func presentScanner(completion: @escaping (_ number: String?, _ yy: Int?, _ mm: Int?) -> Void) -> UIViewController? {
		if let viewController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "CardScanerViewController") as? CardScanerViewController {
      		viewController.onScannerResult = { (numbres) in
      			completion(numbres, nil, nil)
			}
			
    		return viewController
		}
		
		return nil
	}
}

Пример работы

Оплата товара по реквизитам карты или с ранее сохраненной карты

AcquiringUISDK.presentPaymentView(on presentingViewController: UIViewController, 
								paymentData: PaymentInitData, 
								configuration: AcquiringViewConfigration,
								completionHandler: @escaping PaymentCompletionHandler)

Оплата товара через Apple Pay

AcquiringUISDK.presentPaymentApplePay(on presentingViewController: UIViewController,
									paymentData data: PaymentInitData,
									viewConfiguration: AcquiringViewConfigration,
									paymentConfiguration: AcquiringUISDK.ApplePayConfiguration,
									completionHandler: @escaping PaymentCompletionHandler)

Список сохраненных карт

AcquiringUISDK.presentCardList(on: self, customerKey: customerKey, configuration: cardListViewConfigration)

Пример создания экземпляра sdk:

// терминал
let credentional = AcquiringSdkCredential(terminalKey: ASDKStageTestData.terminalKey, publicKey: ASDKStageTestData.testPublicKey)
// конфигурация для старта sdk
let acquiringSDKConfiguration = AcquiringSdkConfiguration(credential: credentional)		
// включаем логи, результаты работы запросов пишутся в консоль 
acquiringSDKConfiguration.logger = AcquiringLoggerDefault()

if let sdk = try? AcquiringUISDK.init(configuration: acquiringSDKConfiguration) {
	// SDK проинициализировалось, можно приступать к работе
	sdk.present ...
}

Пример создание AcquiringViewConfigration:

let viewConfigration = AcquiringViewConfigration.init()
// подключаем сканер
viewConfigration.scaner = scaner
// Формируем поля для экрана оплаты
viewConfigration.fields = []
// Заголовок в UINavigationBar, для случая когда экран оплаты раскрыт на весь экран
viewConfigration.viewTitle = NSLocalizedString("title.pay", comment: "Оплата")
// 1. Заголовок экрана "Оплата на сумму хх.хх руб."
let title = NSAttributedString.init(string: NSLocalizedString("title.paymeny", comment: "Оплата"), attributes: [.font: UIFont.boldSystemFont(ofSize: 22)])
let amountString = Utils.formatAmount(NSDecimalNumber.init(floatLiteral: productsAmount()))
let amountTitle = NSAttributedString.init(string: "\(NSLocalizedString("text.totalAmount", comment: "на сумму")) \(amountString)", attributes: [.font : UIFont.systemFont(ofSize: 17)])
// fields.append
viewConfigration.fields.append(AcquiringViewConfigration.InfoFields.amount(title: title, amount: amountTitle))

// 2. Описание товаров которые оплачиваем
let productsDetatils = NSMutableAttributedString.init()
productsDetatils.append(NSAttributedString.init(string: "Книги\n", attributes: [.font : UIFont.systemFont(ofSize: 17)]))
let productsDetails = products.map { (product) -> String in
  return product.name
}.joined(separator: ", ")
productsDetatils.append(NSAttributedString.init(string: productsDetails, attributes: [.font : UIFont.systemFont(ofSize: 13), .foregroundColor: UIColor(red: 0.573, green: 0.6, blue: 0.635, alpha: 1)]))
viewConfigration.fields.append(AcquiringViewConfigration.InfoFields.detail(title: productsDetatils))

// 3. Добавляем поле email 
if AppSetting.shared.showEmailField {
  viewConfigration.fields.append(AcquiringViewConfigration.InfoFields.email(value: nil, placeholder: NSLocalizedString("plaseholder.email", comment: "Отправить квитанцию по адресу")))
}

// 4. Кнопка для оплаты используя Систему Быстрых Платежей
if AppSetting.shared.paySBP {
  viewConfigration.fields.append(AcquiringViewConfigration.InfoFields.buttonPaySPB)
}
// На каком яэыке отображется экран оплаты
viewConfigration.localizableInfo = AcquiringViewConfigration.LocalizableInfo.init(lang: AppSetting.shared.languageId)

Кастомизация

Для того что бы кастомизировать UI компонента(цвет кнопки оплатить и т.д.), необходимо реализовать протокол Style и передать его экземпляр как параметр style при инициализации объекта SDK.

import TinkoffASDKUI

struct MyAwesomeStyle: Style {
...
}
if let sdk = try? AcquiringUISDK(configuration: acquiringSDKConfiguration, 
				 style: MyAwesomeStyle()) {
...
}

ASDKSample

Содержит пример интеграции Tinkoff Acquiring SDK в мобильное приложение по продаже книг. Главный экран список заготовок товаров. Второй экран - детали товара и выбор сбособа оплаты.

Поддержка

  • Просьба, по возникающим вопросам обращаться на oplata@tinkoff.ru
  • Баги и feature-реквесты можно направлять в раздел issues
  • Документация на сайте, описание API методов