diff --git a/CHANGELOG.md b/CHANGELOG.md
index b7071ea3..2d4aa445 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
## Changelog
+### 6.0.0
+
+1. Добавили поддержку `SberPay`.
+2. Добавили авторизацию в `ЮMoney` через мобильное приложение.
+3. Обновили иконки платежных систем и банков.
+
+> Необходимо выполнить [инструкцию](https://github.com/yoomoney/yookassa-payments-swift/blob/master/MIGRATION.md) по миграции с версий ниже.
+
## 5.4.1
1. Обновление версии `YandexMobileMetrica`.
diff --git a/MIGRATION.md b/MIGRATION.md
index e9b77c36..aabd378c 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -1,16 +1,127 @@
# Migration guide
- [Migration guide](#migration-guide)
+ - [5.\*.\* -> 6.\*.\*](#5---6)
+ - [Изменить код интеграции](#изменить-код-интеграции)
+ - [Конфигурация проекта](#конфигурация-проекта)
+ - [Изменить код подтверждения платежа](#изменить-код-подтверждения-платежа)
+ - [Добавить поддержку SberPay](#добавить-поддержку-SberPay)
+ - [Добавить поддержку авторизации в ЮMoney через мобильное приложение](#добавить-поддержку-авторизации-в-ЮMoney-через-мобильное-приложение)
+ - [5.\*.\* -> 5.3.0](#5---530)
- [4.\*.\* -> 5.\*.\*](#4---5)
- [Изменить Podfile](#изменить-podfile)
- - [Изменить код интеграции](#изменить-код-интеграции)
+ - [Изменить код интеграции](#изменить-код-интеграции-1)
- [\*.\*.\* -> 4.\*.\*](#---4)
- [Удалить `YandexLoginSDK`](#удалить-yandexloginsdk)
- [Добавить новые зависимости](#добавить-новые-зависимости)
- - [Если вы используете метод оплаты "Яндекс.Деньги"](#если-вы-используете-метод-оплаты-яндексденьги)
+ - [Если вы используете метод оплаты "ЮMoney"](#если-вы-используете-метод-оплаты-юmoney)
- [2.\*.\* -> 3.\*.\*](#2---3)
- [2.1.0 -> 2.2.0](#210---220)
+## 5.\*.\* -> 6.\*.\*
+
+Для корректной работы сценария `Sberpay` и авторизации в `ЮMoney` через мобильное приложение, необходимо изменить некоторые парамтеры.
+
+### Изменить код интеграции
+
+1. В `TokenizationModuleInputData` необходимо передавать `applicationScheme` - схема для возврата в приложение после успешной оплаты с помощью `Sberpay` в приложении СберБанк Онлайн или после успешной авторизации в `ЮMoney` через мобильное приложение.
+
+Пример `applicationScheme`:
+
+```swift
+let moduleData = TokenizationModuleInputData(
+ ...
+ applicationScheme: "examplescheme://"
+```
+
+2. В `AppDelegate` импортировать зависимость `YooKassaPayments`:
+
+ ```swift
+ import YooKassaPayments
+ ```
+
+3. Добавить обработку ссылок через `YKSdk` в `AppDelegate`:
+
+```swift
+func application(
+ _ application: UIApplication,
+ open url: URL,
+ sourceApplication: String?,
+ annotation: Any
+) -> Bool {
+ return YKSdk.shared.handleOpen(
+ url: url,
+ sourceApplication: sourceApplication
+ )
+}
+
+@available(iOS 9.0, *)
+func application(
+ _ app: UIApplication,
+ open url: URL,
+ options: [UIApplication.OpenURLOptionsKey: Any] = [:]
+) -> Bool {
+ return YKSdk.shared.handleOpen(
+ url: url,
+ sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String
+ )
+}
+```
+
+4. Реализовать метод `didSuccessfullyConfirmation(paymentMethodType:)` протокола `TokenizationModuleOutput`, который будет вызван после успешного подтверждения платежа.
+
+### Конфигурация проекта
+
+В `Info.plist` добавить следующие строки:
+
+```plistbase
+CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ ${BUNDLE_ID}
+ CFBundleURLSchemes
+
+ examplescheme
+
+
+
+```
+
+где `examplescheme` - схема для открытия вашего приложения, которую вы указали в `applicationScheme` при создании `TokenizationModuleInputData`.
+
+### Изменить код подтверждения платежа
+
+Для подтверждения платежа необходимо вызвать метод `startConfirmationProcess(confirmationUrl:paymentMethodType:)`.
+
+После успешного прохождения подтверждения будет вызван метод `didSuccessfullyConfirmation(paymentMethodType:)` протокола `TokenizationModuleOutput`.
+
+> Обратите внимание, что методы `start3dsProcess(requestUrl:)` и `didSuccessfullyPassedCardSec(on module:)` помечены как `deprecated` - используйте `startConfirmationProcess(confirmationUrl:paymentMethodType:)` и `didSuccessfullyConfirmation(paymentMethodType:)` вместо них.
+
+### Добавить поддержку `SberPay`
+
+В `Info.plist` добавить следующие строки:
+
+```plistbase
+LSApplicationQueriesSchemes
+
+ sberpay
+
+```
+
+### Добавить поддержку авторизации в `ЮMoney` через мобильное приложение
+
+В `Info.plist` добавить следующие строки:
+
+```plistbase
+LSApplicationQueriesSchemes
+
+ yoomoneyauth
+
+```
+
## 5.\*.\* -> 5.3.0
В версии 5.3.0 зависимости `TMXProfiling` и `TMXProfilingConnections` используются в виде `.xcframework`.
diff --git a/Podfile b/Podfile
index f37a6f8c..bd628df6 100644
--- a/Podfile
+++ b/Podfile
@@ -20,7 +20,6 @@ post_install do |installer|
target.build_configurations.each do |config|
config.build_settings['PROVISIONING_PROFILE_SPECIFIER'] = ''
config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO'
- config.build_settings['SWIFT_SUPPRESS_WARNINGS'] = 'YES'
config.build_settings['CLANG_WARN_DOCUMENTATION_COMMENTS'] = 'NO'
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '10.0'
end
diff --git a/Podfile.lock b/Podfile.lock
index 443d3332..ec2961ef 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -1,27 +1,27 @@
PODS:
- CardIO (5.4.1)
- FunctionalSwift (1.2.0)
- - MoneyAuth (2.21.0):
+ - MoneyAuth (2.29.0):
- FunctionalSwift
- ThreatMetrixAdapter
- YooMoneyCoreApi
- Reveal-SDK (28)
- SwiftLint (0.43.1)
- - ThreatMetrixAdapter (2.0.0)
+ - ThreatMetrixAdapter (3.2.0)
- YandexMobileMetrica/Dynamic (3.16.0):
- YandexMobileMetrica/Dynamic/Core (= 3.16.0)
- YandexMobileMetrica/Dynamic/Crashes (= 3.16.0)
- YandexMobileMetrica/Dynamic/Core (3.16.0)
- YandexMobileMetrica/Dynamic/Crashes (3.16.0):
- YandexMobileMetrica/Dynamic/Core
- - YooKassaPayments (5.4.1):
- - MoneyAuth (~> 2.21.0)
- - ThreatMetrixAdapter (~> 2.0.0)
+ - YooKassaPayments (6.0.0):
+ - MoneyAuth (~> 2.29.0)
+ - ThreatMetrixAdapter (~> 3.2.0)
- YandexMobileMetrica/Dynamic (~> 3.0)
- - YooKassaPaymentsApi (~> 2.3.0)
+ - YooKassaPaymentsApi (~> 2.5.0)
- YooKassaWalletApi (~> 2.3.0)
- YooMoneyCoreApi (~> 1.9.0)
- - YooKassaPaymentsApi (2.3.0):
+ - YooKassaPaymentsApi (2.5.0):
- FunctionalSwift (~> 1.2.0)
- YooMoneyCoreApi (~> 1.9.0)
- YooKassaWalletApi (2.3.0):
@@ -57,16 +57,16 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
CardIO: 56983b39b62f495fc6dae9ad7cf875143df06443
FunctionalSwift: 1b839fcf50b8db067885938df54f818518356dbb
- MoneyAuth: 60e6885924b05d05dbe13c91d034db37cd2d918f
+ MoneyAuth: daefde9745613c5f9cdd178467d3e3fa48d903c0
Reveal-SDK: 1a2a678648fc4d277bad71c86d15530424324288
SwiftLint: 99f82d07b837b942dd563c668de129a03fc3fb52
- ThreatMetrixAdapter: c5ea4cc20dd82931fabeb596c3e86d300c2d14fb
+ ThreatMetrixAdapter: 8d2e93fb6ec1e93128116fa584dca52d84dd8736
YandexMobileMetrica: 2ab55883ea552a4c4ea4516a3353ca0577a747cc
- YooKassaPayments: 5ef043a777957ba1b173af02f0777875de802df3
- YooKassaPaymentsApi: 66302803d0389fbc27238d8ff9447c10ac1e31bf
+ YooKassaPayments: d780e5dd8e27be125600e4980933c353dd0da73a
+ YooKassaPaymentsApi: c32f1f256f0d9f7bc69d89348bb480453872337c
YooKassaWalletApi: 9ca2fa3c2827721d646ee8f770509e7eeea98743
YooMoneyCoreApi: 0d4da37a1619a4c92df0962ecc48d1665dbae4f4
-PODFILE CHECKSUM: ecceb3f227ebee8f464a40c7b9ed3b7a0bc72774
+PODFILE CHECKSUM: 06cb858f1cc4e9551da88e13962348b7590eea82
COCOAPODS: 1.10.0
diff --git a/README.md b/README.md
index c56e4579..14f4b913 100644
--- a/README.md
+++ b/README.md
@@ -28,8 +28,9 @@
- [ЮMoney](#юmoney)
- [Как получить `client id` центра авторизации системы `ЮMoney`](#как-получить-client-id-центра-авторизации-системы-юmoney)
- [Передать `client id` в параметре `moneyAuthClientId`](#передать-client-id-в-параметре-moneyauthclientid)
+ - [Поддержка авторизации через мобильное приложение](#поддержка-авторизации-через-мобильное-приложение)
- [Банковская карта](#банковская-карта)
- - [Сбербанк Онлайн](#сбербанк-онлайн)
+ - [SberPay](#sberpay)
- [Apple Pay](#apple-pay)
- [Описание публичных параметров](#описание-публичных-параметров)
- [TokenizationFlow](#tokenizationflow)
@@ -43,7 +44,7 @@
- [CustomizationSettings](#customizationsettings)
- [SavePaymentMethod](#savepaymentmethod)
- [Сканирование банковских карт](#сканирование-банковских-карт)
- - [Настройка 3D Secure](#настройка-3d-secure)
+ - [Настройка подтверждения платежа](#настройка-подтверждения-платежа)
- [Логирование](#логирование)
- [Тестовый режим](#тестовый-режим)
- [Запуск Example](#запуск-example)
@@ -121,7 +122,6 @@ end
```swift
import YooKassaPayments
-import YooKassaPaymentsApi
```
Пример создания `TokenizationModuleInputData`:
@@ -161,9 +161,11 @@ present(viewController, animated: true, completion: nil)
```swift
extension ViewController: TokenizationModuleOutput {
- func tokenizationModule(_ module: TokenizationModuleInput,
- didTokenize token: Tokens,
- paymentMethodType: PaymentMethodType) {
+ func tokenizationModule(
+ _ module: TokenizationModuleInput,
+ didTokenize token: Tokens,
+ paymentMethodType: PaymentMethodType
+ ) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.dismiss(animated: true)
@@ -171,18 +173,22 @@ extension ViewController: TokenizationModuleOutput {
// Отправьте токен в вашу систему
}
- func didFinish(on module: TokenizationModuleInput,
- with error: YooKassaPaymentsError?) {
+ func didFinish(
+ on module: TokenizationModuleInput,
+ with error: YooKassaPaymentsError?
+ ) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.dismiss(animated: true)
}
}
- func didSuccessfullyPassedCardSec(on module: TokenizationModuleInput) {
+ func didSuccessfullyConfirmation(
+ paymentMethodType: PaymentMethodType
+ ) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
- // Создать экран успеха после прохождения 3DS
+ // Создать экран успеха после прохождения подтверждения (3DS или Sberpay)
self.dismiss(animated: true)
// Показать экран успеха
}
@@ -198,7 +204,7 @@ extension ViewController: TokenizationModuleOutput {
`.yooMoney` — ЮMoney (платежи из кошелька или привязанной картой)\
`.bankCard` — банковская карта (карты можно сканировать)\
-`.sberbank` — Сбербанк Онлайн (с подтверждением по смс)\
+`.sberbank` — SberPay (с подтверждением через приложение Сбербанк Онлайн, если оно установленно, иначе с подтверждением по смс)\
`.applePay` — Apple Pay
## Настройка способов оплаты
@@ -276,20 +282,164 @@ let moduleData = TokenizationModuleInputData(
2. Получите токен.
3. [Создайте платеж](https://yookassa.ru/developers/api#create_payment) с токеном по API ЮKassa.
+#### Поддержка авторизации через мобильное приложение
+
+1. В `TokenizationModuleInputData` необходимо передавать `applicationScheme` – схема для возврата в приложение после успешной авторизации в `ЮMoney` через мобильное приложение.
+
+Пример `applicationScheme`:
+
+```swift
+let moduleData = TokenizationModuleInputData(
+ ...
+ applicationScheme: "examplescheme://"
+```
+
+2. В `AppDelegate` импортировать зависимость `YooKassaPayments`:
+
+ ```swift
+ import YooKassaPayments
+ ```
+
+3. Добавить обработку ссылок через `YKSdk` в `AppDelegate`:
+
+```swift
+func application(
+ _ application: UIApplication,
+ open url: URL,
+ sourceApplication: String?,
+ annotation: Any
+) -> Bool {
+ return YKSdk.shared.handleOpen(
+ url: url,
+ sourceApplication: sourceApplication
+ )
+}
+
+@available(iOS 9.0, *)
+func application(
+ _ app: UIApplication,
+ open url: URL,
+ options: [UIApplication.OpenURLOptionsKey: Any] = [:]
+) -> Bool {
+ return YKSdk.shared.handleOpen(
+ url: url,
+ sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String
+ )
+}
+```
+
+4. В `Info.plist` добавьте следующие строки:
+
+```plistbase
+LSApplicationQueriesSchemes
+
+ yoomoneyauth
+
+CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ ${BUNDLE_ID}
+ CFBundleURLSchemes
+
+ examplescheme
+
+
+
+```
+
+где `examplescheme` - схема для открытия вашего приложения, которую вы указали в `applicationScheme` при создании `TokenizationModuleInputData`. Через эту схему будет открываться ваше приложение после успешной авторизации в `ЮMoney` через мобильное приложение.
+
### Банковская карта
1. При создании `TokenizationModuleInputData` передайте значение `.bankcard` в `paymentMethodTypes`.
2. Получите токен.
3. [Создайте платеж](https://yookassa.ru/developers/api#create_payment) с токеном по API ЮKassa.
-### Сбербанк Онлайн
+### SberPay
+
+С помощью SDK можно провести платеж через «Мобильный банк» Сбербанка — с подтверждением оплаты через приложение Сбербанк Онлайн, если оно установленно, иначе с подтверждением по смс.
+
+В `TokenizationModuleInputData` необходимо передавать `applicationScheme` – схема для возврата в приложение после успешной оплаты с помощью `SberPay` в приложении СберБанк Онлайн.
+
+Пример `applicationScheme`:
+
+```swift
+let moduleData = TokenizationModuleInputData(
+ ...
+ applicationScheme: "examplescheme://"
+```
-С помощью SDK можно провести платеж через «Мобильный банк» Сбербанка — с подтверждением оплаты по смс:
+Чтобы провести платёж:
1. При создании `TokenizationModuleInputData` передайте значение `.sberbank` в `paymentMethodTypes`.
2. Получите токен.
3. [Создайте платеж](https://yookassa.ru/developers/api#create_payment) с токеном по API ЮKassa.
+Для подтверждения платежа через приложение СберБанк Онлайн:
+
+1. В `AppDelegate` импортируйте зависимость `YooKassaPayments`:
+
+ ```swift
+ import YooKassaPayments
+ ```
+
+2. Добавьте обработку ссылок через `YKSdk` в `AppDelegate`:
+
+```swift
+func application(
+ _ application: UIApplication,
+ open url: URL,
+ sourceApplication: String?,
+ annotation: Any
+) -> Bool {
+ return YKSdk.shared.handleOpen(
+ url: url,
+ sourceApplication: sourceApplication
+ )
+}
+
+@available(iOS 9.0, *)
+func application(
+ _ app: UIApplication,
+ open url: URL,
+ options: [UIApplication.OpenURLOptionsKey: Any] = [:]
+) -> Bool {
+ return YKSdk.shared.handleOpen(
+ url: url,
+ sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String
+ )
+}
+```
+
+3. В `Info.plist` добавьте следующие строки:
+
+```plistbase
+LSApplicationQueriesSchemes
+
+ sberpay
+
+CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ ${BUNDLE_ID}
+ CFBundleURLSchemes
+
+ examplescheme
+
+
+
+```
+
+где `examplescheme` - схема для открытия вашего приложения, которую вы указали в `applicationScheme` при создании `TokenizationModuleInputData`. Через эту схему будет открываться ваше приложение после успешной оплаты с помощью `SberPay`.
+
+4. Реализуйте метод `didSuccessfullyConfirmation(paymentMethodType:)` протокола `TokenizationModuleOutput`, который будет вызван после успешного подтверждения платежа (см. [Настройка подтверждения платежа](#настройка-подтверждения-платежа)).
+
### Apple Pay
1. Чтобы подключить Apple Pay, нужно передать в ЮKassa сертификат, с помощью которого Apple будет шифровать данные банковских карт.
@@ -311,16 +461,8 @@ let moduleData = TokenizationModuleInputData(
```swift
let moduleData = TokenizationModuleInputData(
...
- applePayMerchantIdentifier: "")
-```
-Например, если ваш apple pay identifier — `com.example.identifier`, то код будет следующим:
- >
-```swift
-let moduleData = TokenizationModuleInputData(
- ...
- applePayMerchantIdentifier: "com.example.identifier")
+ applePayMerchantIdentifier: "com.example.identifier"
```
-
2. Получите токен.
3. [Создайте платеж](https://yookassa.ru/developers/api#create_payment) с токеном по API ЮKassa.
@@ -357,19 +499,19 @@ let moduleData = TokenizationModuleInputData(
>Необязательные:
-| Параметр | Тип | Описание |
-| -------------------------- | --------------------- | -------- |
+| Параметр | Тип | Описание |
+| -------------------------- | --------------------- | ------------------------------------------------------------ |
| gatewayId | String | По умолчанию `nil`. Используется, если у вас несколько платежных шлюзов с разными идентификаторами. |
| tokenizationSettings | TokenizationSettings | По умолчанию используется стандартный инициализатор со всеми способами оплаты. Параметр отвечает за настройку токенизации (способы оплаты и логотип ЮKassa). |
-| testModeSettings | TestModeSettings | По умолчанию `nil`. Настройки тестового режима. |
+| testModeSettings | TestModeSettings | По умолчанию `nil`. Настройки тестового режима. |
| cardScanning | CardScanning | По умолчанию `nil`. Возможность сканировать банковские карты. |
| applePayMerchantIdentifier | String | По умолчанию `nil`. Apple Pay merchant ID (обязательно для платежей через Apple Pay). |
-| returnUrl | String | По умолчанию `nil`. URL страницы (поддерживается только `https`), на которую надо вернуться после прохождения 3-D Secure. Необходим только при кастомной реализации 3-D Secure. Если вы используете `start3dsProcess(requestUrl:)`, не задавайте этот параметр. |
+| returnUrl | String | По умолчанию `nil`. URL страницы (поддерживается только `https`), на которую надо вернуться после прохождения 3-D Secure. Необходим только при кастомной реализации 3-D Secure. Если вы используете `startConfirmationProcess(confirmationUrl:paymentMethodType:)`, не задавайте этот параметр. |
| isLoggingEnabled | Bool | По умолчанию `false`. Включает логирование сетевых запросов. |
-| userPhoneNumber | String | По умолчанию `nil`. Телефонный номер пользователя. |
+| userPhoneNumber | String | По умолчанию `nil`. Телефонный номер пользователя. |
| customizationSettings | CustomizationSettings | По умолчанию используется цвет blueRibbon. Цвет основных элементов, кнопки, переключатели, поля ввода. |
-| moneyAuthClientId | String | По умолчанию `nil`. Идентификатор для центра авторизации в системе YooMoney.
-
+| moneyAuthClientId | String | По умолчанию `nil`. Идентификатор для центра авторизации в системе YooMoney. |
+| applicationScheme | String | По умолчанию `nil`. Схема для возврата в приложение после успешной оплаты с помощью `Sberpay` в приложении СберБанк Онлайн или после успешной авторизации в `YooMoney` через мобильное приложение. |
### BankCardRepeatModuleInputData
>Обязательные:
@@ -381,15 +523,17 @@ let moduleData = TokenizationModuleInputData(
| purchaseDescription | String | Описание заказа в форме оплаты |
| paymentMethodId | String | Идентификатор сохраненного способа оплаты |
| amount | Amount | Объект, содержащий сумму заказа и валюту |
+| savePaymentMethod | SavePaymentMethod | Объект, описывающий логику того, будет ли платеж рекуррентным |
>Необязательные:
-| Параметр | Тип | Описание |
-| -------------------------- | --------------------- | -------- |
-| testModeSettings | TestModeSettings | По умолчанию `nil`. Настройки тестового режима. |
-| returnUrl | String | По умолчанию `nil`. URL страницы (поддерживается только `https`), на которую надо вернуться после прохождения 3-D Secure. Необходим только при кастомной реализации 3-D Secure. Если вы используете `start3dsProcess(requestUrl:)`, не задавайте этот параметр. |
-| isLoggingEnabled | Bool | По умолчанию `false`. Включает логирование сетевых запросов. |
-| customizationSettings | CustomizationSettings | По умолчанию используется цвет blueRibbon. Цвет основных элементов, кнопки, переключатели, поля ввода. |
+| Параметр | Тип | Описание |
+| --------------------- | --------------------- | ------------------------------------------------------------ |
+| testModeSettings | TestModeSettings | По умолчанию `nil`. Настройки тестового режима. |
+| returnUrl | String | По умолчанию `nil`. URL страницы (поддерживается только `https`), на которую надо вернуться после прохождения 3-D Secure. Необходим только при кастомной реализации 3-D Secure. Если вы используете `startConfirmationProcess(confirmationUrl:paymentMethodType:)`, не задавайте этот параметр. |
+| isLoggingEnabled | Bool | По умолчанию `false`. Включает логирование сетевых запросов. |
+| customizationSettings | CustomizationSettings | По умолчанию используется цвет blueRibbon. Цвет основных элементов, кнопки, переключатели, поля ввода. |
+| gatewayId | String | По умолчанию `nil`. Используется, если у вас несколько платежных шлюзов с разными идентификаторами. |
### TokenizationSettings
@@ -486,13 +630,13 @@ let inputData = TokenizationModuleInputData(
cardScanning: CardScannerProvider())
```
-## Настройка 3D Secure
+## Настройка подтверждения платежа
-Если вы хотите использовать нашу реализацию 3-D Secure, не закрывайте модуль SDK после получения токена.\
+Если вы хотите использовать нашу реализацию подтверждения платежа, не закрывайте модуль SDK после получения токена.\
Отправьте токен на ваш сервер и после успешной оплаты закройте модуль.\
-Если ваш сервер сообщил о необходимости подтверждения платежа (т.е. платёж пришёл со статусом `pending`), вызовите метод `start3dsProcess(requestUrl:)`
+Если ваш сервер сообщил о необходимости подтверждения платежа (т.е. платёж пришёл со статусом `pending`), вызовите метод `startConfirmationProcess(confirmationUrl:paymentMethodType:)`.
-После успешного прохождения 3-D Secure будет вызван метод `didSuccessfullyPassedCardSec(on module:)` протокола `TokenizationModuleOutput`.
+После успешного прохождения подтверждения будет вызван метод `didSuccessfullyConfirmation(paymentMethodType:)` протокола `TokenizationModuleOutput`.
Примеры кода:
@@ -514,18 +658,19 @@ func tokenizationModule(_ module: TokenizationModuleInput,
}
```
-3. Покажите 3-D Secure, если необходимо подтвердить платеж.
+3. Вызовите подтверждение платежа, если это необходимо.
```swift
-func needsConfirmPayment(requestUrl: String) {
- self.tokenizationViewController.start3dsProcess(requestUrl: requestUrl)
-}
+self.tokenizationViewController.startConfirmationProcess(
+ confirmationUrl: confirmationUrl,
+ paymentMethodType: paymentMethodType
+)
```
-4. После успешного прохождения 3-D Secure будет вызван метод.
+4. После успешного подтверждения платежа будет вызван метод.
```swift
-func didSuccessfullyPassedCardSec(on module: TokenizationModuleInput) {
+func didSuccessfullyConfirmation(paymentMethodType: PaymentMethodType) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
diff --git a/YooKassaPayments.podspec b/YooKassaPayments.podspec
index 08f11b18..dcc2afcc 100644
--- a/YooKassaPayments.podspec
+++ b/YooKassaPayments.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'YooKassaPayments'
- s.version = '5.4.1'
+ s.version = '6.0.0'
s.homepage = 'https://github.com/yoomoney/yookassa-payments-swift'
s.license = {
:type => "MIT",
@@ -30,11 +30,12 @@ Pod::Spec.new do |s|
s.ios.library = 'z'
s.ios.dependency 'YooMoneyCoreApi', '~> 1.9.0'
- s.ios.dependency 'YooKassaPaymentsApi', '~> 2.3.0'
+ s.ios.dependency 'YooKassaPaymentsApi', '~> 2.5.0'
s.ios.dependency 'YooKassaWalletApi', '~> 2.3.0'
- s.ios.dependency 'MoneyAuth', '~> 2.21.0'
- s.ios.dependency 'ThreatMetrixAdapter', '~> 2.0.0'
-
+ s.ios.dependency 'MoneyAuth', '~> 2.29.0'
+ s.ios.dependency 'ThreatMetrixAdapter', '~> 3.2.0'
+
s.ios.dependency 'YandexMobileMetrica/Dynamic', '~> 3.0'
+
end
diff --git a/YooKassaPayments.xcodeproj/project.pbxproj b/YooKassaPayments.xcodeproj/project.pbxproj
index 6a8d2590..88217f35 100644
--- a/YooKassaPayments.xcodeproj/project.pbxproj
+++ b/YooKassaPayments.xcodeproj/project.pbxproj
@@ -130,7 +130,6 @@
402EB49D79C96F3D7EC4D00E /* PlaceholderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EB05B21E70E9A2E81515C /* PlaceholderView.swift */; };
402EB4A38D9458D726EF2973 /* AnalyticsProviderAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EBA44464C238E00928819 /* AnalyticsProviderAssembly.swift */; };
402EB4AA589D5590C3FCD2AE /* InternalStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EB1EEE3DFE557080259B2 /* InternalStyle.swift */; };
- 402EB4D914B8C94E739CAA6C /* Yalta.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EB9AC3E2FC9F00509D779 /* Yalta.swift */; };
402EB4DB7D89DA8CBED9BBBB /* AuthTypeStatesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EBFF57151C710CF91D0E6 /* AuthTypeStatesService.swift */; };
402EB4DC5AAC097E3992B6DE /* SuccessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EB5FD23E637F2CB6C2574 /* SuccessViewController.swift */; };
402EB4DECF9836134B8DFF2C /* SberbankViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EBD38199403D8E554BCA3 /* SberbankViewController.swift */; };
@@ -301,6 +300,7 @@
402EBC2B75C6DA509D902F48 /* TextControl+ClearMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EB17BB3A1CB6CEF3A6554 /* TextControl+ClearMode.swift */; };
402EBC36449C85722080B20C /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EB6F94C13E4F66AD84654 /* ActivityIndicator.swift */; };
402EBC3A36D0ABDDD0D0C4B4 /* PaymentMethodsAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EB4CB069329DCBDCE0E6D /* PaymentMethodsAssembly.swift */; };
+ 402EBC3C07CACF8D2020DBC5 /* PriceViewModelFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EB489BC427881505E2583 /* PriceViewModelFactory.swift */; };
402EBC41089B09F83AC6D4CA /* ApplePayContractModuleIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EB62C69A473FAE82CF019 /* ApplePayContractModuleIO.swift */; };
402EBC42507159E3CD4859EC /* BankCardDataInputRouterIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EB87319D65F5FFA7259D9 /* BankCardDataInputRouterIO.swift */; };
402EBC44F5138C37BC2B7990 /* WebBrowserInteractorIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 402EBAB6F92FA336D7B10073 /* WebBrowserInteractorIO.swift */; };
@@ -410,6 +410,13 @@
781CDD7B25D4064100C34912 /* ApplePayContractViewIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 781CDD7A25D4064100C34912 /* ApplePayContractViewIO.swift */; };
781CDD7D25D408F600C34912 /* ApplePayContractInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 781CDD7C25D408F600C34912 /* ApplePayContractInteractor.swift */; };
781CDD7F25D42A1E00C34912 /* ApplePayContractRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 781CDD7E25D42A1E00C34912 /* ApplePayContractRouter.swift */; };
+ 781FB66B260229CD00567AFA /* BankCardRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 781FB66A260229CD00567AFA /* BankCardRegex.swift */; };
+ 781FB66E260235AD00567AFA /* BankCardImageFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 781FB66D260235AD00567AFA /* BankCardImageFactory.swift */; };
+ 781FB671260235EF00567AFA /* BankCardImageFactoryAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 781FB670260235EF00567AFA /* BankCardImageFactoryAssembly.swift */; };
+ 781FB673260235F700567AFA /* BankCardImageFactoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 781FB672260235F700567AFA /* BankCardImageFactoryImpl.swift */; };
+ 782E75E1260CCF7500CF2BFD /* YKSdkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782E75E0260CCF7500CF2BFD /* YKSdkService.swift */; };
+ 783CCEFB25FA347C005A298A /* ProcessConfirmation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 783CCEFA25FA347C005A298A /* ProcessConfirmation.swift */; };
+ 783CCEFD25FB651C005A298A /* UIScreen+Short.swift in Sources */ = {isa = PBXBuildFile; fileRef = 783CCEFC25FB651C005A298A /* UIScreen+Short.swift */; };
784A1A8B25B5A44600637CB5 /* ApplePayService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784A1A8A25B5A44600637CB5 /* ApplePayService.swift */; };
784A1A8D25B5A45000637CB5 /* ApplePayServiceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784A1A8C25B5A45000637CB5 /* ApplePayServiceImpl.swift */; };
784A1A8F25B5A5A300637CB5 /* ApplePayConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784A1A8E25B5A5A300637CB5 /* ApplePayConstants.swift */; };
@@ -467,6 +474,8 @@
7860294D25D1723B00B9E961 /* LinkedCardRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7860294C25D1723B00B9E961 /* LinkedCardRouter.swift */; };
7860294F25D1726800B9E961 /* LinkedCardRouterIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7860294E25D1726800B9E961 /* LinkedCardRouterIO.swift */; };
7860295225D194D300B9E961 /* LinkedCardViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7860295125D194D300B9E961 /* LinkedCardViewModel.swift */; };
+ 7890B24D25FA14BD0054D6B6 /* ProcessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7890B24B25FA118A0054D6B6 /* ProcessViewController.swift */; };
+ 7890B24F25FA196D0054D6B6 /* TextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7890B24E25FA196D0054D6B6 /* TextFieldView.swift */; };
78997A2425D562160093CAE2 /* LargeIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78997A2325D562160093CAE2 /* LargeIconView.swift */; };
789C373D25AF260400BA94D1 /* PaymentMethodHandlerServiceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 789C373C25AF260400BA94D1 /* PaymentMethodHandlerServiceImpl.swift */; };
789C373F25AF260F00BA94D1 /* PaymentMethodHandlerServiceAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 789C373E25AF260F00BA94D1 /* PaymentMethodHandlerServiceAssembly.swift */; };
@@ -474,6 +483,18 @@
789C376F25B06EB600BA94D1 /* AuthorizationProcessingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 789C376E25B06EB600BA94D1 /* AuthorizationProcessingError.swift */; };
78B6B20E25C1647F00645D8A /* PaymentMethodViewModelFactoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B6B20D25C1647F00645D8A /* PaymentMethodViewModelFactoryImpl.swift */; };
78B6B21025C1648500645D8A /* PaymentMethodViewModelFactoryAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B6B20F25C1648500645D8A /* PaymentMethodViewModelFactoryAssembly.swift */; };
+ 78C1BD1925EFA1010058080F /* SberpayInteractorIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C1BD1825EFA1010058080F /* SberpayInteractorIO.swift */; };
+ 78C1BD1B25EFA1090058080F /* SberpayModuleIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C1BD1A25EFA1090058080F /* SberpayModuleIO.swift */; };
+ 78C1BD1D25EFA1100058080F /* SberpayRouterIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C1BD1C25EFA1100058080F /* SberpayRouterIO.swift */; };
+ 78C1BD1F25EFA11B0058080F /* SberpayViewIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C1BD1E25EFA11B0058080F /* SberpayViewIO.swift */; };
+ 78C1BD2125EFA1280058080F /* SberpayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C1BD2025EFA1280058080F /* SberpayViewController.swift */; };
+ 78C1BD2325EFA1330058080F /* SberpayRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C1BD2225EFA1330058080F /* SberpayRouter.swift */; };
+ 78C1BD2525EFA13C0058080F /* SberpayPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C1BD2425EFA13C0058080F /* SberpayPresenter.swift */; };
+ 78C1BD2725EFA1430058080F /* SberpayInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C1BD2625EFA1430058080F /* SberpayInteractor.swift */; };
+ 78C1BD2925EFA14C0058080F /* SberpayAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C1BD2825EFA14C0058080F /* SberpayAssembly.swift */; };
+ 78C1BD2C25EFA2470058080F /* SberpayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C1BD2B25EFA2470058080F /* SberpayViewModel.swift */; };
+ 78C1BDF525F230550058080F /* DeeplinkFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C1BDF425F230550058080F /* DeeplinkFactory.swift */; };
+ 78C1BDF725F2305F0058080F /* Deeplink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C1BDF625F2305F0058080F /* Deeplink.swift */; };
78CF11DD25D4361300F7154E /* ApplePayContractViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78CF11DC25D4361300F7154E /* ApplePayContractViewController.swift */; };
78CF11E025D438AC00F7154E /* ApplePayContractViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78CF11DF25D438AC00F7154E /* ApplePayContractViewModel.swift */; };
E06532A71FF13B9B00831588 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0148CD51FE958810087CF5F /* AppDelegate.swift */; };
@@ -611,6 +632,7 @@
402EB47F1567A31864612F7C /* TokenizationModuleIO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenizationModuleIO.swift; sourceTree = ""; };
402EB47F288FC29560EE036E /* KeyboardObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardObservable.swift; sourceTree = ""; };
402EB487A00F4E5CBA7498D9 /* PaymentMethodTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaymentMethodTypes.swift; sourceTree = ""; };
+ 402EB489BC427881505E2583 /* PriceViewModelFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PriceViewModelFactory.swift; sourceTree = ""; };
402EB4CB069329DCBDCE0E6D /* PaymentMethodsAssembly.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaymentMethodsAssembly.swift; sourceTree = ""; };
402EB4D0C2AEFC68A22F5BA3 /* UILabel+Stylable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+Stylable.swift"; sourceTree = ""; };
402EB4F728CFBA828884E459 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.info; path = Info.plist; sourceTree = ""; };
@@ -693,7 +715,6 @@
402EB98341C0EAD3DE8902C2 /* TableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; };
402EB9A46DDCE237E4AB1B00 /* SberbankInteractorIO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SberbankInteractorIO.swift; sourceTree = ""; };
402EB9A63FFF964EED27D4DE /* InputCvcView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputCvcView.swift; sourceTree = ""; };
- 402EB9AC3E2FC9F00509D779 /* Yalta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Yalta.swift; sourceTree = ""; };
402EB9AEA4AE184596686453 /* UITableViewCell+Identifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+Identifier.swift"; sourceTree = ""; };
402EB9CB53DAB7D15EB12891 /* AuthorizationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthorizationService.swift; sourceTree = ""; };
402EB9D4435E4C37E367DF6A /* RootViewController+CardScanning+Pods.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RootViewController+CardScanning+Pods.swift"; sourceTree = ""; };
@@ -852,6 +873,13 @@
781CDD7A25D4064100C34912 /* ApplePayContractViewIO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplePayContractViewIO.swift; sourceTree = ""; };
781CDD7C25D408F600C34912 /* ApplePayContractInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplePayContractInteractor.swift; sourceTree = ""; };
781CDD7E25D42A1E00C34912 /* ApplePayContractRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplePayContractRouter.swift; sourceTree = ""; };
+ 781FB66A260229CD00567AFA /* BankCardRegex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankCardRegex.swift; sourceTree = ""; };
+ 781FB66D260235AD00567AFA /* BankCardImageFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankCardImageFactory.swift; sourceTree = ""; };
+ 781FB670260235EF00567AFA /* BankCardImageFactoryAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankCardImageFactoryAssembly.swift; sourceTree = ""; };
+ 781FB672260235F700567AFA /* BankCardImageFactoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankCardImageFactoryImpl.swift; sourceTree = ""; };
+ 782E75E0260CCF7500CF2BFD /* YKSdkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YKSdkService.swift; sourceTree = ""; };
+ 783CCEFA25FA347C005A298A /* ProcessConfirmation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessConfirmation.swift; sourceTree = ""; };
+ 783CCEFC25FB651C005A298A /* UIScreen+Short.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScreen+Short.swift"; sourceTree = ""; };
784A1A8A25B5A44600637CB5 /* ApplePayService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplePayService.swift; sourceTree = ""; };
784A1A8C25B5A45000637CB5 /* ApplePayServiceImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplePayServiceImpl.swift; sourceTree = ""; };
784A1A8E25B5A5A300637CB5 /* ApplePayConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplePayConstants.swift; sourceTree = ""; };
@@ -909,6 +937,8 @@
7860294C25D1723B00B9E961 /* LinkedCardRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedCardRouter.swift; sourceTree = ""; };
7860294E25D1726800B9E961 /* LinkedCardRouterIO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedCardRouterIO.swift; sourceTree = ""; };
7860295125D194D300B9E961 /* LinkedCardViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedCardViewModel.swift; sourceTree = ""; };
+ 7890B24B25FA118A0054D6B6 /* ProcessViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessViewController.swift; sourceTree = ""; };
+ 7890B24E25FA196D0054D6B6 /* TextFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldView.swift; sourceTree = ""; };
78997A2325D562160093CAE2 /* LargeIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeIconView.swift; sourceTree = ""; };
789C373C25AF260400BA94D1 /* PaymentMethodHandlerServiceImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentMethodHandlerServiceImpl.swift; sourceTree = ""; };
789C373E25AF260F00BA94D1 /* PaymentMethodHandlerServiceAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentMethodHandlerServiceAssembly.swift; sourceTree = ""; };
@@ -916,6 +946,18 @@
789C376E25B06EB600BA94D1 /* AuthorizationProcessingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthorizationProcessingError.swift; sourceTree = ""; };
78B6B20D25C1647F00645D8A /* PaymentMethodViewModelFactoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentMethodViewModelFactoryImpl.swift; sourceTree = ""; };
78B6B20F25C1648500645D8A /* PaymentMethodViewModelFactoryAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentMethodViewModelFactoryAssembly.swift; sourceTree = ""; };
+ 78C1BD1825EFA1010058080F /* SberpayInteractorIO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SberpayInteractorIO.swift; sourceTree = ""; };
+ 78C1BD1A25EFA1090058080F /* SberpayModuleIO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SberpayModuleIO.swift; sourceTree = ""; };
+ 78C1BD1C25EFA1100058080F /* SberpayRouterIO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SberpayRouterIO.swift; sourceTree = ""; };
+ 78C1BD1E25EFA11B0058080F /* SberpayViewIO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SberpayViewIO.swift; sourceTree = ""; };
+ 78C1BD2025EFA1280058080F /* SberpayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SberpayViewController.swift; sourceTree = ""; };
+ 78C1BD2225EFA1330058080F /* SberpayRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SberpayRouter.swift; sourceTree = ""; };
+ 78C1BD2425EFA13C0058080F /* SberpayPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SberpayPresenter.swift; sourceTree = ""; };
+ 78C1BD2625EFA1430058080F /* SberpayInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SberpayInteractor.swift; sourceTree = ""; };
+ 78C1BD2825EFA14C0058080F /* SberpayAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SberpayAssembly.swift; sourceTree = ""; };
+ 78C1BD2B25EFA2470058080F /* SberpayViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SberpayViewModel.swift; sourceTree = ""; };
+ 78C1BDF425F230550058080F /* DeeplinkFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeeplinkFactory.swift; sourceTree = ""; };
+ 78C1BDF625F2305F0058080F /* Deeplink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deeplink.swift; sourceTree = ""; };
78CF11DC25D4361300F7154E /* ApplePayContractViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplePayContractViewController.swift; sourceTree = ""; };
78CF11DF25D438AC00F7154E /* ApplePayContractViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplePayContractViewModel.swift; sourceTree = ""; };
8BCB318DC03111CB9C1FFD14 /* Pods_YooKassaPaymentsExamplePods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_YooKassaPaymentsExamplePods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1219,6 +1261,7 @@
children = (
402EB36269018072CE3287E2 /* TokenizationAssembly.swift */,
402EB47F1567A31864612F7C /* TokenizationModuleIO.swift */,
+ 782E75E0260CCF7500CF2BFD /* YKSdkService.swift */,
402EB1E94429DDB4E260133C /* YooKassaPaymentsError.swift */,
402EB731317D09367414E230 /* InputData */,
402EBA421DDACFD7FFA52F93 /* Models */,
@@ -1420,15 +1463,16 @@
402EBFD0D5D351F93484B633 /* 3DS */,
402EB6ADA278BB94CDCE91CD /* ApplePay */,
781CDD6F25D4053400C34912 /* ApplePayContract */,
+ 402EB8DC7B8A023C9B3961A7 /* BankCard */,
402EB03AF715BD6339B32F52 /* BankCardRepeat */,
7860293525D166E200B9E961 /* LinkedCard */,
402EB84797D7E864A73001A0 /* LogoutConfirmation */,
786028F125C981CF00B9E961 /* PaymentAuthorization */,
402EBA5D1D4920494878050E /* PaymentMethods */,
308E41782385500E00B44490 /* SavePaymentMethodInfo */,
- 7811066625C2CD4D004DB71D /* YooMoney */,
402EB8437EC5A9EC41268B10 /* Sberbank */,
- 402EB8DC7B8A023C9B3961A7 /* BankCard */,
+ 78E1BA6325EEA99000E5F275 /* Sberpay */,
+ 7811066625C2CD4D004DB71D /* YooMoney */,
);
path = Modules;
sourceTree = "";
@@ -1598,6 +1642,7 @@
children = (
402EBB549599D3B7DB0DABEC /* Settings.swift */,
402EB7D5657B7828E3545FDC /* TestSettings.swift */,
+ 783CCEFA25FA347C005A298A /* ProcessConfirmation.swift */,
);
path = Models;
sourceTree = "";
@@ -1697,11 +1742,12 @@
402EB6F5F5ECFC24A3415F23 /* UserStories */ = {
isa = PBXGroup;
children = (
+ 402EB3EF1A220B8E654BC3ED /* Cards */,
+ 7890B24A25FA11720054D6B6 /* Process */,
402EBE7B78B736A4D48B9797 /* Root */,
402EBC01AAF87DAB5FB939E1 /* Settings */,
- 402EB3EF1A220B8E654BC3ED /* Cards */,
- 402EB22CA276A9F82F3268E5 /* Test Settings */,
402EB56FC0F4030503F712A9 /* Success */,
+ 402EB22CA276A9F82F3268E5 /* Test Settings */,
);
path = UserStories;
sourceTree = "";
@@ -1709,9 +1755,10 @@
402EB72FA2F9530A4A971FF5 /* Extensions */ = {
isa = PBXGroup;
children = (
+ 402EB93F77A21ACAE76D751D /* Bundle+Tools.swift */,
402EB35771FFB12A221F3905 /* String+Tools.swift */,
+ 783CCEFC25FB651C005A298A /* UIScreen+Short.swift */,
781CDD6D25D2B95300C34912 /* UITextField+InputView.swift */,
- 402EB93F77A21ACAE76D751D /* Bundle+Tools.swift */,
);
path = Extensions;
sourceTree = "";
@@ -1737,7 +1784,6 @@
402EB76B4E70AD0A3902C605 /* SheetView */ = {
isa = PBXGroup;
children = (
- 402EB9AC3E2FC9F00509D779 /* Yalta.swift */,
402EB046F2F92BF86F22CE06 /* SheetSize.swift */,
402EB761066455B29DCA05B5 /* SheetOptions.swift */,
402EB8291A43AFF991536FFA /* SheetTransition.swift */,
@@ -1762,15 +1808,15 @@
402EB8437EC5A9EC41268B10 /* Sberbank */ = {
isa = PBXGroup;
children = (
- 402EB6BB7C6741CD830FA20D /* View */,
+ 402EB9A46DDCE237E4AB1B00 /* SberbankInteractorIO.swift */,
+ 402EBB4D71D63BBDA95DB6C4 /* SberbankModuleIO.swift */,
+ 402EBD5D02739AA84ECC7981 /* SberbankRouterIO.swift */,
402EBCACE6EE65EF9B341007 /* SberbankViewIO.swift */,
402EB675A587DD13842E5702 /* Assembly */,
- 402EB492C4DA8738C0F8DBCF /* Presenter */,
402EBADCF9C4CF55E0B06A5E /* Interactor */,
- 402EB9A46DDCE237E4AB1B00 /* SberbankInteractorIO.swift */,
- 402EBB4D71D63BBDA95DB6C4 /* SberbankModuleIO.swift */,
+ 402EB492C4DA8738C0F8DBCF /* Presenter */,
402EB46229DE0340B1F89025 /* Router */,
- 402EBD5D02739AA84ECC7981 /* SberbankRouterIO.swift */,
+ 402EB6BB7C6741CD830FA20D /* View */,
);
path = Sberbank;
sourceTree = "";
@@ -1823,14 +1869,14 @@
isa = PBXGroup;
children = (
402EBC0AAB711E47B4FDF1E4 /* BankCardInteractorIO.swift */,
- 402EBCE7F595FBF58EEEF96F /* BankCardViewIO.swift */,
- 402EBE3EEE70AA9D7309A7A0 /* Assembly */,
402EB3C75516A9743F28AAB5 /* BankCardModuleIO.swift */,
402EB02843790D7A273BDA2A /* BankCardRouterIO.swift */,
- 402EB9BBCDD811A24D83A302 /* View */,
+ 402EBCE7F595FBF58EEEF96F /* BankCardViewIO.swift */,
+ 402EBE3EEE70AA9D7309A7A0 /* Assembly */,
402EB2244D468BB6D6BF9AFB /* Interactor */,
402EB8DC46E920C41B8F0991 /* Presenter */,
402EB1ED411EC5BD00482333 /* Router */,
+ 402EB9BBCDD811A24D83A302 /* View */,
);
path = BankCard;
sourceTree = "";
@@ -1865,7 +1911,10 @@
402EB033A1DFDB46B5EBA6D1 /* PhoneNumberFormatterAssembly.swift */,
402EB96DFA8C9D871DE124D0 /* SavePaymentMethodViewModelFactory.swift */,
402EBF7AC88FCC8EA3154C56 /* UserAgentFactory.swift */,
+ 781FB66F260235D400567AFA /* BankCardImage */,
+ 78C1BDF325F230460058080F /* Deeplink */,
78B6B20C25C1646D00645D8A /* PaymentMethodViewModel */,
+ 402EB489BC427881505E2583 /* PriceViewModelFactory.swift */,
);
path = Factory;
sourceTree = "";
@@ -1964,6 +2013,7 @@
402EBA8BE37B25AAD457F8D1 /* Models */ = {
isa = PBXGroup;
children = (
+ 781FB66A260229CD00567AFA /* BankCardRegex.swift */,
402EBA3F8618D17FB47AC873 /* BankSettings.swift */,
402EBA04C707C5EA568200DE /* CardData.swift */,
402EBC6E1AC8DE3F0701FE15 /* HostsConfig.swift */,
@@ -2084,9 +2134,10 @@
isa = PBXGroup;
children = (
402EBDCA8CE554E9A5B4FFEB /* ContainerTableViewCell.swift */,
- 402EB6A48AE583D297E41E9A /* TitledSwitchView.swift */,
- 402EB7AF716BE8C008EA6059 /* TextValueView.swift */,
+ 7890B24E25FA196D0054D6B6 /* TextFieldView.swift */,
402EB7A951380D72FF1C56D6 /* TextHeaderFooterView.swift */,
+ 402EB7AF716BE8C008EA6059 /* TextValueView.swift */,
+ 402EB6A48AE583D297E41E9A /* TitledSwitchView.swift */,
402EB0F399573DCE0B6FF15B /* TextControl */,
);
path = Views;
@@ -2481,6 +2532,16 @@
path = View;
sourceTree = "";
};
+ 781FB66F260235D400567AFA /* BankCardImage */ = {
+ isa = PBXGroup;
+ children = (
+ 781FB66D260235AD00567AFA /* BankCardImageFactory.swift */,
+ 781FB670260235EF00567AFA /* BankCardImageFactoryAssembly.swift */,
+ 781FB672260235F700567AFA /* BankCardImageFactoryImpl.swift */,
+ );
+ path = BankCardImage;
+ sourceTree = "";
+ };
784A1A8925B5A42900637CB5 /* ApplePay */ = {
isa = PBXGroup;
children = (
@@ -2727,6 +2788,14 @@
path = ViewModel;
sourceTree = "";
};
+ 7890B24A25FA11720054D6B6 /* Process */ = {
+ isa = PBXGroup;
+ children = (
+ 7890B24B25FA118A0054D6B6 /* ProcessViewController.swift */,
+ );
+ path = Process;
+ sourceTree = "";
+ };
78997A2225D562080093CAE2 /* LargeIconView */ = {
isa = PBXGroup;
children = (
@@ -2755,6 +2824,64 @@
path = PaymentMethodViewModel;
sourceTree = "";
};
+ 78C1BD1325EFA0D40058080F /* Assembly */ = {
+ isa = PBXGroup;
+ children = (
+ 78C1BD2825EFA14C0058080F /* SberpayAssembly.swift */,
+ );
+ path = Assembly;
+ sourceTree = "";
+ };
+ 78C1BD1425EFA0E10058080F /* Interactor */ = {
+ isa = PBXGroup;
+ children = (
+ 78C1BD2625EFA1430058080F /* SberpayInteractor.swift */,
+ );
+ path = Interactor;
+ sourceTree = "";
+ };
+ 78C1BD1525EFA0E80058080F /* Presenter */ = {
+ isa = PBXGroup;
+ children = (
+ 78C1BD2425EFA13C0058080F /* SberpayPresenter.swift */,
+ );
+ path = Presenter;
+ sourceTree = "";
+ };
+ 78C1BD1625EFA0ED0058080F /* Router */ = {
+ isa = PBXGroup;
+ children = (
+ 78C1BD2225EFA1330058080F /* SberpayRouter.swift */,
+ );
+ path = Router;
+ sourceTree = "";
+ };
+ 78C1BD1725EFA0F10058080F /* View */ = {
+ isa = PBXGroup;
+ children = (
+ 78C1BD2025EFA1280058080F /* SberpayViewController.swift */,
+ 78C1BD2A25EFA23C0058080F /* ViewModel */,
+ );
+ path = View;
+ sourceTree = "";
+ };
+ 78C1BD2A25EFA23C0058080F /* ViewModel */ = {
+ isa = PBXGroup;
+ children = (
+ 78C1BD2B25EFA2470058080F /* SberpayViewModel.swift */,
+ );
+ path = ViewModel;
+ sourceTree = "";
+ };
+ 78C1BDF325F230460058080F /* Deeplink */ = {
+ isa = PBXGroup;
+ children = (
+ 78C1BDF625F2305F0058080F /* Deeplink.swift */,
+ 78C1BDF425F230550058080F /* DeeplinkFactory.swift */,
+ );
+ path = Deeplink;
+ sourceTree = "";
+ };
78CF11DE25D438A100F7154E /* ViewModel */ = {
isa = PBXGroup;
children = (
@@ -2763,6 +2890,22 @@
path = ViewModel;
sourceTree = "";
};
+ 78E1BA6325EEA99000E5F275 /* Sberpay */ = {
+ isa = PBXGroup;
+ children = (
+ 78C1BD1825EFA1010058080F /* SberpayInteractorIO.swift */,
+ 78C1BD1A25EFA1090058080F /* SberpayModuleIO.swift */,
+ 78C1BD1C25EFA1100058080F /* SberpayRouterIO.swift */,
+ 78C1BD1E25EFA11B0058080F /* SberpayViewIO.swift */,
+ 78C1BD1325EFA0D40058080F /* Assembly */,
+ 78C1BD1425EFA0E10058080F /* Interactor */,
+ 78C1BD1525EFA0E80058080F /* Presenter */,
+ 78C1BD1625EFA0ED0058080F /* Router */,
+ 78C1BD1725EFA0F10058080F /* View */,
+ );
+ path = Sberpay;
+ sourceTree = "";
+ };
9133B1C75F52034A6FB79687 /* Frameworks */ = {
isa = PBXGroup;
children = (
@@ -3051,6 +3194,7 @@
402EB87BAE962D317D13D7F4 /* CscInputPresenterStyle.swift in Sources */,
7860293D25D1671000B9E961 /* LinkedCardViewIO.swift in Sources */,
402EBB1D932FB9AA14055422 /* PanInputPresenterStyle.swift in Sources */,
+ 781FB673260235F700567AFA /* BankCardImageFactoryImpl.swift in Sources */,
402EBCE6F2A52CBB1C0D225A /* ExpiryDateInputPresenterStyle.swift in Sources */,
402EBE1BE753B698DAB812C0 /* PhoneNumberStyleWithAutoCorrection.swift in Sources */,
402EB5B4BFA3AAFB7B8EFAC1 /* PhoneNumberFormatter.swift in Sources */,
@@ -3103,6 +3247,7 @@
780D0D3925DC0DF100CF15D1 /* BankCardRepeatModuleIO.swift in Sources */,
402EBB0834948CFD4278DF96 /* UILabel+Style.swift in Sources */,
402EB52D1B69D9D12BA05F02 /* UIFont+Style.swift in Sources */,
+ 78C1BD1F25EFA11B0058080F /* SberpayViewIO.swift in Sources */,
784A1AE625B9CE8A00637CB5 /* PaymentMethod.swift in Sources */,
402EBC73F5045B2B33EED401 /* UIImage+Style.swift in Sources */,
402EB2F13EA4EBCE01956D61 /* UIImageView+Style.swift in Sources */,
@@ -3110,6 +3255,7 @@
402EBD2767032917D2486044 /* UINavigationBar+Style.swift in Sources */,
402EBFAAC2EC4686CE4488A8 /* UIBarButtonItem+Style.swift in Sources */,
784A1ADB25B9C57B00637CB5 /* CheckoutTokenIssueExecute.swift in Sources */,
+ 78C1BD2725EFA1430058080F /* SberpayInteractor.swift in Sources */,
402EB00B9EC9B560D706EC06 /* UIScrollView+Style.swift in Sources */,
402EB43FAD5652F9095B47FF /* ActivityIndicatorView+Style.swift in Sources */,
402EB1BC568F568FD6E10047 /* ActivityIndicatorView.swift in Sources */,
@@ -3142,6 +3288,7 @@
78CF11DD25D4361300F7154E /* ApplePayContractViewController.swift in Sources */,
402EBE3D748B1AD41EBD2771 /* PresentableError.swift in Sources */,
3089EF4923846F6300CB7319 /* SwitcherSavePaymentMethodViewModel.swift in Sources */,
+ 78C1BD1925EFA1010058080F /* SberpayInteractorIO.swift in Sources */,
7860291025C9913A00B9E961 /* UITextField+Style.swift in Sources */,
402EBD7014CFF4A56F23439F /* PresentableNotification.swift in Sources */,
780D0D3C25DC0E0300CF15D1 /* BankCardRepeatAssembly.swift in Sources */,
@@ -3149,6 +3296,7 @@
402EB9AF1EC6377E4EB997A7 /* NotificationPresenting.swift in Sources */,
402EB3B8F359111B86058AB0 /* ActivityIndicatorFullViewPresenting.swift in Sources */,
402EB469B4798293A88ABAF4 /* ActivityIndicatorPresenting.swift in Sources */,
+ 78C1BD2925EFA14C0058080F /* SberpayAssembly.swift in Sources */,
402EB45C4D87D353D57D4A2C /* WebBrowserViewController.swift in Sources */,
781CDD7725D4063000C34912 /* ApplePayContractInteractorIO.swift in Sources */,
402EB12A36444BFD7ECCD081 /* WebBrowserRouter.swift in Sources */,
@@ -3174,6 +3322,7 @@
402EBFC348B3BCF7D33FCAB5 /* CustomizationColors.swift in Sources */,
7860292A25CD406500B9E961 /* ImageDownloadServiceImpl.swift in Sources */,
7811066E25C2CD91004DB71D /* YooMoneyInteractor.swift in Sources */,
+ 781FB66E260235AD00567AFA /* BankCardImageFactory.swift in Sources */,
7860294525D1673800B9E961 /* LinkedCardInteractor.swift in Sources */,
402EB9C4BBC1F6EF4241A9C9 /* CardData.swift in Sources */,
7811066C25C2CD7B004DB71D /* YooMoneyAssembly.swift in Sources */,
@@ -3194,6 +3343,7 @@
402EBBC990C2C0F2A2A0BD69 /* PhoneNumberFormatterAssembly.swift in Sources */,
402EB7C3CF2DBEAF200479D6 /* UserAgentFactory.swift in Sources */,
402EB6BE01411608D225E157 /* Localization.swift in Sources */,
+ 78C1BD2C25EFA2470058080F /* SberpayViewModel.swift in Sources */,
7860294D25D1723B00B9E961 /* LinkedCardRouter.swift in Sources */,
402EBFA22A30FD6DA60F6FA3 /* PrefferedContentSizeCategory.swift in Sources */,
402EBD79F5F3730B73E8E2B6 /* ResourceLoader.swift in Sources */,
@@ -3252,7 +3402,10 @@
7860290025C982CA00B9E961 /* PaymentAuthorizationAssembly.swift in Sources */,
402EB636FC3E40E0EDE5A096 /* LogoutConfirmationModuleIO.swift in Sources */,
402EB002BD1EC142641CB2D4 /* LogoutConfirmationAssembly.swift in Sources */,
+ 783CCEFD25FB651C005A298A /* UIScreen+Short.swift in Sources */,
402EB90A3E9FD8733C4EBEA9 /* LoginConfirmationViewController.swift in Sources */,
+ 78C1BD1D25EFA1100058080F /* SberpayRouterIO.swift in Sources */,
+ 781FB66B260229CD00567AFA /* BankCardRegex.swift in Sources */,
402EBBE91DE40AA08E3AB8B5 /* ApplePayAssembly.swift in Sources */,
402EB7C74860DFA65827A00C /* ApplePayModuleIO.swift in Sources */,
30B20E2B2535FC1C00941574 /* KeyValueStoringKeys.swift in Sources */,
@@ -3260,6 +3413,7 @@
402EB8D43DDD23DC7A645AA1 /* BankCardRepeatInteractor.swift in Sources */,
402EB1F0EAA3B2E1422FD95D /* BankCardRepeatInteractorIO.swift in Sources */,
3089EF4D23846F7400CB7319 /* SavePaymentMethodViewModel.swift in Sources */,
+ 781FB671260235EF00567AFA /* BankCardImageFactoryAssembly.swift in Sources */,
402EB38B0B0D9EA3988F9922 /* PaymentService.swift in Sources */,
402EBC0B94C8D1B32962BBA8 /* PaymentProcessingError.swift in Sources */,
402EBD95FD7CAFB6AD2F29FC /* PaymentServiceMock.swift in Sources */,
@@ -3267,6 +3421,7 @@
402EB8203E2273B472E5819C /* HostProvider.swift in Sources */,
402EB70F599439C2EAF90432 /* ApiLogger.swift in Sources */,
402EB96B6F077D9807AD790B /* PaymentMethodHandlerService.swift in Sources */,
+ 78C1BD2525EFA13C0058080F /* SberpayPresenter.swift in Sources */,
7811067C25C3FCB8004DB71D /* OrderView.swift in Sources */,
402EB8D9C4C6674F176C1E2C /* AuthorizationService.swift in Sources */,
402EBCB6D5E6441798A2DC59 /* AuthorizationServiceImpl.swift in Sources */,
@@ -3278,6 +3433,7 @@
402EB3397345E69677F29347 /* KeychainStorage.swift in Sources */,
402EB52D11504C419695D49F /* UserDefaultsStorage.swift in Sources */,
402EBFE2285B7B73BF2290ED /* WalletLoginServiceImpl.swift in Sources */,
+ 782E75E1260CCF7500CF2BFD /* YKSdkService.swift in Sources */,
402EBDA16A62F97489BF95AC /* WalletLoginService.swift in Sources */,
402EB5745ABA5C4E1A40294A /* WalletLoginServiceMock.swift in Sources */,
402EBF72D88566C67130C623 /* AuthTypeStatesProvider.swift in Sources */,
@@ -3301,6 +3457,7 @@
402EB10E2B1F8A4E8BD2DEB8 /* BankSettingsService.swift in Sources */,
402EB324EC0352C1CF0A8A59 /* BankSettingsServiceImpl.swift in Sources */,
7860292C25CD406500B9E961 /* ImageCacheImpl.swift in Sources */,
+ 78C1BD1B25EFA1090058080F /* SberpayModuleIO.swift in Sources */,
402EBF6F0AF3AB32E9C844AC /* TokenizationAssembly.swift in Sources */,
402EB337CF8A33565554E71D /* CardScanning.swift in Sources */,
789C374125AF285C00BA94D1 /* PaymentServiceImpl.swift in Sources */,
@@ -3311,6 +3468,7 @@
786028F825C982B100B9E961 /* PaymentAuthorizationInteractorIO.swift in Sources */,
780D0D4425DC11FB00CF15D1 /* BankCardRepeatRouter.swift in Sources */,
402EB0FBD2A8AFE719A2A013 /* TokenizationSettings.swift in Sources */,
+ 78C1BDF725F2305F0058080F /* Deeplink.swift in Sources */,
308E417A2385501E00B44490 /* SavePaymentMethodInfoViewController.swift in Sources */,
78CF11E025D438AC00F7154E /* ApplePayContractViewModel.swift in Sources */,
784A1AEA25B9CFA700637CB5 /* PaymentMethodBankCard.swift in Sources */,
@@ -3331,7 +3489,6 @@
781CDD7925D4063A00C34912 /* ApplePayContractRouterIO.swift in Sources */,
402EB34C8185FBEF0E13025B /* UIViewController+Keyboard.swift in Sources */,
402EB952EA7B9CD954D7654E /* KeyboardObservingAccessoryView.swift in Sources */,
- 402EB4D914B8C94E739CAA6C /* Yalta.swift in Sources */,
402EBB6E80134E0C43050D07 /* SheetSize.swift in Sources */,
402EB4040D6DB2DC51F53B51 /* SheetOptions.swift in Sources */,
402EBE4E6840A072C11BF813 /* SheetTransition.swift in Sources */,
@@ -3348,6 +3505,7 @@
402EB35A07609716EDED7B93 /* SberbankViewModel.swift in Sources */,
402EB9182B2015B619CAAF81 /* SberbankInteractor.swift in Sources */,
402EB636AA2031BCCDFB4139 /* SberbankInteractorIO.swift in Sources */,
+ 78C1BDF525F230550058080F /* DeeplinkFactory.swift in Sources */,
402EB3F61C8155EC17F69065 /* SberbankModuleIO.swift in Sources */,
402EB2CB501761740839F22B /* SberbankRouterIO.swift in Sources */,
402EB069F4DDF647DF893AC6 /* SberbankRouter.swift in Sources */,
@@ -3366,6 +3524,7 @@
402EB408F40569680354E993 /* BankCardRouter.swift in Sources */,
402EB571173B519C0F71C65A /* InputCvcView.swift in Sources */,
402EB420CBCEF91141DDBD23 /* InputPanCardView.swift in Sources */,
+ 78C1BD2125EFA1280058080F /* SberpayViewController.swift in Sources */,
402EBD521E90D0EE32F6611D /* InputExpiryDateView.swift in Sources */,
402EBAAC323B2686A6C43781 /* BankCardDataInputView.swift in Sources */,
402EBC52D30F0A1A37263A97 /* BankCardDataInputAssembly.swift in Sources */,
@@ -3379,6 +3538,7 @@
402EB3AB073F3C17EAE8919C /* BankCardDataInputViewModel.swift in Sources */,
402EBBDF51F6662CEE54298A /* Bundle+Tools.swift in Sources */,
402EBB94AB27CC90E71D3A6D /* TextControl.swift in Sources */,
+ 78C1BD2325EFA1330058080F /* SberpayRouter.swift in Sources */,
402EBAA71096200333711566 /* TextControl+State.swift in Sources */,
402EB53E4E9278E5E38D3773 /* TextControl+Layout.swift in Sources */,
402EB35B6BF9FBB145DCCA2A /* TextControl+LineMode.swift in Sources */,
@@ -3398,6 +3558,7 @@
402EB4E93D7A00A973BB93FA /* UITableViewHeaderFooterView+Identifier.swift in Sources */,
402EBF5E02044CFB30A2A40A /* UITableViewCell+Identifier.swift in Sources */,
402EBB42DE308BDC3E21CA74 /* NavigationController.swift in Sources */,
+ 402EBC3C07CACF8D2020DBC5 /* PriceViewModelFactory.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3479,6 +3640,7 @@
30AB53082383315D0098C7C1 /* UITextView+Styles.swift in Sources */,
402EB8CBDC1DC409DB149528 /* TransitionHandler.swift in Sources */,
402EB76B159E9C503FA2B433 /* ActivityIndicatorPresenting.swift in Sources */,
+ 7890B24D25FA14BD0054D6B6 /* ProcessViewController.swift in Sources */,
402EB45A688AF741FD5813E3 /* Anchor.swift in Sources */,
402EBB8F4B7C1AF1E2394308 /* UIScreen+SafeAreaInsets.swift in Sources */,
402EBA03EBFAB4CE09BE7DF5 /* UIView+SpecificLayoutGuide.swift in Sources */,
@@ -3487,6 +3649,7 @@
402EB081707DFFC2F5DF3999 /* UITableView+Identifier.swift in Sources */,
402EB38E8A91E6F075E54A78 /* UIViewController+TransitionHandler.swift in Sources */,
402EB059A64C4ADEB677C006 /* UIImage+Tools.swift in Sources */,
+ 7890B24F25FA196D0054D6B6 /* TextFieldView.swift in Sources */,
402EBAE66EB113D0D9C6F92F /* TextControl.swift in Sources */,
402EB7195FF2185F9F28BF17 /* TextControl+State.swift in Sources */,
402EB1B9B220F60B15DD61C1 /* TextControl+Layout.swift in Sources */,
@@ -3502,6 +3665,7 @@
402EB600C417B9FFA92A9014 /* TextControl+LayoutController.swift in Sources */,
402EBEBBF870303E32A2F04B /* TextControl+Style.swift in Sources */,
402EB49D79C96F3D7EC4D00E /* PlaceholderView.swift in Sources */,
+ 783CCEFB25FA347C005A298A /* ProcessConfirmation.swift in Sources */,
402EB201DCE41E5997E60960 /* UIView+Tools.swift in Sources */,
402EBD74B5D25C398B38EC0B /* Identifier.swift in Sources */,
402EBC75F208A0541A979ECC /* UITableViewCell+Identifier.swift in Sources */,
diff --git a/YooKassaPayments/Info.plist b/YooKassaPayments/Info.plist
index c7412356..2462f202 100644
--- a/YooKassaPayments/Info.plist
+++ b/YooKassaPayments/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 5.4.1
+ 6.0.0
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
NSPrincipalClass
diff --git a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/Assembly/BankCardDataInputAssembly.swift b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/Assembly/BankCardDataInputAssembly.swift
index e98fdfb3..08871d92 100644
--- a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/Assembly/BankCardDataInputAssembly.swift
+++ b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/Assembly/BankCardDataInputAssembly.swift
@@ -43,6 +43,7 @@ enum BankCardDataInputAssembly {
private static func makePresenter(
inputData: BankCardDataInputModuleInputData
) -> BankCardDataInputPresenter {
+ let bankCardImageFactory = BankCardImageFactoryAssembly.makeFactory()
let presenter = BankCardDataInputPresenter(
inputPanHint: inputData.inputPanHint,
inputPanPlaceholder: inputData.inputPanPlaceholder,
@@ -50,7 +51,8 @@ enum BankCardDataInputAssembly {
inputExpiryDatePlaceholder: inputData.inputExpiryDatePlaceholder,
inputCvcHint: inputData.inputCvcHint,
inputCvcPlaceholder: inputData.inputCvcPlaceholder,
- cardScanner: inputData.cardScanner
+ cardScanner: inputData.cardScanner,
+ bankCardImageFactory: bankCardImageFactory
)
return presenter
}
diff --git a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/BankCardDataInputInteractorIO.swift b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/BankCardDataInputInteractorIO.swift
index e1ccc96f..c6342736 100644
--- a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/BankCardDataInputInteractorIO.swift
+++ b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/BankCardDataInputInteractorIO.swift
@@ -19,5 +19,7 @@ protocol BankCardDataInputInteractorOutput: class {
func didFetchBankSettings(
_ bankSettings: BankSettings
)
- func didFailFetchBankSettings()
+ func didFailFetchBankSettings(
+ _ cardMask: String
+ )
}
diff --git a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/BankCardDataInputViewIO.swift b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/BankCardDataInputViewIO.swift
index 49242919..70b88a0d 100644
--- a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/BankCardDataInputViewIO.swift
+++ b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/BankCardDataInputViewIO.swift
@@ -4,6 +4,7 @@ enum BankCardDataInputViewErrorState {
case noError
case panError
case expiryDateError
+ case invalidCvc
}
protocol BankCardDataInputViewInput: class {
@@ -38,6 +39,8 @@ protocol BankCardDataInputViewOutput: class {
func didPressScan()
func panDidBeginEditing()
func expiryDateDidBeginEditing()
+ func expiryDateDidEndEditing()
+ func cvcDidEndEditing()
func nextDidPress()
func clearDidPress()
diff --git a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/Interactor/BankCardDataInputInteractor.swift b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/Interactor/BankCardDataInputInteractor.swift
index ee3c947d..0b3f092e 100644
--- a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/Interactor/BankCardDataInputInteractor.swift
+++ b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/Interactor/BankCardDataInputInteractor.swift
@@ -46,7 +46,7 @@ extension BankCardDataInputInteractor: BankCardDataInputInteractorInput {
guard let bankSettings = bankSettingsService.bankSettings(
cardMask
) else {
- output?.didFailFetchBankSettings()
+ output?.didFailFetchBankSettings(cardMask)
return
}
output?.didFetchBankSettings(bankSettings)
diff --git a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/Presenter/BankCardDataInputPresenter.swift b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/Presenter/BankCardDataInputPresenter.swift
index 69b5c17e..8739b451 100644
--- a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/Presenter/BankCardDataInputPresenter.swift
+++ b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/Presenter/BankCardDataInputPresenter.swift
@@ -16,6 +16,7 @@ final class BankCardDataInputPresenter {
private let inputCvcHint: String
private let inputCvcPlaceholder: String
private let cardScanner: CardScanning?
+ private let bankCardImageFactory: BankCardImageFactory
init(
inputPanHint: String,
@@ -24,7 +25,8 @@ final class BankCardDataInputPresenter {
inputExpiryDatePlaceholder: String,
inputCvcHint: String,
inputCvcPlaceholder: String,
- cardScanner: CardScanning?
+ cardScanner: CardScanning?,
+ bankCardImageFactory: BankCardImageFactory
) {
self.inputPanHint = inputPanHint
self.inputPanPlaceholder = inputPanPlaceholder
@@ -33,6 +35,7 @@ final class BankCardDataInputPresenter {
self.inputCvcHint = inputCvcHint
self.inputCvcPlaceholder = inputCvcPlaceholder
self.cardScanner = cardScanner
+ self.bankCardImageFactory = bankCardImageFactory
}
// MARK: - Stored data
@@ -109,9 +112,9 @@ extension BankCardDataInputPresenter: BankCardDataInputViewOutput {
func didChangeCvc(
_ value: String
) {
+ self.cardData.csc = value
DispatchQueue.global(qos: .userInteractive).async { [weak self] in
guard let self = self else { return }
- self.cardData.csc = value
self.interactor.validate(
cardData: self.cardData,
shouldMoveFocus: true
@@ -150,6 +153,26 @@ extension BankCardDataInputPresenter: BankCardDataInputViewOutput {
)
}
}
+
+ func expiryDateDidEndEditing() {
+ DispatchQueue.global(qos: .userInteractive).async { [weak self] in
+ guard let self = self else { return }
+ self.interactor.validate(
+ cardData: self.cardData,
+ shouldMoveFocus: false
+ )
+ }
+ }
+
+ func cvcDidEndEditing() {
+ DispatchQueue.global(qos: .userInteractive).async { [weak self] in
+ guard let self = self else { return }
+ self.interactor.validate(
+ cardData: self.cardData,
+ shouldMoveFocus: false
+ )
+ }
+ }
func nextDidPress() {
setViewFocus(.expiryDate)
@@ -159,13 +182,6 @@ extension BankCardDataInputPresenter: BankCardDataInputViewOutput {
func clearDidPress() {
trackCardNumberClearAction()
}
-
- private func setModifiedPan() {
- guard let panValue = cardData.pan,
- let view = view else { return }
- let modifiedPanValue = "••••" + panValue.suffix(4)
- view.setPanValue(modifiedPanValue)
- }
}
// MARK: - BankCardDataInputInteractorOutput
@@ -181,6 +197,7 @@ extension BankCardDataInputPresenter: BankCardDataInputInteractorOutput {
DispatchQueue.main.async { [weak self] in
guard let view = self?.view else { return }
+ view.setErrorState(.noError)
if view.focus == .pan {
view.setCardViewMode(.next)
}
@@ -228,10 +245,14 @@ extension BankCardDataInputPresenter: BankCardDataInputInteractorOutput {
}
}
- func didFailFetchBankSettings() {
+ func didFailFetchBankSettings(
+ _ cardMask: String
+ ) {
DispatchQueue.main.async { [weak self] in
- guard let view = self?.view else { return }
- view.setBankLogoImage(nil)
+ guard let self = self,
+ let view = self.view else { return }
+ let image = self.bankCardImageFactory.makeImage(cardMask)
+ view.setBankLogoImage(image)
}
}
}
@@ -254,7 +275,6 @@ extension BankCardDataInputPresenter: BankCardDataInputRouterOutput {
}
private func setPanAndMoveFocusNext(_ value: String) {
- guard let view = view else { return }
cardData.pan = value
setViewFocus(.expiryDate)
}
@@ -301,13 +321,18 @@ private extension BankCardDataInputPresenter {
view.focus == .pan {
view.setErrorState(.panError)
trackCardNumberInputError()
- } else if expiryDateText.count == Constants.MoveFocusLength.expiryDate,
+ } else if (view.focus == nil ||
+ view.focus == .expiryDate
+ && expiryDateText.count == Constants.MoveFocusLength.expiryDate),
errors.contains(.expirationDateIsExpired)
|| errors.contains(.expiryDateEmpty)
- || errors.contains(.invalidMonth),
- view.focus == .expiryDate {
+ || errors.contains(.invalidMonth) {
view.setErrorState(.expiryDateError)
trackCardExpiryInputError()
+ } else if errors.contains(.cscInvalidLength),
+ view.focus == nil {
+ view.setErrorState(.invalidCvc)
+ trackCardCvcInputError()
} else {
view.setErrorState(.noError)
}
@@ -349,7 +374,12 @@ private extension BankCardDataInputPresenter {
case .expiryDate?, .cvc?:
view.setInputState(.uncollapsed)
view.setCardViewMode(.empty)
- let modifiedPanValue = "••••" + panValue.suffix(4)
+ let modifiedPanValue: String
+ if UIScreen.main.isNarrow {
+ modifiedPanValue = String(panValue.suffix(4))
+ } else {
+ modifiedPanValue = "••••" + panValue.suffix(4)
+ }
view.setPanValue(modifiedPanValue)
default:
break
@@ -385,6 +415,14 @@ private extension BankCardDataInputPresenter {
)
trackEvent(event)
}
+
+ func trackCardCvcInputError() {
+ let event: AnalyticsEvent = .actionBankCardForm(
+ action: .cardCvcInputError,
+ sdkVersion: Bundle.frameworkVersion
+ )
+ trackEvent(event)
+ }
func trackCardNumberClearAction() {
let event: AnalyticsEvent = .actionBankCardForm(
diff --git a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/BankCardDataInputView.swift b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/BankCardDataInputView.swift
index 87ea3c5a..01b5866e 100644
--- a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/BankCardDataInputView.swift
+++ b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/BankCardDataInputView.swift
@@ -57,7 +57,9 @@ final class BankCardDataInputView: UIView {
let view = UIStackView()
view.translatesAutoresizingMaskIntoConstraints = false
view.axis = .horizontal
- view.spacing = Space.double
+ view.spacing = UIScreen.main.isNarrow
+ ? Space.single
+ : Space.double
return view
}()
@@ -318,6 +320,7 @@ extension BankCardDataInputView: BankCardDataInputViewInput {
self.inputsContainerView.setStyles(UIView.Styles.grayBorder)
self.inputPanCardView.setStyles(InputPanCardView.Styles.default)
self.inputExpiryDateView.setStyles(InputExpiryDateView.Styles.default)
+ self.inputCvcView.setStyles(InputCvcView.Styles.default)
self.bottomHintLabel.text = ""
self.bottomHintLabel.alpha = 0
case .panError:
@@ -329,8 +332,16 @@ extension BankCardDataInputView: BankCardDataInputViewInput {
self.inputsContainerView.setStyles(UIView.Styles.alertBorder)
self.inputExpiryDateView.setStyles(InputExpiryDateView.Styles.error)
self.inputPanCardView.setStyles(InputPanCardView.Styles.default)
+ self.inputCvcView.setStyles(InputCvcView.Styles.default)
self.bottomHintLabel.text = §Localized.BottomHint.invalidExpiry
self.bottomHintLabel.alpha = 1
+ case .invalidCvc:
+ self.inputsContainerView.setStyles(UIView.Styles.alertBorder)
+ self.inputCvcView.setStyles(InputCvcView.Styles.error)
+ self.inputExpiryDateView.setStyles(InputExpiryDateView.Styles.default)
+ self.inputPanCardView.setStyles(InputPanCardView.Styles.default)
+ self.bottomHintLabel.text = §Localized.BottomHint.invalidCvc
+ self.bottomHintLabel.alpha = 1
}
}
}
@@ -374,6 +385,10 @@ extension BankCardDataInputView: InputExpiryDateViewDelegate {
func expiryDateDidBeginEditing() {
output.expiryDateDidBeginEditing()
}
+
+ func expiryDateDidEndEditing() {
+ output.expiryDateDidEndEditing()
+ }
}
// MARK: - InputCvcViewDelegate
@@ -384,6 +399,10 @@ extension BankCardDataInputView: InputCvcViewDelegate {
) {
output.didChangeCvc(value)
}
+
+ func cvcDidEndEditing() {
+ output.cvcDidEndEditing()
+ }
}
// MARK: - UITextField from BankCardView.BankCardFocus
@@ -408,6 +427,7 @@ private extension BankCardDataInputView {
enum BottomHint: String {
case invalidPan = "BankCardDataInputView.BottomHint.invalidPan"
case invalidExpiry = "BankCardDataInputView.BottomHint.invalidExpiry"
+ case invalidCvc = "BankCardDataInputView.BottomHint.invalidCvc"
}
}
}
diff --git a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/Subviews/InputCvcView.swift b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/Subviews/InputCvcView.swift
index 9459e251..e4fb0786 100644
--- a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/Subviews/InputCvcView.swift
+++ b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/Subviews/InputCvcView.swift
@@ -4,6 +4,7 @@ protocol InputCvcViewDelegate: class {
func cvcDidChange(
_ value: String
)
+ func cvcDidEndEditing()
}
final class InputCvcView: UIView {
@@ -156,4 +157,31 @@ extension InputCvcView: UITextFieldDelegate {
delegate?.cvcDidChange(cachedCvc)
return false
}
+
+ func textFieldDidEndEditing(
+ _ textField: UITextField
+ ) {
+ delegate?.cvcDidEndEditing()
+ }
+}
+
+// MARK: Styles
+
+extension InputCvcView {
+ enum Styles {
+ static let `default` = InternalStyle(name: "InputCvcView.Default") { (view: InputCvcView) in
+ view.cvcHintLabel.setStyles(
+ UILabel.DynamicStyle.caption1,
+ UILabel.ColorStyle.ghost,
+ UILabel.Styles.singleLine
+ )
+ }
+ static let error = InternalStyle(name: "InputCvcView.Error") { (view: InputCvcView) in
+ view.cvcHintLabel.setStyles(
+ UILabel.DynamicStyle.caption1,
+ UILabel.ColorStyle.alert,
+ UILabel.Styles.singleLine
+ )
+ }
+ }
}
diff --git a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/Subviews/InputExpiryDateView.swift b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/Subviews/InputExpiryDateView.swift
index 06c1fc21..34c52056 100644
--- a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/Subviews/InputExpiryDateView.swift
+++ b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/Subviews/InputExpiryDateView.swift
@@ -5,6 +5,7 @@ protocol InputExpiryDateViewDelegate: class {
_ value: String
)
func expiryDateDidBeginEditing()
+ func expiryDateDidEndEditing()
}
final class InputExpiryDateView: UIView {
@@ -161,7 +162,12 @@ extension InputExpiryDateView: UITextFieldDelegate {
) {
delegate?.expiryDateDidBeginEditing()
}
-
+
+ func textFieldDidEndEditing(
+ _ textField: UITextField
+ ) {
+ delegate?.expiryDateDidEndEditing()
+ }
}
// MARK: Styles
diff --git a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/Subviews/InputPanCardView.swift b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/Subviews/InputPanCardView.swift
index 34fd454c..47564d39 100644
--- a/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/Subviews/InputPanCardView.swift
+++ b/YooKassaPayments/Private/Atomic Design/Views/BankCardDataInput/View/Subviews/InputPanCardView.swift
@@ -245,6 +245,7 @@ extension InputPanCardView: UITextFieldDelegate {
_ textField: UITextField
) {
delegate?.panDidBeginEditing()
+ setCursorToEnd()
}
}
@@ -268,6 +269,17 @@ private extension InputPanCardView {
delegate?.nextDidPress()
}
}
+
+ func setCursorToEnd() {
+ DispatchQueue.main.async() { [weak self] in
+ guard let self = self else { return }
+ let newPosition = self.cardPanTextField.endOfDocument
+ self.cardPanTextField.selectedTextRange = self.cardPanTextField.textRange(
+ from: newPosition,
+ to: newPosition
+ )
+ }
+ }
}
// MARK: Styles
diff --git a/YooKassaPayments/Private/Atomic Design/Views/NavigationController.swift b/YooKassaPayments/Private/Atomic Design/Views/NavigationController.swift
index 223e6d36..9809deef 100644
--- a/YooKassaPayments/Private/Atomic Design/Views/NavigationController.swift
+++ b/YooKassaPayments/Private/Atomic Design/Views/NavigationController.swift
@@ -10,4 +10,14 @@ extension NavigationController: TokenizationModuleInput {
func start3dsProcess(requestUrl: String) {
moduleOutput?.start3dsProcess(requestUrl: requestUrl)
}
+
+ func startConfirmationProcess(
+ confirmationUrl: String,
+ paymentMethodType: PaymentMethodType
+ ) {
+ moduleOutput?.startConfirmationProcess(
+ confirmationUrl: confirmationUrl,
+ paymentMethodType: paymentMethodType
+ )
+ }
}
diff --git a/YooKassaPayments/Private/Extensions/UIScreen+Short.swift b/YooKassaPayments/Private/Extensions/UIScreen+Short.swift
new file mode 100644
index 00000000..6bf8250d
--- /dev/null
+++ b/YooKassaPayments/Private/Extensions/UIScreen+Short.swift
@@ -0,0 +1,9 @@
+extension UIScreen {
+ var isShort: Bool {
+ bounds.height < 600
+ }
+
+ var isNarrow: Bool {
+ bounds.width < 350
+ }
+}
diff --git a/YooKassaPayments/Private/Factory/BankCardImage/BankCardImageFactory.swift b/YooKassaPayments/Private/Factory/BankCardImage/BankCardImageFactory.swift
new file mode 100644
index 00000000..16b3e292
--- /dev/null
+++ b/YooKassaPayments/Private/Factory/BankCardImage/BankCardImageFactory.swift
@@ -0,0 +1,8 @@
+protocol BankCardImageFactory {
+
+ // MARK: - Make bank card image from card mask
+
+ func makeImage(
+ _ cardMask: String
+ ) -> UIImage?
+}
diff --git a/YooKassaPayments/Private/Factory/BankCardImage/BankCardImageFactoryAssembly.swift b/YooKassaPayments/Private/Factory/BankCardImage/BankCardImageFactoryAssembly.swift
new file mode 100644
index 00000000..a350c6c2
--- /dev/null
+++ b/YooKassaPayments/Private/Factory/BankCardImage/BankCardImageFactoryAssembly.swift
@@ -0,0 +1,5 @@
+enum BankCardImageFactoryAssembly {
+ static func makeFactory() -> BankCardImageFactory {
+ return BankCardImageFactoryImpl()
+ }
+}
diff --git a/YooKassaPayments/Private/Factory/BankCardImage/BankCardImageFactoryImpl.swift b/YooKassaPayments/Private/Factory/BankCardImage/BankCardImageFactoryImpl.swift
new file mode 100644
index 00000000..650bd13c
--- /dev/null
+++ b/YooKassaPayments/Private/Factory/BankCardImage/BankCardImageFactoryImpl.swift
@@ -0,0 +1,67 @@
+final class BankCardImageFactoryImpl {
+
+ // MARK: - Stored properties
+
+ private let bankCardRegex: [BankCardRegex] = [
+ BankCardRegex(
+ type: .americanExpress,
+ regex: "^3[47][0-9]{5,}$"
+ ),
+ BankCardRegex(
+ type: .masterCard,
+ regex: "^5[1-5][0-9]{5,}$"
+ ),
+ BankCardRegex(
+ type: .visa,
+ regex: "^4[0-9]{6,}$"
+ ),
+ BankCardRegex(
+ type: .mir,
+ regex: "^220[0-4]\\d+$"
+ ),
+ BankCardRegex(
+ type: .maestro,
+ regex: "^(5018|5020|5038|5612|5893|6304|6759|6761|6762|6763|0604|6390)\\d+$"
+ ),
+ ]
+}
+
+extension BankCardImageFactoryImpl: BankCardImageFactory {
+
+ // MARK: - Make bank card image from card mask
+
+ func makeImage(
+ _ cardMask: String
+ ) -> UIImage? {
+ guard let cardType = cardTypeFromCardMask(cardMask) else {
+ return nil
+ }
+
+ let image: UIImage
+ switch cardType {
+ case .americanExpress:
+ image = PaymentMethodResources.Image.americanExpress
+ case .masterCard:
+ image = PaymentMethodResources.Image.mastercard
+ case .visa:
+ image = PaymentMethodResources.Image.visa
+ case .mir:
+ image = PaymentMethodResources.Image.mir
+ case .maestro:
+ image = PaymentMethodResources.Image.maestro
+ }
+ return image
+ }
+
+ private func cardTypeFromCardMask(
+ _ cardMask: String
+ ) -> BankCardRegexType? {
+ for bankCard in bankCardRegex {
+ let predicate = NSPredicate(format: "SELF MATCHES %@", bankCard.regex)
+ if predicate.evaluate(with: cardMask) {
+ return bankCard.type
+ }
+ }
+ return nil
+ }
+}
diff --git a/YooKassaPayments/Private/Factory/Deeplink/Deeplink.swift b/YooKassaPayments/Private/Factory/Deeplink/Deeplink.swift
new file mode 100644
index 00000000..8796b1c8
--- /dev/null
+++ b/YooKassaPayments/Private/Factory/Deeplink/Deeplink.swift
@@ -0,0 +1,10 @@
+/// Model for open app with specific deeplink.
+enum DeepLink {
+ /// Открывает экран завершения оплаты через SberPay.
+ /// - Example: `scheme://invoicing/sberpay`
+ case invoicingSberpay
+
+ /// Открывает окончание авторизации в приложении YooMoney с криптограмой.
+ /// - Example: `scheme://yoomoney/exchange?cryptogram=someCryptogram`
+ case yooMoneyExchange(cryptogram: String)
+}
diff --git a/YooKassaPayments/Private/Factory/Deeplink/DeeplinkFactory.swift b/YooKassaPayments/Private/Factory/Deeplink/DeeplinkFactory.swift
new file mode 100644
index 00000000..9dcecfd5
--- /dev/null
+++ b/YooKassaPayments/Private/Factory/Deeplink/DeeplinkFactory.swift
@@ -0,0 +1,57 @@
+import Foundation
+
+enum DeepLinkFactory {
+
+ static let invoicingHost = "invoicing"
+ static let sberpayPath = "sberpay"
+
+ enum YooMoney {
+ static let host = "yoomoney"
+ enum exchange {
+ static let firstPath = "exchange"
+ static let cryptogram = "cryptogram"
+ }
+ }
+
+ // swiftlint:disable:next cyclomatic_complexity
+ static func makeDeepLink(url: URL) -> DeepLink? {
+ guard
+ let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
+ let host = components.host
+ else { return nil }
+
+ let firstPathComponent = components.path
+ .split(separator: "/")
+ .filter { !$0.isEmpty }
+ .map(String.init)
+ .first
+
+ let query = components
+ .queryItems?
+ .map { ($0.name, $0.value) }
+ .reduce(into: [:]) { $0[$1.0] = $1.1 }
+ ?? [:]
+
+ let action = components.fragment
+
+ let deepLink: DeepLink?
+
+ switch (host, firstPathComponent, query, action) {
+ case (invoicingHost, sberpayPath, _, _):
+ deepLink = .invoicingSberpay
+
+ case (YooMoney.host, YooMoney.exchange.firstPath, query, _):
+ guard let cryptogram = query["cryptogram"],
+ !cryptogram.isEmpty else {
+ deepLink = nil
+ break
+ }
+ deepLink = .yooMoneyExchange(cryptogram: cryptogram)
+
+ default:
+ deepLink = nil
+ }
+
+ return deepLink
+ }
+}
diff --git a/YooKassaPayments/Private/Factory/PaymentMethodViewModel/PaymentMethodViewModelFactoryImpl.swift b/YooKassaPayments/Private/Factory/PaymentMethodViewModel/PaymentMethodViewModelFactoryImpl.swift
index a7433645..f11246cb 100644
--- a/YooKassaPayments/Private/Factory/PaymentMethodViewModel/PaymentMethodViewModelFactoryImpl.swift
+++ b/YooKassaPayments/Private/Factory/PaymentMethodViewModel/PaymentMethodViewModelFactoryImpl.swift
@@ -162,7 +162,7 @@ extension PaymentMethodViewModelFactoryImpl: PaymentMethodViewModelFactory {
case .applePay:
name = §PaymentMethodResources.Localized.applePay
case .sberbank:
- name = §PaymentMethodResources.Localized.sberbank
+ name = §PaymentMethodResources.Localized.sberpay
default:
assertionFailure("Unsupported PaymentMethodType")
name = "Unsupported"
@@ -209,7 +209,7 @@ extension PaymentMethodViewModelFactoryImpl: PaymentMethodViewModelFactory {
case .applePay:
image = PaymentMethodResources.Image.applePay
case .sberbank:
- image = PaymentMethodResources.Image.sberbank
+ image = PaymentMethodResources.Image.sberpay
default:
assertionFailure("Unsupported PaymentMethodType")
image = UIImage()
diff --git a/YooKassaPayments/Private/Factory/PriceViewModelFactory.swift b/YooKassaPayments/Private/Factory/PriceViewModelFactory.swift
new file mode 100644
index 00000000..46bfcba9
--- /dev/null
+++ b/YooKassaPayments/Private/Factory/PriceViewModelFactory.swift
@@ -0,0 +1,74 @@
+import YooKassaPaymentsApi
+
+enum PriceViewModelFactoryAssembly {
+ static func makeFactory() -> PriceViewModelFactory {
+ PriceViewModelFactoryImpl()
+ }
+}
+
+protocol PriceViewModelFactory {
+ func makeAmountPriceViewModel(
+ _ paymentOption: PaymentOption
+ ) -> PriceViewModel
+
+ func makeFeePriceViewModel(
+ _ paymentOption: PaymentOption
+ ) -> PriceViewModel?
+}
+
+final class PriceViewModelFactoryImpl {}
+
+// MARK: - PriceViewModelFactory
+
+extension PriceViewModelFactoryImpl: PriceViewModelFactory {
+ func makeAmountPriceViewModel(
+ _ paymentOption: PaymentOption
+ ) -> PriceViewModel {
+ let amountString = paymentOption.charge.value.description
+ var integerPart = ""
+ var fractionalPart = ""
+
+ if let separatorIndex = amountString.firstIndex(of: ".") {
+ integerPart = String(amountString[amountString.startIndex.. PriceViewModel? {
+ guard let fee = paymentOption.fee,
+ let service = fee.service else { return nil }
+
+ let amountString = service.charge.value.description
+ var integerPart = ""
+ var fractionalPart = ""
+
+ if let separatorIndex = amountString.firstIndex(of: ".") {
+ integerPart = String(amountString[amountString.startIndex.. UIViewController {
- let presenter = CardSecPresenter()
+ let presenter = CardSecPresenter(
+ isConfirmation: inputData.isConfirmation
+ )
let analyticsService = AnalyticsServiceAssembly.makeService(
isLoggingEnabled: inputData.isLoggingEnabled
diff --git a/YooKassaPayments/Private/Modules/3DS/CardSecModuleIO.swift b/YooKassaPayments/Private/Modules/3DS/CardSecModuleIO.swift
index 414d9581..7a849035 100644
--- a/YooKassaPayments/Private/Modules/3DS/CardSecModuleIO.swift
+++ b/YooKassaPayments/Private/Modules/3DS/CardSecModuleIO.swift
@@ -5,24 +5,30 @@ struct CardSecModuleInputData {
let requestUrl: String
let redirectUrl: String
let isLoggingEnabled: Bool
+ let isConfirmation: Bool
// MARK: - Init
init(
requestUrl: String,
redirectUrl: String,
- isLoggingEnabled: Bool
+ isLoggingEnabled: Bool,
+ isConfirmation: Bool
) {
self.requestUrl = requestUrl
self.redirectUrl = redirectUrl
self.isLoggingEnabled = isLoggingEnabled
+ self.isConfirmation = isConfirmation
}
}
protocol CardSecModuleInput: class {}
protocol CardSecModuleOutput: class {
- func didSuccessfullyPassedCardSec(on module: CardSecModuleInput)
+ func didSuccessfullyPassedCardSec(
+ on module: CardSecModuleInput,
+ isConfirmation: Bool
+ )
func didPressCloseButton(on module: CardSecModuleInput)
func viewWillDisappear()
}
diff --git a/YooKassaPayments/Private/Modules/3DS/Presenter/CardSecPresenter.swift b/YooKassaPayments/Private/Modules/3DS/Presenter/CardSecPresenter.swift
index 1daf3ee6..94b24af8 100644
--- a/YooKassaPayments/Private/Modules/3DS/Presenter/CardSecPresenter.swift
+++ b/YooKassaPayments/Private/Modules/3DS/Presenter/CardSecPresenter.swift
@@ -8,6 +8,16 @@ final class CardSecPresenter: WebBrowserPresenter {
// MARK: - Business logic properties
private var shouldCallDidSuccessfullyPassedCardSec = true
+
+ // MARK: - Init data
+
+ private let isConfirmation: Bool
+
+ // MARK: - Init
+
+ init(isConfirmation: Bool) {
+ self.isConfirmation = isConfirmation
+ }
// MARK: - Overridden funcs
@@ -38,7 +48,10 @@ extension CardSecPresenter: CardSecInteractorOutput {
func didSuccessfullyPassedCardSec() {
guard shouldCallDidSuccessfullyPassedCardSec else { return }
shouldCallDidSuccessfullyPassedCardSec = false
- cardSecModuleOutput?.didSuccessfullyPassedCardSec(on: self)
+ cardSecModuleOutput?.didSuccessfullyPassedCardSec(
+ on: self,
+ isConfirmation: isConfirmation
+ )
}
}
diff --git a/YooKassaPayments/Private/Modules/ApplePayContract/Interactor/ApplePayContractInteractor.swift b/YooKassaPayments/Private/Modules/ApplePayContract/Interactor/ApplePayContractInteractor.swift
index cf91d327..847add73 100644
--- a/YooKassaPayments/Private/Modules/ApplePayContract/Interactor/ApplePayContractInteractor.swift
+++ b/YooKassaPayments/Private/Modules/ApplePayContract/Interactor/ApplePayContractInteractor.swift
@@ -60,7 +60,7 @@ extension ApplePayContractInteractor: ApplePayContractInteractorInput {
paymentData: paymentData,
savePaymentMethod: savePaymentMethod,
amount: amount,
- tmxSessionId: tmxSessionId
+ tmxSessionId: tmxSessionId.value
)
case let .failure(error):
diff --git a/YooKassaPayments/Private/Modules/ApplePayContract/View/ApplePayContractViewController.swift b/YooKassaPayments/Private/Modules/ApplePayContract/View/ApplePayContractViewController.swift
index c51b6903..25df9f87 100644
--- a/YooKassaPayments/Private/Modules/ApplePayContract/View/ApplePayContractViewController.swift
+++ b/YooKassaPayments/Private/Modules/ApplePayContract/View/ApplePayContractViewController.swift
@@ -365,7 +365,7 @@ extension ApplePayContractViewController: ApplePayContractViewInput {
let linkAttributedText = NSMutableAttributedString(string: hyperText, attributes: attributes)
let linkRange = NSRange(location: 0, length: hyperText.count)
- let fakeLink = URL(string: "https://yookassa.ru")
+ let fakeLink = URL(string: "https://yookassa.ru")!
linkAttributedText.addAttribute(.link, value: fakeLink, range: linkRange)
attributedText.append(linkAttributedText)
diff --git a/YooKassaPayments/Private/Modules/BankCard/Interactor/BankCardInteractor.swift b/YooKassaPayments/Private/Modules/BankCard/Interactor/BankCardInteractor.swift
index 3e19abc6..4dbbfc77 100644
--- a/YooKassaPayments/Private/Modules/BankCard/Interactor/BankCardInteractor.swift
+++ b/YooKassaPayments/Private/Modules/BankCard/Interactor/BankCardInteractor.swift
@@ -60,8 +60,8 @@ extension BankCardInteractor: BankCardInteractorInput {
confirmation: confirmation,
savePaymentMethod: savePaymentMethod,
amount: self.amount,
- tmxSessionId: tmxSessionId
- ) { [weak self] result in
+ tmxSessionId: tmxSessionId.value
+ ) { result in
switch result {
case .success(let data):
output.didTokenize(data)
diff --git a/YooKassaPayments/Private/Modules/BankCard/Presenter/BankCardPresenter.swift b/YooKassaPayments/Private/Modules/BankCard/Presenter/BankCardPresenter.swift
index daa94c8e..701a0a01 100644
--- a/YooKassaPayments/Private/Modules/BankCard/Presenter/BankCardPresenter.swift
+++ b/YooKassaPayments/Private/Modules/BankCard/Presenter/BankCardPresenter.swift
@@ -149,8 +149,7 @@ extension BankCardPresenter: BankCardInteractorOutput {
_ data: Tokens
) {
DispatchQueue.main.async { [weak self] in
- guard let self = self,
- let view = self.view else { return }
+ guard let self = self else { return }
self.moduleOutput?.bankCardModule(
self,
didTokenize: data,
diff --git a/YooKassaPayments/Private/Modules/BankCard/View/BankCardViewController.swift b/YooKassaPayments/Private/Modules/BankCard/View/BankCardViewController.swift
index b5a3227a..aae7b2d3 100644
--- a/YooKassaPayments/Private/Modules/BankCard/View/BankCardViewController.swift
+++ b/YooKassaPayments/Private/Modules/BankCard/View/BankCardViewController.swift
@@ -337,7 +337,7 @@ extension BankCardViewController: BankCardViewInput {
let linkAttributedText = NSMutableAttributedString(string: hyperText, attributes: attributes)
let linkRange = NSRange(location: 0, length: hyperText.count)
- let fakeLink = URL(string: "https://yookassa.ru")
+ let fakeLink = URL(string: "https://yookassa.ru")!
linkAttributedText.addAttribute(.link, value: fakeLink, range: linkRange)
attributedText.append(linkAttributedText)
diff --git a/YooKassaPayments/Private/Modules/BankCardRepeat/Assembly/BankCardRepeatAssembly.swift b/YooKassaPayments/Private/Modules/BankCardRepeat/Assembly/BankCardRepeatAssembly.swift
index dbada1af..3fc8d643 100644
--- a/YooKassaPayments/Private/Modules/BankCardRepeat/Assembly/BankCardRepeatAssembly.swift
+++ b/YooKassaPayments/Private/Modules/BankCardRepeat/Assembly/BankCardRepeatAssembly.swift
@@ -11,8 +11,8 @@ enum BankCardRepeatAssembly {
let view = BankCardRepeatViewController()
let cardService = CardService()
- let paymentMethodViewModelFactory =
- PaymentMethodViewModelFactoryAssembly.makeFactory()
+ let paymentMethodViewModelFactory = PaymentMethodViewModelFactoryAssembly.makeFactory()
+ let priceViewModelFactory = PriceViewModelFactoryAssembly.makeFactory()
let termsOfService = TermsOfServiceFactory.makeTermsOfService()
let initialSavePaymentMethod = makeInitialSavePaymentMethod(inputData.savePaymentMethod)
let savePaymentMethodViewModel = SavePaymentMethodViewModelFactory.makeSavePaymentMethodViewModel(
@@ -22,12 +22,12 @@ enum BankCardRepeatAssembly {
let presenter = BankCardRepeatPresenter(
cardService: cardService,
paymentMethodViewModelFactory: paymentMethodViewModelFactory,
+ priceViewModelFactory: priceViewModelFactory,
isLoggingEnabled: inputData.isLoggingEnabled,
returnUrl: inputData.returnUrl,
paymentMethodId: inputData.paymentMethodId,
shopName: inputData.shopName,
purchaseDescription: inputData.purchaseDescription,
- amount: inputData.amount,
termsOfService: termsOfService,
savePaymentMethodViewModel: savePaymentMethodViewModel,
initialSavePaymentMethod: initialSavePaymentMethod
@@ -45,12 +45,16 @@ enum BankCardRepeatAssembly {
testModeSettings: inputData.testModeSettings
)
let threatMetrixService = ThreatMetrixServiceFactory.makeService()
+ let amountNumberFormatter = AmountNumberFormatterAssembly.makeAmountNumberFormatter()
let interactor = BankCardRepeatInteractor(
analyticsService: analyticsService,
analyticsProvider: analyticsProvider,
paymentService: paymentService,
threatMetrixService: threatMetrixService,
- clientApplicationKey: inputData.clientApplicationKey
+ amountNumberFormatter: amountNumberFormatter,
+ clientApplicationKey: inputData.clientApplicationKey,
+ gatewayId: inputData.gatewayId,
+ amount: inputData.amount
)
let router = BankCardRepeatRouter()
diff --git a/YooKassaPayments/Private/Modules/BankCardRepeat/BankCardRepeatInteractorIO.swift b/YooKassaPayments/Private/Modules/BankCardRepeat/BankCardRepeatInteractorIO.swift
index d1ebcf4a..f91b297e 100644
--- a/YooKassaPayments/Private/Modules/BankCardRepeat/BankCardRepeatInteractorIO.swift
+++ b/YooKassaPayments/Private/Modules/BankCardRepeat/BankCardRepeatInteractorIO.swift
@@ -1,3 +1,5 @@
+import YooKassaPaymentsApi
+
protocol BankCardRepeatInteractorInput: AnalyticsTrack, AnalyticsProvider {
func fetchPaymentMethod(
paymentMethodId: String
@@ -9,6 +11,8 @@ protocol BankCardRepeatInteractorInput: AnalyticsTrack, AnalyticsProvider {
paymentMethodId: String,
csc: String
)
+
+ func fetchPaymentMethods()
}
protocol BankCardRepeatInteractorOutput: class {
@@ -18,11 +22,18 @@ protocol BankCardRepeatInteractorOutput: class {
func didFailFetchPaymentMethod(
_ error: Error
)
-
+
func didTokenize(
_ tokens: Tokens
)
func didFailTokenize(
_ error: Error
)
+
+ func didFetchPaymentMethods(
+ _ paymentMethods: [PaymentOption]
+ )
+ func didFetchPaymentMethods(
+ _ error: Error
+ )
}
diff --git a/YooKassaPayments/Private/Modules/BankCardRepeat/Interactor/BankCardRepeatInteractor.swift b/YooKassaPayments/Private/Modules/BankCardRepeat/Interactor/BankCardRepeatInteractor.swift
index 7053c963..1457ef6a 100644
--- a/YooKassaPayments/Private/Modules/BankCardRepeat/Interactor/BankCardRepeatInteractor.swift
+++ b/YooKassaPayments/Private/Modules/BankCardRepeat/Interactor/BankCardRepeatInteractor.swift
@@ -12,8 +12,11 @@ final class BankCardRepeatInteractor {
private let analyticsProvider: AnalyticsProvider
private let paymentService: PaymentService
private let threatMetrixService: ThreatMetrixService
+ private let amountNumberFormatter: AmountNumberFormatter
private let clientApplicationKey: String
+ private let gatewayId: String?
+ private let amount: Amount
// MARK: - Init
@@ -22,14 +25,20 @@ final class BankCardRepeatInteractor {
analyticsProvider: AnalyticsProvider,
paymentService: PaymentService,
threatMetrixService: ThreatMetrixService,
- clientApplicationKey: String
+ amountNumberFormatter: AmountNumberFormatter,
+ clientApplicationKey: String,
+ gatewayId: String?,
+ amount: Amount
) {
self.analyticsService = analyticsService
self.analyticsProvider = analyticsProvider
self.paymentService = paymentService
self.threatMetrixService = threatMetrixService
+ self.amountNumberFormatter = amountNumberFormatter
self.clientApplicationKey = clientApplicationKey
+ self.gatewayId = gatewayId
+ self.amount = amount
}
}
@@ -69,7 +78,7 @@ extension BankCardRepeatInteractor: BankCardRepeatInteractorInput {
self.paymentService.tokenizeRepeatBankCard(
clientApplicationKey: self.clientApplicationKey,
amount: amount,
- tmxSessionId: tmxSessionId,
+ tmxSessionId: tmxSessionId.value,
confirmation: confirmation,
savePaymentMethod: savePaymentMethod,
paymentMethodId: paymentMethodId,
@@ -91,6 +100,25 @@ extension BankCardRepeatInteractor: BankCardRepeatInteractorInput {
}
}
+ func fetchPaymentMethods() {
+ paymentService.fetchPaymentOptions(
+ clientApplicationKey: clientApplicationKey,
+ authorizationToken: nil,
+ gatewayId: gatewayId,
+ amount: amountNumberFormatter.string(from: amount.value),
+ currency: amount.currency.rawValue,
+ getSavePaymentMethod: false
+ ) { [weak self] result in
+ guard let output = self?.output else { return }
+ switch result {
+ case let .success(data):
+ output.didFetchPaymentMethods(data)
+ case let .failure(error):
+ output.didFetchPaymentMethods(error)
+ }
+ }
+ }
+
func trackEvent(_ event: AnalyticsEvent) {
analyticsService.trackEvent(event)
}
diff --git a/YooKassaPayments/Private/Modules/BankCardRepeat/Presenter/BankCardRepeatPresenter.swift b/YooKassaPayments/Private/Modules/BankCardRepeat/Presenter/BankCardRepeatPresenter.swift
index 0db914a9..086378ef 100644
--- a/YooKassaPayments/Private/Modules/BankCardRepeat/Presenter/BankCardRepeatPresenter.swift
+++ b/YooKassaPayments/Private/Modules/BankCardRepeat/Presenter/BankCardRepeatPresenter.swift
@@ -9,20 +9,12 @@ final class BankCardRepeatPresenter {
weak var moduleOutput: TokenizationModuleOutput?
weak var view: BankCardRepeatViewInput?
-
- // MARK: - Number formatter
-
- private lazy var numberFormatter: NumberFormatter = {
- let numberFormatter = NumberFormatter()
- numberFormatter.maximumFractionDigits = 2
- numberFormatter.decimalSeparator = Constants.decimalSeparator
- return numberFormatter
- }()
// MARK: - Init data
private let cardService: CardService
private let paymentMethodViewModelFactory: PaymentMethodViewModelFactory
+ private let priceViewModelFactory: PriceViewModelFactory
private let isLoggingEnabled: Bool
private let returnUrl: String?
@@ -30,7 +22,6 @@ final class BankCardRepeatPresenter {
private let paymentMethodId: String
private let shopName: String
private let purchaseDescription: String
- private let amount: Amount
private let termsOfService: TermsOfService
private let savePaymentMethodViewModel: SavePaymentMethodViewModel?
private var initialSavePaymentMethod: Bool
@@ -40,18 +31,19 @@ final class BankCardRepeatPresenter {
init(
cardService: CardService,
paymentMethodViewModelFactory: PaymentMethodViewModelFactory,
+ priceViewModelFactory: PriceViewModelFactory,
isLoggingEnabled: Bool,
returnUrl: String?,
paymentMethodId: String,
shopName: String,
purchaseDescription: String,
- amount: Amount,
termsOfService: TermsOfService,
savePaymentMethodViewModel: SavePaymentMethodViewModel?,
initialSavePaymentMethod: Bool
) {
self.cardService = cardService
self.paymentMethodViewModelFactory = paymentMethodViewModelFactory
+ self.priceViewModelFactory = priceViewModelFactory
self.isLoggingEnabled = isLoggingEnabled
self.returnUrl = returnUrl
@@ -59,7 +51,6 @@ final class BankCardRepeatPresenter {
self.paymentMethodId = paymentMethodId
self.shopName = shopName
self.purchaseDescription = purchaseDescription
- self.amount = amount
self.termsOfService = termsOfService
self.savePaymentMethodViewModel = savePaymentMethodViewModel
self.initialSavePaymentMethod = initialSavePaymentMethod
@@ -68,6 +59,7 @@ final class BankCardRepeatPresenter {
// MARK: - Stored Data
private var paymentMethod: PaymentMethod?
+ private var paymentOption: PaymentOption?
private var csc: String?
}
@@ -76,13 +68,12 @@ final class BankCardRepeatPresenter {
extension BankCardRepeatPresenter: BankCardRepeatViewOutput {
func setupView() {
guard let view = view else { return }
-
+
view.showActivity()
-
+
DispatchQueue.global().async { [weak self] in
guard let self = self,
let interactor = self.interactor else { return }
-
let (authType, _) = interactor.makeTypeAnalyticsParameters()
let event: AnalyticsEvent = .screenPaymentContract(
authType: authType,
@@ -90,9 +81,7 @@ extension BankCardRepeatPresenter: BankCardRepeatViewOutput {
sdkVersion: Bundle.frameworkVersion
)
interactor.trackEvent(event)
- interactor.fetchPaymentMethod(
- paymentMethodId: self.paymentMethodId
- )
+ interactor.fetchPaymentMethods()
}
}
@@ -134,7 +123,7 @@ extension BankCardRepeatPresenter: BankCardRepeatViewOutput {
do {
try self.cardService.validate(csc: csc)
} catch {
- if let error = error as? CardService.ValidationError {
+ if error is CardService.ValidationError {
DispatchQueue.main.async { [weak self] in
guard let view = self?.view else { return }
view.setConfirmButtonEnabled(false)
@@ -160,7 +149,7 @@ extension BankCardRepeatPresenter: BankCardRepeatViewOutput {
do {
try self.cardService.validate(csc: csc)
} catch {
- if let error = error as? CardService.ValidationError {
+ if error is CardService.ValidationError {
DispatchQueue.main.async { [weak self] in
guard let view = self?.view else { return }
view.setCardState(.error)
@@ -183,7 +172,7 @@ extension BankCardRepeatPresenter: BankCardRepeatInteractorOutput {
_ paymentMethod: PaymentMethod
) {
self.paymentMethod = paymentMethod
-
+
guard let card = paymentMethod.card,
card.first6.isEmpty == false,
card.last4.isEmpty == false else {
@@ -196,17 +185,22 @@ extension BankCardRepeatPresenter: BankCardRepeatInteractorOutput {
}
return
}
-
+
+ guard let paymentOption = self.paymentOption else {
+ assertionFailure("PaymentOption should be")
+ return
+ }
+
let cardMask = card.first6 + "••••••" + card.last4
let cardLogo = paymentMethodViewModelFactory.makeBankCardImage(card)
-
+ let priceViewModel = priceViewModelFactory.makeAmountPriceViewModel(paymentOption)
+ let feeViewModel = priceViewModelFactory.makeFeePriceViewModel(paymentOption)
+
let viewModel = BankCardRepeatViewModel(
shopName: shopName,
description: purchaseDescription,
- price: makePriceViewModel(
- amount,
- numberFormatter: numberFormatter
- ),
+ price: priceViewModel,
+ fee: feeViewModel,
cardMask: formattingCardMask(cardMask),
cardLogo: cardLogo,
terms: termsOfService
@@ -215,16 +209,16 @@ extension BankCardRepeatPresenter: BankCardRepeatInteractorOutput {
DispatchQueue.main.async { [weak self] in
guard let self = self,
let view = self.view else { return }
-
+
view.hideActivity()
-
+
view.setupViewModel(viewModel)
view.setConfirmButtonEnabled(false)
-
+
if let savePaymentMethodViewModel = self.savePaymentMethodViewModel {
view.setSavePaymentMethodViewModel(savePaymentMethodViewModel)
}
-
+
DispatchQueue.global().async { [weak self] in
let event: AnalyticsEvent = .screenRecurringCardForm(
sdkVersion: Bundle.frameworkVersion
@@ -235,11 +229,9 @@ extension BankCardRepeatPresenter: BankCardRepeatInteractorOutput {
}
func didFailFetchPaymentMethod(_ error: Error) {
- let authType = AnalyticsEvent.AuthType.withoutAuth
- let scheme = AnalyticsEvent.TokenizeScheme.recurringCard
let event = AnalyticsEvent.screenError(
- authType: authType,
- scheme: scheme,
+ authType: .withoutAuth,
+ scheme: .recurringCard,
sdkVersion: Bundle.frameworkVersion
)
interactor.trackEvent(event)
@@ -264,7 +256,7 @@ extension BankCardRepeatPresenter: BankCardRepeatInteractorOutput {
guard let self = self, let interactor = self.interactor else { return }
let type = interactor.makeTypeAnalyticsParameters()
let event: AnalyticsEvent = .actionTokenize(
- scheme: .bankCard,
+ scheme: .recurringCard,
authType: type.authType,
tokenType: type.tokenType,
sdkVersion: Bundle.frameworkVersion
@@ -283,6 +275,34 @@ extension BankCardRepeatPresenter: BankCardRepeatInteractorOutput {
}
}
+ func didFetchPaymentMethods(_ paymentMethods: [PaymentOption]) {
+ guard let bankCard = paymentMethods.first(where: {
+ $0.paymentMethodType == .bankCard
+ }) else {
+ DispatchQueue.main.async { [weak self] in
+ guard let self = self,
+ let view = self.view else { return }
+ view.hideActivity()
+ view.showPlaceholder()
+ }
+ return
+ }
+ self.paymentOption = bankCard
+ interactor.fetchPaymentMethod(
+ paymentMethodId: paymentMethodId
+ )
+ }
+
+ func didFetchPaymentMethods(_ error: Error) {
+ let message = makeMessage(error)
+
+ DispatchQueue.main.async { [weak self] in
+ guard let view = self?.view else { return }
+ view.hideActivity()
+ view.showPlaceholder(with: message)
+ }
+ }
+
private func tokenize() {
guard let csc = csc else { return }
@@ -290,9 +310,14 @@ extension BankCardRepeatPresenter: BankCardRepeatInteractorOutput {
type: .redirect,
returnUrl: returnUrl ?? GlobalConstants.returnUrl
)
-
+
+ guard let paymentOption = paymentOption else {
+ assertionFailure("PaymentOption should be")
+ return
+ }
+
interactor.tokenize(
- amount: MonetaryAmountFactory.makeMonetaryAmount(amount),
+ amount: paymentOption.charge.plain,
confirmation: confirmation,
savePaymentMethod: initialSavePaymentMethod,
paymentMethodId: paymentMethodId,
@@ -310,10 +335,12 @@ extension BankCardRepeatPresenter: ActionTitleTextDialogDelegate {
guard let view = view else { return }
view.hidePlaceholder()
view.showActivity()
-
+
DispatchQueue.global().async { [weak self] in
guard let self = self else { return }
- if self.paymentMethod == nil {
+ if self.paymentOption == nil {
+ self.interactor.fetchPaymentMethods()
+ } else if self.paymentMethod == nil {
self.interactor.fetchPaymentMethod(
paymentMethodId: self.paymentMethodId
)
@@ -333,7 +360,27 @@ extension BankCardRepeatPresenter: TokenizationModuleInput {
let moduleInputData = CardSecModuleInputData(
requestUrl: requestUrl,
redirectUrl: returnUrl ?? GlobalConstants.returnUrl,
- isLoggingEnabled: isLoggingEnabled
+ isLoggingEnabled: isLoggingEnabled,
+ isConfirmation: false
+ )
+ DispatchQueue.main.async { [weak self] in
+ guard let self = self else { return }
+ self.router.present3dsModule(
+ inputData: moduleInputData,
+ moduleOutput: self
+ )
+ }
+ }
+
+ func startConfirmationProcess(
+ confirmationUrl: String,
+ paymentMethodType: PaymentMethodType
+ ) {
+ let moduleInputData = CardSecModuleInputData(
+ requestUrl: confirmationUrl,
+ redirectUrl: returnUrl ?? GlobalConstants.returnUrl,
+ isLoggingEnabled: isLoggingEnabled,
+ isConfirmation: true
)
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
@@ -348,8 +395,19 @@ extension BankCardRepeatPresenter: TokenizationModuleInput {
// MARK: - CardSecModuleOutput
extension BankCardRepeatPresenter: CardSecModuleOutput {
- func didSuccessfullyPassedCardSec(on module: CardSecModuleInput) {
- moduleOutput?.didSuccessfullyPassedCardSec(on: self)
+ func didSuccessfullyPassedCardSec(
+ on module: CardSecModuleInput,
+ isConfirmation: Bool
+ ) {
+ if isConfirmation {
+ moduleOutput?.didSuccessfullyConfirmation(
+ paymentMethodType: .bankCard
+ )
+ } else {
+ moduleOutput?.didSuccessfullyPassedCardSec(
+ on: self
+ )
+ }
}
func didPressCloseButton(on module: CardSecModuleInput) {
@@ -378,30 +436,6 @@ extension BankCardRepeatPresenter: BankCardRepeatModuleInput {
// MARK: - Private global helpers
-private func makePriceViewModel(
- _ amount: Amount,
- numberFormatter: NumberFormatter
-) -> PriceViewModel {
- let decimalNumber = NSDecimalNumber(decimal: amount.value)
- let amountString = numberFormatter.string(from: decimalNumber) ?? amount.value.description
- var integerPart = ""
- var fractionalPart = ""
-
- if let separatorIndex = amountString.firstIndex(of: Character(Constants.decimalSeparator)) {
- integerPart = String(amountString[amountString.startIndex.. String {
let message: String
diff --git a/YooKassaPayments/Private/Modules/BankCardRepeat/View/BankCardRepeatViewController.swift b/YooKassaPayments/Private/Modules/BankCardRepeat/View/BankCardRepeatViewController.swift
index f989109e..0d8d0f9b 100644
--- a/YooKassaPayments/Private/Modules/BankCardRepeat/View/BankCardRepeatViewController.swift
+++ b/YooKassaPayments/Private/Modules/BankCardRepeat/View/BankCardRepeatViewController.swift
@@ -418,8 +418,12 @@ extension BankCardRepeatViewController: BankCardRepeatViewInput {
orderView.title = viewModel.shopName
orderView.subtitle = viewModel.description
orderView.value = makePrice(viewModel.price)
- orderView.subvalue = nil
-
+ if let fee = viewModel.fee {
+ orderView.subvalue = "\(§Localized.fee) " + makePrice(fee)
+ } else {
+ orderView.subvalue = nil
+ }
+
maskedCardView.cardNumber = viewModel.cardMask
maskedCardView.cardLogo = viewModel.cardLogo
@@ -533,7 +537,7 @@ extension BankCardRepeatViewController: BankCardRepeatViewInput {
let linkAttributedText = NSMutableAttributedString(string: hyperText, attributes: attributes)
let linkRange = NSRange(location: 0, length: hyperText.count)
- let fakeLink = URL(string: "https://yookassa.ru")
+ let fakeLink = URL(string: "https://yookassa.ru")!
linkAttributedText.addAttribute(.link, value: fakeLink, range: linkRange)
attributedText.append(linkAttributedText)
diff --git a/YooKassaPayments/Private/Modules/BankCardRepeat/View/ViewModel/BankCardRepeatViewModel.swift b/YooKassaPayments/Private/Modules/BankCardRepeat/View/ViewModel/BankCardRepeatViewModel.swift
index 156a1c32..a9c5f280 100644
--- a/YooKassaPayments/Private/Modules/BankCardRepeat/View/ViewModel/BankCardRepeatViewModel.swift
+++ b/YooKassaPayments/Private/Modules/BankCardRepeat/View/ViewModel/BankCardRepeatViewModel.swift
@@ -2,6 +2,7 @@ struct BankCardRepeatViewModel {
let shopName: String
let description: String?
let price: PriceViewModel
+ let fee: PriceViewModel?
let cardMask: String
let cardLogo: UIImage
let terms: TermsOfService
diff --git a/YooKassaPayments/Private/Modules/LinkedCard/Interactor/LinkedCardInteractor.swift b/YooKassaPayments/Private/Modules/LinkedCard/Interactor/LinkedCardInteractor.swift
index 5d11f965..3a5e321e 100644
--- a/YooKassaPayments/Private/Modules/LinkedCard/Interactor/LinkedCardInteractor.swift
+++ b/YooKassaPayments/Private/Modules/LinkedCard/Interactor/LinkedCardInteractor.swift
@@ -94,7 +94,7 @@ extension LinkedCardInteractor: LinkedCardInteractorInput {
savePaymentMethod: savePaymentMethod,
paymentMethodType: paymentMethodType,
amount: amount,
- tmxSessionId: tmxSessionId
+ tmxSessionId: tmxSessionId.value
)
case let .failure(error):
diff --git a/YooKassaPayments/Private/Modules/LinkedCard/Presenter/LinkedCardPresenter.swift b/YooKassaPayments/Private/Modules/LinkedCard/Presenter/LinkedCardPresenter.swift
index 1a04854b..e8d0a11c 100644
--- a/YooKassaPayments/Private/Modules/LinkedCard/Presenter/LinkedCardPresenter.swift
+++ b/YooKassaPayments/Private/Modules/LinkedCard/Presenter/LinkedCardPresenter.swift
@@ -126,7 +126,7 @@ extension LinkedCardPresenter: LinkedCardViewOutput {
do {
try self.cardService.validate(csc: csc)
} catch {
- if let error = error as? CardService.ValidationError {
+ if error is CardService.ValidationError {
DispatchQueue.main.async { [weak self] in
guard let view = self?.view else { return }
view.setConfirmButtonEnabled(false)
@@ -179,7 +179,7 @@ extension LinkedCardPresenter: LinkedCardViewOutput {
do {
try self.cardService.validate(csc: csc)
} catch {
- if let error = error as? CardService.ValidationError {
+ if error is CardService.ValidationError {
DispatchQueue.main.async { [weak self] in
guard let view = self?.view else { return }
view.setCardState(.error)
diff --git a/YooKassaPayments/Private/Modules/LinkedCard/View/LinkedCardViewController.swift b/YooKassaPayments/Private/Modules/LinkedCard/View/LinkedCardViewController.swift
index ec437f23..d9f328ab 100644
--- a/YooKassaPayments/Private/Modules/LinkedCard/View/LinkedCardViewController.swift
+++ b/YooKassaPayments/Private/Modules/LinkedCard/View/LinkedCardViewController.swift
@@ -271,8 +271,6 @@ final class LinkedCardViewController: UIViewController, PlaceholderProvider {
)
}
- scrollViewHeightConstraint.priority = .defaultLow
-
let constraints = [
scrollViewHeightConstraint,
diff --git a/YooKassaPayments/Private/Modules/PaymentAuthorization/View/PaymentAuthorizationViewController.swift b/YooKassaPayments/Private/Modules/PaymentAuthorization/View/PaymentAuthorizationViewController.swift
index 64010823..c52bb36b 100644
--- a/YooKassaPayments/Private/Modules/PaymentAuthorization/View/PaymentAuthorizationViewController.swift
+++ b/YooKassaPayments/Private/Modules/PaymentAuthorization/View/PaymentAuthorizationViewController.swift
@@ -6,6 +6,10 @@ final class PaymentAuthorizationViewController: UIViewController, PlaceholderPro
// MARK: - UI properties
+ private lazy var shouldShowTitleOnNavBar: Bool = {
+ return UIScreen.main.isShort
+ }()
+
private lazy var titleLabel: UILabel = {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.styledText = §Localized.smsCodePlaceholder
@@ -97,8 +101,13 @@ final class PaymentAuthorizationViewController: UIViewController, PlaceholderPro
override func loadView() {
view = UIView()
view.setStyles(UIView.Styles.grayBackground)
+
setupView()
setupConstraints()
+
+ if shouldShowTitleOnNavBar {
+ navigationItem.title = §Localized.smsCodePlaceholder
+ }
}
override func viewDidLoad() {
@@ -113,7 +122,7 @@ final class PaymentAuthorizationViewController: UIViewController, PlaceholderPro
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
- codeControl.becomeFirstResponder()
+ _ = codeControl.becomeFirstResponder()
}
override func viewWillDisappear(_ animated: Bool) {
@@ -124,8 +133,11 @@ final class PaymentAuthorizationViewController: UIViewController, PlaceholderPro
// MARK: - Setup
private func setupView() {
+ if !shouldShowTitleOnNavBar {
+ view.addSubview(titleLabel)
+ }
+
[
- titleLabel,
codeControl,
codeErrorLabel,
descriptionLabel,
@@ -135,12 +147,20 @@ final class PaymentAuthorizationViewController: UIViewController, PlaceholderPro
}
private func setupConstraints() {
- let titleLabelTopConstraint: NSLayoutConstraint
+ let topConstraint: NSLayoutConstraint
if #available(iOS 11.0, *) {
- titleLabelTopConstraint = titleLabel.topAnchor.constraint(
- equalTo: view.safeAreaLayoutGuide.topAnchor,
- constant: Space.single / 4
- )
+ if shouldShowTitleOnNavBar {
+ topConstraint = codeControl.topAnchor.constraint(
+ equalTo: view.safeAreaLayoutGuide.topAnchor,
+ constant: 2 * Space.triple
+ )
+ } else {
+ topConstraint = titleLabel.topAnchor.constraint(
+ equalTo: view.safeAreaLayoutGuide.topAnchor,
+ constant: Space.single / 4
+ )
+ }
+
resendButtonBottomConstraint = resendCodeButton.bottomAnchor.constraint(
equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: -Space.double
@@ -149,10 +169,18 @@ final class PaymentAuthorizationViewController: UIViewController, PlaceholderPro
equalTo: view.safeAreaLayoutGuide.bottomAnchor
)
} else {
- titleLabelTopConstraint = titleLabel.topAnchor.constraint(
- equalTo: topLayoutGuide.bottomAnchor,
- constant: Space.single / 4
- )
+ if shouldShowTitleOnNavBar {
+ topConstraint = codeControl.topAnchor.constraint(
+ equalTo: topLayoutGuide.bottomAnchor,
+ constant: 2 * Space.triple
+ )
+ } else {
+ topConstraint = titleLabel.topAnchor.constraint(
+ equalTo: topLayoutGuide.bottomAnchor,
+ constant: Space.single / 4
+ )
+ }
+
resendButtonBottomConstraint = resendCodeButton.bottomAnchor.constraint(
equalTo: bottomLayoutGuide.topAnchor,
constant: -Space.double
@@ -162,21 +190,8 @@ final class PaymentAuthorizationViewController: UIViewController, PlaceholderPro
)
}
- let constraints = [
- titleLabelTopConstraint,
- titleLabel.leadingAnchor.constraint(
- equalTo: view.leadingAnchor,
- constant: Space.double
- ),
- titleLabel.trailingAnchor.constraint(
- equalTo: view.trailingAnchor,
- constant: -Space.double
- ),
-
- codeControl.topAnchor.constraint(
- equalTo: titleLabel.bottomAnchor,
- constant: 2 * Space.triple
- ),
+ var constraints = [
+ topConstraint,
codeControl.centerXAnchor.constraint(equalTo: view.centerXAnchor),
codeErrorLabel.topAnchor.constraint(
@@ -217,6 +232,25 @@ final class PaymentAuthorizationViewController: UIViewController, PlaceholderPro
placeholderView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
placeholderViewBottomConstraint,
]
+
+ if !shouldShowTitleOnNavBar {
+ constraints += [
+ titleLabel.leadingAnchor.constraint(
+ equalTo: view.leadingAnchor,
+ constant: Space.double
+ ),
+ titleLabel.trailingAnchor.constraint(
+ equalTo: view.trailingAnchor,
+ constant: -Space.double
+ ),
+
+ codeControl.topAnchor.constraint(
+ equalTo: titleLabel.bottomAnchor,
+ constant: 2 * Space.triple
+ ),
+ ]
+ }
+
NSLayoutConstraint.activate(constraints)
}
diff --git a/YooKassaPayments/Private/Modules/PaymentMethods/Assembly/PaymentMethodsAssembly.swift b/YooKassaPayments/Private/Modules/PaymentMethods/Assembly/PaymentMethodsAssembly.swift
index d3428c36..d059651b 100644
--- a/YooKassaPayments/Private/Modules/PaymentMethods/Assembly/PaymentMethodsAssembly.swift
+++ b/YooKassaPayments/Private/Modules/PaymentMethods/Assembly/PaymentMethodsAssembly.swift
@@ -1,4 +1,5 @@
import UIKit.UIViewController
+import MoneyAuth
import YooMoneyCoreApi
enum PaymentMethodsAssembly {
@@ -19,9 +20,12 @@ enum PaymentMethodsAssembly {
let moneyAuthCustomization = MoneyAuthAssembly.makeMoneyAuthCustomization()
let paymentMethodViewModelFactory = PaymentMethodViewModelFactoryAssembly.makeFactory()
+ let priceViewModelFactory = PriceViewModelFactoryAssembly.makeFactory()
let presenter = PaymentMethodsPresenter(
isLogoVisible: inputData.tokenizationSettings.showYooKassaLogo,
paymentMethodViewModelFactory: paymentMethodViewModelFactory,
+ applicationScheme: inputData.applicationScheme,
+ priceViewModelFactory: priceViewModelFactory,
clientApplicationKey: inputData.clientApplicationKey,
applePayMerchantIdentifier: inputData.applePayMerchantIdentifier,
testModeSettings: inputData.testModeSettings,
@@ -51,18 +55,27 @@ enum PaymentMethodsAssembly {
let analyticsService = AnalyticsServiceAssembly.makeService(
isLoggingEnabled: inputData.isLoggingEnabled
)
+ let accountService = AccountServiceFactory.makeService(
+ config: moneyAuthConfig
+ )
let analyticsProvider = AnalyticsProviderAssembly.makeProvider(
testModeSettings: inputData.testModeSettings
)
let threatMetrixService = ThreatMetrixServiceFactory.makeService()
let amountNumberFormatter = AmountNumberFormatterAssembly.makeAmountNumberFormatter()
+ let appDataTransferMediator = AppDataTransferMediatorFactory.makeMediator(
+ config: moneyAuthConfig
+ )
+
let interactor = PaymentMethodsInteractor(
paymentService: paymentService,
authorizationService: authorizationService,
analyticsService: analyticsService,
+ accountService: accountService,
analyticsProvider: analyticsProvider,
threatMetrixService: threatMetrixService,
amountNumberFormatter: amountNumberFormatter,
+ appDataTransferMediator: appDataTransferMediator,
clientApplicationKey: inputData.clientApplicationKey,
gatewayId: inputData.gatewayId,
amount: inputData.amount,
diff --git a/YooKassaPayments/Private/Modules/PaymentMethods/Interactor/PaymentMethodsInteractor.swift b/YooKassaPayments/Private/Modules/PaymentMethods/Interactor/PaymentMethodsInteractor.swift
index 94677757..6c30f179 100644
--- a/YooKassaPayments/Private/Modules/PaymentMethods/Interactor/PaymentMethodsInteractor.swift
+++ b/YooKassaPayments/Private/Modules/PaymentMethods/Interactor/PaymentMethodsInteractor.swift
@@ -12,9 +12,11 @@ class PaymentMethodsInteractor {
private let paymentService: PaymentService
private let authorizationService: AuthorizationService
private let analyticsService: AnalyticsService
+ private let accountService: AccountService
private let analyticsProvider: AnalyticsProvider
private let threatMetrixService: ThreatMetrixService
private let amountNumberFormatter: AmountNumberFormatter
+ private let appDataTransferMediator: AppDataTransferMediator
private let clientApplicationKey: String
private let gatewayId: String?
@@ -27,9 +29,11 @@ class PaymentMethodsInteractor {
paymentService: PaymentService,
authorizationService: AuthorizationService,
analyticsService: AnalyticsService,
+ accountService: AccountService,
analyticsProvider: AnalyticsProvider,
threatMetrixService: ThreatMetrixService,
amountNumberFormatter: AmountNumberFormatter,
+ appDataTransferMediator: AppDataTransferMediator,
clientApplicationKey: String,
gatewayId: String?,
amount: Amount,
@@ -38,9 +42,11 @@ class PaymentMethodsInteractor {
self.paymentService = paymentService
self.authorizationService = authorizationService
self.analyticsService = analyticsService
+ self.accountService = accountService
self.analyticsProvider = analyticsProvider
self.threatMetrixService = threatMetrixService
self.amountNumberFormatter = amountNumberFormatter
+ self.appDataTransferMediator = appDataTransferMediator
self.clientApplicationKey = clientApplicationKey
self.gatewayId = gatewayId
@@ -94,6 +100,34 @@ extension PaymentMethodsInteractor: PaymentMethodsInteractorInput {
}
}
+ func fetchAccount(
+ oauthToken: String
+ ) {
+ accountService.fetchAccount(
+ oauthToken: oauthToken
+ ) { [weak self] in
+ guard let output = self?.output else { return }
+ $0.map {
+ output.didFetchAccount($0)
+ }.mapLeft {
+ output.didFailFetchAccount($0)
+ }
+ }
+ }
+
+ func decryptCryptogram(
+ _ cryptogram: String
+ ) {
+ appDataTransferMediator.decryptData(cryptogram) { [weak self] in
+ guard let output = self?.output else { return }
+ $0.map {
+ output.didDecryptCryptogram($0)
+ }.mapLeft {
+ output.didFailDecryptCryptogram($0)
+ }
+ }
+ }
+
func getWalletDisplayName() -> String? {
return authorizationService.getWalletDisplayName()
}
@@ -140,7 +174,7 @@ extension PaymentMethodsInteractor {
paymentData: paymentData,
savePaymentMethod: savePaymentMethod,
amount: amount,
- tmxSessionId: tmxSessionId
+ tmxSessionId: tmxSessionId.value
)
case let .failure(error):
diff --git a/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsInteractorIO.swift b/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsInteractorIO.swift
index e01d53a2..3b66dc99 100644
--- a/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsInteractorIO.swift
+++ b/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsInteractorIO.swift
@@ -8,6 +8,14 @@ protocol PaymentMethodsInteractorInput: AnalyticsTrack, AnalyticsProvider {
moneyCenterAuthToken: String
)
+ func fetchAccount(
+ oauthToken: String
+ )
+
+ func decryptCryptogram(
+ _ cryptogram: String
+ )
+
func getWalletDisplayName() -> String?
func setAccount(_ account: UserAccount)
@@ -40,6 +48,20 @@ protocol PaymentMethodsInteractorOutput: class {
_ error: Error
)
+ func didFetchAccount(
+ _ account: UserAccount
+ )
+ func didFailFetchAccount(
+ _ error: Error
+ )
+
+ func didDecryptCryptogram(
+ _ token: String
+ )
+ func didFailDecryptCryptogram(
+ _ error: Error
+ )
+
func didTokenizeApplePay(
_ token: Tokens
)
diff --git a/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsModuleIO.swift b/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsModuleIO.swift
index 97ebc5eb..b6c22e56 100644
--- a/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsModuleIO.swift
+++ b/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsModuleIO.swift
@@ -1,6 +1,7 @@
import YooKassaPaymentsApi
struct PaymentMethodsModuleInputData {
+ let applicationScheme: String?
let clientApplicationKey: String
let applePayMerchantIdentifier: String?
let gatewayId: String?
@@ -18,4 +19,8 @@ struct PaymentMethodsModuleInputData {
let cardScanning: CardScanning?
}
-protocol PaymentMethodsModuleInput: SheetViewModuleOutput {}
+protocol PaymentMethodsModuleInput: SheetViewModuleOutput {
+ func authorizeInYooMoney(
+ with cryptogram: String
+ )
+}
diff --git a/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsRouterIO.swift b/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsRouterIO.swift
index 476679cd..658918af 100644
--- a/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsRouterIO.swift
+++ b/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsRouterIO.swift
@@ -41,6 +41,11 @@ protocol PaymentMethodsRouterInput: class {
inputData: SberbankModuleInputData,
moduleOutput: SberbankModuleOutput
)
+
+ func openSberpayModule(
+ inputData: SberpayModuleInputData,
+ moduleOutput: SberpayModuleOutput
+ )
func openBankCardModule(
inputData: BankCardModuleInputData,
diff --git a/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsViewIO.swift b/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsViewIO.swift
index 809c13ca..c3346c2f 100644
--- a/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsViewIO.swift
+++ b/YooKassaPayments/Private/Modules/PaymentMethods/PaymentMethodsViewIO.swift
@@ -23,6 +23,7 @@ protocol PaymentMethodsViewInput: ActivityIndicatorFullViewPresenting, Notificat
protocol PaymentMethodsViewOutput: ActionTitleTextDialogDelegate {
func setupView()
func viewDidAppear()
+ func applicationDidBecomeActive()
func numberOfRows() -> Int
func viewModelForRow(at indexPath: IndexPath) -> PaymentMethodViewModel?
func didSelect(at indexPath: IndexPath)
diff --git a/YooKassaPayments/Private/Modules/PaymentMethods/Presenter/PaymentMethodsPresenter.swift b/YooKassaPayments/Private/Modules/PaymentMethods/Presenter/PaymentMethodsPresenter.swift
index f275aa5e..5d5b860d 100644
--- a/YooKassaPayments/Private/Modules/PaymentMethods/Presenter/PaymentMethodsPresenter.swift
+++ b/YooKassaPayments/Private/Modules/PaymentMethods/Presenter/PaymentMethodsPresenter.swift
@@ -10,6 +10,11 @@ final class PaymentMethodsPresenter: NSObject {
case cancel
}
+ private enum App2AppState {
+ case idle
+ case yoomoney
+ }
+
// MARK: - VIPER
var interactor: PaymentMethodsInteractorInput!
@@ -24,7 +29,9 @@ final class PaymentMethodsPresenter: NSObject {
private let isLogoVisible: Bool
private let paymentMethodViewModelFactory: PaymentMethodViewModelFactory
-
+ private let priceViewModelFactory: PriceViewModelFactory
+
+ private let applicationScheme: String?
private let clientApplicationKey: String
private let applePayMerchantIdentifier: String?
private let testModeSettings: TestModeSettings?
@@ -47,6 +54,8 @@ final class PaymentMethodsPresenter: NSObject {
init(
isLogoVisible: Bool,
paymentMethodViewModelFactory: PaymentMethodViewModelFactory,
+ applicationScheme: String?,
+ priceViewModelFactory: PriceViewModelFactory,
clientApplicationKey: String,
applePayMerchantIdentifier: String?,
testModeSettings: TestModeSettings?,
@@ -64,7 +73,9 @@ final class PaymentMethodsPresenter: NSObject {
) {
self.isLogoVisible = isLogoVisible
self.paymentMethodViewModelFactory = paymentMethodViewModelFactory
-
+ self.priceViewModelFactory = priceViewModelFactory
+
+ self.applicationScheme = applicationScheme
self.clientApplicationKey = clientApplicationKey
self.applePayMerchantIdentifier = applePayMerchantIdentifier
self.testModeSettings = testModeSettings
@@ -96,6 +107,8 @@ final class PaymentMethodsPresenter: NSObject {
}()
private var shouldReloadOnViewDidAppear = false
+ private var moneyCenterAuthToken: String?
+ private var app2AppState: App2AppState = .idle
// MARK: - Apple Pay properties
@@ -127,6 +140,14 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
}
}
+ func applicationDidBecomeActive() {
+ if app2AppState == .idle,
+ paymentMethods?.count == 1,
+ paymentMethods?.first?.paymentMethodType == .yooMoney {
+ didFinish(module: self, error: nil)
+ }
+ }
+
func numberOfRows() -> Int {
viewModels.count
}
@@ -170,7 +191,12 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
openYooMoneyAuthorization()
case let paymentOption where paymentOption.paymentMethodType == .sberbank:
- openSberbankModule(paymentOption: paymentOption, needReplace: needReplace)
+ if shouldOpenSberpay(paymentOption),
+ let returnUrl = makeSberpayReturnUrl() {
+ openSberpayModule(paymentOption: paymentOption, needReplace: needReplace, returnUrl: returnUrl)
+ } else {
+ openSberbankModule(paymentOption: paymentOption, needReplace: needReplace)
+ }
case let paymentOption where paymentOption.paymentMethodType == .applePay:
openApplePay(paymentOption: paymentOption, needReplace: needReplace)
@@ -184,7 +210,7 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
}
private func openYooMoneyAuthorization() {
- if self.testModeSettings != nil {
+ if testModeSettings != nil {
view?.showActivity()
DispatchQueue.global().async {
self.interactor.fetchYooMoneyPaymentMethods(
@@ -192,30 +218,38 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
)
}
} else {
- do {
- moneyAuthCoordinator = try router.presentYooMoneyAuthorizationModule(
- config: moneyAuthConfig,
- customization: moneyAuthCustomization,
- output: self
- )
- let event = AnalyticsEvent.userStartAuthorization(
- sdkVersion: Bundle.frameworkVersion
- )
- interactor.trackEvent(event)
- } catch {
- DispatchQueue.main.async { [weak self] in
- guard let self = self else { return }
- self.view?.showActivity()
- DispatchQueue.global().async { [weak self] in
- self?.interactor.fetchPaymentMethods()
- }
+ if shouldOpenYooMoneyApp2App() {
+ openYooMoneyApp2App()
+ } else {
+ openMoneyAuth()
+ }
+ }
+ }
+
+ private func openMoneyAuth() {
+ do {
+ moneyAuthCoordinator = try router.presentYooMoneyAuthorizationModule(
+ config: moneyAuthConfig,
+ customization: moneyAuthCustomization,
+ output: self
+ )
+ let event = AnalyticsEvent.userStartAuthorization(
+ sdkVersion: Bundle.frameworkVersion
+ )
+ interactor.trackEvent(event)
+ } catch {
+ DispatchQueue.main.async { [weak self] in
+ guard let self = self else { return }
+ self.view?.showActivity()
+ DispatchQueue.global().async { [weak self] in
+ self?.interactor.fetchPaymentMethods()
}
-
- let event = AnalyticsEvent.userCancelAuthorization(
- sdkVersion: Bundle.frameworkVersion
- )
- interactor.trackEvent(event)
}
+
+ let event = AnalyticsEvent.userCancelAuthorization(
+ sdkVersion: Bundle.frameworkVersion
+ )
+ interactor.trackEvent(event)
}
}
@@ -234,6 +268,8 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
savePaymentMethod,
initialState: initialSavePaymentMethod
)
+ let priceViewModel = priceViewModelFactory.makeAmountPriceViewModel(paymentOption)
+ let feeViewModel = priceViewModelFactory.makeFeePriceViewModel(paymentOption)
let inputData = YooMoneyModuleInputData(
clientApplicationKey: clientApplicationKey,
testModeSettings: testModeSettings,
@@ -242,8 +278,8 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
tokenizationSettings: tokenizationSettings,
shopName: shopName,
purchaseDescription: purchaseDescription,
- price: makePriceViewModel(paymentOption),
- fee: makeFeePriceViewModel(paymentOption),
+ price: priceViewModel,
+ fee: feeViewModel,
paymentMethod: paymentMethod,
paymentOption: paymentOption,
termsOfService: termsOfService,
@@ -264,6 +300,8 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
needReplace: Bool
) {
let initialSavePaymentMethod = makeInitialSavePaymentMethod(savePaymentMethod)
+ let priceViewModel = priceViewModelFactory.makeAmountPriceViewModel(paymentOption)
+ let feeViewModel = priceViewModelFactory.makeFeePriceViewModel(paymentOption)
let inputData = LinkedCardModuleInputData(
clientApplicationKey: clientApplicationKey,
testModeSettings: testModeSettings,
@@ -272,8 +310,8 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
tokenizationSettings: tokenizationSettings,
shopName: shopName,
purchaseDescription: purchaseDescription,
- price: makePriceViewModel(paymentOption),
- fee: makeFeePriceViewModel(paymentOption),
+ price: priceViewModel,
+ fee: feeViewModel,
paymentOption: paymentOption,
termsOfService: termsOfService,
returnUrl: returnUrl,
@@ -304,6 +342,8 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
savePaymentMethod,
initialState: initialSavePaymentMethod
)
+ let priceViewModel = priceViewModelFactory.makeAmountPriceViewModel(paymentOption)
+ let feeViewModel = priceViewModelFactory.makeFeePriceViewModel(paymentOption)
let inputData = ApplePayContractModuleInputData(
clientApplicationKey: clientApplicationKey,
testModeSettings: testModeSettings,
@@ -311,8 +351,8 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
tokenizationSettings: tokenizationSettings,
shopName: shopName,
purchaseDescription: purchaseDescription,
- price: makePriceViewModel(paymentOption),
- fee: makeFeePriceViewModel(paymentOption),
+ price: priceViewModel,
+ fee: feeViewModel,
paymentOption: paymentOption,
termsOfService: termsOfService,
merchantIdentifier: applePayMerchantIdentifier,
@@ -346,11 +386,8 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
paymentOption: PaymentOption,
needReplace: Bool
) {
- let paymentMethod = paymentMethodViewModelFactory.makePaymentMethodViewModel(
- paymentOption: paymentOption
- )
- let priceViewModel = makePriceViewModel(paymentOption)
- let feeViewModel = makeFeePriceViewModel(paymentOption)
+ let priceViewModel = priceViewModelFactory.makeAmountPriceViewModel(paymentOption)
+ let feeViewModel = priceViewModelFactory.makeFeePriceViewModel(paymentOption)
let inputData = SberbankModuleInputData(
paymentOption: paymentOption,
clientApplicationKey: clientApplicationKey,
@@ -371,12 +408,39 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
)
}
+ private func openSberpayModule(
+ paymentOption: PaymentOption,
+ needReplace: Bool,
+ returnUrl: String
+ ) {
+ let priceViewModel = priceViewModelFactory.makeAmountPriceViewModel(paymentOption)
+ let feeViewModel = priceViewModelFactory.makeFeePriceViewModel(paymentOption)
+ let inputData = SberpayModuleInputData(
+ paymentOption: paymentOption,
+ clientApplicationKey: clientApplicationKey,
+ tokenizationSettings: tokenizationSettings,
+ testModeSettings: testModeSettings,
+ isLoggingEnabled: isLoggingEnabled,
+ shopName: shopName,
+ purchaseDescription: purchaseDescription,
+ priceViewModel: priceViewModel,
+ feeViewModel: feeViewModel,
+ termsOfService: termsOfService,
+ returnUrl: returnUrl,
+ isBackBarButtonHidden: needReplace
+ )
+ router.openSberpayModule(
+ inputData: inputData,
+ moduleOutput: self
+ )
+ }
+
private func openBankCardModule(
paymentOption: PaymentOption,
needReplace: Bool
) {
- let priceViewModel = makePriceViewModel(paymentOption)
- let feeViewModel = makeFeePriceViewModel(paymentOption)
+ let priceViewModel = priceViewModelFactory.makeAmountPriceViewModel(paymentOption)
+ let feeViewModel = priceViewModelFactory.makeFeePriceViewModel(paymentOption)
let initialSavePaymentMethod = makeInitialSavePaymentMethod(savePaymentMethod)
let savePaymentMethodViewModel = SavePaymentMethodViewModelFactory.makeSavePaymentMethodViewModel(
paymentOption,
@@ -405,6 +469,86 @@ extension PaymentMethodsPresenter: PaymentMethodsViewOutput {
moduleOutput: self
)
}
+
+ private func shouldOpenSberpay(
+ _ paymentOption: PaymentOption
+ ) -> Bool {
+ guard let confirmationTypes = paymentOption.confirmationTypes,
+ confirmationTypes.contains(.mobileApplication) else {
+ return false
+ }
+
+ return UIApplication.shared.canOpenURL(Constants.sberpayUrlScheme)
+ }
+
+ private func shouldOpenYooMoneyApp2App() -> Bool {
+ guard let url = URL(string: Constants.YooMoneyApp2App.scheme) else {
+ return false
+ }
+
+ return UIApplication.shared.canOpenURL(url)
+ }
+
+ private func openYooMoneyApp2App() {
+ guard let clientId = moneyAuthClientId,
+ let redirectUri = makeYooMoneyExchangeRedirectUri() else {
+ return
+ }
+
+ let scope = makeYooMoneyApp2AppScope()
+ let fullPathUrl = Constants.YooMoneyApp2App.scheme
+ + "\(Constants.YooMoneyApp2App.host)/"
+ + "\(Constants.YooMoneyApp2App.firstPath)?"
+ + "\(Constants.YooMoneyApp2App.clientId)=\(clientId)&"
+ + "\(Constants.YooMoneyApp2App.scope)=\(scope)&"
+ + "\(Constants.YooMoneyApp2App.redirectUri)=\(redirectUri)"
+
+ guard let url = URL(string: fullPathUrl) else {
+ return
+ }
+
+ DispatchQueue.main.async {
+ UIApplication.shared.open(
+ url,
+ options: [:],
+ completionHandler: nil
+ )
+ }
+ }
+
+ private func makeYooMoneyExchangeRedirectUri() -> String? {
+ guard let applicationScheme = applicationScheme else {
+ assertionFailure("Application scheme should be")
+ return nil
+ }
+
+ return applicationScheme
+ + DeepLinkFactory.YooMoney.host
+ + "/"
+ + DeepLinkFactory.YooMoney.exchange.firstPath
+ + "?"
+ + DeepLinkFactory.YooMoney.exchange.cryptogram
+ + "="
+ }
+
+ private func makeYooMoneyApp2AppScope() -> String {
+ return [
+ Constants.YooMoneyApp2App.Scope.accountInfo,
+ Constants.YooMoneyApp2App.Scope.balance,
+ ].joined(separator: ",")
+ }
+
+ private func makeSberpayReturnUrl() -> String? {
+ guard let applicationScheme = applicationScheme else {
+ assertionFailure("Application scheme should be")
+ return nil
+ }
+
+ return applicationScheme
+ + DeepLinkFactory.invoicingHost
+ + "/"
+ + DeepLinkFactory.sberpayPath
+ }
}
// MARK: - ActionTitleTextDialogDelegate
@@ -427,6 +571,26 @@ extension PaymentMethodsPresenter: ActionTitleTextDialogDelegate {
// MARK: - PaymentMethodsModuleInput
extension PaymentMethodsPresenter: PaymentMethodsModuleInput {
+ func authorizeInYooMoney(
+ with cryptogram: String
+ ) {
+ guard !cryptogram.isEmpty else {
+ return
+ }
+ app2AppState = .yoomoney
+
+ DispatchQueue.main.async { [weak self] in
+ guard let self = self,
+ let view = self.view else { return }
+ view.showActivity()
+
+ DispatchQueue.global().async { [weak self] in
+ guard let self = self else { return }
+ self.interactor.decryptCryptogram(cryptogram)
+ }
+ }
+ }
+
func didFinish(
on module: TokenizationModuleInput,
with error: YooKassaPaymentsError?
@@ -481,8 +645,7 @@ extension PaymentMethodsPresenter: PaymentMethodsInteractorOutput {
paymentMethods.count == 1 {
let needReplace = self.paymentMethods?.count == 1
DispatchQueue.main.async { [weak self] in
- guard let self = self,
- let view = self.view else { return }
+ guard let self = self else { return }
self.openYooMoneyWallet(
paymentOption: paymentOption,
needReplace: needReplace
@@ -508,6 +671,61 @@ extension PaymentMethodsPresenter: PaymentMethodsInteractorOutput {
presentError(error)
}
+ func didFetchAccount(
+ _ account: UserAccount
+ ) {
+ guard let moneyCenterAuthToken = moneyCenterAuthToken else {
+ return
+ }
+ interactor.setAccount(account)
+ interactor.fetchYooMoneyPaymentMethods(
+ moneyCenterAuthToken: moneyCenterAuthToken
+ )
+ }
+
+ func didFailFetchAccount(
+ _ error: Error
+ ) {
+ guard let moneyCenterAuthToken = moneyCenterAuthToken else {
+ return
+ }
+ interactor.fetchYooMoneyPaymentMethods(
+ moneyCenterAuthToken: moneyCenterAuthToken
+ )
+ }
+
+ func didDecryptCryptogram(
+ _ token: String
+ ) {
+ moneyCenterAuthToken = token
+ DispatchQueue.global().async { [weak self] in
+ guard let self = self else { return }
+ let event: AnalyticsEvent = .actionMoneyAuthLogin(
+ scheme: .yoomoneyApp,
+ status: .success,
+ sdkVersion: Bundle.frameworkVersion
+ )
+ self.interactor.trackEvent(event)
+ self.interactor.fetchAccount(oauthToken: token)
+ }
+ }
+
+ func didFailDecryptCryptogram(
+ _ error: Error
+ ) {
+ let event: AnalyticsEvent = .actionMoneyAuthLogin(
+ scheme: .yoomoneyApp,
+ status: .fail(error.localizedDescription),
+ sdkVersion: Bundle.frameworkVersion
+ )
+ interactor.trackEvent(event)
+ DispatchQueue.main.async { [weak self] in
+ guard let view = self?.view else { return }
+ view.hideActivity()
+ view.presentError(with: §CommonLocalized.Error.unknown)
+ }
+ }
+
func didTokenizeApplePay(
_ token: Tokens
) {
@@ -648,29 +866,11 @@ extension PaymentMethodsPresenter: AuthorizationCoordinatorDelegate {
moneyCenterAuthToken: token
)
- let event: AnalyticsEvent
- switch authorizationProcess {
- case .login:
- event = .userSuccessAuthorization(
- moneyAuthProcessType: .login,
- sdkVersion: Bundle.frameworkVersion
- )
- case .enrollment:
- event = .userSuccessAuthorization(
- moneyAuthProcessType: .enrollment,
- sdkVersion: Bundle.frameworkVersion
- )
- case .migration:
- event = .userSuccessAuthorization(
- moneyAuthProcessType: .migration,
- sdkVersion: Bundle.frameworkVersion
- )
- case .none:
- event = .userSuccessAuthorization(
- moneyAuthProcessType: .unknown,
- sdkVersion: Bundle.frameworkVersion
- )
- }
+ let event: AnalyticsEvent = .actionMoneyAuthLogin(
+ scheme: .moneyAuthSdk,
+ status: .success,
+ sdkVersion: Bundle.frameworkVersion
+ )
self.interactor.trackEvent(event)
}
}
@@ -703,8 +903,9 @@ extension PaymentMethodsPresenter: AuthorizationCoordinatorDelegate {
) {
self.moneyAuthCoordinator = nil
- let event = AnalyticsEvent.userFailedAuthorization(
- error: error.localizedDescription,
+ let event: AnalyticsEvent = .actionMoneyAuthLogin(
+ scheme: .moneyAuthSdk,
+ status: .fail(error.localizedDescription),
sdkVersion: Bundle.frameworkVersion
)
interactor.trackEvent(event)
@@ -741,10 +942,23 @@ extension PaymentMethodsPresenter: YooMoneyModuleOutput {
) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
- self.router.closeYooMoneyModule()
- self.view?.showActivity()
- DispatchQueue.global().async { [weak self] in
- self?.interactor.fetchPaymentMethods()
+ self.moneyCenterAuthToken = nil
+ self.app2AppState = .idle
+ let condition: (PaymentOption) -> Bool = {
+ $0 is PaymentInstrumentYooMoneyLinkedBankCard
+ || $0 is PaymentInstrumentYooMoneyWallet
+ || $0.paymentMethodType == .yooMoney
+ }
+ if let paymentMethods = self.paymentMethods,
+ paymentMethods.allSatisfy(condition) {
+ self.didFinish(module: self, error: nil)
+ } else {
+ self.shouldReloadOnViewDidAppear = false
+ self.router.closeYooMoneyModule()
+ self.view?.showActivity()
+ DispatchQueue.global().async { [weak self] in
+ self?.interactor.fetchPaymentMethods()
+ }
}
}
}
@@ -797,10 +1011,10 @@ extension PaymentMethodsPresenter: ApplePayModuleOutput {
let message = §Localized.applePayUnavailableTitle
if self.paymentMethods?.count == 1 {
- self.view?.hideActivity()
- self.view?.showPlaceholder(message: message)
+ view.hideActivity()
+ view.showPlaceholder(message: message)
} else {
- self.view?.presentError(with: message)
+ view.presentError(with: message)
}
}
}
@@ -886,6 +1100,22 @@ extension PaymentMethodsPresenter: SberbankModuleOutput {
}
}
+// MARK: - SberpayModuleOutput
+
+extension PaymentMethodsPresenter: SberpayModuleOutput {
+ func sberpayModule(
+ _ module: SberpayModuleInput,
+ didTokenize token: Tokens,
+ paymentMethodType: PaymentMethodType
+ ) {
+ didTokenize(
+ tokens: token,
+ paymentMethodType: paymentMethodType,
+ scheme: .sberpay
+ )
+ }
+}
+
// MARK: - BankCardModuleOutput
extension PaymentMethodsPresenter: BankCardModuleOutput {
@@ -912,7 +1142,8 @@ extension PaymentMethodsPresenter: TokenizationModuleInput {
let inputData = CardSecModuleInputData(
requestUrl: requestUrl,
redirectUrl: GlobalConstants.returnUrl,
- isLoggingEnabled: isLoggingEnabled
+ isLoggingEnabled: isLoggingEnabled,
+ isConfirmation: false
)
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
@@ -922,18 +1153,60 @@ extension PaymentMethodsPresenter: TokenizationModuleInput {
)
}
}
+
+ func startConfirmationProcess(
+ confirmationUrl: String,
+ paymentMethodType: PaymentMethodType
+ ) {
+ switch paymentMethodType {
+ case .sberbank:
+ guard let url = URL(string: confirmationUrl) else {
+ return
+ }
+
+ DispatchQueue.main.async {
+ UIApplication.shared.open(
+ url,
+ options: [:],
+ completionHandler: nil
+ )
+ }
+
+ default:
+ let inputData = CardSecModuleInputData(
+ requestUrl: confirmationUrl,
+ redirectUrl: GlobalConstants.returnUrl,
+ isLoggingEnabled: isLoggingEnabled,
+ isConfirmation: true
+ )
+ DispatchQueue.main.async { [weak self] in
+ guard let self = self else { return }
+ self.router.openCardSecModule(
+ inputData: inputData,
+ moduleOutput: self
+ )
+ }
+ }
+ }
}
// MARK: - CardSecModuleOutput
extension PaymentMethodsPresenter: CardSecModuleOutput {
func didSuccessfullyPassedCardSec(
- on module: CardSecModuleInput
+ on module: CardSecModuleInput,
+ isConfirmation: Bool
) {
interactor.stopAnalyticsService()
- tokenizationModuleOutput?.didSuccessfullyPassedCardSec(
- on: self
- )
+ if isConfirmation {
+ tokenizationModuleOutput?.didSuccessfullyConfirmation(
+ paymentMethodType: .bankCard
+ )
+ } else {
+ tokenizationModuleOutput?.didSuccessfullyPassedCardSec(
+ on: self
+ )
+ }
}
func didPressCloseButton(
@@ -986,6 +1259,26 @@ private extension PaymentMethodsPresenter {
private extension PaymentMethodsPresenter {
enum Constants {
static let dismissApplePayTimeout: TimeInterval = 0.5
+
+ // swiftlint:disable:next force_unwrapping
+ static let sberpayUrlScheme = URL(string: "sberpay://")!
+
+ enum YooMoneyApp2App {
+ // yoomoneyauth://app2app/exchange?clientId={clientId}&scope={scope}&redirect_uri={redirect_uri}
+ // swiftlint:disable:next force_unwrapping
+ static let scheme = "yoomoneyauth://"
+ static let host = "app2app"
+ static let firstPath = "exchange"
+ static let clientId = "clientId"
+ static let scope = "scope"
+ static let redirectUri = "redirect_uri"
+
+ enum Scope {
+ static let accountInfo = "user_auth_center:account_info"
+ static let balance = "wallet:balance"
+ }
+
+ }
}
}
@@ -1004,57 +1297,6 @@ private extension PaymentMethodsPresenter {
// MARK: - Private global helpers
-private func makePriceViewModel(
- _ paymentOption: PaymentOption
-) -> PriceViewModel {
- let amountString = paymentOption.charge.value.description
- var integerPart = ""
- var fractionalPart = ""
-
- if let separatorIndex = amountString.firstIndex(of: ".") {
- integerPart = String(amountString[amountString.startIndex.. PriceViewModel? {
- guard let fee = paymentOption.fee,
- let service = fee.service else { return nil }
-
- let amountString = service.charge.value.description
- var integerPart = ""
- var fractionalPart = ""
-
- if let separatorIndex = amountString.firstIndex(of: ".") {
- integerPart = String(amountString[amountString.startIndex.. Bool {
diff --git a/YooKassaPayments/Private/Modules/PaymentMethods/Router/PaymentMethodsRouter.swift b/YooKassaPayments/Private/Modules/PaymentMethods/Router/PaymentMethodsRouter.swift
index 4933d8d6..e4d9469a 100644
--- a/YooKassaPayments/Private/Modules/PaymentMethods/Router/PaymentMethodsRouter.swift
+++ b/YooKassaPayments/Private/Modules/PaymentMethods/Router/PaymentMethodsRouter.swift
@@ -126,6 +126,20 @@ extension PaymentMethodsRouter: PaymentMethodsRouterInput {
animated: true
)
}
+
+ func openSberpayModule(
+ inputData: SberpayModuleInputData,
+ moduleOutput: SberpayModuleOutput
+ ) {
+ let viewController = SberpayAssembly.makeModule(
+ inputData: inputData,
+ moduleOutput: moduleOutput
+ )
+ transitionHandler?.push(
+ viewController,
+ animated: true
+ )
+ }
func openBankCardModule(
inputData: BankCardModuleInputData,
diff --git a/YooKassaPayments/Private/Modules/PaymentMethods/View/PaymentMethodsViewController.swift b/YooKassaPayments/Private/Modules/PaymentMethods/View/PaymentMethodsViewController.swift
index 13df1eff..e5bfb12a 100644
--- a/YooKassaPayments/Private/Modules/PaymentMethods/View/PaymentMethodsViewController.swift
+++ b/YooKassaPayments/Private/Modules/PaymentMethods/View/PaymentMethodsViewController.swift
@@ -74,6 +74,7 @@ final class PaymentMethodsViewController: UIViewController, PlaceholderProvider
view.backgroundColor = .clear
setupView()
setupConstraints()
+ setupObserver()
}
override func viewDidLoad() {
@@ -116,6 +117,15 @@ final class PaymentMethodsViewController: UIViewController, PlaceholderProvider
leftItem.text = §Localized.paymentMethods
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: leftItem)
}
+
+ private func setupObserver() {
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(didBecomeActiveNotification(_:)),
+ name: UIApplication.didBecomeActiveNotification,
+ object: nil
+ )
+ }
// MARK: - Configuring the View’s Layout Behavior
@@ -145,6 +155,13 @@ final class PaymentMethodsViewController: UIViewController, PlaceholderProvider
)
}
}
+
+ // MARK: - Actions
+
+ @objc
+ private func didBecomeActiveNotification(_ notification: Notification) {
+ output.applicationDidBecomeActive()
+ }
}
// MARK: - UITableViewDataSource
diff --git a/YooKassaPayments/Private/Modules/Sberbank/Interactor/SberbankInteractor.swift b/YooKassaPayments/Private/Modules/Sberbank/Interactor/SberbankInteractor.swift
index 9002dcb9..4c54be81 100644
--- a/YooKassaPayments/Private/Modules/Sberbank/Interactor/SberbankInteractor.swift
+++ b/YooKassaPayments/Private/Modules/Sberbank/Interactor/SberbankInteractor.swift
@@ -54,8 +54,8 @@ extension SberbankInteractor: SberbankInteractorInput {
confirmation: confirmation,
savePaymentMethod: false,
amount: self.amount,
- tmxSessionId: tmxSessionId
- ) { [weak self] result in
+ tmxSessionId: tmxSessionId.value
+ ) { result in
switch result {
case .success(let data):
output.didTokenize(data)
diff --git a/YooKassaPayments/Private/Modules/Sberbank/View/SberbankViewController.swift b/YooKassaPayments/Private/Modules/Sberbank/View/SberbankViewController.swift
index 5a50607a..799199ae 100644
--- a/YooKassaPayments/Private/Modules/Sberbank/View/SberbankViewController.swift
+++ b/YooKassaPayments/Private/Modules/Sberbank/View/SberbankViewController.swift
@@ -349,7 +349,7 @@ extension SberbankViewController: UIGestureRecognizerDelegate {
private extension SberbankViewController {
enum Localized: String {
- case title = "Sberbank.Contract.Title"
+ case title = "Sberpay.Contract.Title"
case `continue` = "Contract.next"
enum PlaceholderView: String {
diff --git a/YooKassaPayments/Private/Modules/Sberpay/Assembly/SberpayAssembly.swift b/YooKassaPayments/Private/Modules/Sberpay/Assembly/SberpayAssembly.swift
new file mode 100644
index 00000000..5862a5ea
--- /dev/null
+++ b/YooKassaPayments/Private/Modules/Sberpay/Assembly/SberpayAssembly.swift
@@ -0,0 +1,53 @@
+import UIKit
+
+enum SberpayAssembly {
+ static func makeModule(
+ inputData: SberpayModuleInputData,
+ moduleOutput: SberpayModuleOutput?
+ ) -> UIViewController {
+ let view = SberpayViewController()
+ let presenter = SberpayPresenter(
+ shopName: inputData.shopName,
+ purchaseDescription: inputData.purchaseDescription,
+ priceViewModel: inputData.priceViewModel,
+ feeViewModel: inputData.feeViewModel,
+ termsOfService: inputData.termsOfService,
+ isBackBarButtonHidden: inputData.isBackBarButtonHidden
+ )
+ let paymentService = PaymentServiceAssembly.makeService(
+ tokenizationSettings: inputData.tokenizationSettings,
+ testModeSettings: inputData.testModeSettings,
+ isLoggingEnabled: inputData.isLoggingEnabled
+ )
+ let analyticsProvider = AnalyticsProviderAssembly.makeProvider(
+ testModeSettings: inputData.testModeSettings
+ )
+ let analyticsService = AnalyticsServiceAssembly.makeService(
+ isLoggingEnabled: inputData.isLoggingEnabled
+ )
+ let threatMetrixService = ThreatMetrixServiceFactory.makeService()
+ let interactor = SberpayInteractor(
+ paymentService: paymentService,
+ analyticsProvider: analyticsProvider,
+ analyticsService: analyticsService,
+ threatMetrixService: threatMetrixService,
+ clientApplicationKey: inputData.clientApplicationKey,
+ amount: inputData.paymentOption.charge.plain,
+ returnUrl: inputData.returnUrl
+ )
+ let router = SberpayRouter()
+
+ view.output = presenter
+
+ presenter.view = view
+ presenter.interactor = interactor
+ presenter.router = router
+ presenter.moduleOutput = moduleOutput
+
+ interactor.output = presenter
+
+ router.transitionHandler = view
+
+ return view
+ }
+}
diff --git a/YooKassaPayments/Private/Modules/Sberpay/Interactor/SberpayInteractor.swift b/YooKassaPayments/Private/Modules/Sberpay/Interactor/SberpayInteractor.swift
new file mode 100644
index 00000000..8bbffbaa
--- /dev/null
+++ b/YooKassaPayments/Private/Modules/Sberpay/Interactor/SberpayInteractor.swift
@@ -0,0 +1,100 @@
+import ThreatMetrixAdapter
+
+final class SberpayInteractor {
+
+ // MARK: - VIPER
+
+ weak var output: SberpayInteractorOutput?
+
+ // MARK: - Init
+
+ private let paymentService: PaymentService
+ private let analyticsProvider: AnalyticsProvider
+ private let analyticsService: AnalyticsService
+ private let threatMetrixService: ThreatMetrixService
+ private let clientApplicationKey: String
+ private let amount: MonetaryAmount
+ private let returnUrl: String
+
+ init(
+ paymentService: PaymentService,
+ analyticsProvider: AnalyticsProvider,
+ analyticsService: AnalyticsService,
+ threatMetrixService: ThreatMetrixService,
+ clientApplicationKey: String,
+ amount: MonetaryAmount,
+ returnUrl: String
+ ) {
+ self.paymentService = paymentService
+ self.analyticsProvider = analyticsProvider
+ self.analyticsService = analyticsService
+ self.threatMetrixService = threatMetrixService
+ self.clientApplicationKey = clientApplicationKey
+ self.amount = amount
+ self.returnUrl = returnUrl
+ }
+}
+
+// MARK: - SberpayInteractorInput
+
+extension SberpayInteractor: SberpayInteractorInput {
+ func tokenizeSberpay() {
+ threatMetrixService.profileApp { [weak self] result in
+ guard let self = self,
+ let output = self.output else { return }
+
+ switch result {
+ case let .success(tmxSessionId):
+ let confirmation = Confirmation(
+ type: .mobileApplication,
+ returnUrl: self.returnUrl
+ )
+ self.paymentService.tokenizeSberpay(
+ clientApplicationKey: self.clientApplicationKey,
+ confirmation: confirmation,
+ savePaymentMethod: false,
+ amount: self.amount,
+ tmxSessionId: tmxSessionId.value
+ ) { [weak self] result in
+ switch result {
+ case .success(let data):
+ output.didTokenize(data)
+ case .failure(let error):
+ let mappedError = mapError(error)
+ output.didFailTokenize(mappedError)
+ }
+ }
+
+ case let .failure(error):
+ let mappedError = mapError(error)
+ output.didFailTokenize(mappedError)
+ }
+ }
+ }
+
+ func makeTypeAnalyticsParameters() -> (
+ authType: AnalyticsEvent.AuthType,
+ tokenType: AnalyticsEvent.AuthTokenType?
+ ) {
+ analyticsProvider.makeTypeAnalyticsParameters()
+ }
+
+ func trackEvent(_ event: AnalyticsEvent) {
+ analyticsService.trackEvent(event)
+ }
+}
+
+// MARK: - Private global helpers
+
+private func mapError(
+ _ error: Error
+) -> Error {
+ switch error {
+ case ProfileError.connectionFail:
+ return PaymentProcessingError.internetConnection
+ case let error as NSError where error.domain == NSURLErrorDomain:
+ return PaymentProcessingError.internetConnection
+ default:
+ return error
+ }
+}
diff --git a/YooKassaPayments/Private/Modules/Sberpay/Presenter/SberpayPresenter.swift b/YooKassaPayments/Private/Modules/Sberpay/Presenter/SberpayPresenter.swift
new file mode 100644
index 00000000..06edd10a
--- /dev/null
+++ b/YooKassaPayments/Private/Modules/Sberpay/Presenter/SberpayPresenter.swift
@@ -0,0 +1,225 @@
+import UIKit
+
+final class SberpayPresenter {
+
+ // MARK: - VIPER
+
+ weak var moduleOutput: SberpayModuleOutput?
+ weak var view: SberpayViewInput?
+ var interactor: SberpayInteractorInput!
+ var router: SberpayRouterInput!
+
+ // MARK: - Init
+
+ private let shopName: String
+ private let purchaseDescription: String
+ private let priceViewModel: PriceViewModel
+ private let feeViewModel: PriceViewModel?
+ private let termsOfService: TermsOfService
+ private let isBackBarButtonHidden: Bool
+
+ init(
+ shopName: String,
+ purchaseDescription: String,
+ priceViewModel: PriceViewModel,
+ feeViewModel: PriceViewModel?,
+ termsOfService: TermsOfService,
+ isBackBarButtonHidden: Bool
+ ) {
+ self.shopName = shopName
+ self.purchaseDescription = purchaseDescription
+ self.priceViewModel = priceViewModel
+ self.feeViewModel = feeViewModel
+ self.termsOfService = termsOfService
+ self.isBackBarButtonHidden = isBackBarButtonHidden
+ }
+}
+
+// MARK: - SberpayViewOutput
+
+extension SberpayPresenter: SberpayViewOutput {
+ func setupView() {
+ guard let view = view else { return }
+ let priceValue = makePrice(priceViewModel)
+
+ var feeValue: String? = nil
+ if let feeViewModel = feeViewModel {
+ feeValue = "\(§Localized.fee) " + makePrice(feeViewModel)
+ }
+
+ let termsOfServiceValue = makeTermsOfService(
+ termsOfService,
+ font: UIFont.dynamicCaption2,
+ foregroundColor: UIColor.AdaptiveColors.secondary
+ )
+ let viewModel = SberpayViewModel(
+ shopName: shopName,
+ description: purchaseDescription,
+ priceValue: priceValue,
+ feeValue: feeValue,
+ termsOfService: termsOfServiceValue
+ )
+ view.setupViewModel(viewModel)
+
+ view.setBackBarButtonHidden(isBackBarButtonHidden)
+
+ DispatchQueue.global().async { [weak self] in
+ guard let self = self,
+ let interactor = self.interactor else { return }
+ let (authType, _) = interactor.makeTypeAnalyticsParameters()
+ let event: AnalyticsEvent = .screenPaymentContract(
+ authType: authType,
+ scheme: .sberpay,
+ sdkVersion: Bundle.frameworkVersion
+ )
+ interactor.trackEvent(event)
+ }
+ }
+
+ func didTapActionButton() {
+ guard let view = view else { return }
+ view.showActivity()
+ DispatchQueue.global().async { [weak self] in
+ guard let self = self,
+ let interactor = self.interactor else { return }
+ interactor.tokenizeSberpay()
+ }
+ }
+
+ func didTapTermsOfService(
+ _ url: URL
+ ) {
+ router.presentTermsOfServiceModule(url)
+ }
+}
+
+// MARK: - SberpayInteractorOutput
+
+extension SberpayPresenter: SberpayInteractorOutput {
+ func didTokenize(
+ _ data: Tokens
+ ) {
+ let analyticsParameters = interactor.makeTypeAnalyticsParameters()
+ let event: AnalyticsEvent = .actionTokenize(
+ scheme: .sberpay,
+ authType: analyticsParameters.authType,
+ tokenType: analyticsParameters.tokenType,
+ sdkVersion: Bundle.frameworkVersion
+ )
+ interactor.trackEvent(event)
+ moduleOutput?.sberpayModule(
+ self,
+ didTokenize: data,
+ paymentMethodType: .sberbank
+ )
+
+ DispatchQueue.main.async { [weak self] in
+ guard let view = self?.view else { return }
+ view.hideActivity()
+ }
+ }
+
+ func didFailTokenize(
+ _ error: Error
+ ) {
+ let parameters = interactor.makeTypeAnalyticsParameters()
+ let event: AnalyticsEvent = .screenError(
+ authType: parameters.authType,
+ scheme: .smsSbol,
+ sdkVersion: Bundle.frameworkVersion
+ )
+ interactor.trackEvent(event)
+
+ let message = makeMessage(error)
+
+ DispatchQueue.main.async { [weak self] in
+ guard let view = self?.view else { return }
+ view.hideActivity()
+ view.showPlaceholder(with: message)
+ }
+ }
+}
+
+// MARK: - ActionTitleTextDialogDelegate
+
+extension SberpayPresenter: ActionTitleTextDialogDelegate {
+ func didPressButton(
+ in actionTitleTextDialog: ActionTitleTextDialog
+ ) {
+ guard let view = view else { return }
+ view.hidePlaceholder()
+ view.showActivity()
+ DispatchQueue.global().async { [weak self] in
+ guard let self = self,
+ let interactor = self.interactor else { return }
+ interactor.tokenizeSberpay()
+ }
+ }
+}
+
+// MARK: - SberpayModuleInput
+
+extension SberpayPresenter: SberpayModuleInput {}
+
+// MARK: - Private helpers
+
+private extension SberpayPresenter {
+ func makePrice(
+ _ priceViewModel: PriceViewModel
+ ) -> String {
+ priceViewModel.integerPart
+ + priceViewModel.decimalSeparator
+ + priceViewModel.fractionalPart
+ + priceViewModel.currency
+ }
+
+ func makeTermsOfService(
+ _ terms: TermsOfService,
+ font: UIFont,
+ foregroundColor: UIColor
+ ) -> NSMutableAttributedString {
+ let attributedText: NSMutableAttributedString
+
+ let attributes: [NSAttributedString.Key: Any] = [
+ .font: font,
+ .foregroundColor: foregroundColor,
+ ]
+ attributedText = NSMutableAttributedString(
+ string: "\(terms.text) ",
+ attributes: attributes
+ )
+
+ let linkAttributedText = NSMutableAttributedString(
+ string: terms.hyperlink,
+ attributes: attributes
+ )
+ let linkRange = NSRange(location: 0, length: terms.hyperlink.count)
+ linkAttributedText.addAttribute(.link, value: terms.url, range: linkRange)
+ attributedText.append(linkAttributedText)
+
+ return attributedText
+ }
+
+ func makeMessage(
+ _ error: Error
+ ) -> String {
+ let message: String
+
+ switch error {
+ case let error as PresentableError:
+ message = error.message
+ default:
+ message = §CommonLocalized.Error.unknown
+ }
+
+ return message
+ }
+}
+
+// MARK: - Localized
+
+private extension SberpayPresenter {
+ enum Localized: String {
+ case fee = "Contract.fee"
+ }
+}
diff --git a/YooKassaPayments/Private/Modules/Sberpay/Router/SberpayRouter.swift b/YooKassaPayments/Private/Modules/Sberpay/Router/SberpayRouter.swift
new file mode 100644
index 00000000..dcd8ab03
--- /dev/null
+++ b/YooKassaPayments/Private/Modules/Sberpay/Router/SberpayRouter.swift
@@ -0,0 +1,22 @@
+import UIKit
+import SafariServices
+
+final class SberpayRouter {
+ weak var transitionHandler: TransitionHandler?
+}
+
+// MARK: - SberpayRouterInput
+
+extension SberpayRouter: SberpayRouterInput {
+ func presentTermsOfServiceModule(
+ _ url: URL
+ ) {
+ let viewController = SFSafariViewController(url: url)
+ viewController.modalPresentationStyle = .overFullScreen
+ transitionHandler?.present(
+ viewController,
+ animated: true,
+ completion: nil
+ )
+ }
+}
diff --git a/YooKassaPayments/Private/Modules/Sberpay/SberpayInteractorIO.swift b/YooKassaPayments/Private/Modules/Sberpay/SberpayInteractorIO.swift
new file mode 100644
index 00000000..831d0297
--- /dev/null
+++ b/YooKassaPayments/Private/Modules/Sberpay/SberpayInteractorIO.swift
@@ -0,0 +1,17 @@
+protocol SberpayInteractorInput: AnalyticsTrack {
+ func tokenizeSberpay()
+
+ func makeTypeAnalyticsParameters() -> (
+ authType: AnalyticsEvent.AuthType,
+ tokenType: AnalyticsEvent.AuthTokenType?
+ )
+}
+
+protocol SberpayInteractorOutput: class {
+ func didTokenize(
+ _ data: Tokens
+ )
+ func didFailTokenize(
+ _ error: Error
+ )
+}
diff --git a/YooKassaPayments/Private/Modules/Sberpay/SberpayModuleIO.swift b/YooKassaPayments/Private/Modules/Sberpay/SberpayModuleIO.swift
new file mode 100644
index 00000000..7bb2c567
--- /dev/null
+++ b/YooKassaPayments/Private/Modules/Sberpay/SberpayModuleIO.swift
@@ -0,0 +1,27 @@
+import YooKassaPaymentsApi
+
+struct SberpayModuleInputData {
+ let paymentOption: PaymentOption
+ let clientApplicationKey: String
+ let tokenizationSettings: TokenizationSettings
+ let testModeSettings: TestModeSettings?
+ let isLoggingEnabled: Bool
+
+ let shopName: String
+ let purchaseDescription: String
+ let priceViewModel: PriceViewModel
+ let feeViewModel: PriceViewModel?
+ let termsOfService: TermsOfService
+ let returnUrl: String
+ let isBackBarButtonHidden: Bool
+}
+
+protocol SberpayModuleOutput: class {
+ func sberpayModule(
+ _ module: SberpayModuleInput,
+ didTokenize token: Tokens,
+ paymentMethodType: PaymentMethodType
+ )
+}
+
+protocol SberpayModuleInput: class {}
diff --git a/YooKassaPayments/Private/Modules/Sberpay/SberpayRouterIO.swift b/YooKassaPayments/Private/Modules/Sberpay/SberpayRouterIO.swift
new file mode 100644
index 00000000..394a976b
--- /dev/null
+++ b/YooKassaPayments/Private/Modules/Sberpay/SberpayRouterIO.swift
@@ -0,0 +1,3 @@
+protocol SberpayRouterInput: class {
+ func presentTermsOfServiceModule(_ url: URL)
+}
diff --git a/YooKassaPayments/Private/Modules/Sberpay/SberpayViewIO.swift b/YooKassaPayments/Private/Modules/Sberpay/SberpayViewIO.swift
new file mode 100644
index 00000000..8a288502
--- /dev/null
+++ b/YooKassaPayments/Private/Modules/Sberpay/SberpayViewIO.swift
@@ -0,0 +1,21 @@
+protocol SberpayViewInput:
+ ActivityIndicatorFullViewPresenting,
+ PlaceholderPresenting,
+ NotificationPresenting
+{
+ func setupViewModel(
+ _ viewModel: SberpayViewModel
+ )
+ func setBackBarButtonHidden(
+ _ isHidden: Bool
+ )
+ func showPlaceholder(
+ with message: String
+ )
+}
+
+protocol SberpayViewOutput: ActionTitleTextDialogDelegate {
+ func setupView()
+ func didTapActionButton()
+ func didTapTermsOfService(_ url: URL)
+}
diff --git a/YooKassaPayments/Private/Modules/Sberpay/View/SberpayViewController.swift b/YooKassaPayments/Private/Modules/Sberpay/View/SberpayViewController.swift
new file mode 100644
index 00000000..02d2217e
--- /dev/null
+++ b/YooKassaPayments/Private/Modules/Sberpay/View/SberpayViewController.swift
@@ -0,0 +1,315 @@
+final class SberpayViewController: UIViewController, PlaceholderProvider {
+
+ // MARK: - VIPER
+
+ var output: SberpayViewOutput!
+
+ // MARK: - UI properties
+
+ private lazy var scrollView: UIScrollView = {
+ $0.setStyles(UIView.Styles.grayBackground)
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ $0.keyboardDismissMode = .interactive
+ return $0
+ }(UIScrollView())
+
+ private lazy var contentView: UIView = {
+ $0.setStyles(UIView.Styles.grayBackground)
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ return $0
+ }(UIView())
+
+ private lazy var contentStackView: UIStackView = {
+ $0.setStyles(UIView.Styles.grayBackground)
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ $0.axis = .vertical
+ return $0
+ }(UIStackView())
+
+ private lazy var orderView: OrderView = {
+ $0.setStyles(UIView.Styles.grayBackground)
+ return $0
+ }(OrderView())
+
+ private lazy var sberpayMethodView: LargeIconView = {
+ $0.setStyles(
+ UIView.Styles.grayBackground
+ )
+ $0.image = PaymentMethodResources.Image.sberpay
+ $0.title = §Localized.paymentMethodTitle
+ return $0
+ }(LargeIconView())
+
+ private lazy var actionButtonStackView: UIStackView = {
+ $0.setStyles(UIView.Styles.grayBackground)
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ $0.axis = .vertical
+ $0.spacing = Space.double
+ return $0
+ }(UIStackView())
+
+ private lazy var submitButton: Button = {
+ $0.tintColor = CustomizationStorage.shared.mainScheme
+ $0.setStyles(
+ UIButton.DynamicStyle.primary,
+ UIView.Styles.heightAsContent
+ )
+ $0.setStyledTitle(§Localized.continue, for: .normal)
+ $0.addTarget(
+ self,
+ action: #selector(didPressActionButton),
+ for: .touchUpInside
+ )
+ return $0
+ }(Button(type: .custom))
+
+ private lazy var termsOfServiceLinkedTextView: LinkedTextView = {
+ $0.tintColor = CustomizationStorage.shared.mainScheme
+ $0.setStyles(
+ UIView.Styles.grayBackground,
+ UITextView.Styles.linked
+ )
+ $0.delegate = self
+ return $0
+ }(LinkedTextView())
+
+ private var activityIndicatorView: UIView?
+
+ // MARK: - PlaceholderProvider
+
+ lazy var placeholderView: PlaceholderView = {
+ $0.setStyles(UIView.Styles.defaultBackground)
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ $0.contentView = self.actionTitleTextDialog
+ return $0
+ }(PlaceholderView())
+
+ lazy var actionTitleTextDialog: ActionTitleTextDialog = {
+ $0.tintColor = CustomizationStorage.shared.mainScheme
+ $0.setStyles(ActionTitleTextDialog.Styles.fail)
+ $0.buttonTitle = §Localized.PlaceholderView.buttonTitle
+ $0.text = §Localized.PlaceholderView.text
+ $0.delegate = output
+ return $0
+ }(ActionTitleTextDialog())
+
+ // MARK: - Constraints
+
+ private lazy var scrollViewHeightConstraint =
+ scrollView.heightAnchor.constraint(equalToConstant: 0)
+
+ // MARK: - Managing the View
+
+ override func loadView() {
+ view = UIView()
+ view.setStyles(UIView.Styles.grayBackground)
+ navigationItem.title = §Localized.title
+
+ setupView()
+ setupConstraints()
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ output.setupView()
+ }
+
+ // MARK: - Setup
+
+ private func setupView() {
+ [
+ scrollView,
+ actionButtonStackView,
+ ].forEach(view.addSubview)
+
+ scrollView.addSubview(contentView)
+
+ [
+ contentStackView,
+ ].forEach(contentView.addSubview)
+
+ [
+ orderView,
+ sberpayMethodView,
+ ].forEach(contentStackView.addArrangedSubview)
+
+ [
+ submitButton,
+ termsOfServiceLinkedTextView,
+ ].forEach(actionButtonStackView.addArrangedSubview)
+ }
+
+ private func setupConstraints() {
+ let bottomConstraint: NSLayoutConstraint
+ let topConstraint: NSLayoutConstraint
+ if #available(iOS 11.0, *) {
+ bottomConstraint = actionButtonStackView.bottomAnchor.constraint(
+ equalTo: view.safeAreaLayoutGuide.bottomAnchor,
+ constant: -Space.double
+ )
+ topConstraint = scrollView.topAnchor.constraint(
+ equalTo: view.safeAreaLayoutGuide.topAnchor
+ )
+ } else {
+ bottomConstraint = actionButtonStackView.bottomAnchor.constraint(
+ equalTo: bottomLayoutGuide.topAnchor,
+ constant: -Space.double
+ )
+ topConstraint = scrollView.topAnchor.constraint(
+ equalTo: topLayoutGuide.bottomAnchor
+ )
+ }
+
+ let constraints = [
+ scrollViewHeightConstraint,
+
+ topConstraint,
+ scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
+ scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
+ scrollView.bottomAnchor.constraint(
+ equalTo: actionButtonStackView.topAnchor,
+ constant: -Space.double
+ ),
+
+ actionButtonStackView.leadingAnchor.constraint(
+ equalTo: view.leadingAnchor,
+ constant: Space.double
+ ),
+ actionButtonStackView.trailingAnchor.constraint(
+ equalTo: view.trailingAnchor,
+ constant: -Space.double
+ ),
+ bottomConstraint,
+
+ contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
+ contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
+ contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
+ contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
+ contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
+
+ contentStackView.topAnchor.constraint(equalTo: contentView.topAnchor),
+ contentStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
+ contentStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
+ contentStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
+ ]
+ NSLayoutConstraint.activate(constraints)
+ }
+
+ // MARK: - Configuring the View’s Layout Behavior
+
+ override func viewDidLayoutSubviews() {
+ super.viewDidLayoutSubviews()
+ DispatchQueue.main.async {
+ self.fixTableViewHeight()
+ }
+ }
+
+ private func fixTableViewHeight() {
+ scrollViewHeightConstraint.constant = contentStackView.bounds.height
+ }
+
+ // MARK: - Action
+
+ @objc
+ private func didPressActionButton(
+ _ sender: UIButton
+ ) {
+ output?.didTapActionButton()
+ }
+}
+
+// MARK: - SberpayViewInput
+
+extension SberpayViewController: SberpayViewInput {
+ func setupViewModel(
+ _ viewModel: SberpayViewModel
+ ) {
+ orderView.title = viewModel.shopName
+ orderView.subtitle = viewModel.description
+ orderView.value = viewModel.priceValue
+ orderView.subvalue = viewModel.feeValue
+ termsOfServiceLinkedTextView.attributedText = viewModel.termsOfService
+ termsOfServiceLinkedTextView.textAlignment = .center
+ }
+
+ func setBackBarButtonHidden(
+ _ isHidden: Bool
+ ) {
+ navigationItem.hidesBackButton = isHidden
+ }
+
+ func showActivity() {
+ guard activityIndicatorView == nil else { return }
+
+ let activityIndicatorView = ActivityIndicatorView()
+ activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
+ activityIndicatorView.activity.startAnimating()
+ activityIndicatorView.setStyles(ActivityIndicatorView.Styles.heavyLight)
+ view.addSubview(activityIndicatorView)
+
+ self.activityIndicatorView = activityIndicatorView
+
+ let constraints = [
+ activityIndicatorView.leading.constraint(equalTo: view.leading),
+ activityIndicatorView.trailing.constraint(equalTo: view.trailing),
+ activityIndicatorView.top.constraint(equalTo: view.top),
+ activityIndicatorView.bottom.constraint(equalTo: view.bottom),
+ ]
+
+ NSLayoutConstraint.activate(constraints)
+ }
+
+ func hideActivity() {
+ UIView.animate(
+ withDuration: 0.2,
+ animations: {
+ self.activityIndicatorView?.alpha = 0
+ },
+ completion: { _ in
+ self.activityIndicatorView?.removeFromSuperview()
+ self.activityIndicatorView = nil
+ }
+ )
+ }
+
+ func showPlaceholder(
+ with message: String
+ ) {
+ actionTitleTextDialog.title = message
+ showPlaceholder()
+ }
+}
+
+// MARK: - UITextViewDelegate
+
+extension SberpayViewController: UITextViewDelegate {
+ func textView(
+ _ textView: UITextView,
+ shouldInteractWith URL: URL,
+ in characterRange: NSRange
+ ) -> Bool {
+ switch textView {
+ case termsOfServiceLinkedTextView:
+ output?.didTapTermsOfService(URL)
+ default:
+ assertionFailure("Unsupported textView")
+ }
+ return false
+ }
+}
+
+// MARK: - Localized
+
+private extension SberpayViewController {
+ enum Localized: String {
+ case title = "Sberpay.Contract.Title"
+ case `continue` = "Contract.next"
+ case fee = "Contract.fee"
+ case paymentMethodTitle = "Sberpay.paymentMethodTitle"
+
+ enum PlaceholderView: String {
+ case buttonTitle = "Common.PlaceholderView.buttonTitle"
+ case text = "Common.PlaceholderView.text"
+ }
+ }
+}
diff --git a/YooKassaPayments/Private/Modules/Sberpay/View/ViewModel/SberpayViewModel.swift b/YooKassaPayments/Private/Modules/Sberpay/View/ViewModel/SberpayViewModel.swift
new file mode 100644
index 00000000..40ff4e7e
--- /dev/null
+++ b/YooKassaPayments/Private/Modules/Sberpay/View/ViewModel/SberpayViewModel.swift
@@ -0,0 +1,7 @@
+struct SberpayViewModel {
+ let shopName: String
+ let description: String?
+ let priceValue: String
+ let feeValue: String?
+ let termsOfService: NSAttributedString
+}
diff --git a/YooKassaPayments/Private/Modules/YooMoney/Interactor/YooMoneyInteractor.swift b/YooKassaPayments/Private/Modules/YooMoney/Interactor/YooMoneyInteractor.swift
index 39e15397..4f23138c 100644
--- a/YooKassaPayments/Private/Modules/YooMoney/Interactor/YooMoneyInteractor.swift
+++ b/YooKassaPayments/Private/Modules/YooMoney/Interactor/YooMoneyInteractor.swift
@@ -91,7 +91,7 @@ extension YooMoneyInteractor: YooMoneyInteractorInput {
savePaymentMethod: savePaymentMethod,
paymentMethodType: paymentMethodType,
amount: amount,
- tmxSessionId: tmxSessionId
+ tmxSessionId: tmxSessionId.value
)
case let .failure(error):
diff --git a/YooKassaPayments/Private/Modules/YooMoney/View/YooMoneyViewController.swift b/YooKassaPayments/Private/Modules/YooMoney/View/YooMoneyViewController.swift
index a86acea1..c2b6cad3 100644
--- a/YooKassaPayments/Private/Modules/YooMoney/View/YooMoneyViewController.swift
+++ b/YooKassaPayments/Private/Modules/YooMoney/View/YooMoneyViewController.swift
@@ -487,7 +487,7 @@ extension YooMoneyViewController: YooMoneyViewInput {
let linkAttributedText = NSMutableAttributedString(string: hyperText, attributes: attributes)
let linkRange = NSRange(location: 0, length: hyperText.count)
- let fakeLink = URL(string: "https://yookassa.ru")
+ let fakeLink = URL(string: "https://yookassa.ru")!
linkAttributedText.addAttribute(.link, value: fakeLink, range: linkRange)
attributedText.append(linkAttributedText)
diff --git a/YooKassaPayments/Private/Services/Analytics/AnalyticsEvent.swift b/YooKassaPayments/Private/Services/Analytics/AnalyticsEvent.swift
index c3a24508..42631d86 100644
--- a/YooKassaPayments/Private/Services/Analytics/AnalyticsEvent.swift
+++ b/YooKassaPayments/Private/Services/Analytics/AnalyticsEvent.swift
@@ -42,8 +42,15 @@ enum AnalyticsEvent {
case userStartAuthorization(sdkVersion: String)
case userCancelAuthorization(sdkVersion: String)
- case userSuccessAuthorization(moneyAuthProcessType: MoneyAuthProcessType, sdkVersion: String)
- case userFailedAuthorization(error: String, sdkVersion: String)
+
+ case actionMoneyAuthLogin(
+ scheme: MoneyAuthLoginScheme,
+ status: MoneyAuthLoginStatus,
+ sdkVersion: String
+ )
+
+ /// SberPay confirmation
+ case actionSberPayConfirmation(sberPayConfirmationStatus: SberPayConfirmationStatus, sdkVersion: String)
// MARK: - Analytic parameters.
@@ -72,6 +79,7 @@ enum AnalyticsEvent {
case smsSbol = "sms-sbol"
case applePay = "apple-pay"
case recurringCard = "recurring-card"
+ case sberpay = "sber-pay"
var key: String {
return Key.tokenizeScheme.rawValue
@@ -107,21 +115,10 @@ enum AnalyticsEvent {
case authType
case authTokenType
case authPaymentStatus
- case moneyAuthProcessType
case action
- }
-
- // MARK: - Authorization
-
- enum MoneyAuthProcessType: String {
- case enrollment
- case login
- case migration
- case unknown
-
- var key: String {
- return Key.moneyAuthProcessType.rawValue
- }
+ case moneyAuthLoginScheme
+ case moneyAuthLoginStatus
+ case sberPayConfirmationStatus
}
// MARK: - BankCardForm actions
@@ -148,6 +145,46 @@ enum AnalyticsEvent {
Key.action.rawValue
}
}
+
+ enum MoneyAuthLoginScheme: String {
+ case moneyAuthSdk
+ case yoomoneyApp
+
+ var key: String {
+ Key.moneyAuthLoginScheme.rawValue
+ }
+ }
+
+ enum MoneyAuthLoginStatus {
+ case success
+ case fail(String)
+ case canceled
+
+ var rawValue: String {
+ switch self {
+ case .success:
+ return "Success"
+ case .fail:
+ return "Fail"
+ case .canceled:
+ return "Canceled"
+ }
+ }
+
+ var key: String {
+ Key.moneyAuthLoginStatus.rawValue
+ }
+ }
+
+ // MARK: - SberPayConfirmationStatus
+
+ enum SberPayConfirmationStatus: String {
+ case success = "Success"
+
+ var key: String {
+ return Key.sberPayConfirmationStatus.rawValue
+ }
+ }
}
// MARK: - Primitive type keys
diff --git a/YooKassaPayments/Private/Services/Analytics/AnalyticsServiceImpl.swift b/YooKassaPayments/Private/Services/Analytics/AnalyticsServiceImpl.swift
index 8d72fdfc..01e1e476 100644
--- a/YooKassaPayments/Private/Services/Analytics/AnalyticsServiceImpl.swift
+++ b/YooKassaPayments/Private/Services/Analytics/AnalyticsServiceImpl.swift
@@ -101,14 +101,14 @@ extension AnalyticsServiceImpl: AnalyticsService {
case .userCancelAuthorization:
eventName = EventKey.userCancelAuthorization.rawValue
- case .userSuccessAuthorization:
- eventName = EventKey.userSuccessAuthorization.rawValue
-
- case .userFailedAuthorization:
- eventName = EventKey.userFailedAuthorization.rawValue
-
case .actionBankCardForm:
eventName = EventKey.actionBankCardForm.rawValue
+
+ case .actionMoneyAuthLogin:
+ eventName = EventKey.actionMoneyAuthLogin.rawValue
+
+ case .actionSberPayConfirmation:
+ eventName = EventKey.actionSberPayConfirmation.rawValue
}
return eventName
}
@@ -200,21 +200,26 @@ extension AnalyticsServiceImpl: AnalyticsService {
AnalyticsEvent.Keys.msdkVersion.rawValue: sdkVersion,
]
- case let .userSuccessAuthorization(moneyAuthProcessType, sdkVersion):
+ case let .actionBankCardForm(action, sdkVersion):
parameters = [
- moneyAuthProcessType.key: moneyAuthProcessType.rawValue,
+ action.key: action.rawValue,
AnalyticsEvent.Keys.msdkVersion.rawValue: sdkVersion,
]
- case let .userFailedAuthorization(errorLocalizedDescription, sdkVersion):
+ case let .actionMoneyAuthLogin(scheme, status, sdkVersion):
parameters = [
- AnalyticsEvent.Keys.error.rawValue: errorLocalizedDescription,
+ scheme.key: scheme.rawValue,
+ status.key: status.rawValue,
AnalyticsEvent.Keys.msdkVersion.rawValue: sdkVersion,
]
- case let .actionBankCardForm(action, sdkVersion):
+ if case .fail(let errorLocalizedDescription) = status {
+ parameters?[AnalyticsEvent.Keys.error.rawValue] = errorLocalizedDescription
+ }
+
+ case let .actionSberPayConfirmation(sberPayConfirmationStatus, sdkVersion):
parameters = [
- action.key: action.rawValue,
+ sberPayConfirmationStatus.key: sberPayConfirmationStatus.rawValue,
AnalyticsEvent.Keys.msdkVersion.rawValue: sdkVersion,
]
}
@@ -236,13 +241,13 @@ extension AnalyticsServiceImpl: AnalyticsService {
case actionLogout
case actionAuthWithoutWallet
case actionBankCardForm
+ case actionMoneyAuthLogin
+ case actionSberPayConfirmation
// MARK: - Authorization
case userStartAuthorization
case userCancelAuthorization
- case userSuccessAuthorization
- case userFailedAuthorization
}
}
diff --git a/YooKassaPayments/Private/Services/Authorization/AuthorizationServiceImpl.swift b/YooKassaPayments/Private/Services/Authorization/AuthorizationServiceImpl.swift
index 75631e36..ce2d1af7 100644
--- a/YooKassaPayments/Private/Services/Authorization/AuthorizationServiceImpl.swift
+++ b/YooKassaPayments/Private/Services/Authorization/AuthorizationServiceImpl.swift
@@ -72,14 +72,18 @@ extension AuthorizationServiceImpl: AuthorizationService {
completion: { _ in }
)
}
- tokenStorage.set(
- string: nil,
- for: KeyValueStoringKeys.moneyCenterAuthToken
- )
- tokenStorage.set(
- string: nil,
- for: KeyValueStoringKeys.walletToken
- )
+ [
+ KeyValueStoringKeys.moneyCenterAuthToken,
+ KeyValueStoringKeys.walletToken,
+ Constants.Keys.walletDisplayName,
+ Constants.Keys.walletPhoneTitle,
+ Constants.Keys.walletAvatarURL,
+ ].forEach {
+ tokenStorage.set(
+ string: nil,
+ for: $0
+ )
+ }
}
func setWalletDisplayName(
@@ -183,7 +187,7 @@ extension AuthorizationServiceImpl {
instanceName: instanceName,
singleAmountMax: amount,
paymentUsageLimit: paymentUsageLimit,
- tmxSessionId: tmxSessionId,
+ tmxSessionId: tmxSessionId.value,
completion: completion
)
diff --git a/YooKassaPayments/Private/Services/Payment/PaymentService.swift b/YooKassaPayments/Private/Services/Payment/PaymentService.swift
index cd82a9f1..a69d760f 100644
--- a/YooKassaPayments/Private/Services/Payment/PaymentService.swift
+++ b/YooKassaPayments/Private/Services/Payment/PaymentService.swift
@@ -60,6 +60,15 @@ protocol PaymentService {
tmxSessionId: String,
completion: @escaping (Result) -> Void
)
+
+ func tokenizeSberpay(
+ clientApplicationKey: String,
+ confirmation: Confirmation,
+ savePaymentMethod: Bool,
+ amount: MonetaryAmount?,
+ tmxSessionId: String,
+ completion: @escaping (Result) -> Void
+ )
func tokenizeApplePay(
clientApplicationKey: String,
diff --git a/YooKassaPayments/Private/Services/Payment/PaymentServiceImpl.swift b/YooKassaPayments/Private/Services/Payment/PaymentServiceImpl.swift
index e138640f..6484e169 100644
--- a/YooKassaPayments/Private/Services/Payment/PaymentServiceImpl.swift
+++ b/YooKassaPayments/Private/Services/Payment/PaymentServiceImpl.swift
@@ -68,8 +68,7 @@ extension PaymentServiceImpl: PaymentService {
paymentMethodId: paymentMethodId
)
- session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { [weak self] result in
- guard let self = self else { return }
+ session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { result in
switch result {
case let .left(error):
let mappedError = mapError(error)
@@ -102,8 +101,7 @@ extension PaymentServiceImpl: PaymentService {
tokensRequest: tokensRequest
)
- session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { [weak self] result in
- guard let self = self else { return }
+ session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { result in
switch result {
case let .left(error):
let mappedError = mapError(error)
@@ -141,8 +139,7 @@ extension PaymentServiceImpl: PaymentService {
tokensRequest: tokensRequest
)
- session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { [weak self] result in
- guard let self = self else { return }
+ session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { result in
switch result {
case let .left(error):
let mappedError = mapError(error)
@@ -184,8 +181,7 @@ extension PaymentServiceImpl: PaymentService {
tokensRequest: tokensRequest
)
- session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { [weak self] result in
- guard let self = self else { return }
+ session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { result in
switch result {
case let .left(error):
let mappedError = mapError(error)
@@ -219,8 +215,7 @@ extension PaymentServiceImpl: PaymentService {
tokensRequest: tokensRequest
)
- session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { [weak self] result in
- guard let self = self else { return }
+ session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { result in
switch result {
case let .left(error):
let mappedError = mapError(error)
@@ -255,6 +250,40 @@ extension PaymentServiceImpl: PaymentService {
tokensRequest: tokensRequest
)
+ session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { result in
+ switch result {
+ case let .left(error):
+ let mappedError = mapError(error)
+ completion(.failure(mappedError))
+ case let .right(data):
+ completion(.success(data.plain))
+ }
+ }
+ }
+
+ func tokenizeSberpay(
+ clientApplicationKey: String,
+ confirmation: Confirmation,
+ savePaymentMethod: Bool,
+ amount: MonetaryAmount?,
+ tmxSessionId: String,
+ completion: @escaping (Result) -> Void
+ ) {
+ let paymentMethodData = PaymentMethodDataSberbank(
+ phone: nil
+ )
+ let tokensRequest = TokensRequestPaymentMethodData(
+ amount: amount?.paymentsModel,
+ tmxSessionId: tmxSessionId,
+ confirmation: confirmation.paymentsModel,
+ savePaymentMethod: savePaymentMethod,
+ paymentMethodData: paymentMethodData
+ )
+ let apiMethod = YooKassaPaymentsApi.Tokens.Method(
+ oauthToken: clientApplicationKey,
+ tokensRequest: tokensRequest
+ )
+
session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { [weak self] result in
guard let self = self else { return }
switch result {
@@ -290,8 +319,7 @@ extension PaymentServiceImpl: PaymentService {
tokensRequest: tokensRequest
)
- session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { [weak self] result in
- guard let self = self else { return }
+ session.perform(apiMethod: apiMethod).responseApi(queue: .global()) { result in
switch result {
case let .left(error):
let mappedError = mapError(error)
diff --git a/YooKassaPayments/Private/Services/Payment/PaymentServiceMock.swift b/YooKassaPayments/Private/Services/Payment/PaymentServiceMock.swift
index 3958414e..d92ac456 100644
--- a/YooKassaPayments/Private/Services/Payment/PaymentServiceMock.swift
+++ b/YooKassaPayments/Private/Services/Payment/PaymentServiceMock.swift
@@ -137,6 +137,17 @@ extension PaymentServiceMock: PaymentService {
) {
makeTokensPromise(completion: completion)
}
+
+ func tokenizeSberpay(
+ clientApplicationKey: String,
+ confirmation: Confirmation,
+ savePaymentMethod: Bool,
+ amount: MonetaryAmount?,
+ tmxSessionId: String,
+ completion: @escaping (Result) -> Void
+ ) {
+ makeTokensPromise(completion: completion)
+ }
func tokenizeApplePay(
clientApplicationKey: String,
diff --git a/YooKassaPayments/Private/SheetView/SheetContentViewController.swift b/YooKassaPayments/Private/SheetView/SheetContentViewController.swift
index eb81d4e5..fe2f213b 100644
--- a/YooKassaPayments/Private/SheetView/SheetContentViewController.swift
+++ b/YooKassaPayments/Private/SheetView/SheetContentViewController.swift
@@ -13,26 +13,60 @@ final class SheetContentViewController: UIViewController {
return $0
}(UIView())
- private var contentWrapperView = UIView()
- private var pullBarView = UIView()
- private var gripView = UIView()
- private let overflowView = UIView()
+ private lazy var contentWrapperView: UIView = {
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ $0.layer.masksToBounds = true
+ if #available(iOS 11.0, *) {
+ $0.layer.maskedCorners = [
+ .layerMaxXMinYCorner,
+ .layerMinXMinYCorner,
+ ]
+ }
+ return $0
+ }(UIView())
+
+ private lazy var pullBarView: UIView = {
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ return $0
+ }(UIView())
+
+ private lazy var gripView: UIView = {
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ return $0
+ }(UIView())
+
+ private lazy var overflowView: UIView = {
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ return $0
+ }(UIView())
private lazy var childContainerView: UIView = {
- let view = UIView()
- view.translatesAutoresizingMaskIntoConstraints = false
+ $0.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 13.0, *) {
- view.backgroundColor = UIColor.systemBackground
+ $0.backgroundColor = UIColor.systemBackground
} else {
- view.backgroundColor = UIColor.white
+ $0.backgroundColor = UIColor.white
}
- return view
- }()
+ $0.layer.masksToBounds = true
+ if #available(iOS 11.0, *) {
+ $0.layer.maskedCorners = [
+ .layerMaxXMinYCorner,
+ .layerMinXMinYCorner,
+ ]
+ }
+ return $0
+ }(UIView())
// MARK: - NSLayoutConstraint
- private var contentTopConstraint: NSLayoutConstraint?
- private var contentBottomConstraint: NSLayoutConstraint?
+ private lazy var contentTopConstraint: NSLayoutConstraint = {
+ return contentView.topAnchor.constraint(equalTo: view.topAnchor)
+ }()
+
+ private lazy var contentBottomConstraint: NSLayoutConstraint = {
+ return childViewController.view.bottomAnchor.constraint(equalTo: childContainerView.bottomAnchor)
+ }()
+
private var navigationHeightConstraint: NSLayoutConstraint?
private var gripSizeConstraints: [NSLayoutConstraint] = []
@@ -123,45 +157,42 @@ final class SheetContentViewController: UIViewController {
private func setupContentView() {
view.addSubview(contentView)
+ contentView.addSubview(contentWrapperView)
+ contentView.addSubview(overflowView)
NSLayoutConstraint.activate([
- contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
+ contentTopConstraint,
+ contentView.leftAnchor.constraint(equalTo: view.leftAnchor),
+ contentView.rightAnchor.constraint(equalTo: view.rightAnchor),
contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
+
+ contentWrapperView.leftAnchor.constraint(equalTo: contentView.leftAnchor),
+ contentWrapperView.rightAnchor.constraint(equalTo: contentView.rightAnchor),
+ contentWrapperView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
+ contentWrapperView.topAnchor.constraint(equalTo: contentView.topAnchor),
+
+ overflowView.leftAnchor.constraint(equalTo: contentView.leftAnchor),
+ overflowView.rightAnchor.constraint(equalTo: contentView.rightAnchor),
+ overflowView.heightAnchor.constraint(equalToConstant: 200),
+ overflowView.topAnchor.constraint(
+ equalTo: contentView.bottomAnchor,
+ constant: -1
+ ),
])
-
- contentTopConstraint = contentView.topAnchor.constraint(equalTo: view.topAnchor)
- if let contentTopConstraint = contentTopConstraint {
- NSLayoutConstraint.activate([
- contentTopConstraint,
- ])
- }
-
- contentView.addSubview(contentWrapperView) {
- $0.edges.pinToSuperview()
- }
-
- contentWrapperView.layer.masksToBounds = true
- if #available(iOS 11.0, *) {
- contentWrapperView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
- }
-
- contentView.addSubview(overflowView) {
- $0.edges(.left, .right).pinToSuperview()
- $0.height.set(200)
- $0.top.align(with: contentView.al.bottom - 1)
- }
}
private func setupChildContainerView() {
contentWrapperView.addSubview(childContainerView)
-
- Constraints(for: childContainerView) { view in
- view.top.pinToSuperview(inset: options.pullBarHeight)
- view.left.pinToSuperview()
- view.right.pinToSuperview()
- view.bottom.pinToSuperview()
- }
+
+ NSLayoutConstraint.activate([
+ childContainerView.topAnchor.constraint(
+ equalTo: contentWrapperView.topAnchor,
+ constant: options.pullBarHeight
+ ),
+ childContainerView.leftAnchor.constraint(equalTo: contentWrapperView.leftAnchor),
+ childContainerView.rightAnchor.constraint(equalTo: contentWrapperView.rightAnchor),
+ childContainerView.bottomAnchor.constraint(equalTo: contentWrapperView.bottomAnchor),
+ ])
}
private func setupChildViewController() {
@@ -175,21 +206,10 @@ final class SheetContentViewController: UIViewController {
childViewController.view.leadingAnchor.constraint(equalTo: childContainerView.leadingAnchor),
childViewController.view.trailingAnchor.constraint(equalTo: childContainerView.trailingAnchor),
childViewController.view.topAnchor.constraint(equalTo: childContainerView.topAnchor),
+ contentBottomConstraint,
])
- contentBottomConstraint = childViewController.view.bottomAnchor.constraint(equalTo: childContainerView.bottomAnchor)
- if let contentBottomConstraint = contentBottomConstraint {
- NSLayoutConstraint.activate([
- contentBottomConstraint,
- ])
- }
-
childViewController.didMove(toParent: self)
-
- childContainerView.layer.masksToBounds = true
- if #available(iOS 11.0, *) {
- childContainerView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
- }
}
private func setupOverflowView() {
@@ -197,10 +217,12 @@ final class SheetContentViewController: UIViewController {
}
private func setupGripSize() {
- gripSizeConstraints.forEach({ $0.isActive = false })
- Constraints(for: gripView) {
- gripSizeConstraints = $0.size.set(gripSize)
- }
+ NSLayoutConstraint.deactivate(gripSizeConstraints)
+ gripSizeConstraints = [
+ gripView.heightAnchor.constraint(equalToConstant: gripSize.height),
+ gripView.widthAnchor.constraint(equalToConstant: gripSize.width),
+ ]
+ NSLayoutConstraint.activate(gripSizeConstraints)
gripView.layer.cornerRadius = gripSize.height / 2
}
@@ -214,12 +236,12 @@ final class SheetContentViewController: UIViewController {
pullBarView.isUserInteractionEnabled = true
pullBarView.backgroundColor = pullBarBackgroundColor
contentWrapperView.addSubview(pullBarView)
- Constraints(for: pullBarView) {
- $0.top.pinToSuperview()
- $0.left.pinToSuperview()
- $0.right.pinToSuperview()
- $0.height.set(options.pullBarHeight)
- }
+ NSLayoutConstraint.activate([
+ pullBarView.topAnchor.constraint(equalTo: contentWrapperView.topAnchor),
+ pullBarView.leftAnchor.constraint(equalTo: contentWrapperView.leftAnchor),
+ pullBarView.rightAnchor.constraint(equalTo: contentWrapperView.rightAnchor),
+ pullBarView.heightAnchor.constraint(equalToConstant: options.pullBarHeight),
+ ])
self.pullBarView = pullBarView
let gripView = self.gripView
@@ -227,12 +249,16 @@ final class SheetContentViewController: UIViewController {
gripView.layer.cornerRadius = gripSize.height / 2
gripView.layer.masksToBounds = true
pullBarView.addSubview(gripView)
- gripSizeConstraints.forEach({ $0.isActive = false })
- Constraints(for: gripView) {
- $0.centerY.alignWithSuperview()
- $0.centerX.alignWithSuperview()
- gripSizeConstraints = $0.size.set(gripSize)
- }
+ NSLayoutConstraint.deactivate(gripSizeConstraints)
+ gripSizeConstraints = [
+ gripView.heightAnchor.constraint(equalToConstant: gripSize.height),
+ gripView.widthAnchor.constraint(equalToConstant: gripSize.width),
+ ]
+ NSLayoutConstraint.activate([
+ gripView.centerYAnchor.constraint(equalTo: pullBarView.centerYAnchor),
+ gripView.centerXAnchor.constraint(equalTo: pullBarView.centerXAnchor),
+ ])
+ NSLayoutConstraint.activate(gripSizeConstraints)
}
private func setupCornerRadius() {
@@ -277,7 +303,7 @@ extension SheetContentViewController {
let oldPreferredHeight = preferredHeight
var fittingSize = UIView.layoutFittingCompressedSize
fittingSize.width = width
- contentTopConstraint?.isActive = false
+ contentTopConstraint.isActive = false
UIView.performWithoutAnimation {
contentView.layoutSubviews()
@@ -287,7 +313,7 @@ extension SheetContentViewController {
withHorizontalFittingPriority: .required,
verticalFittingPriority: .defaultLow
).height
- contentTopConstraint?.isActive = true
+ contentTopConstraint.isActive = true
UIView.performWithoutAnimation {
contentView.layoutSubviews()
@@ -306,7 +332,7 @@ private extension SheetContentViewController {
func updateNavigationControllerHeight() {
guard let navigationController = childViewController as? UINavigationController else { return }
navigationHeightConstraint?.isActive = false
- contentTopConstraint?.isActive = false
+ contentTopConstraint.isActive = false
if let viewController = navigationController.visibleViewController {
let sizeFitting = CGSize(width: view.bounds.width, height: 0)
@@ -321,13 +347,13 @@ private extension SheetContentViewController {
}
}
navigationHeightConstraint?.isActive = true
- contentTopConstraint?.isActive = true
+ contentTopConstraint.isActive = true
}
func updateChildViewControllerBottomConstraint(
adjustment: CGFloat
) {
- contentBottomConstraint?.constant = adjustment
+ contentBottomConstraint.constant = adjustment
}
}
diff --git a/YooKassaPayments/Private/SheetView/SheetViewController.swift b/YooKassaPayments/Private/SheetView/SheetViewController.swift
index 47e220b0..3bdd505a 100644
--- a/YooKassaPayments/Private/SheetView/SheetViewController.swift
+++ b/YooKassaPayments/Private/SheetView/SheetViewController.swift
@@ -4,6 +4,11 @@ protocol SheetViewModuleOutput: class {
func start3dsProcess(
requestUrl: String
)
+
+ func startConfirmationProcess(
+ confirmationUrl: String,
+ paymentMethodType: PaymentMethodType
+ )
func didFinish(
on module: TokenizationModuleInput,
@@ -237,17 +242,11 @@ final class SheetViewController: UIViewController {
) -> CGFloat? {
let convertedKeyboardFrame = view.convert(keyboardFrame, from: nil)
let intersectionViewFrame = convertedKeyboardFrame.intersection(view.bounds)
- var safeOffset: CGFloat = 0
- if #available(iOS 11.0, *) {
- let intersectionSafeFrame = convertedKeyboardFrame.intersection(view.safeAreaLayoutGuide.layoutFrame)
- safeOffset = intersectionViewFrame.height - intersectionSafeFrame.height
- }
let intersectionOffset = intersectionViewFrame.size.height
guard convertedKeyboardFrame.minY.isInfinite == false else {
return nil
}
- let keyboardOffset = intersectionOffset - safeOffset
- return keyboardOffset
+ return intersectionOffset
}
}
@@ -310,7 +309,15 @@ private extension SheetViewController {
func height(
for size: SheetSize
) -> CGFloat {
- let fullscreenHeight = view.bounds.height
+ var statusBarHeight: CGFloat = 0
+ if #available(iOS 13.0, *) {
+ let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
+ statusBarHeight = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
+ } else {
+ statusBarHeight = UIApplication.shared.statusBarFrame.height
+ }
+
+ let fullscreenHeight = view.bounds.height - statusBarHeight
let contentHeight: CGFloat
switch size {
@@ -596,4 +603,14 @@ extension SheetViewController: TokenizationModuleInput {
func start3dsProcess(requestUrl: String) {
moduleOutput?.start3dsProcess(requestUrl: requestUrl)
}
+
+ func startConfirmationProcess(
+ confirmationUrl: String,
+ paymentMethodType: PaymentMethodType
+ ) {
+ moduleOutput?.startConfirmationProcess(
+ confirmationUrl: confirmationUrl,
+ paymentMethodType: paymentMethodType
+ )
+ }
}
diff --git a/YooKassaPayments/Private/SheetView/Yalta.swift b/YooKassaPayments/Private/SheetView/Yalta.swift
deleted file mode 100644
index badb3140..00000000
--- a/YooKassaPayments/Private/SheetView/Yalta.swift
+++ /dev/null
@@ -1,410 +0,0 @@
-import UIKit
-
-protocol LayoutItem { // `UIView`, `UILayoutGuide`
- var superview: UIView? { get }
-}
-
-extension UIView: LayoutItem {}
-extension UILayoutGuide: LayoutItem {
- var superview: UIView? { return self.owningView }
-}
-
-extension LayoutItem { // Yalta methods available via `LayoutProxy`
- @nonobjc var al: LayoutProxy { return LayoutProxy(base: self) }
-}
-
-// MARK: - LayoutProxy
-
-struct LayoutProxy {
- let base: Base
-}
-
-extension LayoutProxy where Base: LayoutItem {
-
- // MARK: SheetAnchors
-
- var top: SheetAnchor { return SheetAnchor(base, .top) }
- var bottom: SheetAnchor { return SheetAnchor(base, .bottom) }
- var left: SheetAnchor { return SheetAnchor(base, .left) }
- var right: SheetAnchor { return SheetAnchor(base, .right) }
- var leading: SheetAnchor { return SheetAnchor(base, .leading) }
- var trailing: SheetAnchor { return SheetAnchor(base, .trailing) }
-
- var centerX: SheetAnchor { return SheetAnchor(base, .centerX) }
- var centerY: SheetAnchor { return SheetAnchor(base, .centerY) }
-
- var firstBaseline: SheetAnchor { return SheetAnchor(base, .firstBaseline) }
- var lastBaseline: SheetAnchor { return SheetAnchor(base, .lastBaseline) }
-
- var width: SheetAnchor { return SheetAnchor(base, .width) }
- var height: SheetAnchor { return SheetAnchor(base, .height) }
-
- // MARK: SheetAnchor Collections
-
- func edges(_ edges: LayoutEdge...) -> SheetAnchorCollectionEdges { return SheetAnchorCollectionEdges(item: base, edges: edges) }
- var edges: SheetAnchorCollectionEdges { return SheetAnchorCollectionEdges(item: base, edges: [.left, .right, .bottom, .top]) }
- var center: SheetAnchorCollectionCenter { return SheetAnchorCollectionCenter(x: centerX, y: centerY) }
- var size: SheetAnchorCollectionSize { return SheetAnchorCollectionSize(width: width, height: height) }
-}
-
-extension LayoutProxy where Base: UIView {
- var margins: LayoutProxy { return base.layoutMarginsGuide.al }
-
- @available(iOS 11.0, tvOS 11.0, *)
- var safeArea: LayoutProxy { return base.safeAreaLayoutGuide.al }
-}
-
-// MARK: - SheetAnchors
-
-// phantom types
-enum SheetAnchorAxis {
- class Horizontal {}
- class Vertical {}
-}
-
-enum SheetAnchorType {
- class Dimension {}
- /// Includes `center`, `edge` and `baselines` anchors.
- class Alignment {}
- class Center: Alignment {}
- class Edge: Alignment {}
- class Baseline: Alignment {}
-}
-
-/// An anchor represents one of the view's layout attributes (e.g. `left`,
-/// `centerX`, `width`, etc). Use the anchor’s methods to construct constraints.
-struct SheetAnchor { // type and axis are phantom types
- internal let item: LayoutItem
- internal let attribute: NSLayoutConstraint.Attribute
- internal let offset: CGFloat
- internal let multiplier: CGFloat
-
- init(_ item: LayoutItem, _ attribute: NSLayoutConstraint.Attribute, offset: CGFloat = 0, multiplier: CGFloat = 1) {
- self.item = item; self.attribute = attribute; self.offset = offset; self.multiplier = multiplier
- }
-
- /// Returns a new anchor offset by a given amount.
- internal func offsetting(by offset: CGFloat) -> SheetAnchor {
- return SheetAnchor(item, attribute, offset: self.offset + offset, multiplier: self.multiplier)
- }
-
- /// Returns a new anchor with a given multiplier.
- internal func multiplied(by multiplier: CGFloat) -> SheetAnchor {
- return SheetAnchor(item, attribute, offset: self.offset * multiplier, multiplier: self.multiplier * multiplier)
- }
-}
-
-func + (anchor: SheetAnchor, offset: CGFloat) -> SheetAnchor {
- return anchor.offsetting(by: offset)
-}
-
-func - (anchor: SheetAnchor, offset: CGFloat) -> SheetAnchor {
- return anchor.offsetting(by: -offset)
-}
-
-func * (anchor: SheetAnchor, multiplier: CGFloat) -> SheetAnchor {
- return anchor.multiplied(by: multiplier)
-}
-
-// MARK: - SheetAnchors (SheetAnchorType.Alignment)
-
-extension SheetAnchor where Type: SheetAnchorType.Alignment {
- /// Aligns two anchors.
- @discardableResult func align(with anchor: SheetAnchor, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
- return Constraints.constrain(self, anchor, relation: relation)
- }
-}
-
-// MARK: - SheetAnchors (SheetAnchorType.Edge)
-
-extension SheetAnchor where Type: SheetAnchorType.Edge {
- /// Pins the edge to the same edge of the superview.
- @discardableResult func pinToSuperview(inset: CGFloat = 0, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
- return _pin(to: item.superview!, attribute: attribute, inset: inset, relation: relation)
- }
-
- /// Pins the edge to the respected margin of the superview.
- @discardableResult func pinToSuperviewMargin(inset: CGFloat = 0, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
- return _pin(to: item.superview!, attribute: attribute.toMargin, inset: inset, relation: relation)
- }
-
- /// Pins the edge to the respected edges of the given container.
- @discardableResult func pin(to container: LayoutItem, inset: CGFloat = 0, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
- return _pin(to: container, attribute: attribute, inset: inset, relation: relation)
- }
-
- /// Pins the edge to the safe area of the view controller. Falls back to
- /// layout guides (`.topLayoutGuide` and `.bottomLayoutGuide` on iOS 10.
- @discardableResult func pinToSafeArea(of vc: UIViewController, inset: CGFloat = 0, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
- let item2: Any, attr2: NSLayoutConstraint.Attribute
- if #available(iOS 11, tvOS 11, *) {
- // Pin to `safeAreaLayoutGuide` on iOS 11
- (item2, attr2) = (vc.view.safeAreaLayoutGuide, self.attribute)
- } else {
- switch self.attribute {
- // Fall back to .topLayoutGuide and pin to it's .bottom.
- case .top: (item2, attr2) = (vc.topLayoutGuide, .bottom)
- // Fall back to .bottomLayoutGuide and pin to it's .top.
- case .bottom: (item2, attr2) = (vc.bottomLayoutGuide, .top)
- // There are no layout guides for .left and .right, so just pin
- // to the superview instead.
- default: (item2, attr2) = (vc.view!, self.attribute)
- }
- }
- return _pin(to: item2, attribute: attr2, inset: inset, relation: relation)
- }
-
- // Pin the anchor to another layout item.
- private func _pin(to item2: Any, attribute attr2: NSLayoutConstraint.Attribute, inset: CGFloat, relation: NSLayoutConstraint.Relation) -> NSLayoutConstraint {
- // Invert attribute and relation in certain cases. The `pin` semantics
- // are inspired by https://github.com/PureLayout/PureLayout
- let isInverted = [.trailing, .right, .bottom].contains(attribute)
- return Constraints.constrain(self, toItem: item2, attribute: attr2, offset: (isInverted ? -inset : inset), relation: (isInverted ? relation.inverted : relation))
- }
-}
-
-// MARK: - SheetAnchors (SheetAnchorType.Center)
-
-extension SheetAnchor where Type: SheetAnchorType.Center {
- /// Aligns the axis with a superview axis.
- @discardableResult func alignWithSuperview(offset: CGFloat = 0, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
- return align(with: SheetAnchor(self.item.superview!, self.attribute) + offset, relation: relation)
- }
-}
-
-// MARK: - SheetAnchors (SheetAnchorType.Dimension)
-
-extension SheetAnchor where Type: SheetAnchorType.Dimension {
- /// Sets the dimension to a specific size.
- @discardableResult func set(_ constant: CGFloat, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
- return Constraints.constrain(item: item, attribute: attribute, relatedBy: relation, constant: constant)
- }
-
- @discardableResult func match(_ anchor: SheetAnchor, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
- return Constraints.constrain(self, anchor, relation: relation)
- }
-}
-
-// MARK: - SheetAnchorCollectionEdges
-
-struct SheetAnchorCollectionEdges {
- internal let item: LayoutItem
- internal let edges: [LayoutEdge]
- private var anchors: [SheetAnchor] { return edges.map { SheetAnchor(item, $0.attribute) } }
-
- /// Pins the edges of the view to the edges of the superview so the the view
- /// fills the available space in a container.
- @discardableResult func pinToSuperview(insets: UIEdgeInsets = .zero, relation: NSLayoutConstraint.Relation = .equal) -> [NSLayoutConstraint] {
- return anchors.map { $0.pinToSuperview(inset: insets.inset(for: $0.attribute), relation: relation) }
- }
-
- /// Pins the edges of the view to the margins of the superview so the the view
- /// fills the available space in a container.
- @discardableResult func pinToSuperviewMargins(insets: UIEdgeInsets = .zero, relation: NSLayoutConstraint.Relation = .equal) -> [NSLayoutConstraint] {
- return anchors.map { $0.pinToSuperviewMargin(inset: insets.inset(for: $0.attribute), relation: relation) }
- }
-
- /// Pins the edges of the view to the edges of the given view so the the
- /// view fills the available space in a container.
- @discardableResult func pin(to item2: LayoutItem, insets: UIEdgeInsets = .zero, relation: NSLayoutConstraint.Relation = .equal) -> [NSLayoutConstraint] {
- return anchors.map { $0.pin(to: item2, inset: insets.inset(for: $0.attribute), relation: relation) }
- }
-
- /// Pins the edges to the safe area of the view controller.
- /// Falls back to layout guides on iOS 10.
- @discardableResult func pinToSafeArea(of vc: UIViewController, insets: UIEdgeInsets = .zero, relation: NSLayoutConstraint.Relation = .equal) -> [NSLayoutConstraint] {
- return anchors.map { $0.pinToSafeArea(of: vc, inset: insets.inset(for: $0.attribute), relation: relation) }
- }
-}
-
-// MARK: - SheetAnchorCollectionCenter
-
-struct SheetAnchorCollectionCenter {
- internal let x: SheetAnchor
- internal let y: SheetAnchor
-
- /// Centers the view in the superview.
- @discardableResult func alignWithSuperview() -> [NSLayoutConstraint] {
- return [x.alignWithSuperview(), y.alignWithSuperview()]
- }
-
- /// Makes the axis equal to the other collection of axis.
- @discardableResult func align(with anchors: SheetAnchorCollectionCenter) -> [NSLayoutConstraint] {
- return [x.align(with: anchors.x), y.align(with: anchors.y)]
- }
-}
-
-
-// MARK: - SheetAnchorCollectionSize
-
-struct SheetAnchorCollectionSize {
- internal let width: SheetAnchor
- internal let height: SheetAnchor
-
- /// Set the size of item.
- @discardableResult func set(_ size: CGSize, relation: NSLayoutConstraint.Relation = .equal) -> [NSLayoutConstraint] {
- return [width.set(size.width, relation: relation), height.set(size.height, relation: relation)]
- }
-
- /// Makes the size of the item equal to the size of the other item.
- @discardableResult func match(_ anchors: SheetAnchorCollectionSize, insets: CGSize = .zero, multiplier: CGFloat = 1, relation: NSLayoutConstraint.Relation = .equal) -> [NSLayoutConstraint] {
- return [width.match(anchors.width * multiplier - insets.width, relation: relation),
- height.match(anchors.height * multiplier - insets.height, relation: relation)]
- }
-}
-
-// MARK: - Constraints
-
-final class Constraints {
- var constraints = [NSLayoutConstraint]()
-
- /// All of the constraints created in the given closure are automatically
- /// activated at the same time. This is more efficient then installing them
- /// one-be-one. More importantly, it allows to make changes to the constraints
- /// before they are installed (e.g. change `priority`).
- @discardableResult init(_ closure: () -> Void) {
- Constraints._stack.append(self)
- closure() // create constraints
- Constraints._stack.removeLast()
- NSLayoutConstraint.activate(constraints)
- }
-
- /// Creates and automatically installs a constraint.
- internal static func constrain(item item1: Any, attribute attr1: NSLayoutConstraint.Attribute, relatedBy relation: NSLayoutConstraint.Relation = .equal, toItem item2: Any? = nil, attribute attr2: NSLayoutConstraint.Attribute? = nil, multiplier: CGFloat = 1, constant: CGFloat = 0) -> NSLayoutConstraint {
- precondition(Thread.isMainThread, "Yalta APIs can only be used from the main thread")
- (item1 as? UIView)?.translatesAutoresizingMaskIntoConstraints = false
- let constraint = NSLayoutConstraint(item: item1, attribute: attr1, relatedBy: relation, toItem: item2, attribute: attr2 ?? .notAnAttribute, multiplier: multiplier, constant: constant)
- _install(constraint)
- return constraint
- }
-
- /// Creates and automatically installs a constraint between two anchors.
- internal static func constrain(_ lhs: SheetAnchor, _ rhs: SheetAnchor, offset: CGFloat = 0, multiplier: CGFloat = 1, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
- return constrain(item: lhs.item, attribute: lhs.attribute, relatedBy: relation, toItem: rhs.item, attribute: rhs.attribute, multiplier: (multiplier / lhs.multiplier) * rhs.multiplier, constant: offset - lhs.offset + rhs.offset)
- }
-
- /// Creates and automatically installs a constraint between an anchor and
- /// a given item.
- internal static func constrain(_ lhs: SheetAnchor, toItem item2: Any?, attribute attr2: NSLayoutConstraint.Attribute?, offset: CGFloat = 0, multiplier: CGFloat = 1, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
- return constrain(item: lhs.item, attribute: lhs.attribute, relatedBy: relation, toItem: item2, attribute: attr2, multiplier: multiplier / lhs.multiplier, constant: offset - lhs.offset)
- }
-
- private static var _stack = [Constraints]() // this is what enabled constraint auto-installing
-
- private static func _install(_ constraint: NSLayoutConstraint) {
- if _stack.isEmpty { // not creating a group of constraints
- constraint.isActive = true
- } else { // remember which constaints to install when group is completed
- let group = _stack.last!
- group.constraints.append(constraint)
- }
- }
-}
-
-// MARK: - UIView + Constraints
-
-extension UIView {
- @discardableResult @nonobjc func addSubview(_ a: UIView, constraints: (LayoutProxy) -> Void) -> Constraints {
- addSubview(a)
- return Constraints(for: a, constraints)
- }
-
- @discardableResult @nonobjc func addSubview(_ a: UIView, _ b: UIView, constraints: (LayoutProxy, LayoutProxy) -> Void) -> Constraints {
- [a, b].forEach { addSubview($0) }
- return Constraints(for: a, b, constraints)
- }
-
- @discardableResult @nonobjc func addSubview(_ a: UIView, _ b: UIView, _ c: UIView, constraints: (LayoutProxy, LayoutProxy, LayoutProxy) -> Void) -> Constraints {
- [a, b, c].forEach { addSubview($0) }
- return Constraints(for: a, b, c, constraints)
- }
-
- @discardableResult @nonobjc func addSubview(_ a: UIView, _ b: UIView, _ c: UIView, _ d: UIView, constraints: (LayoutProxy, LayoutProxy, LayoutProxy, LayoutProxy) -> Void) -> Constraints {
- [a, b, c, d].forEach { addSubview($0) }
- return Constraints(for: a, b, c, d, constraints)
- }
-}
-
-// MARK: - Constraints (Arity)
-
-extension Constraints {
- @discardableResult convenience init(for a: A, _ closure: (LayoutProxy) -> Void) {
- self.init { closure(a.al) }
- }
-
- @discardableResult convenience init(for a: A, _ b: B, _ closure: (LayoutProxy, LayoutProxy) -> Void) {
- self.init { closure(a.al, b.al) }
- }
-
- @discardableResult convenience init(for a: A, _ b: B, _ c: C, _ closure: (LayoutProxy, LayoutProxy, LayoutProxy) -> Void) {
- self.init { closure(a.al, b.al, c.al) }
- }
-
- @discardableResult convenience init(for a: A, _ b: B, _ c: C, _ d: D, _ closure: (LayoutProxy, LayoutProxy, LayoutProxy, LayoutProxy) -> Void) {
- self.init { closure(a.al, b.al, c.al, d.al) }
- }
-}
-
-// MARK: - Misc
-
-enum LayoutEdge {
- case top, bottom, leading, trailing, left, right
-
- internal var attribute: NSLayoutConstraint.Attribute {
- switch self {
- case .top: return .top; case .bottom: return .bottom
- case .leading: return .leading; case .trailing: return .trailing
- case .left: return .left; case .right: return .right
- }
- }
-}
-
-internal extension NSLayoutConstraint.Attribute {
- var toMargin: NSLayoutConstraint.Attribute {
- switch self {
- case .top: return .topMargin; case .bottom: return .bottomMargin
- case .leading: return .leadingMargin; case .trailing: return .trailingMargin
- case .left: return .leftMargin case .right: return .rightMargin
- default: return self
- }
- }
-}
-
-internal extension NSLayoutConstraint.Relation {
- var inverted: NSLayoutConstraint.Relation {
- switch self {
- case .greaterThanOrEqual: return .lessThanOrEqual
- case .lessThanOrEqual: return .greaterThanOrEqual
- case .equal: return self
- @unknown default:
- return self
- }
- }
-}
-
-internal extension UIEdgeInsets {
- func inset(for attribute: NSLayoutConstraint.Attribute) -> CGFloat {
- switch attribute {
- case .top: return top; case .bottom: return bottom
- case .left, .leading: return left
- case .right, .trailing: return right
- default: return 0
- }
- }
-}
-
-// MARK: - Deprecated
-
-extension SheetAnchor where Type: SheetAnchorType.Alignment {
- @available(*, deprecated, message: "Please use operators instead, e.g. `view.top.align(with: view.bottom * 2 + 10)`.")
- @discardableResult func align(with anchor: SheetAnchor, offset: CGFloat = 0, multiplier: CGFloat = 1, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
- return Constraints.constrain(self, anchor, offset: offset, multiplier: multiplier, relation: relation)
- }
-}
-
-extension SheetAnchor where Type: SheetAnchorType.Dimension {
- @available(*, deprecated, message: "Please use operators instead, e.g. `view.width.match(view.height * 2 + 10)`.")
- @discardableResult func match(_ anchor: SheetAnchor, offset: CGFloat = 0, multiplier: CGFloat = 1, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
- return Constraints.constrain(self, anchor, offset: offset, multiplier: multiplier, relation: relation)
- }
-}
diff --git a/YooKassaPayments/Public/InputData/BankCardRepeatModuleInputData.swift b/YooKassaPayments/Public/InputData/BankCardRepeatModuleInputData.swift
index f9ea8fb4..eb6cb1c3 100644
--- a/YooKassaPayments/Public/InputData/BankCardRepeatModuleInputData.swift
+++ b/YooKassaPayments/Public/InputData/BankCardRepeatModuleInputData.swift
@@ -31,6 +31,10 @@ public struct BankCardRepeatModuleInputData {
/// Setting for saving payment method.
let savePaymentMethod: SavePaymentMethod
+ /// Gateway ID. Setup, is provided at check in YooKassa.
+ /// The cashier at the division of payment flows within a single account.
+ let gatewayId: String?
+
/// Creates instance of `BankCardRepeatModuleInputData`.
///
/// - Parameters:
@@ -44,6 +48,8 @@ public struct BankCardRepeatModuleInputData {
/// - isLoggingEnabled: Enable logging
/// - customizationSettings: Settings to customize SDK interface.
/// - savePaymentMethod: Setting for saving payment method.
+ /// - gatewayId: Gateway ID. Setup, is provided at check in YooKassa.
+ /// The cashier at the division of payment flows within a single account.
///
/// - Returns: Instance of `BankCardRepeatModuleInputData`.
public init(
@@ -56,7 +62,8 @@ public struct BankCardRepeatModuleInputData {
returnUrl: String? = nil,
isLoggingEnabled: Bool = false,
customizationSettings: CustomizationSettings = CustomizationSettings(),
- savePaymentMethod: SavePaymentMethod
+ savePaymentMethod: SavePaymentMethod,
+ gatewayId: String? = nil
) {
self.clientApplicationKey = (clientApplicationKey + ":").base64Encoded()
self.shopName = shopName
@@ -68,5 +75,6 @@ public struct BankCardRepeatModuleInputData {
self.isLoggingEnabled = isLoggingEnabled
self.customizationSettings = customizationSettings
self.savePaymentMethod = savePaymentMethod
+ self.gatewayId = gatewayId
}
}
diff --git a/YooKassaPayments/Public/InputData/TokenizationModuleInputData.swift b/YooKassaPayments/Public/InputData/TokenizationModuleInputData.swift
index 4e9dfc98..d6a39631 100644
--- a/YooKassaPayments/Public/InputData/TokenizationModuleInputData.swift
+++ b/YooKassaPayments/Public/InputData/TokenizationModuleInputData.swift
@@ -47,6 +47,10 @@ public struct TokenizationModuleInputData {
/// Money center authorization identifier.
let moneyAuthClientId: String?
+
+ /// Application scheme for returning after opening a deeplink.
+ /// Example: myapplication://
+ let applicationScheme: String?
/// Creates instance of `TokenizationModuleInputData`.
///
@@ -68,6 +72,7 @@ public struct TokenizationModuleInputData {
/// - customizationSettings: Settings to customize SDK interface.
/// - savePaymentMethod: Setting for saving payment method.
/// - moneyAuthClientId: Money center authorization identifier
+ /// - applicationScheme: Application scheme for returning after opening a deeplink.
///
/// - Returns: Instance of `TokenizationModuleInputData`.
public init(
@@ -85,7 +90,8 @@ public struct TokenizationModuleInputData {
userPhoneNumber: String? = nil,
customizationSettings: CustomizationSettings = CustomizationSettings(),
savePaymentMethod: SavePaymentMethod,
- moneyAuthClientId: String? = nil
+ moneyAuthClientId: String? = nil,
+ applicationScheme: String? = nil
) {
self.clientApplicationKey = (clientApplicationKey + ":").base64Encoded()
self.shopName = shopName
@@ -102,5 +108,6 @@ public struct TokenizationModuleInputData {
self.customizationSettings = customizationSettings
self.savePaymentMethod = savePaymentMethod
self.moneyAuthClientId = moneyAuthClientId
+ self.applicationScheme = applicationScheme
}
}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/Common/avatar.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/Common/avatar.imageset/Contents.json
index c9b4dfed..975e7427 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/Common/avatar.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/Common/avatar.imageset/Contents.json
@@ -3,6 +3,16 @@
{
"filename" : "avatar.pdf",
"idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "avatar-1.pdf",
+ "idiom" : "universal"
}
],
"info" : {
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/Common/avatar.imageset/avatar-1.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/Common/avatar.imageset/avatar-1.pdf
new file mode 100644
index 00000000..6ba574c9
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/Common/avatar.imageset/avatar-1.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.AmericanExpress.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.AmericanExpress.imageset/Contents.json
index 83a66311..11ba9ac4 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.AmericanExpress.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.AmericanExpress.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "americanExpress.pdf"
+ "filename" : "PaymentMethod.AmericanExpress.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.AmericanExpress.imageset/PaymentMethod.AmericanExpress.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.AmericanExpress.imageset/PaymentMethod.AmericanExpress.pdf
new file mode 100644
index 00000000..a4a1ff9f
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.AmericanExpress.imageset/PaymentMethod.AmericanExpress.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.AmericanExpress.imageset/americanExpress.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.AmericanExpress.imageset/americanExpress.pdf
deleted file mode 100644
index 939796a8..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.AmericanExpress.imageset/americanExpress.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Cup.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Cup.imageset/Contents.json
index 387809ed..745cf7a7 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Cup.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Cup.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "cup.pdf"
+ "filename" : "PaymentMethod.Cup.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Cup.imageset/PaymentMethod.Cup.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Cup.imageset/PaymentMethod.Cup.pdf
new file mode 100644
index 00000000..35b17bca
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Cup.imageset/PaymentMethod.Cup.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Cup.imageset/cup.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Cup.imageset/cup.pdf
deleted file mode 100644
index 0bfde833..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Cup.imageset/cup.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Dankort.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Dankort.imageset/Contents.json
index 51c4418f..475af0fd 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Dankort.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Dankort.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "dankort.pdf"
+ "filename" : "PaymentMethod.Dankort.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Dankort.imageset/PaymentMethod.Dankort.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Dankort.imageset/PaymentMethod.Dankort.pdf
new file mode 100644
index 00000000..67a4a7ef
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Dankort.imageset/PaymentMethod.Dankort.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Dankort.imageset/dankort.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Dankort.imageset/dankort.pdf
deleted file mode 100644
index 0ca7e50f..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Dankort.imageset/dankort.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DinersClub.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DinersClub.imageset/Contents.json
index 20c9a5b4..e9fee15c 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DinersClub.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DinersClub.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "dinersClub.pdf"
+ "filename" : "PaymentMethod.DinersClub.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DinersClub.imageset/PaymentMethod.DinersClub.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DinersClub.imageset/PaymentMethod.DinersClub.pdf
new file mode 100644
index 00000000..f7435285
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DinersClub.imageset/PaymentMethod.DinersClub.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DinersClub.imageset/dinersClub.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DinersClub.imageset/dinersClub.pdf
deleted file mode 100644
index 33e566dc..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DinersClub.imageset/dinersClub.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DiscoverCard.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DiscoverCard.imageset/Contents.json
index d2e8e636..38009de9 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DiscoverCard.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DiscoverCard.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "discoverCard.pdf"
+ "filename" : "PaymentMethod.DiscoverCard.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DiscoverCard.imageset/PaymentMethod.DiscoverCard.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DiscoverCard.imageset/PaymentMethod.DiscoverCard.pdf
new file mode 100644
index 00000000..803267d0
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DiscoverCard.imageset/PaymentMethod.DiscoverCard.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DiscoverCard.imageset/discoverCard.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DiscoverCard.imageset/discoverCard.pdf
deleted file mode 100644
index 4dafc220..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.DiscoverCard.imageset/discoverCard.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Instapay.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Instapay.imageset/Contents.json
index e8df349c..0a8ab126 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Instapay.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Instapay.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "instapay.pdf"
+ "filename" : "PaymentMethod.Instapay.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Instapay.imageset/PaymentMethod.Instapay.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Instapay.imageset/PaymentMethod.Instapay.pdf
new file mode 100644
index 00000000..4a3f58ed
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Instapay.imageset/PaymentMethod.Instapay.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Instapay.imageset/instapay.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Instapay.imageset/instapay.pdf
deleted file mode 100644
index 4f6acf63..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Instapay.imageset/instapay.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Jcb.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Jcb.imageset/Contents.json
index c28dcb66..83a2a5f3 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Jcb.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Jcb.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "jcb.pdf"
+ "filename" : "PaymentMethod.Jcb.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Jcb.imageset/PaymentMethod.Jcb.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Jcb.imageset/PaymentMethod.Jcb.pdf
new file mode 100644
index 00000000..4c37d59d
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Jcb.imageset/PaymentMethod.Jcb.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Jcb.imageset/jcb.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Jcb.imageset/jcb.pdf
deleted file mode 100644
index dcd592b4..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Jcb.imageset/jcb.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Lazer.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Lazer.imageset/Contents.json
index 669470b3..b181ad2e 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Lazer.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Lazer.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "lazer.pdf"
+ "filename" : "PaymentMethod.Lazer.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Lazer.imageset/PaymentMethod.Lazer.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Lazer.imageset/PaymentMethod.Lazer.pdf
new file mode 100644
index 00000000..4cf072ee
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Lazer.imageset/PaymentMethod.Lazer.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Lazer.imageset/lazer.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Lazer.imageset/lazer.pdf
deleted file mode 100644
index 21e6faef..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Lazer.imageset/lazer.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Maestro.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Maestro.imageset/Contents.json
index bbf2094c..a08c4afc 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Maestro.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Maestro.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "maestro.pdf"
+ "filename" : "PaymentMethod.Maestro.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Maestro.imageset/PaymentMethod.Maestro.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Maestro.imageset/PaymentMethod.Maestro.pdf
new file mode 100644
index 00000000..fb4b5b54
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Maestro.imageset/PaymentMethod.Maestro.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Maestro.imageset/maestro.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Maestro.imageset/maestro.pdf
deleted file mode 100644
index 0cd8ceef..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Maestro.imageset/maestro.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mastercard.imageset/PaymentMethod.Mastercard.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mastercard.imageset/PaymentMethod.Mastercard.pdf
index 3e9a98e0..000e5bee 100644
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mastercard.imageset/PaymentMethod.Mastercard.pdf and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mastercard.imageset/PaymentMethod.Mastercard.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mir.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mir.imageset/Contents.json
index 0e5cc298..4ae3e800 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mir.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mir.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "mir.pdf"
+ "filename" : "PaymentMethod.Mir.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mir.imageset/PaymentMethod.Mir.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mir.imageset/PaymentMethod.Mir.pdf
new file mode 100644
index 00000000..368df429
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mir.imageset/PaymentMethod.Mir.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mir.imageset/mir.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mir.imageset/mir.pdf
deleted file mode 100644
index cbe70086..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Mir.imageset/mir.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Sberbank.imageset/PaymentMethod.Sberbank.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Sberbank.imageset/PaymentMethod.Sberbank.pdf
deleted file mode 100644
index de0ec1bb..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Sberbank.imageset/PaymentMethod.Sberbank.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Sberbank.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Sberpay.imageset/Contents.json
similarity index 71%
rename from YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Sberbank.imageset/Contents.json
rename to YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Sberpay.imageset/Contents.json
index 481207db..19205fa3 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Sberbank.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Sberpay.imageset/Contents.json
@@ -1,7 +1,7 @@
{
"images" : [
{
- "filename" : "PaymentMethod.Sberbank.pdf",
+ "filename" : "PaymentMethod.Sberpay.pdf",
"idiom" : "universal"
}
],
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Sberpay.imageset/PaymentMethod.Sberpay.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Sberpay.imageset/PaymentMethod.Sberpay.pdf
new file mode 100644
index 00000000..1ad84e29
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Sberpay.imageset/PaymentMethod.Sberpay.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Solo.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Solo.imageset/Contents.json
index cc0d0988..1b5359bf 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Solo.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Solo.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "solo.pdf"
+ "filename" : "PaymentMethod.Solo.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Solo.imageset/PaymentMethod.Solo.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Solo.imageset/PaymentMethod.Solo.pdf
new file mode 100644
index 00000000..f2059f48
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Solo.imageset/PaymentMethod.Solo.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Solo.imageset/solo.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Solo.imageset/solo.pdf
deleted file mode 100644
index 8246c7a6..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Solo.imageset/solo.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Switch.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Switch.imageset/Contents.json
index fed15410..5ad74f9b 100644
--- a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Switch.imageset/Contents.json
+++ b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Switch.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "switch.pdf"
+ "filename" : "PaymentMethod.Switch.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Switch.imageset/PaymentMethod.Switch.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Switch.imageset/PaymentMethod.Switch.pdf
new file mode 100644
index 00000000..b5327e9a
Binary files /dev/null and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Switch.imageset/PaymentMethod.Switch.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Switch.imageset/switch.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Switch.imageset/switch.pdf
deleted file mode 100644
index e695a4e4..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Switch.imageset/switch.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Visa.imageset/PaymentMethod.Visa.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Visa.imageset/PaymentMethod.Visa.pdf
index 574ec52b..1f8d8ceb 100644
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Visa.imageset/PaymentMethod.Visa.pdf and b/YooKassaPayments/Public/Resources/Media.xcassets/PaymentMethods/PaymentMethod.Visa.imageset/PaymentMethod.Visa.pdf differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/Contents.json
deleted file mode 100644
index da4a164c..00000000
--- a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/Contents.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-}
\ No newline at end of file
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_maestro_textControl.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_maestro_textControl.imageset/Contents.json
deleted file mode 100644
index 88e9b6c1..00000000
--- a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_maestro_textControl.imageset/Contents.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "images" : [
- {
- "idiom" : "universal",
- "filename" : "paymentSystem_maestro_textControl.pdf"
- }
- ],
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-}
\ No newline at end of file
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_maestro_textControl.imageset/paymentSystem_maestro_textControl.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_maestro_textControl.imageset/paymentSystem_maestro_textControl.pdf
deleted file mode 100644
index ef674b84..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_maestro_textControl.imageset/paymentSystem_maestro_textControl.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_masterCard_textControl.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_masterCard_textControl.imageset/Contents.json
deleted file mode 100644
index ec842545..00000000
--- a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_masterCard_textControl.imageset/Contents.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "images" : [
- {
- "idiom" : "universal",
- "filename" : "paymentSystem_masterCard_textControl.pdf"
- }
- ],
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-}
\ No newline at end of file
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_masterCard_textControl.imageset/paymentSystem_masterCard_textControl.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_masterCard_textControl.imageset/paymentSystem_masterCard_textControl.pdf
deleted file mode 100644
index 71a3d4da..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_masterCard_textControl.imageset/paymentSystem_masterCard_textControl.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_mir_textControl.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_mir_textControl.imageset/Contents.json
deleted file mode 100644
index 34db428e..00000000
--- a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_mir_textControl.imageset/Contents.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "images" : [
- {
- "idiom" : "universal",
- "filename" : "paymentSystem_mir_textControl.pdf"
- }
- ],
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-}
\ No newline at end of file
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_mir_textControl.imageset/paymentSystem_mir_textControl.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_mir_textControl.imageset/paymentSystem_mir_textControl.pdf
deleted file mode 100644
index 2357c77c..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_mir_textControl.imageset/paymentSystem_mir_textControl.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_unknownCard_textControl.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_unknownCard_textControl.imageset/Contents.json
deleted file mode 100644
index 17c3b885..00000000
--- a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_unknownCard_textControl.imageset/Contents.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "images" : [
- {
- "idiom" : "universal",
- "filename" : "paymentSystem_unknownCard_textControl.pdf"
- }
- ],
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-}
\ No newline at end of file
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_unknownCard_textControl.imageset/paymentSystem_unknownCard_textControl.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_unknownCard_textControl.imageset/paymentSystem_unknownCard_textControl.pdf
deleted file mode 100644
index 9eb02e0f..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_unknownCard_textControl.imageset/paymentSystem_unknownCard_textControl.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_visa_textControl.imageset/Contents.json b/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_visa_textControl.imageset/Contents.json
deleted file mode 100644
index cac6ca3c..00000000
--- a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_visa_textControl.imageset/Contents.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "images" : [
- {
- "idiom" : "universal",
- "filename" : "paymentSystem_visa_textControl.pdf"
- }
- ],
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-}
\ No newline at end of file
diff --git a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_visa_textControl.imageset/paymentSystem_visa_textControl.pdf b/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_visa_textControl.imageset/paymentSystem_visa_textControl.pdf
deleted file mode 100644
index f1d88e03..00000000
Binary files a/YooKassaPayments/Public/Resources/Media.xcassets/paymentSystem_old/paymentSystem_visa_textControl.imageset/paymentSystem_visa_textControl.pdf and /dev/null differ
diff --git a/YooKassaPayments/Public/Resources/en.lproj/Localizable.strings b/YooKassaPayments/Public/Resources/en.lproj/Localizable.strings
index 5a51db03..68a4bb10 100644
--- a/YooKassaPayments/Public/Resources/en.lproj/Localizable.strings
+++ b/YooKassaPayments/Public/Resources/en.lproj/Localizable.strings
@@ -1,7 +1,7 @@
// Payment methods
"PaymentMethods.paymentMethods" = "Payment method";
-"PaymentMethod.sberbank" = "Sberbank Online";
+"PaymentMethod.sberpay" = "SberPay";
"PaymentMethod.wallet" = "YooMoney";
"PaymentMethod.applePay" = "Apple Pay";
"PaymentMethod.bankCard" = "Bank card";
@@ -16,6 +16,7 @@
"BankCardView.inputCvcPlaceholder" = "CVC";
"BankCardDataInputView.BottomHint.invalidPan" = "Check the card number";
"BankCardDataInputView.BottomHint.invalidExpiry" = "Check the month and date";
+"BankCardDataInputView.BottomHint.invalidCvc" = "Check CVC";
"BankCard.savePaymentMethod.title" = "Link a card";
// Contract
@@ -128,7 +129,7 @@
"ApplePayContract.fee" = "Commission";
-"Sberbank.Contract.Title" = "Sberbank Online";
+"Sberpay.Contract.Title" = "SberPay";
"Contract.resendSms" = "Send again";
@@ -136,6 +137,8 @@
"YooMoney.title" = "YooMoney";
+"Sberpay.paymentMethodTitle" = "Next, open the Sberbank Online application - confirm the payment";
+
"LogoutConfirmation.format.title" = "Are you sure you want to sign out of account '%@'?";
"BankCardDataInput.navigationBarTitle" = "Bank card";
diff --git a/YooKassaPayments/Public/Resources/ru.lproj/Localizable.strings b/YooKassaPayments/Public/Resources/ru.lproj/Localizable.strings
index 3841d1d5..bee4d175 100644
--- a/YooKassaPayments/Public/Resources/ru.lproj/Localizable.strings
+++ b/YooKassaPayments/Public/Resources/ru.lproj/Localizable.strings
@@ -1,7 +1,7 @@
// Payment methods
"PaymentMethods.paymentMethods" = "Способ оплаты";
-"PaymentMethod.sberbank" = "Сбербанк Онлайн";
+"PaymentMethod.sberpay" = "SberPay";
"PaymentMethod.wallet" = "ЮMoney";
"PaymentMethod.applePay" = "Apple Pay";
"PaymentMethod.bankCard" = "Банковская карта";
@@ -16,6 +16,7 @@
"BankCardView.inputCvcPlaceholder" = "CVC";
"BankCardDataInputView.BottomHint.invalidPan" = "Проверьте номер карты";
"BankCardDataInputView.BottomHint.invalidExpiry" = "Проверьте месяц и год";
+"BankCardDataInputView.BottomHint.invalidCvc" = "Проверьте CVC";
"BankCard.savePaymentMethod.title" = "Привязать карту";
// Contract
@@ -103,8 +104,6 @@
"Contract.Sberbank.PhoneInput.Placeholder" = "+ 7 987 654 32 10";
-"Sberbank.Contract.Title" = "СберБанк Онлайн";
-
/*В процессе токенизации ApplePay произошла ошибка*/
"Error.ApplePayStrategy.failTokenizeData" = "В процессе токенизации ApplePay произошла ошибка";
@@ -130,12 +129,16 @@
"ApplePayContract.fee" = "Комиссия";
+"Sberpay.Contract.Title" = "SberPay";
+
"Contract.resendSms" = "Отправить снова";
"ApplePayContract.title" = "Apple Pay";
"YooMoney.title" = "ЮMoney";
+"Sberpay.paymentMethodTitle" = "Дальше откроем приложение Сбербанк Онлайн — подтвердите оплату";
+
"LogoutConfirmation.format.title" = "Уверены, что хотите выйти из аккаунта '%@'?";
"BankCardDataInput.navigationBarTitle" = "Банковская карта";
diff --git a/YooKassaPayments/Public/TokenizationAssembly.swift b/YooKassaPayments/Public/TokenizationAssembly.swift
index 0e93b19d..2dc89a10 100644
--- a/YooKassaPayments/Public/TokenizationAssembly.swift
+++ b/YooKassaPayments/Public/TokenizationAssembly.swift
@@ -49,7 +49,14 @@ public enum TokenizationAssembly {
_ inputData: TokenizationModuleInputData,
moduleOutput: TokenizationModuleOutput
) -> UIViewController & TokenizationModuleInput {
+ YKSdk.shared.moduleOutput = moduleOutput
+ YKSdk.shared.applicationScheme = inputData.applicationScheme
+ YKSdk.shared.analyticsService = AnalyticsServiceAssembly.makeService(
+ isLoggingEnabled: inputData.isLoggingEnabled
+ )
+
let paymentMethodsModuleInputData = PaymentMethodsModuleInputData(
+ applicationScheme: inputData.applicationScheme,
clientApplicationKey: inputData.clientApplicationKey,
applePayMerchantIdentifier: inputData.applePayMerchantIdentifier,
gatewayId: inputData.gatewayId,
@@ -90,6 +97,10 @@ public enum TokenizationAssembly {
viewControllerToReturn = sheetViewController
}
+
+ YKSdk.shared.moduleOutput = moduleOutput
+ YKSdk.shared.applicationScheme = inputData.applicationScheme
+ YKSdk.shared.paymentMethodsModuleInput = moduleInput
return viewControllerToReturn
}
diff --git a/YooKassaPayments/Public/TokenizationModuleIO.swift b/YooKassaPayments/Public/TokenizationModuleIO.swift
index a488cad3..d3754c3a 100644
--- a/YooKassaPayments/Public/TokenizationModuleIO.swift
+++ b/YooKassaPayments/Public/TokenizationModuleIO.swift
@@ -7,7 +7,18 @@ public protocol TokenizationModuleInput: class {
///
/// - Parameters:
/// - requestUrl: URL string for request website.
+ @available(*, deprecated, message: "Use startConfirmationProcess(confirmationUrl:paymentMethodType:) instead")
func start3dsProcess(requestUrl: String)
+
+ /// Start confirmation process
+ ///
+ /// - Parameters:
+ /// - requestUrl: Deeplink.
+ /// - paymentMethodType: Type of the source of funds for the payment.
+ func startConfirmationProcess(
+ confirmationUrl: String,
+ paymentMethodType: PaymentMethodType
+ )
}
/// Output for tokenization module.
@@ -31,9 +42,18 @@ public protocol TokenizationModuleOutput: class {
/// - module: Input for tokenization module.
/// In the process of running mSDK, allows you to run processes using the
/// `TokenizationModuleInput` protocol methods.
+ @available(*, deprecated, message: "Use didSuccessfullyConfirmation(paymentMethodType:) instead")
func didSuccessfullyPassedCardSec(
on module: TokenizationModuleInput
)
+
+ /// Will be called when the confirmation process successfully passes.
+ ///
+ /// - Parameters:
+ /// - paymentMethodType: Type of the source of funds for the payment.
+ func didSuccessfullyConfirmation(
+ paymentMethodType: PaymentMethodType
+ )
/// Will be called when the tokenization process successfully passes.
///
diff --git a/YooKassaPayments/Public/YKSdkService.swift b/YooKassaPayments/Public/YKSdkService.swift
new file mode 100644
index 00000000..db6696ab
--- /dev/null
+++ b/YooKassaPayments/Public/YKSdkService.swift
@@ -0,0 +1,46 @@
+/// Class for handle open url.
+public final class YKSdk {
+
+ /// Input for payment methods module.
+ weak var paymentMethodsModuleInput: PaymentMethodsModuleInput?
+
+ /// Output for tokenization module.
+ weak var moduleOutput: TokenizationModuleOutput?
+
+ /// Application scheme for returning after opening a deeplink.
+ var applicationScheme: String?
+
+ var analyticsService: AnalyticsService?
+
+ private init() {}
+
+ /// Shared YooKassa sdk service.
+ public static let shared = YKSdk()
+
+ public func handleOpen(
+ url: URL,
+ sourceApplication: String?
+ ) -> Bool {
+ guard let scheme = url.scheme,
+ let applicationScheme = applicationScheme,
+ "\(scheme)://" == applicationScheme,
+ let deeplink = DeepLinkFactory.makeDeepLink(url: url) else {
+ return false
+ }
+
+ switch deeplink {
+ case .invoicingSberpay:
+ let event: AnalyticsEvent = .actionSberPayConfirmation(
+ sberPayConfirmationStatus: .success,
+ sdkVersion: Bundle.frameworkVersion
+ )
+ analyticsService?.trackEvent(event)
+ moduleOutput?.didSuccessfullyConfirmation(paymentMethodType: .sberbank)
+
+ case .yooMoneyExchange(let cryptogram):
+ paymentMethodsModuleInput?.authorizeInYooMoney(with: cryptogram)
+ }
+
+ return true
+ }
+}
diff --git a/YooKassaPaymentsExample/AppDelegate.swift b/YooKassaPaymentsExample/AppDelegate.swift
index a5bd16ce..e6311f9a 100644
--- a/YooKassaPaymentsExample/AppDelegate.swift
+++ b/YooKassaPaymentsExample/AppDelegate.swift
@@ -6,9 +6,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
- func application(_ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
-
+ func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let viewController = UINavigationController(rootViewController: RootViewController())
window?.rootViewController = viewController
@@ -48,6 +49,34 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}
+// MARK: - Handle open url
+
+extension AppDelegate {
+ func application(
+ _ application: UIApplication,
+ open url: URL,
+ sourceApplication: String?,
+ annotation: Any
+ ) -> Bool {
+ return YKSdk.shared.handleOpen(
+ url: url,
+ sourceApplication: sourceApplication
+ )
+ }
+
+ @available(iOS 9.0, *)
+ func application(
+ _ app: UIApplication,
+ open url: URL,
+ options: [UIApplication.OpenURLOptionsKey: Any] = [:]
+ ) -> Bool {
+ return YKSdk.shared.handleOpen(
+ url: url,
+ sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String
+ )
+ }
+}
+
extension AppDelegate {
private func setupAppearance() {
diff --git a/YooKassaPaymentsExample/Resources/Info.plist b/YooKassaPaymentsExample/Resources/Info.plist
index 7cd5af0f..a16071c4 100644
--- a/YooKassaPaymentsExample/Resources/Info.plist
+++ b/YooKassaPaymentsExample/Resources/Info.plist
@@ -18,8 +18,26 @@
APPL
CFBundleShortVersionString
1.0
+ CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ ${BUNDLE_ID}
+ CFBundleURLSchemes
+
+ yookassapaymentsexample
+
+
+
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
+ LSApplicationQueriesSchemes
+
+ sberpay
+ yoomoneyauth
+
LSRequiresIPhoneOS
NSAppTransportSecurity
diff --git a/YooKassaPaymentsExample/Resources/en.lproj/Localizable.strings b/YooKassaPaymentsExample/Resources/en.lproj/Localizable.strings
index c15c3a04..bfcb546d 100644
--- a/YooKassaPaymentsExample/Resources/en.lproj/Localizable.strings
+++ b/YooKassaPaymentsExample/Resources/en.lproj/Localizable.strings
@@ -16,6 +16,11 @@
"test_mode.payment_auth" = "The customer has logged into the Wallet";
"test_mode.attached_cards" = "Number of linked cards";
"test_mode.payment_error" = "Complete the payment with an error";
+"test_mode.process_type" = "Payment confirmation";
+"test_mode.process.3ds" = "3ds";
+"test_mode.process.app2app" = "app2app";
+
+"process.title" = "Confirmation";
"settings.title" = "Settings";
"settings.payment_methods.title" = "Payment methods";
diff --git a/YooKassaPaymentsExample/Resources/ru.lproj/Localizable.strings b/YooKassaPaymentsExample/Resources/ru.lproj/Localizable.strings
index 6d11c807..ea7893a9 100644
--- a/YooKassaPaymentsExample/Resources/ru.lproj/Localizable.strings
+++ b/YooKassaPaymentsExample/Resources/ru.lproj/Localizable.strings
@@ -16,6 +16,11 @@
"test_mode.payment_auth" = "Покупатель вошёл в кошелёк";
"test_mode.attached_cards" = "Количество привязанных карт";
"test_mode.payment_error" = "Завершить платёж ошибкой";
+"test_mode.process_type" = "Подтверждение платежа";
+"test_mode.process.3ds" = "3ds";
+"test_mode.process.app2app" = "app2app";
+
+"process.title" = "Тип подтверждение";
"settings.title" = "Настройки";
"settings.payment_methods.title" = "Способы оплаты";
diff --git a/YooKassaPaymentsExample/RootViewController+TokenizationModuleOutput.swift b/YooKassaPaymentsExample/RootViewController+TokenizationModuleOutput.swift
index 71b4f1c5..060c58aa 100644
--- a/YooKassaPaymentsExample/RootViewController+TokenizationModuleOutput.swift
+++ b/YooKassaPaymentsExample/RootViewController+TokenizationModuleOutput.swift
@@ -3,41 +3,82 @@ import YooKassaPayments
// MARK: - TokenizationModuleOutput
extension RootViewController: TokenizationModuleOutput {
- func tokenizationModule(_ module: TokenizationModuleInput,
- didTokenize token: Tokens,
- paymentMethodType: PaymentMethodType) {
-
+ func tokenizationModule(
+ _ module: TokenizationModuleInput,
+ didTokenize token: Tokens,
+ paymentMethodType: PaymentMethodType
+ ) {
self.token = token
self.paymentMethodType = paymentMethodType
-
+
+ if settings.testModeSettings.isTestModeEnadled,
+ let processConfirmation = settings.testModeSettings.processConfirmation {
+ switch processConfirmation {
+ case let .threeDSecure(requestUrl):
+ tokenizationModuleInput?.startConfirmationProcess(
+ confirmationUrl: requestUrl,
+ paymentMethodType: paymentMethodType
+ )
+
+ case let .app2app(confirmationUrl):
+ tokenizationModuleInput?.startConfirmationProcess(
+ confirmationUrl: confirmationUrl,
+ paymentMethodType: paymentMethodType
+ )
+ }
+ } else {
+ DispatchQueue.main.async { [weak self] in
+ guard let self = self else { return }
+
+ self.dismiss(animated: true)
+
+ let successViewController = SuccessViewController()
+ let navigationController = UINavigationController(
+ rootViewController: successViewController
+ )
+ successViewController.delegate = self
+ self.present(navigationController, animated: true)
+ }
+ }
+ }
+
+ func didFinish(
+ on module: TokenizationModuleInput,
+ with error: YooKassaPaymentsError?
+ ) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
-
self.dismiss(animated: true)
-
- let successViewController = SuccessViewController()
- let navigationController = UINavigationController(
- rootViewController: successViewController
- )
- successViewController.delegate = self
- self.present(navigationController, animated: true)
}
}
-
- func didFinish(on module: TokenizationModuleInput,
- with error: YooKassaPaymentsError?) {
+
+ func didSuccessfullyPassedCardSec(
+ on module: TokenizationModuleInput
+ ) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
+ let alertController = UIAlertController(
+ title: "3D-Sec",
+ message: "Successfully passed 3d-sec",
+ preferredStyle: .alert
+ )
+ let action = UIAlertAction(title: "OK", style: .default)
+ alertController.addAction(action)
self.dismiss(animated: true)
+ self.present(alertController, animated: true)
}
}
-
- func didSuccessfullyPassedCardSec(on module: TokenizationModuleInput) {
+
+ func didSuccessfullyConfirmation(
+ paymentMethodType: PaymentMethodType
+ ) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
- let alertController = UIAlertController(title: "3D-Sec",
- message: "Successfully passed 3d-sec",
- preferredStyle: .alert)
+ let alertController = UIAlertController(
+ title: "Confirmation",
+ message: "Successfully confirmation",
+ preferredStyle: .alert
+ )
let action = UIAlertAction(title: "OK", style: .default)
alertController.addAction(action)
self.dismiss(animated: true)
diff --git a/YooKassaPaymentsExample/Source/Models/ProcessConfirmation.swift b/YooKassaPaymentsExample/Source/Models/ProcessConfirmation.swift
new file mode 100644
index 00000000..27bc4707
--- /dev/null
+++ b/YooKassaPaymentsExample/Source/Models/ProcessConfirmation.swift
@@ -0,0 +1,71 @@
+enum ProcessConfirmation {
+ case threeDSecure(String)
+ case app2app(String)
+
+ static var allCasesWithNil: [ProcessConfirmation?] = [
+ nil,
+ .threeDSecure(""),
+ .app2app("sberpay://invoicing/v2?redirect_uri="),
+ ]
+}
+
+extension ProcessConfirmation {
+ var description: String {
+ let value: String
+ switch self {
+ case .threeDSecure:
+ value = translate(Localized.process3ds)
+ case .app2app:
+ value = translate(Localized.processApp2App)
+ }
+ return value
+ }
+
+ var url: String {
+ let value: String
+ switch self {
+ case let .threeDSecure(requestUrl):
+ value = requestUrl
+ case let .app2app(confirmationUrl):
+ value = confirmationUrl
+ }
+ return value
+ }
+
+ private enum Localized: String {
+ case process3ds = "test_mode.process.3ds"
+ case processApp2App = "test_mode.process.app2app"
+ }
+}
+
+extension ProcessConfirmation: Codable {
+ enum CodingKeys: CodingKey {
+ case threeDSecure
+ case app2app
+ }
+
+ init(from decoder: Decoder) throws {
+ let values = try decoder.container(keyedBy: CodingKeys.self)
+ if let value = try? values.decode(String.self, forKey: .threeDSecure) {
+ self = .threeDSecure(value)
+ } else if let value = try? values.decode(String.self, forKey: .app2app) {
+ self = .app2app(value)
+ } else {
+ throw DecodingError.dataCorrupted
+ }
+ }
+
+ func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ switch self {
+ case let .threeDSecure(requestUrl):
+ try container.encode(requestUrl, forKey: .threeDSecure)
+ case let .app2app(confirmationUrl):
+ try container.encode(confirmationUrl, forKey: .app2app)
+ }
+ }
+
+ enum DecodingError: Error {
+ case dataCorrupted
+ }
+}
diff --git a/YooKassaPaymentsExample/Source/Models/TestSettings.swift b/YooKassaPaymentsExample/Source/Models/TestSettings.swift
index b12a246f..75329a53 100644
--- a/YooKassaPaymentsExample/Source/Models/TestSettings.swift
+++ b/YooKassaPaymentsExample/Source/Models/TestSettings.swift
@@ -5,4 +5,5 @@ struct TestSettings {
var isPaymentAuthorizationPassed = false
var isPaymentWithError = false
var cardsCount: Int? = 2
+ var processConfirmation: ProcessConfirmation?
}
diff --git a/YooKassaPaymentsExample/Source/Services/SettingsService.swift b/YooKassaPaymentsExample/Source/Services/SettingsService.swift
index 452b9585..3d459afd 100644
--- a/YooKassaPaymentsExample/Source/Services/SettingsService.swift
+++ b/YooKassaPaymentsExample/Source/Services/SettingsService.swift
@@ -1,36 +1,47 @@
import Foundation
final class SettingsService {
-
+
// MARK: - Private properties
-
+
private let storage: KeyValueStoring
-
+
// MARK: - Initialization/Deinitialization
-
+
init(storage: KeyValueStoring) {
self.storage = storage
}
-
+
func loadSettingsFromStorage() -> Settings? {
if let isTestModeEnadled = storage.getBool(for: Constants.isTestModeEnadledKey),
let isPaymentAuthorizationPassed = storage.getBool(for: Constants.isPaymentAuthorizationPassedKey),
let isPaymentWithError = storage.getBool(for: Constants.isPaymentWithErrorKey) {
-
+
let cardsCount = storage.getInt(for: Constants.cardsCountKey)
-
- let testSettings = TestSettings(isTestModeEnadled: isTestModeEnadled,
- isPaymentAuthorizationPassed: isPaymentAuthorizationPassed,
- isPaymentWithError: isPaymentWithError,
- cardsCount: cardsCount)
-
+
+ var processConfirmation: ProcessConfirmation?
+ if let processConfirmationData = storage.getAny(for: Constants.processConfirmationKey) as? Data {
+ processConfirmation = try? JSONDecoder().decode(
+ ProcessConfirmation.self,
+ from: processConfirmationData
+ )
+ }
+
+ let testSettings = TestSettings(
+ isTestModeEnadled: isTestModeEnadled,
+ isPaymentAuthorizationPassed: isPaymentAuthorizationPassed,
+ isPaymentWithError: isPaymentWithError,
+ cardsCount: cardsCount,
+ processConfirmation: processConfirmation
+ )
+
if let isYooMoneyEnabled = storage.getBool(for: Constants.isYooMoneyEnabledKey),
let isBankCardEnabled = storage.getBool(for: Constants.isBankCardEnabledKey),
let isApplePayEnabled = storage.getBool(for: Constants.isApplePayEnabledKey),
let isSberbankEnabled = storage.getBool(for: Constants.isSberbankEnabledKey),
let isShowingYooMoneyLogoEnabled = storage.getBool(for: Constants.isShowingYooMoneyLogoEnabledKey),
let price = storage.getDecimal(for: Constants.priceKey) {
-
+
return Settings(
isYooMoneyEnabled: isYooMoneyEnabled,
isBankCardEnabled: isBankCardEnabled,
@@ -42,32 +53,56 @@ final class SettingsService {
)
}
}
-
+
return nil
}
-
+
func saveSettingsToStorage(settings: Settings) {
- storage.setBool(settings.isYooMoneyEnabled,
- for: Constants.isYooMoneyEnabledKey)
- storage.setBool(settings.isBankCardEnabled,
- for: Constants.isBankCardEnabledKey)
- storage.setBool(settings.isApplePayEnabled,
- for: Constants.isApplePayEnabledKey)
- storage.setBool(settings.isSberbankEnabled,
- for: Constants.isSberbankEnabledKey)
- storage.setBool(settings.isShowingYooMoneyLogoEnabled,
- for: Constants.isShowingYooMoneyLogoEnabledKey)
- storage.setDecimal(settings.price,
- for: Constants.priceKey)
-
- storage.setBool(settings.testModeSettings.isTestModeEnadled,
- for: Constants.isTestModeEnadledKey)
- storage.setBool(settings.testModeSettings.isPaymentAuthorizationPassed,
- for: Constants.isPaymentAuthorizationPassedKey)
- storage.setBool(settings.testModeSettings.isPaymentWithError,
- for: Constants.isPaymentWithErrorKey)
- storage.setInt(settings.testModeSettings.cardsCount,
- for: Constants.cardsCountKey)
+ storage.setBool(
+ settings.isYooMoneyEnabled,
+ for: Constants.isYooMoneyEnabledKey
+ )
+ storage.setBool(
+ settings.isBankCardEnabled,
+ for: Constants.isBankCardEnabledKey
+ )
+ storage.setBool(
+ settings.isApplePayEnabled,
+ for: Constants.isApplePayEnabledKey
+ )
+ storage.setBool(
+ settings.isSberbankEnabled,
+ for: Constants.isSberbankEnabledKey
+ )
+ storage.setBool(
+ settings.isShowingYooMoneyLogoEnabled,
+ for: Constants.isShowingYooMoneyLogoEnabledKey
+ )
+ storage.setDecimal(
+ settings.price,
+ for: Constants.priceKey
+ )
+
+ storage.setBool(
+ settings.testModeSettings.isTestModeEnadled,
+ for: Constants.isTestModeEnadledKey
+ )
+ storage.setBool(
+ settings.testModeSettings.isPaymentAuthorizationPassed,
+ for: Constants.isPaymentAuthorizationPassedKey
+ )
+ storage.setBool(
+ settings.testModeSettings.isPaymentWithError,
+ for: Constants.isPaymentWithErrorKey
+ )
+ storage.setInt(
+ settings.testModeSettings.cardsCount,
+ for: Constants.cardsCountKey
+ )
+ storage.setAny(
+ try? JSONEncoder().encode(settings.testModeSettings.processConfirmation),
+ for: Constants.processConfirmationKey
+ )
}
}
@@ -79,10 +114,12 @@ private extension SettingsService {
static let isSberbankEnabledKey = "isSberbankEnabled"
static let isShowingYooMoneyLogoEnabledKey = "isShowingYooMoneyLogoEnabled"
static let priceKey = "price"
-
+
static let isTestModeEnadledKey = "isTestModeEnadled"
static let isPaymentAuthorizationPassedKey = "isPaymentAuthorizationPassed"
static let isPaymentWithErrorKey = "isPaymentWithError"
static let cardsCountKey = "cardsCount"
+
+ static let processConfirmationKey = "processConfirmation"
}
}
diff --git a/YooKassaPaymentsExample/Source/UserStories/Cards/AttachedCardCountViewController.swift b/YooKassaPaymentsExample/Source/UserStories/Cards/AttachedCardCountViewController.swift
index 49e0e265..26217b89 100644
--- a/YooKassaPaymentsExample/Source/UserStories/Cards/AttachedCardCountViewController.swift
+++ b/YooKassaPaymentsExample/Source/UserStories/Cards/AttachedCardCountViewController.swift
@@ -1,19 +1,21 @@
import UIKit
protocol AttachedCardCountViewControllerDelegate: class {
- func attachedCardCountViewController(_ attachedCardCountViewController: AttachedCardCountViewController,
- didSaveCardCount cardCount: Int?)
+ func attachedCardCountViewController(
+ _ attachedCardCountViewController: AttachedCardCountViewController,
+ didSaveCardCount cardCount: Int?
+ )
}
final class AttachedCardCountViewController: UIViewController {
- public static func makeModule(cardCount: Int?,
- delegate: AttachedCardCountViewControllerDelegate? = nil) -> UIViewController {
-
+ public static func makeModule(
+ cardCount: Int?,
+ delegate: AttachedCardCountViewControllerDelegate? = nil
+ ) -> UIViewController {
let controller = AttachedCardCountViewController()
controller.delegate = delegate
controller.initialCardCount = cardCount
-
return controller
}
@@ -36,10 +38,12 @@ final class AttachedCardCountViewController: UIViewController {
return $0
}(UIPickerView())
- fileprivate lazy var saveBarItem = UIBarButtonItem(title: translate(CommonLocalized.save),
- style: .plain,
- target: self,
- action: #selector(saveButtonDidPress))
+ fileprivate lazy var saveBarItem = UIBarButtonItem(
+ title: translate(CommonLocalized.save),
+ style: .plain,
+ target: self,
+ action: #selector(saveButtonDidPress)
+ )
// MARK: - Managing the View
@@ -73,34 +77,49 @@ final class AttachedCardCountViewController: UIViewController {
@objc
private func saveButtonDidPress() {
let cardCount = countPickerView.selectedRow(inComponent: 0)
- delegate?.attachedCardCountViewController(self,
- didSaveCardCount: cardCount == 0 ? nil : cardCount)
+ delegate?.attachedCardCountViewController(
+ self,
+ didSaveCardCount: cardCount == 0 ? nil : cardCount
+ )
navigationController?.popViewController(animated: true)
}
}
extension AttachedCardCountViewController: UIPickerViewDelegate {
-
- func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
+ func pickerView(
+ _ pickerView: UIPickerView,
+ titleForRow row: Int,
+ forComponent component: Int
+ ) -> String? {
return cards[row] == 0 ? translate(CommonLocalized.none) : String(cards[row])
}
- func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
+ func pickerView(
+ _ pickerView: UIPickerView,
+ rowHeightForComponent component: Int
+ ) -> CGFloat {
return Space.quadruple
}
- public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
-
- }
+ public func pickerView(
+ _ pickerView: UIPickerView,
+ didSelectRow row: Int,
+ inComponent component: Int
+ ) {}
}
extension AttachedCardCountViewController: UIPickerViewDataSource {
- public func numberOfComponents(in pickerView: UIPickerView) -> Int {
+ public func numberOfComponents(
+ in pickerView: UIPickerView
+ ) -> Int {
return 1
}
- public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
+ public func pickerView(
+ _ pickerView: UIPickerView,
+ numberOfRowsInComponent component: Int
+ ) -> Int {
return cards.count
}
}
diff --git a/YooKassaPaymentsExample/Source/UserStories/Process/ProcessViewController.swift b/YooKassaPaymentsExample/Source/UserStories/Process/ProcessViewController.swift
new file mode 100644
index 00000000..7cc7a9d3
--- /dev/null
+++ b/YooKassaPaymentsExample/Source/UserStories/Process/ProcessViewController.swift
@@ -0,0 +1,141 @@
+import UIKit
+
+protocol ProcessViewControllerDelegate: class {
+ func processViewController(
+ _ processViewController: ProcessViewController,
+ processConfirmation: ProcessConfirmation?
+ )
+}
+
+final class ProcessViewController: UIViewController {
+ public static func makeModule(
+ processConfirmation: ProcessConfirmation?,
+ delegate: ProcessViewControllerDelegate? = nil
+ ) -> UIViewController {
+ let controller = ProcessViewController()
+ controller.delegate = delegate
+ controller.initialProcessConfirmation = processConfirmation
+ return controller
+ }
+
+ weak var delegate: ProcessViewControllerDelegate?
+
+ private let processesConfirmation = ProcessConfirmation.allCasesWithNil
+
+ // MARK: - Initial values
+
+ private var initialProcessConfirmation: ProcessConfirmation?
+
+ // MARK: - UI properties
+
+ fileprivate lazy var countPickerView: UIPickerView = {
+ $0.dataSource = self
+ $0.delegate = self
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ $0.setStyles(UIPickerView.Styles.defaultBackground)
+ $0.selectRow(
+ processesConfirmation.firstIndex(where: {
+ switch ($0, initialProcessConfirmation) {
+ case (.threeDSecure, .threeDSecure): return true
+ case (.app2app, .app2app): return true
+ default: return false
+ }
+ }) ?? 0,
+ inComponent: 0,
+ animated: false
+ )
+ return $0
+ }(UIPickerView())
+
+ fileprivate lazy var saveBarItem = UIBarButtonItem(
+ title: translate(CommonLocalized.save),
+ style: .plain,
+ target: self,
+ action: #selector(saveButtonDidPress)
+ )
+
+ // MARK: - Managing the View
+
+ override func loadView() {
+ view = UIView()
+ view.setStyles(UIView.Styles.grayBackground)
+ navigationItem.title = translate(Localized.title)
+
+ loadSubviews()
+ loadConstraints()
+ }
+
+ private func loadSubviews() {
+ view.addSubview(countPickerView)
+ navigationItem.rightBarButtonItem = saveBarItem
+ }
+
+ private func loadConstraints() {
+ let constraints = [
+ countPickerView.leading.constraint(equalTo: view.leading),
+ topLayoutGuide.bottom.constraint(equalTo: countPickerView.top),
+ countPickerView.trailing.constraint(equalTo: view.trailing),
+ countPickerView.height.constraint(equalToConstant: 154),
+ ]
+
+ NSLayoutConstraint.activate(constraints)
+ }
+
+ // MARK: - Actions
+
+ @objc
+ private func saveButtonDidPress() {
+ let selectedRow = countPickerView.selectedRow(inComponent: 0)
+ delegate?.processViewController(
+ self,
+ processConfirmation: processesConfirmation[selectedRow]
+ )
+ navigationController?.popViewController(animated: true)
+ }
+}
+
+extension ProcessViewController: UIPickerViewDelegate {
+ func pickerView(
+ _ pickerView: UIPickerView,
+ titleForRow row: Int,
+ forComponent component: Int
+ ) -> String? {
+ return processesConfirmation[row]?.description
+ ?? translate(CommonLocalized.none)
+ }
+
+ func pickerView(
+ _ pickerView: UIPickerView,
+ rowHeightForComponent component: Int
+ ) -> CGFloat {
+ return Space.quadruple
+ }
+
+ public func pickerView(
+ _ pickerView: UIPickerView,
+ didSelectRow row: Int,
+ inComponent component: Int
+ ) {}
+}
+
+extension ProcessViewController: UIPickerViewDataSource {
+
+ public func numberOfComponents(
+ in pickerView: UIPickerView
+ ) -> Int {
+ return 1
+ }
+
+ public func pickerView(
+ _ pickerView: UIPickerView,
+ numberOfRowsInComponent component: Int
+ ) -> Int {
+ return processesConfirmation.count
+ }
+}
+
+private extension ProcessViewController {
+ enum Localized: String {
+ case title = "process.title"
+ }
+}
diff --git a/YooKassaPaymentsExample/Source/UserStories/Root/RootViewController.swift b/YooKassaPaymentsExample/Source/UserStories/Root/RootViewController.swift
index f7f50500..c8ce71ef 100644
--- a/YooKassaPaymentsExample/Source/UserStories/Root/RootViewController.swift
+++ b/YooKassaPaymentsExample/Source/UserStories/Root/RootViewController.swift
@@ -86,10 +86,12 @@ final class RootViewController: UIViewController {
fileprivate lazy var imageView = UIImageView(image: #imageLiteral(resourceName: "Root.Comet"))
- fileprivate lazy var settingsBarItem = UIBarButtonItem(image: #imageLiteral(resourceName: "Root.Settings"),
- style: .plain,
- target: self,
- action: #selector(settingsButtonDidPress))
+ fileprivate lazy var settingsBarItem = UIBarButtonItem(
+ image: #imageLiteral(resourceName: "Root.Settings"),
+ style: .plain,
+ target: self,
+ action: #selector(settingsButtonDidPress)
+ )
fileprivate lazy var ratingImageView = UIImageView(image: #imageLiteral(resourceName: "Root.Rating"))
@@ -103,7 +105,15 @@ final class RootViewController: UIViewController {
// MARK: - Private properties
- private lazy var settings: Settings = {
+ private let settingsService: SettingsService
+
+ private var currentKeyboardOffset: CGFloat = 0
+
+ // MARK: - Data properties
+
+ var token: Tokens?
+ var paymentMethodType: PaymentMethodType?
+ lazy var settings: Settings = {
if let settings = settingsService.loadSettingsFromStorage() {
return settings
} else {
@@ -114,15 +124,6 @@ final class RootViewController: UIViewController {
}
}()
- private let settingsService: SettingsService
-
- private var currentKeyboardOffset: CGFloat = 0
-
- // MARK: - Data properties
-
- var token: Tokens?
- var paymentMethodType: PaymentMethodType?
-
// MARK: - Initialization/Deinitialization
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
@@ -201,8 +202,10 @@ final class RootViewController: UIViewController {
contentView.addSubview(subview)
}
- let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self,
- action: #selector(rootViewPressed))
+ let tap: UITapGestureRecognizer = UITapGestureRecognizer(
+ target: self,
+ action: #selector(rootViewPressed)
+ )
view.addGestureRecognizer(tap)
}
@@ -323,12 +326,13 @@ final class RootViewController: UIViewController {
let amount = Amount(value: settings.price, currency: .rub)
if settings.testModeSettings.isTestModeEnadled {
-
let paymentAuthorizationPassed = settings.testModeSettings.isPaymentAuthorizationPassed
- testSettings = TestModeSettings(paymentAuthorizationPassed: paymentAuthorizationPassed,
- cardsCount: settings.testModeSettings.cardsCount ?? 0,
- charge: amount,
- enablePaymentError: settings.testModeSettings.isPaymentWithError)
+ testSettings = TestModeSettings(
+ paymentAuthorizationPassed: paymentAuthorizationPassed,
+ cardsCount: settings.testModeSettings.cardsCount ?? 0,
+ charge: amount,
+ enablePaymentError: settings.testModeSettings.isPaymentWithError
+ )
} else {
testSettings = nil
}
@@ -356,7 +360,8 @@ final class RootViewController: UIViewController {
userPhoneNumber: "7",
customizationSettings: CustomizationSettings(mainScheme: .blueRibbon),
savePaymentMethod: .userSelects,
- moneyAuthClientId: "hitm6hg51j1d3g1u3ln040bajiol903b"
+ moneyAuthClientId: "hitm6hg51j1d3g1u3ln040bajiol903b",
+ applicationScheme: "yookassapaymentsexample://"
))
// let inputData: TokenizationFlow = .bankCardRepeat(BankCardRepeatModuleInputData(
@@ -368,7 +373,8 @@ final class RootViewController: UIViewController {
// testModeSettings: testSettings,
// isLoggingEnabled: true,
// customizationSettings: CustomizationSettings(mainScheme: .blueRibbon),
-// savePaymentMethod: .userSelects
+// savePaymentMethod: .userSelects,
+// gatewayId: nil
// ))
let viewController = TokenizationAssembly.makeModule(
@@ -403,10 +409,12 @@ final class RootViewController: UIViewController {
// MARK: - Notifications
private func subscribeOnNotifications() {
- NotificationCenter.default.addObserver(self,
- selector: #selector(accessibilityReapply),
- name: UIContentSizeCategory.didChangeNotification,
- object: nil)
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(accessibilityReapply),
+ name: UIContentSizeCategory.didChangeNotification,
+ object: nil
+ )
}
private func unsubscribeFromNotifications() {
@@ -421,17 +429,21 @@ final class RootViewController: UIViewController {
if traitCollection.horizontalSizeClass == .regular {
payButtonBottomConstraint.constant = Space.fivefold
nameLabelTopConstraint.constant = Constants.nameTopOffset * 2
- contentView.layoutMargins = UIEdgeInsets(top: Space.quadruple,
- left: view.frame.width * Constants.widthRatio,
- bottom: 0,
- right: view.frame.width * Constants.widthRatio)
+ contentView.layoutMargins = UIEdgeInsets(
+ top: Space.quadruple,
+ left: view.frame.width * Constants.widthRatio,
+ bottom: 0,
+ right: view.frame.width * Constants.widthRatio
+ )
} else {
payButtonBottomConstraint.constant = Space.double
nameLabelTopConstraint.constant = Constants.nameTopOffset
- contentView.layoutMargins = UIEdgeInsets(top: Space.single,
- left: Space.double,
- bottom: 0,
- right: Space.double)
+ contentView.layoutMargins = UIEdgeInsets(
+ top: Space.single,
+ left: Space.double,
+ bottom: 0,
+ right: Space.double
+ )
}
updatePayButtonBottomConstraint()
@@ -455,17 +467,20 @@ final class RootViewController: UIViewController {
paymentTypes.insert(.sberbank)
}
- return TokenizationSettings(paymentMethodTypes: paymentTypes,
- showYooKassaLogo: settings.isShowingYooMoneyLogoEnabled)
+ return TokenizationSettings(
+ paymentMethodTypes: paymentTypes,
+ showYooKassaLogo: settings.isShowingYooMoneyLogoEnabled
+ )
}
}
// MARK: - SettingsViewControllerDelegate
extension RootViewController: SettingsViewControllerDelegate {
- func settingsViewController(_ settingsViewController: SettingsViewController,
- didChangeSettings settings: Settings) {
-
+ func settingsViewController(
+ _ settingsViewController: SettingsViewController,
+ didChangeSettings settings: Settings
+ ) {
self.settings = settings
settingsService.saveSettingsToStorage(settings: settings)
}
@@ -476,16 +491,19 @@ extension RootViewController: SettingsViewControllerDelegate {
extension RootViewController {
func startKeyboardObserving() {
-
- NotificationCenter.default.addObserver(self,
- selector: #selector(keyboardWillShow(_:)),
- name: UIResponder.keyboardWillShowNotification,
- object: nil)
-
- NotificationCenter.default.addObserver(self,
- selector: #selector(keyboardWillHide(_:)),
- name: UIResponder.keyboardWillHideNotification,
- object: nil)
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(keyboardWillShow(_:)),
+ name: UIResponder.keyboardWillShowNotification,
+ object: nil
+ )
+
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(keyboardWillHide(_:)),
+ name: UIResponder.keyboardWillHideNotification,
+ object: nil
+ )
}
func stopKeyboardObserving() {
@@ -531,26 +549,31 @@ extension RootViewController {
}
extension RootViewController: MFMailComposeViewControllerDelegate {
- func mailComposeController(_ controller: MFMailComposeViewController,
- didFinishWith result: MFMailComposeResult,
- error: Error?) {
+ func mailComposeController(
+ _ controller: MFMailComposeViewController,
+ didFinishWith result: MFMailComposeResult,
+ error: Error?
+ ) {
controller.dismiss(animated: true)
}
}
extension RootViewController: PriceInputViewControllerDelegate {
- func priceInputViewController(_ priceInputViewController: PriceInputViewController,
- didChangePrice price: Decimal?,
- valid: Bool) {
-
+ func priceInputViewController(
+ _ priceInputViewController: PriceInputViewController,
+ didChangePrice price: Decimal?,
+ valid: Bool
+ ) {
if price != nil, valid == true {
payButton.isEnabled = true
} else {
payButton.isEnabled = false
}
- scrollView.scrollRectToVisible(priceInputViewController.view.frame,
- animated: true)
+ scrollView.scrollRectToVisible(
+ priceInputViewController.view.frame,
+ animated: true
+ )
}
}
@@ -598,9 +621,11 @@ extension RootViewController: SuccessViewControllerDelegate {
let viewController: UIViewController
if MFMailComposeViewController.canSendMail() == false {
- let alertController = UIAlertController(title: "Token",
- message: message,
- preferredStyle: .alert)
+ let alertController = UIAlertController(
+ title: "Token",
+ message: message,
+ preferredStyle: .alert
+ )
let action = UIAlertAction(title: "OK", style: .default)
alertController.addAction(action)
viewController = alertController
diff --git a/YooKassaPaymentsExample/Source/UserStories/Settings/SettingsViewController.swift b/YooKassaPaymentsExample/Source/UserStories/Settings/SettingsViewController.swift
index 19c92581..205dd0f4 100644
--- a/YooKassaPaymentsExample/Source/UserStories/Settings/SettingsViewController.swift
+++ b/YooKassaPaymentsExample/Source/UserStories/Settings/SettingsViewController.swift
@@ -1,18 +1,20 @@
import UIKit
protocol SettingsViewControllerDelegate: class {
- func settingsViewController(_ settingsViewController: SettingsViewController,
- didChangeSettings settings: Settings)
+ func settingsViewController(
+ _ settingsViewController: SettingsViewController,
+ didChangeSettings settings: Settings
+ )
}
final class SettingsViewController: UIViewController {
- public static func makeModule(settings: Settings,
- delegate: SettingsViewControllerDelegate? = nil) -> UIViewController {
-
+ public static func makeModule(
+ settings: Settings,
+ delegate: SettingsViewControllerDelegate? = nil
+ ) -> UIViewController {
let controller = SettingsViewController(settings: settings)
controller.delegate = delegate
-
return controller
}
@@ -21,11 +23,13 @@ final class SettingsViewController: UIViewController {
// MARK: - UI properties
private lazy var tableViewController = TableViewController(style: .grouped)
-
- private lazy var closeBarItem = UIBarButtonItem(image: #imageLiteral(resourceName: "Settings.Close"),
- style: .plain,
- target: self,
- action: #selector(closeBarButtonItemDidPress))
+
+ private lazy var closeBarItem = UIBarButtonItem(
+ image: #imageLiteral(resourceName: "Settings.Close"),
+ style: .plain,
+ target: self,
+ action: #selector(closeBarButtonItemDidPress)
+ )
// MARK: - Private properties
@@ -74,7 +78,12 @@ final class SettingsViewController: UIViewController {
navigationItem.leftBarButtonItem = closeBarItem
navigationItem.title = Localized.title
- navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
+ navigationItem.backBarButtonItem = UIBarButtonItem(
+ title: "",
+ style: .plain,
+ target: nil,
+ action: nil
+ )
tableViewController.sections = sectionDescriptors
tableViewController.reload()
@@ -104,7 +113,10 @@ final class SettingsViewController: UIViewController {
@objc
private func closeBarButtonItemDidPress() {
- navigationController?.presentingViewController?.dismiss(animated: true, completion: nil)
+ navigationController?.presentingViewController?.dismiss(
+ animated: true,
+ completion: nil
+ )
}
}
@@ -122,47 +134,71 @@ extension SettingsViewController {
}
private var paymentMethodsSection: SectionDescriptor {
- let yooMoneyCell = switchCellWith(title: Localized.yooMoney,
- initialValue: { $0.isYooMoneyEnabled },
- settingHandler: { $0.isYooMoneyEnabled = $1 })
-
- let sberbankCell = switchCellWith(title: Localized.sberbank,
- initialValue: { $0.isSberbankEnabled },
- settingHandler: { $0.isSberbankEnabled = $1 })
-
- let bankCardCell = switchCellWith(title: Localized.bankCard,
- initialValue: { $0.isBankCardEnabled },
- settingHandler: { $0.isBankCardEnabled = $1 })
-
- let applePayCell = switchCellWith(title: Localized.applePay,
- initialValue: { $0.isApplePayEnabled },
- settingHandler: { $0.isApplePayEnabled = $1 })
-
- return SectionDescriptor(headerText: Localized.paymentMethods,
- rows: [yooMoneyCell, sberbankCell, bankCardCell, applePayCell])
+ let yooMoneyCell = switchCellWith(
+ title: Localized.yooMoney,
+ initialValue: { $0.isYooMoneyEnabled },
+ settingHandler: { $0.isYooMoneyEnabled = $1 }
+ )
+
+ let sberbankCell = switchCellWith(
+ title: Localized.sberbank,
+ initialValue: { $0.isSberbankEnabled },
+ settingHandler: { $0.isSberbankEnabled = $1 }
+ )
+
+ let bankCardCell = switchCellWith(
+ title: Localized.bankCard,
+ initialValue: { $0.isBankCardEnabled },
+ settingHandler: { $0.isBankCardEnabled = $1 }
+ )
+
+ let applePayCell = switchCellWith(
+ title: Localized.applePay,
+ initialValue: { $0.isApplePayEnabled },
+ settingHandler: { $0.isApplePayEnabled = $1 }
+ )
+
+ return SectionDescriptor(
+ headerText: Localized.paymentMethods,
+ rows: [
+ yooMoneyCell,
+ sberbankCell,
+ bankCardCell,
+ applePayCell,
+ ]
+ )
}
private var uiCustomizationSection: SectionDescriptor {
- let yooMoneyLogoCell = switchCellWith(title: Localized.yooMoneyLogo,
- initialValue: { $0.isShowingYooMoneyLogoEnabled },
- settingHandler: { $0.isShowingYooMoneyLogoEnabled = $1 })
-
- return SectionDescriptor(rows: [yooMoneyLogoCell])
+ let yooMoneyLogoCell = switchCellWith(
+ title: Localized.yooMoneyLogo,
+ initialValue: { $0.isShowingYooMoneyLogoEnabled },
+ settingHandler: { $0.isShowingYooMoneyLogoEnabled = $1 }
+ )
+
+ return SectionDescriptor(
+ rows: [
+ yooMoneyLogoCell,
+ ]
+ )
}
private var testModeSection: SectionDescriptor {
let testMode = CellDescriptor(configuration: { [unowned self] (cell: ContainerTableViewCell) in
cell.containedView.title = Localized.test_mode
- cell.containedView.value = self.settings.testModeSettings.isTestModeEnadled ?
- translate(CommonLocalized.on) : translate(CommonLocalized.off)
+ cell.containedView.value = self.settings.testModeSettings.isTestModeEnadled
+ ? translate(CommonLocalized.on)
+ : translate(CommonLocalized.off)
cell.accessoryType = .disclosureIndicator
}, selection: { [unowned self] (indexPath) in
self.tableViewController.tableView.deselectRow(at: indexPath, animated: true)
- let controller = TestSettingsViewController.makeModule(settings: self.settings.testModeSettings,
- delegate: self)
+ let controller = TestSettingsViewController.makeModule(
+ settings: self.settings.testModeSettings,
+ delegate: self
+ )
let navigation = UINavigationController(rootViewController: controller)
if #available(iOS 11.0, *) {
@@ -177,27 +213,29 @@ extension SettingsViewController {
return SectionDescriptor(rows: [testMode])
}
- private func switchCellWith(title: String,
- initialValue: @escaping (Settings) -> Bool,
- settingHandler: @escaping (inout Settings, Bool) -> Void)
- -> CellDescriptor {
- return CellDescriptor(configuration: { [unowned self] (cell: ContainerTableViewCell) in
-
- cell.containedView.title = title
- cell.containedView.isOn = initialValue(self.settings)
- cell.containedView.valueChangeHandler = {
- settingHandler(&self.settings, $0)
- }
- })
+ private func switchCellWith(
+ title: String,
+ initialValue: @escaping (Settings) -> Bool,
+ settingHandler: @escaping (inout Settings, Bool) -> Void
+ ) -> CellDescriptor {
+ return CellDescriptor(configuration: { [unowned self] (cell: ContainerTableViewCell) in
+
+ cell.containedView.title = title
+ cell.containedView.isOn = initialValue(self.settings)
+ cell.containedView.valueChangeHandler = {
+ settingHandler(&self.settings, $0)
+ }
+ })
}
}
// MARK: - TestSettingsViewControllerDelegate
extension SettingsViewController: TestSettingsViewControllerDelegate {
- func testSettingsViewController(_ testSettingsViewController: TestSettingsViewController,
- didChangeSettings settings: TestSettings) {
-
+ func testSettingsViewController(
+ _ testSettingsViewController: TestSettingsViewController,
+ didChangeSettings settings: TestSettings
+ ) {
self.settings.testModeSettings = settings
tableViewController.reloadTable()
}
diff --git a/YooKassaPaymentsExample/Source/UserStories/Test Settings/TestSettingsViewController.swift b/YooKassaPaymentsExample/Source/UserStories/Test Settings/TestSettingsViewController.swift
index d89bf7bc..9869e89d 100644
--- a/YooKassaPaymentsExample/Source/UserStories/Test Settings/TestSettingsViewController.swift
+++ b/YooKassaPaymentsExample/Source/UserStories/Test Settings/TestSettingsViewController.swift
@@ -2,18 +2,20 @@ import UIKit
import YooKassaPayments
protocol TestSettingsViewControllerDelegate: class {
- func testSettingsViewController(_ testSettingsViewController: TestSettingsViewController,
- didChangeSettings settings: TestSettings)
+ func testSettingsViewController(
+ _ testSettingsViewController: TestSettingsViewController,
+ didChangeSettings settings: TestSettings
+ )
}
final class TestSettingsViewController: UIViewController {
- public static func makeModule(settings: TestSettings,
- delegate: TestSettingsViewControllerDelegate? = nil) -> UIViewController {
-
+ public static func makeModule(
+ settings: TestSettings,
+ delegate: TestSettingsViewControllerDelegate? = nil
+ ) -> UIViewController {
let controller = TestSettingsViewController(settings: settings)
controller.delegate = delegate
-
return controller
}
@@ -22,15 +24,19 @@ final class TestSettingsViewController: UIViewController {
// MARK: - UI properties
private lazy var tableViewController = TableViewController(style: .grouped)
-
- private lazy var closeBarItem = UIBarButtonItem(image: #imageLiteral(resourceName: "Settings.Close"),
- style: .plain,
- target: self,
- action: #selector(closeBarButtonItemDidPress))
-
- private lazy var testModeCell = switchCellWith(title: translate(Localized.title),
- initialValue: { $0.isTestModeEnadled },
- settingHandler: { $0.isTestModeEnadled = $1 })
+
+ private lazy var closeBarItem = UIBarButtonItem(
+ image: #imageLiteral(resourceName: "Settings.Close"),
+ style: .plain,
+ target: self,
+ action: #selector(closeBarButtonItemDidPress)
+ )
+
+ private lazy var testModeCell = switchCellWith(
+ title: translate(Localized.title),
+ initialValue: { $0.isTestModeEnadled },
+ settingHandler: { $0.isTestModeEnadled = $1 }
+ )
// MARK: - Private properties
@@ -85,6 +91,13 @@ final class TestSettingsViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
+
+ let tapGesture = UITapGestureRecognizer(
+ target: self,
+ action: #selector(hideKeyboard)
+ )
+ tapGesture.cancelsTouchesInView = false
+ view.addGestureRecognizer(tapGesture)
navigationItem.leftBarButtonItem = closeBarItem
navigationItem.title = translate(Localized.title)
@@ -92,6 +105,19 @@ final class TestSettingsViewController: UIViewController {
updateSections(for: settings.isTestModeEnadled)
tableViewController.reload(force: true)
+
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(keyboardWillShow),
+ name: UIResponder.keyboardWillShowNotification,
+ object: nil
+ )
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(keyboardWillHide),
+ name: UIResponder.keyboardWillHideNotification,
+ object: nil
+ )
}
// MARK: - Initialization/Deinitialization
@@ -115,12 +141,30 @@ final class TestSettingsViewController: UIViewController {
}
// MARK: - Action handlers
+
+ @objc
+ private func hideKeyboard(
+ _ gestureRecognizer: UITapGestureRecognizer
+ ) {
+ view.endEditing(true)
+ }
@objc
private func closeBarButtonItemDidPress() {
navigationController?.presentingViewController?.dismiss(animated: true, completion: nil)
}
+ @objc
+ func keyboardWillShow(notification: NSNotification) {
+ if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
+ tableViewController.tableView.contentInset.bottom = keyboardSize.height
+ }
+ }
+
+ @objc
+ func keyboardWillHide(notification: NSNotification) {
+ tableViewController.tableView.contentInset.bottom = 0
+ }
}
// MARK: - TableView data
@@ -129,7 +173,11 @@ extension TestSettingsViewController {
private func updateSections(for testModeEnabled: Bool) {
if testModeEnabled {
- tableViewController.sections = [testModeSection, testModeOptionsSection]
+ tableViewController.sections = [
+ testModeSection,
+ testModeOptionsSection,
+ processOptionsSection,
+ ]
} else {
tableViewController.sections = [testModeSection]
}
@@ -140,33 +188,33 @@ extension TestSettingsViewController {
}
private var testModeOptionsSection: SectionDescriptor {
-
- // MARK: OBIOS-105 temporarily remove
-// let payment3DSCell = switchCellWith(title: translate(Localized.check3ds),
-// initialValue: { $0.is3DSEnabled },
-// settingHandler: { $0.is3DSEnabled = $1 })
-
- let paymentAuthCell = switchCellWith(title: translate(Localized.paymentAuth),
- initialValue: { $0.isPaymentAuthorizationPassed },
- settingHandler: { $0.isPaymentAuthorizationPassed = $1 })
-
- let paymentErrorCell = switchCellWith(title: translate(Localized.paymentError),
- initialValue: { $0.isPaymentWithError },
- settingHandler: { $0.isPaymentWithError = $1 })
+ let paymentAuthCell = switchCellWith(
+ title: translate(Localized.paymentAuth),
+ initialValue: { $0.isPaymentAuthorizationPassed },
+ settingHandler: { $0.isPaymentAuthorizationPassed = $1 }
+ )
+
+ let paymentErrorCell = switchCellWith(
+ title: translate(Localized.paymentError),
+ initialValue: { $0.isPaymentWithError },
+ settingHandler: { $0.isPaymentWithError = $1 }
+ )
let cards = CellDescriptor(configuration: { [unowned self] (cell: ContainerTableViewCell) in
-
cell.containedView.title = translate(Localized.attached)
cell.containedView.value = self.settings.cardsCount.flatMap(String.init) ?? translate(CommonLocalized.none)
cell.accessoryType = .disclosureIndicator
- }, selection: { [unowned self] (indexPath) in
-
- self.tableViewController.tableView.deselectRow(at: indexPath, animated: true)
-
- let controller = AttachedCardCountViewController.makeModule(cardCount: self.settings.cardsCount,
- delegate: self)
- self.navigationController?.pushViewController(controller,
- animated: true)
+ }, selection: { [unowned self] (indexPath) in
+ self.tableViewController.tableView.deselectRow(at: indexPath, animated: true)
+
+ let controller = AttachedCardCountViewController.makeModule(
+ cardCount: self.settings.cardsCount,
+ delegate: self
+ )
+ self.navigationController?.pushViewController(
+ controller,
+ animated: true
+ )
})
return SectionDescriptor(rows: [
@@ -175,39 +223,97 @@ extension TestSettingsViewController {
paymentErrorCell,
])
}
-
- private func switchCellWith(title: String,
- initialValue: @escaping (TestSettings) -> Bool,
- settingHandler: @escaping (inout TestSettings, Bool) -> Void)
- -> CellDescriptor {
- return CellDescriptor(configuration: { [unowned self] (cell: ContainerTableViewCell) in
-
- cell.containedView.title = title
- cell.containedView.isOn = initialValue(self.settings)
+
+ private var processOptionsSection: SectionDescriptor {
+ var rows: [CellDescriptor] = []
+
+ rows.append(CellDescriptor(configuration: {
+ [unowned self] (cell: ContainerTableViewCell) in
+ cell.containedView.title = translate(Localized.processType)
+ cell.containedView.value = self.settings.processConfirmation?.description
+ ?? translate(CommonLocalized.none)
+ cell.accessoryType = .disclosureIndicator
+ }, selection: { [unowned self] (indexPath) in
+ self.tableViewController.tableView.deselectRow(at: indexPath, animated: true)
+
+ let controller = ProcessViewController.makeModule(
+ processConfirmation: self.settings.processConfirmation,
+ delegate: self
+ )
+ self.navigationController?.pushViewController(
+ controller,
+ animated: true
+ )
+ }))
+
+ if let processConfirmation = settings.processConfirmation {
+ rows.append(CellDescriptor(configuration: {
+ [unowned self] (cell: ContainerTableViewCell) in
+ cell.containedView.placeholder = processConfirmation.description
+ cell.containedView.text = processConfirmation.url
cell.containedView.valueChangeHandler = {
- settingHandler(&self.settings, $0)
+ switch processConfirmation {
+ case .threeDSecure:
+ self.settings.processConfirmation = .threeDSecure($0 ?? "")
+
+ case .app2app:
+ self.settings.processConfirmation = .app2app($0 ?? "")
+ }
}
- })
+ }))
+ }
+
+ return SectionDescriptor(rows: rows)
+ }
+
+ private func switchCellWith(
+ title: String,
+ initialValue: @escaping (TestSettings) -> Bool,
+ settingHandler: @escaping (inout TestSettings, Bool) -> Void
+ ) -> CellDescriptor {
+ return CellDescriptor(configuration: { [unowned self] (cell: ContainerTableViewCell) in
+ cell.containedView.title = title
+ cell.containedView.isOn = initialValue(self.settings)
+ cell.containedView.valueChangeHandler = {
+ settingHandler(&self.settings, $0)
+ }
+ })
}
}
-extension TestSettingsViewController: AttachedCardCountViewControllerDelegate {
- func attachedCardCountViewController(_ attachedCardCountViewController: AttachedCardCountViewController,
- didSaveCardCount сardCount: Int?) {
+// MARK: - AttachedCardCountViewControllerDelegate
+extension TestSettingsViewController: AttachedCardCountViewControllerDelegate {
+ func attachedCardCountViewController(
+ _ attachedCardCountViewController: AttachedCardCountViewController,
+ didSaveCardCount сardCount: Int?
+ ) {
settings.cardsCount = сardCount
tableViewController.reloadTable()
}
}
+// MARK: - ProcessViewControllerDelegate
+
+extension TestSettingsViewController: ProcessViewControllerDelegate {
+ func processViewController(
+ _ processViewController: ProcessViewController,
+ processConfirmation: ProcessConfirmation?
+ ) {
+ settings.processConfirmation = processConfirmation
+ updateSections(for: settings.isTestModeEnadled)
+ tableViewController.reloadTable()
+ }
+}
+
// MARK: - Localization
-extension TestSettingsViewController {
+extension TestSettingsViewController {
private enum Localized: String {
case title = "test_mode.title"
- case check3ds = "test_mode.3ds"
case paymentAuth = "test_mode.payment_auth"
case attached = "test_mode.attached_cards"
case paymentError = "test_mode.payment_error"
+ case processType = "test_mode.process_type"
}
}
diff --git a/YooKassaPaymentsExample/Source/ViewControllers/CellDescriptor.swift b/YooKassaPaymentsExample/Source/ViewControllers/CellDescriptor.swift
index a8f249ff..8bf6d34a 100644
--- a/YooKassaPaymentsExample/Source/ViewControllers/CellDescriptor.swift
+++ b/YooKassaPaymentsExample/Source/ViewControllers/CellDescriptor.swift
@@ -8,13 +8,12 @@ final class CellDescriptor {
let configuration: (UITableViewCell) -> Void
let selection: ((IndexPath) -> Void)?
- init(configuration: @escaping (Cell) -> Void,
- selection: ((IndexPath) -> Void)? = nil) {
-
+ init(
+ configuration: @escaping (Cell) -> Void,
+ selection: ((IndexPath) -> Void)? = nil
+ ) {
self.cellClass = Cell.self
-
self.selection = selection
-
self.configuration = { cell in
guard let cell = cell as? Cell else {
assertionFailure("Cell and selection types mismatch")
@@ -24,5 +23,4 @@ final class CellDescriptor {
configuration(cell)
}
}
-
}
diff --git a/YooKassaPaymentsExample/Source/ViewControllers/TableViewController.swift b/YooKassaPaymentsExample/Source/ViewControllers/TableViewController.swift
index b5ad9b9f..cbd47159 100644
--- a/YooKassaPaymentsExample/Source/ViewControllers/TableViewController.swift
+++ b/YooKassaPaymentsExample/Source/ViewControllers/TableViewController.swift
@@ -34,6 +34,7 @@ class TableViewController: UITableViewController {
tableView.register(TextHeaderFooterView.self,
forHeaderFooterViewReuseIdentifier: TextHeaderFooterView.identifier)
+ tableView.keyboardDismissMode = .interactive
tableView.rowHeight = UITableView.automaticDimension
tableView.sectionHeaderHeight = UITableView.automaticDimension
tableView.sectionFooterHeight = UITableView.automaticDimension
diff --git a/YooKassaPaymentsExample/Source/Views/ContainerTableViewCell.swift b/YooKassaPaymentsExample/Source/Views/ContainerTableViewCell.swift
index ddbb333f..e5c033d8 100644
--- a/YooKassaPaymentsExample/Source/Views/ContainerTableViewCell.swift
+++ b/YooKassaPaymentsExample/Source/Views/ContainerTableViewCell.swift
@@ -14,7 +14,6 @@ class ContainerTableViewCell Void)?
+
+ // MARK: - Public properties
+
+ var text: String? {
+ get {
+ return textField.text
+ }
+ set {
+ textField.text = newValue
+ }
+ }
+
+ var placeholder: String? {
+ get {
+ return textField.placeholder
+ }
+ set {
+ textField.placeholder = newValue
+ }
+ }
+
+ // MARK: - UI properties
+
+ private lazy var textField: UITextField = {
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ $0.addTarget(
+ self,
+ action: #selector(textFieldDidChange),
+ for: .editingChanged
+ )
+ return $0
+ }(UITextField())
+
+ // MARK: - Initialization/Deinitialization
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ setupUI()
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ fatalError("init?(coder:) is not implemented")
+ }
+
+ // MARK: - Private methods
+
+ private func setupUI() {
+ [
+ textField,
+ ].forEach(addSubview)
+ setupConstraints()
+ }
+
+ private func setupConstraints() {
+ let constraints = [
+ textField.top.constraint(equalTo: topMargin, constant: Space.single),
+ textField.leading.constraint(equalTo: leadingMargin),
+ textField.trailing.constraint(equalTo: trailingMargin),
+ textField.bottom.constraint(equalTo: bottomMargin, constant: -Space.single),
+ ]
+ NSLayoutConstraint.activate(constraints)
+ }
+
+ // MARK: - Actions
+
+ @objc
+ private func textFieldDidChange(textField: UITextField) {
+ valueChangeHandler?(textField.text)
+ }
+}
+
+// MARK: - TableViewCellDataProviderSupport
+
+extension TextFieldView: TableViewCellDataProviderSupport {
+ class var estimatedCellHeight: CGFloat {
+ return 56.0
+ }
+}