• Проведение платежей по новым и сохраненным банковским картам
• Пополнение счетов экосистемы МТС
• Оформление подписок и прием реккурентных платежей
• Распознавание банковских карт
• Сохранение банковских карт клиента в авторизованной зоне
• Подключение автоплатежей во время и после проведения оплаты
• Поддержка темной темы
• Оплата с СБП
• Поддержка iOS 13.0 и выше
В Podfile добавить
source 'https://gitlab.services.mts.ru/mobile-sdk/ios/podspecs' pod 'MTSPaySDK' ~> 6.0
В случае необходимости реализации кастомного UI оплаты можно напрямую использовать MTSPaySDK/Core
pod "MTSPaySDK/Core" ~> 6.0
Версию, которая находится в процессе разработки и пока не выпущена, можно подключить следующим образом
pod "MTSPaySDK", :git => "https://gitlab.services.mts.ru/mobile-sdk/ios/paymentsdk"
Начиная с версии 4.0 модуль и основной класс sdk были переименованы:
| `>=4.0` | MTSPaySDK | MTSPay | import MTSPaySDK let configuration = MTSPayConfiguration() let sdk = MTSPay(configuration: configuration) |
| 1.0...3.3.6 | MTSPayment | MTSPayment | import MTSPayment let configuration = MTSPaymentConfiguration() let sdk = MTSPayment(configuration: configuration) |
Также SDK распространяется в собранном виде через Artifactory MTS. Подключение выполняется через плагин cocoapods-art либо вручную.
Установка плагина и добавление репозитория
gem install cocoapods-art pod repo-art add mts-artifactory-api-pods-ios-sdk-mtspay-cocoapods-local https://artifactory.mts.ru/artifactory/api/pods/ios-sdk-mtspay-cocoapods-local
В Podfile добавить
plugin "cocoapods-art", :sources => [ "mts-artifactory-api-pods-ios-sdk-mtspay-cocoapods-local" ]
При инициализации необходимо передать объект конфигурации, который отвечает за различные параметры работы sdk.
let configuration = MTSPayConfiguration(
authorization: .authorized(ssoTokenId: "ssoTokenId"),
environment: .production,
applePay: .enabled(merchantId: "Apple Pay Merchant Id"),
darkMode: .automatic,
merchantCapabilities: [.canOpenSupport, .canOpenAutoPayments, .canOpenLewisCard],
appReturnUrl: "paysdk://appReturn"
)
let mtsPay = MTSPay(configuration: configuration)
Рассмотрим каждых из них по отдельности:
• Авторизация
enum Authorization {
/// Пользователь не авторизован.
///
/// Оплата производится в неавторизованной зоне.
/// Привязанные платежные инструменты недостпуны
case unauthorized
/// Пользователь авторизован
///
/// Доступны привязанные платежные инструменты
///
/// - Parameter ssoTokenId: id токен
case authorized(ssoTokenId: String)
/// Отложенная авторизация
///
/// Токен будет запрошен во время работы сценария оплаты
///
/// - Parameter AsyncAuthProvider: провайдер токена
case asyncAuth(AsyncAuthProvider)
}
• Окружение
enum Environment {
/// Боевой стенд (прод)
case production
/// Тестовый стенд
case stage
/// Кастомный стенд (для разработки)
case dev(publicUrl: URL)
}
• Apple Pay. Для включения возможности оплаты по Apple Pay необходимо передать Merchant Identifier
enum ApplePay {
case enabled(merchantId: String)
case disabled
}
Предварительно необходимо зарегистрировать Merchant Identifier в кабинете developer.apple.com и запросить у нас CSR для выпуска Payment Processing Certificate. Полученный сертификат отправить нам.
• Темная тема. По умолчанию используется .automatic, который синхронизирует тему с системной.
enum DarkMode {
case automatic
case dark
case light
}
• Стиль шторки. По умолчанию шторка подстраивает высоту под размер контента.
enum BottomSheetStyle {
/// Динамическая высота. Подстраивается под размер контента
case `dynamic`
/// Фиксированная высота. Высота до `safeArea.top`
case top
/// Полноэкранное представление
case fullscreen
}
• sbpDelayHandler — Обработчик задержки перед открытием приложения банка в СБП
/// Обработчик задержки перед открытием приложения банка
///
/// Для Моего МТС для разблокировки траффика
protocol SbpDelayHandler {
func handleDelay(completion: () -> Void)
}
• Возможности интегратора. Влияют на результирующий колбэк и некоторые элементы интерфейса, которые будут отображены на финальном экране. Если не переданы данные параметры, sdk откроет данные страницы в браузере.
struct MerchantCapabilities: OptionSet {
/// Интегратор может открыть страницу автоплатежей пользователя
///
/// Влияет на чьей стороне будет обработка нажатия на кнопку "К моим автоплатежам"
public static let canOpenAutoPayments = MerchantCapabilities(rawValue: 1 << 0)
/// Интегратор может открыть страницу поддержки
///
/// Влияет на чьей стороне будет обработка нажатия на кнопку "Обратиться в поддержку"
public static let canOpenSupport = MerchantCapabilities(rawValue: 1 << 1)
/// Интегратор может открыть функционал выпуска карты МТС Деньги (Льюис)
///
/// Влияет на отображение баннера выпуска карты МТС Деньги (Льюис)
public static let canOpenLewisCard = MerchantCapabilities(rawValue: 1 << 2)
}
• appReturnUrl — Диплинк возврата в приложение. Опциональный параметр. Используется для возврата в хост приложение после оплаты по СБП в приложении банка. Диплинк не должен модифицировать текущую иерархию вью (только открывать хост приложение). Ограничения API СБП: URL обязательно должен содержать host (быть непустым). host не должен содержать спецсимволы (быть alphanumeric). Пример валидного диплинка: paysdk://appReturn
• Предвыбранный платежный инструмент. Передается в параметрах сценария (флоу)
/// Предвыбранный платежный инструмент
public struct PreSelectedPaymentTool {
/// Тип платежного инструмента
public let type: ComplexType
/// Идентификатор платежного инструмента (только для типа ewalletBinding)
public let id: String?
public init(
type: ComplexType,
id: String?
) {
self.type = type
self.id = type == .ewalletBinding ? id : nil
}
}
• Результат успешной оплаты. Возвращается в коллбэке сценария
/// Успешная оплата
public struct PaymentCompleted: Equatable {
/// Дата платежа
public let paymentDate: Date
/// Идентификатор платежа
public let paymentId: Int
/// Статус платежа
public let status: Status
/// Платежный инструмент
public let paymentTool: PaymentTool
/// Информация о сумме списания
public let amountInfo: AmountInfo
}
Необходимо сконфигурировать и передать идентификатор платежной сессии и SSOTokenId клиента (в случае авторизованного платежа для оплаты по привязанным платежным инструментам)
Привязанные карты и МТС Логин. Если необходимо дать клиенту возможность платежа с привязанных к профилю банковских карт (в том числе карт аккаунта МТС Банка, если он привязан к профилю), тогда для интеграции необходимо наличие валидного id_token МТС Логин, который генерируется в процессе аутентификации пользователя в интегрируемом приложении. Обязательным условием получения id_token является наличие значения openid в параметре scope в URL авторизации МТС Логин (scope=openid). Этот id_token необходимо будет передавать при инициализации в параметр authorization в кейсе .authorized(ssoTokenId: _)
Swift
import MTSPaySDK
func pay() {
let configuration = MTSPayConfiguration(
authorization: .authorized(ssoTokenId: "ssoTokenId"),
environment: .production
)
let paymentFlowParams = PaymentFlowParams(
sessionId: sessionId,
preSelectedPaymentTool: .init(type: .sbp), // Предвыбранный платежный иструмент
hasSuccessScreen: true // Отображать экран успешной оплаты
)
let mtsPay = MTSPay(configuration: configuration)
mtsPay.startPaymentFlow(
on: self, // UIViewController
paymentFlowParams: paymentFlowParams
) { mtsPayResult in
switch mtsPayResult.flowResult {
case .flowComplete(let paymentCompleted):
print("Дата платежа: \(paymentCompleted.paymentDate)")
case .fatalPaymentErrorOccured(let fatalPaymentError):
print "Фатальная ошибка оплаты: \(fatalPaymentError.userMessage)"
case .flowNotFinished:
print("Оплата была отменена клиентом")
}
}
}
Примечание: Необходимо удерживать сильную ссылку на экземпляр MTSPay Экземпляр MTSPay теперь самостоятельно удерживается вью контролером шторки оплаты
В сценарии пополнения счетов платежная сессия создается на стороне SDK, обязательным параметром является только serviceToken. Параметр serviceToken является статичным и запрашивается у команды МТС Pay при интеграции. Также опционально можно передать предзаполненные параметры, такие как:
• Номер телефона/счета
public enum RefillAccount: Equatable {
/// Номер телефона 10 цифр без кода страны
case phone(String)
/// 9-ти или 11..13-ти значный номер лицевого счета
case bill(String)
}
• Сумма платежа (в рублях)
В случае, если передан параметр RefillAccount, экраны выбора счета и пополняемого сервиса пропускаются, будет сразу показан экран оплаты.
let refillPredefinedDetails = RefillPredefinedDetails(
account: .phone("9147773322"), // Реквизит пополнения. Необязательный параметр. Служит для предзаполнения номера телефона, либо счета
amount: 500.0 // Предзаполненная сумма пополнения. Необязательный параметр. Если не будет передан, будет проставлена минимальная возможная.
)
let refillFlowParams = RefillFlowParams(
serviceToken: "SERVICE TOKEN", // Токен вашего сервиса. Обязательный параметр. Отвечает за список доступных сервисов(услуг) к пополнению
predefinedDetails: refillPredefinedDetails
)
mtsPay.startRefillFlow(
on: self, // UIViewController
refillFlowParams: refillFlowParams,
) { mtsPayResult in
switch mtsPayResult.flowResult {
case .flowComplete(let paymentCompleted):
print("Платеж выполнен. Дата платежа: \(paymentCompleted.paymentDate)")
case .fatalPaymentErrorOccured(let fatalPaymentError):
print "Фатальная ошибка оплаты: \(fatalPaymentError.userMessage)"
case .flowNotFinished:
print("Пользователь вышел из платежного сценария, не завершив оплату")
}
switch mtsPayResult.userAction {
case .openAutoPayments:
// Пользователь выбрал открыть настройки автоплатежей по завершению сценария
case .openSupport:
// Пользователь выбрал открыть страницу поддержки по завершению сценария (в случае получения ошибки)
case .openCardLewis:
// Пользователь нажал на баннер открытия карты МТС Деньги (Lewis) после оплаты
case .openCardLewisAndReturnRefill(account: let account):
// Пользователь нажал на баннер открытия карты МТС Деньги (Lewis) до оплаты
// Необходимо после выпуска/пополнения карты вернуть пользователя в обратно в пополнение по реквизиту `account`
default:
return
}
}
В сценарии пополнения карты на вход принимаются следующие параметры:
public struct RefillLewisCardFlowParams {
/// Идентификатор сервисного токена приложения пополнения карты
public let serviceToken: String
/// Номер телефона (только цифры, 11 символов)
public let phone: String
/// Идентификатор карты, которую будут пополнять
public let cardId: String
/// Маскированный номер карты
public let maskedPan: String
/// Предзаполненная сумма пополнения (опциональный параметр)
public let amount: Decimal?
/// Отображать ли экран результата платежа в случае успешного пополнения
public let hasSuccessScreen: Bool
/// Параметры для автоматического запуска сценария refill
/// после успешного пополнения карты (необязательный параметр)
public let refillParams: RefillFlowParams?
public init(
serviceToken: String,
phone: String,
cardId: String,
maskedPan: String,
amount: Decimal? = nil,
hasSuccessScreen: Bool = true,
refillParams: RefillFlowParams? = nil
) {
self.serviceToken = serviceToken
self.phone = phone
self.cardId = cardId
self.maskedPan = maskedPan
self.amount = amount
self.hasSuccessScreen = hasSuccessScreen
self.refillParams = refillParams
}
}
Параметр serviceToken является статичным и запрашивается у команды МТС Pay при интеграции. Ниже приведен пример запуска сценария пополнения карты.
let refillLewis = RefillLewisCardFlowParams(
serviceToken: "SERVICE_TOKEN",
phone: "79030000050",
cardId: "CARD_ID",
maskedPan: "220208******5253"
)
mtsPay.startRefillLewisCardFlow(
on: self, // UIViewController
refillLewisCardFlowParams: flowParams
) { mtsPayResult.flowResult in
switch mtsPayResult.flowResult {
case .flowComplete(let paymentCompleted):
print("Дата платежа: \(paymentCompleted.paymentDate)")
case .fatalPaymentErrorOccured(let fatalPaymentError):
print("Фатальная ошибка оплаты: \(fatalPaymentError.userMessage)"
case .flowNotFinished:
print("Оплата была отменена клиентом")
}
}
Данный сценарий запускается аналогично предыдущим, однако требует обязательной авторизации пользователя. Токен или провайдер токена необходимо передать при инициализации сдк
mtsPay.startNewCardFlow(
on: self, // UIViewController
) { mtsPayResult.flowResult in
switch mtsPayResult.flowResult {
case .flowComplete(let cardBinding):
print("Карта \(cardBinding.userCardMnemonic) добавлена")
case .fatalPaymentErrorOccured(let fatalPaymentError):
print("Фатальная при добавлении карты: \(fatalPaymentError.userMessage)"
case .flowNotFinished:
print("Пользователь покинул флоу до завершения")
}
}
Модуль списка карт имеет следующий интерфейс взаимодействия. Для работы списка так же, как при добавлении новой карты, необходимо передать токен или провайдер токена при инициализации сдк
/// Модуль списка карт пользователя
public protocol CardListModule: UIView {
/// Обновить список карт
/// - Parameter isForceUpdate: признак игнорирования кэша
func refreshCardList(isForceUpdate: Bool)
/// Блок, вызываемый по завершению обновления списка.
/// Необходим для скрытия pull to refresh контрола
var onCardListRefreshed: (() -> Void)? { get set }
/// Блок, вызываемый при обновлении ui
/// Необходим для триггера лэйаута контейнера
var onViewStateChanged: (() -> Void)? { get set }
}
Для создания модуля необходимо вызывать метод
let cardListView = mtsPay.makeCardListView(
navigationController: navigationController
)
На событие viewWillAppear желательно делать вызов cardListView.updateCardList(isForceUpdate: false)
Для проверки наличия карт у пользователя следует создать и вызвать следующий usecase
let checkUserHasCardsUseCase = mtsPay.makeCheckUserHasAnyCardsUseCase()
let checkResult = await checkUserHasCardsUseCase.check(
ignoreCache: false // признак ингнорирования кеша
)
switch checkResult {
case .success(let hasAnyCards):
// hasAnyCards - признак наличия карт
case .failure(let error):
// ошибка получения признака
}
Отправка аналитических метрик реализуется на стороне интегратора. Хост-приложение обогащает получаемые от модуля события всей необходимой информацией, такой как идентификаторы пользователя и прочее, и отправляет в свою систему (AppMetrica, Firebase или иную) . Для получения метрик, при инициализации, необходимо передать объект, реализующий протокол AnalyticsListener.
/// Протокол объекта, реализующего отправку аналитических данных
public protocol AnalyticsListener {
/// Произошло событие
/// - Parameter event: аналитическое событие
func didCommit(event: AnalyticEvent)
}
В SDK предусмотрен функционал сканирования реквизитов банковской карты. По умолчанию данный функционал поставляется вместе с SDK. Если в этом нет необходимости и/или критичен размер приложения (разница ≈ 34MB) можно использовать облегченный вариант без распознавания
pod "MTSPaySDK/PaymentLight"