Руководство по iOS SDK (Checkout)
Требования к подключению
Поддерживаемая версия iOS SDK: 13.0 и выше.
Подключение
Шаг 1. Настройка авторизации
1.1 Получите Client Id
-
Зарегистрируйте приложение в сервисе Яндекс OAuth.
-
В разделе Платформы выберите iOS-приложение и укажите параметры вашего сервиса:
- iOS Appid — точный идентификатор iOS-приложения, например
A1B2C3D4E5.com.domain.application
. Состоит из Prefix и Bundle ID. Подробнее про идентификаторы iOS-приложений читайте в документации Apple. - iOS AppStore URL — ссылка на приложение в AppStore.
- iOS Appid — точный идентификатор iOS-приложения, например
-
Убедитесь, что на Яндекс.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. Установка Яндекс Пэй SDK
Текущая версия YandexPaySDK - 1.6.0
Добавьте зависимость в Podfile:
...
pod 'YandexPaySDK/Static'
...
В окне Xcode навигатора проектов (Project Navigator) выберите свой проект (если у вас используется Workspace). Затем в верхнем меню нажмите File и выберите Add Package Dependencies...
Добавьте пакет по ссылке:
https://github.com/yandexmobile/yandex-pay-ios
На текущий момент есть поддержка только динамической библиотеки, которая включает все необходимые зависимости. Распространение исходным кодом недоступно из-за ограничений, связанных с PCI DSS.
Добавьте package зависимость в Package.swift:
let package = Package(
...
dependencies: [
.package(
name: "YandexPaySDK",
url: " https://github.com/yandexmobile/yandex-pay-ios"
),
],
...
)
На текущий момент есть поддержка только динамической библиотеки, которая включает все необходимые зависимости. Распространение исходным кодом недоступно из-за ограничений, связанных с PCI DSS.
Шаг 3. Инициализация Яндекс Пэй 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 = PaymentURLViewController()
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = controller
window.makeKeyAndVisible()
self.window = window
return true
}
Также в AppDelegate.swift
вашего проекта добавьте нотификацию YandexPaySDK
о событиях жизненного цикла приложения:
import YandexPaySDK
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
// Проверьте, что SDK проинициализирован
guard YandexPaySDKApi.isInitialized else {
assertionFailure("YandexPaySDK is not initialized.")
return false
}
return YandexPaySDKApi.instance.applicationDidReceiveUserActivity(userActivity)
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
// Проверьте, что SDK проинициализирован
guard YandexPaySDKApi.isInitialized else {
assertionFailure("YandexPaySDK is not initialized.")
return false
}
return YandexPaySDKApi.instance.applicationDidReceiveOpen(url, options: options)
}
Примеры использования
Для ознакомления доступен демо-проект.
Добавление кнопки чекаута Яндекс Пэй на экран в Swift проекте
Во ViewController
экрана, на котором необходимо разместить кнопку, создайте ее с помощью метода createCheckoutButton(configuration:delegate:)
класса YandexPaySDKApi
:
import YandexPaySDK
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Проверьте, что 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.createCheckoutButton(configuration: configuration, delegate: self)
// Укажите скругления для кнопки (по умолчанию - 8px)
button.layer.cornerRadius = .zero
// Добавьте кнопку в иерархию
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)
])
}
}
Кнопка для взаимодействия с клиентским приложением использует делегат YandexPayCheckoutButtonDelegate
. Реализуйте данный делегат, чтобы передать его кнопке при ее создании:
import YandexPaySDK
extension ViewController: YandexPayCheckoutButtonDelegate {
// Обработайте результат чекаута
func yandexPayCheckoutButton(_ button: YandexPayButton, didCompletePaymentWithResult result: YPCheckoutResult) {
switch result {
case .succeeded(let checkoutInfo):
// чекаут был совершен успешно
case .failed(let checkoutError):
// В процессе чекаута произошла ошибка
case .cancelled:
// Пользователь закрыл/смахнул форму YandexPay
}
}
// Предоставьте UIViewController, с которого необходимо показать форму YandexPay по нажатию на кнопку
func yandexPayCheckoutButtonDidRequestViewControllerForPresentation(_ button: YandexPayButton) -> UIViewController? {
return self
}
// Предоставьте информацию о заказе
func yandexPayCheckoutButtonDidRequestPaymentSheet(_ button: YandexPayButton) -> YPCheckoutPaymentSheet? {
return YPCheckoutPaymentSheet(
// Код валюты
currencyCode: .rub,
// Информация о корзине покупателя
cart: YPPaymentCart(items: [
YPProduct(id: "1", total: "434.39", quantity: YPQuantity(count: "1")),
YPProduct(id: "2", total: "1474.86", quantity: YPQuantity(count: "2")),
]),
orderId: nil,
metadata: nil
)
}
}
Добавление кнопки чекаута YandexPay на экран в Objective-C проекте
Использовать Yandex Pay SDK из Objective-C можно только при инициализации YandexPaySDKApi
объекта и сообщении о жизненном цикле приложения SDK. Для создания кнопки, YPCheckoutPaymentSheet
объекта и реагирования на события, которые приходят от кнопки, реализуйте прослойку на Swift, доступную для вызова из Objective-C.
Чтобы сделать такую прослойку:
- Сделайте ее доступной из Objective-C, унаследовав данный класс от
NSObject
или предкаNSObject
. - Пометьте атрибутом
@objc
свойства и методы данной прослойки, которые должны быть доступны из Objective-C. - Сделайте доступными в Swift классы из Objective-C, добавив их header-файлы в bridging header вашего проекта.
- Установите коммуникацию между контейнером и, например,
UIViewController
объектом, написанным на Objective-C, с помощью объекта-делегата или любым другим способом.
Пример объекта-прослойки:
import YandexPaySDK
// В этом примере мы используем объект-делегат, чтобы сообщать о событиях
// из контейнера в UIViewController, который написан на Objective-C.
// Чтобы данный протокол был доступен в Objective-C, помечаем его @objc
// атрибутом.
@objc
protocol YandexPayCheckoutButtonContainerDelegate: AnyObject {
// Данный метод будем использовать для сообщения об успешном
// получении токена чекаута.
func yandexPayCheckoutButtonDidSucceedWithOrderId(_ orderId: String)
// Данный метод будем использовать для сообщения об ошибке,
// полученной в процессе.
func yandexPayCheckoutButtonDidFail()
// Данный метод будем использовать для сообщения об отмене.
func yandexPayCheckoutButtonDidCancel()
// Данный метод будем использовать для получения UIViewController
// объекта, из которого необходимо показать форму Яндекс Пэй.
func yandexPayCheckoutButtonDidRequestViewController() -> UIViewController?
// Для примера будем использовать отдельный метод для получения
// информации о корзине покупателя. Модель CardProduct написана
// на Objective-C и используется здесь, так как ее header-файл
// добавлен в bridging header файл проекта.
func yandexPayCheckoutButtonDidRequestCardProducts() -> [CardProduct]
}
// Объект-прослойка, который будет содержать в себе кнопку Яндекс Пэй и
// взаимодействовать с ней, а также взаимодействовать с Objective-C кодом.
final class YandexPayCheckoutButtonContainer: UIView {
// Свойства, которые должны быть доступны из Objective-C помечаем
// атрибутом @objc.
@objc
weak var delegate: YandexPayCheckoutButtonContainerDelegate?
private var payButton: YandexPayButton!
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
// Данный контейнер содержит только кнопку, поэтому переопределим
// intrinsicContentSize. Если же у вас более сложный контейнер,
// то его разметка может быть любой на ваше усмотрение.
override var intrinsicContentSize: CGSize {
return payButton.intrinsicContentSize
}
private func setupView() {
// Проверяем, проинициализирован ли SDK.
guard YandexPaySDKApi.isInitialized else {
assertionFailure("YandexPaySDKApi is not initialized.")
return
}
// Указываем тему для кнопки.
let payButtonTheme = YandexPayButtonTheme(appearance: .dark, dynamic: true)
// Указываем конфигурацию кнопки.
let payButtonConfiguration = YandexPayButtonConfiguration(theme: payButtonTheme)
// Создаем кнопку с помощью YandexPaySDKApi.
payButton = YandexPaySDKApi.instance.createCheckoutButton(configuration: payButtonConfiguration, delegate: self)
// Размещаем кнопку в иерархии.
payButton.autoresizingMask = [.flexibleWidth, .flexibleHeight]
payButton.frame = bounds
addSubview(payButton)
}
}
// Реализуем делегат кнопки, чтобы получать от нее события.
extension YandexPayCheckoutButtonContainer: YandexPayCheckoutButtonDelegate {
func yandexPayCheckoutButton(_ button: YandexPayButton, didCompletePaymentWithResult result: YPCheckoutResult) {
// Получаем события об успешном получении токена или об ошибке.
switch result {
case .succeeded(let checkoutInfo):
// Сообщаем через делегат Objective-C коду о получении.
// платежного токена
delegate?.yandexPayCheckoutButtonDidSucceedWithOrderId(checkoutInfo.orderId)
case .failed:
// Сообщаем через делегат Objective-C коду об ошибке.
delegate?.yandexPayCheckoutButtonDidFail()
case .cancelled:
// Сообщаем через делегат Objective-C коду об отмене.
delegate?.yandexPayCheckoutButtonDidCancel()
@unknown default:
break
}
}
func yandexPayCheckoutButtonDidRequestViewControllerForPresentation(_ button: YandexPayButton) -> UIViewController? {
// Запрашиваем через делегат у Objective-C кода UIViewController
// объект, в котором необходимо показывать форму Яндекс Пэй.
return delegate?.yandexPayCheckoutButtonDidRequestViewController()
}
func yandexPayCheckoutButtonDidRequestPaymentSheet(_ button: YandexPayButton) -> YPCheckoutPaymentSheet? {
guard let delegate = delegate else {
assertionFailure("Delegate is missing.")
return nil
}
// Здесь конструируем YPCheckoutPaymentSheet объект и возвращаем его
// кнопке. Для создания YPCheckoutPaymentSheet объекта, очевидно,
// нужно использовать некоторую бизнес-логику, которая написана
// на Objective-C. Для того, чтобы с данной бизнес-логикой
// взаимодействовать, вы можете использовать тот же объект
// делегата или же можете использовать другие Objective-C
// классы (например, какие-нибудь сервисы или интеракторы),
// которые данную логику инкапсулируют. Для этого
// сделайте данные классы доступными в Swift, добавив их
// header-файлы в bridging header файл вашего проекта.
// Стоит отметить, что бизнес-логика, реализованная в
// Objective-C будет использовать модели, реализованные на
// Objective-C. Поэтому здесь вы можете реализовать их маппинг
// в модели YandexPaySDK.
// В текущем примере мы используем объект делегата.
let products = delegate.yandexPayCheckoutButtonDidRequestCardProducts()
// Конструируем YPCheckoutPaymentSheet
return YPCheckoutPaymentSheet(
currencyCode: .rub,
cart: YPPaymentCart(items: products.map {
YPProduct(id: $0.productID, total: $0.total, quantity: YPQuantity(count: $0.quantity))
}),
orderId: nil,
metadata: nil
)
}
}
Пример UIViewController объекта, взаимодействующего с прослойкой:
#import <YandexPaySDK/YandexPaySDK-Swift.h>
// Добавляем соответствие протоколу.
@interface ViewController (ViewControllerYandexPayButtonContainer) <YandexPayCheckoutButtonContainerDelegate>
@end
// Объявляем приватные методы.
@interface ViewController ()
- (void)showAlertWithMessage:(NSString *)message;
@end
// Реализация ViewController класса.
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor systemBackgroundColor];
// Используем объект-прослойку для показа кнопки Яндекс Пэй.
YandexPayCheckoutButtonContainer *container = [[YandexPayCheckoutButtonContainer alloc] init];
// Указываем объект делегата для прослойки.
container.delegate = self;
// Добавляем объект-прослойку в иерархию.
container.translatesAutoresizingMaskIntoConstraints = false;
[self.view addSubview:container];
[[container.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor] setActive:TRUE];
[[container.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor] setActive:TRUE];
}
- (void)showAlertWithMessage:(NSString *)message {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Message" message:message preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:controller animated:TRUE completion:nil];
}
- (void)yandexPayCheckoutButtonDidSucceedWithOrderId:(NSString *)orderId {
// Получили токен и можем провести чекаут с помощью
// этого токена.
[self showAlertWithMessage:[NSString stringWithFormat:@"Checkout complete: %@", orderId]];
// Обращение к платежному шлюзу
// ...
}
- (void)yandexPayCheckoutButtonDidFail {
// Получили событие об ошибке.
[self showAlertWithMessage:@"Checkout failed"];
}
- (void)yandexPayCheckoutButtonDidCancel {
// Получили событие об отмене.
[self showAlertWithMessage:@"Checkout was cancelled by the user"];
}
- (UIViewController *)yandexPayCheckoutButtonDidRequestViewController {
// Возвращаем текущий UIViewController для показа формы.
return self;
}
- (NSArray<CardProduct *> *)yandexPayCheckoutButtonDidRequestCardProducts {
// Если необходимо, передавайте информацию о заказе и другую
// информацию через делегат или любым другим удобным способом.
NSMutableArray *products = [[NSMutableArray alloc] init];
[items addObject:[[CardProduct alloc] initWithProductID:@"ID-1" total:@"1000.00" quantity: @"1"]];
[items addObject:[[CardProduct alloc] initWithProductID:@"ID-2" total:@"500.00" quantity: @"2"]];
return products;
}
@end
Структура CardProduct
может быть любой вашей моделью, реализованной на Objective-C, а здесь она используется в качестве иллюстрации.
Файл CardProduct.h:
@interface Product : NSObject
@property (readonly, copy, nonatomic) NSString *productID;
@property (readonly, copy, nonatomic) NSString *total;
@property (readonly, copy, nonatomic) NSString *quantity;
- (instancetype)initWithProductID:(NSString *)productID
total:(NSString *)total
quantity:(NSString *)quantity;
@end
Вы можете использовать асинхронный делегат, если не можете моментально предоставить YPCheckoutPaymentSheet
:
-
Создайте кнопку, используя специализированный метод класса
YandexPaySDKApi
:import YandexPaySDK payButtonAsync = YandexPaySDKApi.instance.createCheckoutButton(configuration: configuration, asyncDelegate: self)
-
Реализуйте делегат
YandexPayButtonAsyncDelegate
:import YandexPaySDK extension ViewController: YandexPayButtonAsyncDelegate func yandexPayCheckoutButton(_ button: YandexPayButton, didCompletePaymentWithResult result: YPCheckoutResult) { // ... } func yandexPayCheckoutButtonDidRequestViewControllerForPresentation(_ button: YandexPayButton) -> UIViewController? { // ... } func yandexPayCheckoutButtonDidRequestPaymentSheet(_ button: YandexPayButton, completion: @escaping (YPCheckoutPaymentSheet?) -> Void) { // Покажите лоадер на кнопке, используя предоставленный метод, или же покажите свой лоадер, если необходимо payButtonAsync.setLoaderVisible(true, animated: true) // Сделайте асинхронный вызов DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) { [weak self] in // Получите YPCheckoutPaymentSheet let paymentSheet = YPCheckoutPaymentSheet(...) DispatchQueue.main.async { // Скройте лоадер self?.payButtonAsync.setLoaderVisible(false, animated: true) // Вызовите completion-блок completion(paymentSheet) } } }