Руководство по iOS SDK

Важно

Этот способ интеграции требует настройки API.

Подробнее о способах интеграции см. Интеграция

Требования к подключению

Поддерживаемая версия iOS SDK: 13.0 и выше.

Подключение

Шаг 1. Настройка авторизации

1.1 Получите Client Id

  1. Зарегистрируйте приложение в сервисе Яндекс OAuth.

  2. В разделе Платформы выберите iOS-приложение и укажите параметры вашего сервиса:

    • iOS Appid — точный идентификатор iOS-приложения, например A1B2C3D4E5.com.domain.application. Состоит из Prefix и Bundle ID. Подробнее про идентификаторы iOS-приложений читайте в документации Apple.
    • iOS AppStore URL — ссылка на приложение в AppStore.
  3. Убедитесь, что на Яндекс.OAuth у вашего приложения добавлен доступ к Яндекс Пэй. Для этого перейдите к редактированию приложения и в блоке Какие данные вам нужны? выберите Яндекс ПэйОплата через Яндекс Пэй.

1.2 Настройте Info.plist

Добавьте в файл Info.plist строки:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>yandexauth</string>
    <string>yandexauth2</string>
    <string>yandexauth4</string>
</array>
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>YandexLoginSDK</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>yx<Вставьте ваш Client Id></string>
        </array>
    </dict>
</array>

1.3 Настройте Entitlements

Сервис авторизации обменивается информацией с приложениями Яндекса через Universal Links. Для их работы добавьте в Capability: Associated Domains строку:

applinks:yx<Вставьте ваш Client id>.oauth.yandex.ru

Шаг 2. Установка Yandex Pay SDK

  1. В Podfile для вашего target добавьте зависимость вида pod 'YandexPaySDK/Static' или pod 'YandexPaySDK/Dynamic'.
  2. Если в вашем проекте нет Podfile, то проинициализируйте его в соответствии с инструкциями CocoaPods и повторите предыдущий шаг.
  3. Установите зависимости, выполнив pod install --repo-update.
  4. Откройте сгенерированный .xcworkspace и соберите проект.

Добавьте в Package.swift:

... = Package(
  ...
  targets: [
      .binaryTarget(
        name: "YandexPaySDK",
        url: "https://yandexpay-ios-sdk.s3.yandex.net/1.5.3/YandexPaySDK.xcframework.zip",
        checksum: "36d683c873d92ef2855ebca5db552ba60d06e7ba58b6fb6eedbfcbe9fd6505d0"
        ),
  ]
  ...
)

На текущий момент есть поддержка только динамической библиотеки, которая включает все необходимые зависимости. Распространение исходным кодом недоступно из-за ограничений, связанных с PCI DSS.

Шаг 3. Инициализация Yandex Pay SDK

Инициализируйте SDK в AppDelegate.swift вашего проекта в методе application(_:didFinishLaunchingWithOptions:):

import YandexPaySDK

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    do {
        // Укажите конфигурацию
        let merchant = YandexPaySDKMerchant(
            // ID продавца в системе Яндекс Пэй
            id: "MERCHANT_ID",
            // Имя продавца
            name: "MERCHANT_NAME",
            // URL продавца
            url: "https://example.org/"
        )
        let configuration = YandexPaySDKConfiguration(
            // Необходимое окружение
            environment: .sandbox,
            // Информация о мерчанте
            merchant: merchant,
            // Локализация
            locale: .ru
        )
        // Инициализируйте SDK
        try YandexPaySDKApi.initialize(configuration: configuration)
    } catch {
        // Отреагируйте на ошибку должным образом
        assertionFailure("Unable to initialize YandexPaySDKApi.")
    }

    // Инициализируйте UIWindow и ViewController
    let controller = ViewController()
    let window = UIWindow(frame: UIScreen.main.bounds)
    window.rootViewController = controller
    window.makeKeyAndVisible()
    self.window = window

    return true
}
#import <YandexPaySDK/YandexPaySDK-Swift.h>

    // Укажите конфигурацию
    YandexPaySDKMerchant* merchant = [[YandexPaySDKMerchant alloc] initWithMerchantId: @"MERCHANT_ID" // ID мерчанта в системе YandexPay 
                                                                                 name: @"MERCHANT_NAME" // Имя мерчанта
                                                                                  url: @"https://example.org/"]; // URL мерчанта
    // Необходимое окружение
    YandexPaySDKConfiguration* configuration = [[YandexPaySDKConfiguration alloc] initWithEnvironment: YandexPaySDKEnvironmentSandbox
                                                                                             merchant: merchant // Информация о мерчанте
                                                                                               locale: YandexPaySDKLocaleRU]; // Локализация
    // Инициализируйте SDK
    NSError *initializationError;
    [YandexPaySDKApi initializeWithConfiguration: configuration error:&initializationError];

    // Отреагируйте на ошибку должным образом
    if (initializationError != nil) {
        [NSException raise:@"FailedToInitializePay" format:@"Subclasses must implement a valid init method"];
    }

    // Инициализируйте UIWindow и ViewController
    UIViewController *controller = [[ViewController alloc] init];
    UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [window setRootViewController:controller];
    [window makeKeyAndVisible];
    _window = window;

    return YES;

Также в AppDelegate.swift вашего проекта добавьте нотификацию YandexPaySDK о событиях жизненного цикла приложения:

import YandexPaySDK

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    YandexPaySDKApi.instance.applicationDidReceiveUserActivity(userActivity)
    return true
}

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    YandexPaySDKApi.instance.applicationDidReceiveOpen(url, sourceApplication: options[.sourceApplication] as? String)
    return true
}

func applicationWillEnterForeground(_ application: UIApplication) {
    YandexPaySDKApi.instance.applicationWillEnterForeground()
}

func applicationDidBecomeActive(_ application: UIApplication) {
    YandexPaySDKApi.instance.applicationDidBecomeActive()
}
#import <YandexPaySDK/YandexPaySDK-Swift.h>

- (void)application:(UIApplication *)application didUpdateUserActivity:(NSUserActivity *)userActivity {
    [[YandexPaySDKApi instance] applicationDidReceiveUserActivity:userActivity];
}

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    [[YandexPaySDKApi instance] applicationDidReceiveOpen:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]];
    return YES;
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    [[YandexPaySDKApi instance] applicationWillEnterForeground];
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    [[YandexPaySDKApi instance] applicationDidBecomeActive];
}

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

Для ознакомления доступен демо-проект.

Добавление кнопки Яндекс Пэй с созданием заказа по ссылке на экран в Swift проекте

В демо-проекте есть пример для использования кнопки с созданием заказа по ссылке, но рассмотрим подробнее реализацию ниже.

Если вы хотите создать кнопку с новым методом создания заказа, используйте сигнатуру createButton(configuration:dataSource:delegate:) класса YandexPaySDKApi. Пример использования кнопки во ViewController:

import YandexPaySDK

final class PaymentURLViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .systemBackground

        // Проверьте, что SDK проинициализирован
        guard YandexPaySDKApi.isInitialized else {
          assertionFailure("YandexPaySDK is not initialized.")
          return
        }

        // Укажите тему для кнопки
        // Параметр `dynamic` позволяет указать, нужно ли кнопке
        // менять свою цветовую палитру вместе со сменой системной темы
        let theme = YandexPayButtonTheme(appearance: .dark, dynamic: true)

        // Инициализируйте конфигурацию
        let configuration = YandexPayButtonConfiguration(theme: theme)

        // Создайте кнопку
        let button = YandexPaySDKApi.instance.createButton(configuration: configuration, dataSource: self, delegate: self)

        // Добавьте кнопку в иерархию
        view.addSubview(button)

        // Установите layout для кнопки
        button.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            button.widthAnchor.constraint(equalToConstant: 250)
        ])
    }
}

Кнопка для взаимодействия с клиентским приложением использует делегат YandexPayButtonDelegate. Реализуйте данный делегат, чтобы передать его кнопке при ее создании:

extension PaymentURLViewController: YandexPayButtonDelegate {
    func yandexPayButton(_ button: YandexPayButton, didCompletePaymentWithResult result: YPYandexPayPaymentResult) {
        let title: String
        let message: String
        switch result {
        case .succeeded:
            title = "Success!"
            message = "Payment successfuly proceeded."
        case .cancelled:
            title = "Cancelled!"
            message = "Payment has been cancelled by user."
        case .failed:
            fallthrough
        @unknown default:
            title = "Error!"
            message = "An error occured while payment processing."
        }

        let controller = UIAlertController(title: title, message: message, preferredStyle: .alert)
        controller.addAction(UIAlertAction(title: "OK", style: .default))
        present(controller, animated: true)
    }
}

В этом примере мы показываем пользователю сообщение с текущим статусом оплаты через YandexPay.

Также данная кнопка использует datasource YandexPayButtonDataSource. Реализуйте данный datasource, чтобы передать его кнопке при ее создании вместе с делегатом:

extension PaymentURLViewController: YandexPayButtonDataSource {
  func paymentUrl(for yandexPayButton: YandexPayButton) async throws -> String {
    // Запросите paymentUrl (создайте заказ) асинхронно с вашего бекенда
    await withCheckedContinuation { continuation in
      // Это пример реализации async кода, скорее всего здесь будет сетевой запрос
      DispatchQueue.main.async {
        continuation.resume(returning: "payment-url.ru")
      }
    }
  }
  
  func requiredFields(for yandexPayButton: YandexPayButton) async -> Set<YPRequiredField> {
    // Набор обязательных полей, которые должен предоставить клиент
    [.billingContactEmail]
  }
  
  func billingContact(for yandexPayButton: YandexPaySDK.YandexPayButton) async -> YPBillingContact? {
    // Email клиента для заказа, можно вернуть либо сразу значение, либо асинхронно, либо nil
    YPBillingContact(email: "example@yandex.ru")
  }
  
  func viewControllerForPresentation(for yandexPayButton: YandexPayButton) -> UIViewController {
    // Предоставьте UIViewController, с которого необходимо показать форму YandexPay по нажатию на кнопку
    self
  }
}

Все методы запроса данных являются асинхронными. Это значит, что они должны работать с подходом Swift Concurrency. Если есть необходимость, можно вернуть значение сразу, например как в requiredFields(for yandexPayButton:) и billingContact(for yandexPayButton:). Есть отличный пример от Apple с WWDC 2021, как перевевести элементы вашего кода c подхода GCD на подход Concurrency: Swift concurrency: Update a sample app.