В главное меню

iOS SDK интерфейс


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

  •  Проведение платежей по новым и сохраненным банковским картам

  •  Пополнение счетов экосистемы МТС

  •  Оформление подписок и прием реккурентных платежей

  •  Распознавание банковских карт

  •  Сохранение банковских карт клиента в авторизованной зоне

  •  Подключение автоплатежей во время и после проведения оплаты

  •  Поддержка темной темы

  •  Оплата с СБП

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

  •  Поддержка 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`MTSPaySDKMTSPayimport MTSPaySDK

let configuration = MTSPayConfiguration() let sdk = MTSPay(configuration: configuration)
1.0...3.3.6MTSPaymentMTSPaymentimport 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

Общие параметры и сущности в сценариях SDK

• Предвыбранный платежный инструмент. Передается в параметрах сценария (флоу)

/// Предвыбранный платежный инструмент
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("Пользователь покинул флоу до завершения")
    }
}

Cписок карт. Признак наличия карт

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

/// Модуль списка карт пользователя
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)
}

Cканирование банковских карт

В SDK предусмотрен функционал сканирования реквизитов банковской карты. По умолчанию данный функционал поставляется вместе с SDK. Если в этом нет необходимости и/или критичен размер приложения (разница ≈ 34MB) можно использовать облегченный вариант без распознавания

pod "MTSPaySDK/PaymentLight"