Yandex Pay iOS SDK integration
Yandex Pay iOS SDK is a library that enables you to embed a Yandex Pay payment button into your application and start accepting payments from users.
Requirements for connecting
Required iOS SDK version: 12.0.
Yandex Pay SDK uses YandexLoginSDK. Set it up by following the instructions.
Once you configure YandexLoginSDK, make sure that your app has access to Yandex Pay added in Yandex OAuth. To do this, go to the app editing page and under Which data do you need?, select Yandex Pay
→ Pay by Yandex Pay
.
Yandex Pay SDK installation
- Add a
pod 'YandexPaySDK/Static'
orpod 'YandexPaySDK/Dynamic'
dependency to thePodfile
for your target. - If there's no
Podfile
in your project, initialize it according to theCocoaPods
instructions and repeat the previous step. - Set up the dependencies by executing
pod install --repo-update
. - Open the generated
.xcworkspace
and assemble the project.
Add to Package.swift:
... = Package(
...
targets: [
.binaryTarget(name: "YandexPaySDK",
url: "https://yandexpay-ios-sdk.s3.yandex.net/1.2.1/YandexPaySDK_121222_7524285.xcframework.zip",
checksum: "714698b6e5ff407a76304ce8cf7801fcfc143e432157a1c6624abb04c51bfc2e"),
]
...
)
Only a dynamic library with all the necessary dependencies is currently supported. The source code can't be shared because of PCI DSS restrictions.
Yandex Pay SDK initialization
Initialize the SDK in your project's AppDelegate.swift
in the application(_:didFinishLaunchingWithOptions:)
:
import YandexPaySDK
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
// Specify the configuration
let merchant = YandexPaySDKMerchant(
// Merchant ID in Yandex Pay
id: "MERCHANT_ID",
// Merchant name
name: "MERCHANT_NAME",
// Merchant URL
url: "https://example.org/"
)
let configuration = YandexPaySDKConfiguration(
// Required environment
environment: .sandbox,
// Merchant information
merchant: merchant,
// Localization
locale: .ru
)
// Initialize the SDK
try YandexPaySDKApi.initialize(configuration: configuration)
} catch {
// Handle the error appropriately
assertionFailure("Unable to initialize YandexPaySDKApi.")
}
// Initialize UIWindow and 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>
// Specify the configuration
YandexPaySDKMerchant* merchant = [[YandexPaySDKMerchant alloc] initWithMerchantId: @"MERCHANT_ID" // Merchant ID in YandexPay
name: @"MERCHANT_NAME" // Merchant name
url: @"https://example.org/"]; // Merchant URL
// Required environment
YandexPaySDKConfiguration* configuration = [[YandexPaySDKConfiguration alloc] initWithEnvironment: YandexPaySDKEnvironmentSandbox
merchant: merchant // Merchant information
locale: YandexPaySDKLocaleRU]; // Localization
// Initialize the SDK
NSError *initializationError;
[YandexPaySDKApi initializeWithConfiguration: configuration error:&initializationError];
// Handle the error appropriately
if (initializationError != nil) {
[NSException raise:@"FailedToInitializePay" format:@"Subclasses must implement a valid init method"];
}
// Initialize UIWindow and ViewController
UIViewController *controller = [[ViewController alloc] init];
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[window setRootViewController:controller];
[window makeKeyAndVisible];
_window = window;
return YES;
Add a YandexPaySDK
notification about the app lifecycle events in your project's AppDelegate.swift
.
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];
}
Adding a Yandex Pay button to the screen in a Swift project
In the ViewController
of the screen where you need to add the button, create the button using the createButton(configuration:delegate:)
method of the YandexPaySDKApi
class:
import YandexPaySDK
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Check that the SDK is initialized
guard YandexPaySDKApi.isInitialized else {
assertionFailure("YandexPaySDK is not initialized.")
return
}
// Specify the theme for the button
let theme: YandexPayButtonTheme
if #available(iOS 13.0, *) {
// Using the `dynamic` parameter, you can specify whether the button has
// to change its color palette when the system theme changes
theme = YandexPayButtonTheme(appearance: .dark, dynamic: true)
} else {
theme = YandexPayButtonTheme(appearance: .dark)
}
// Initialize the configuration
let configuration = YandexPayButtonConfiguration(theme: theme)
// Create the button
let button = YandexPaySDKApi.instance.createButton(configuration: configuration, delegate: self)
// Specify the corner radius for the button (the default value is 8px)
button.layer.cornerRadius = .zero
// Add the button to the hierarchy
view.addSubview(button)
// Add the layout for the button
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
button.widthAnchor.constraint(equalToConstant: 250)
])
}
}
The button used for interacting with the client app uses the delegate YandexPayButtonDelegate
. Implement this delegate to pass it to the button at creation:
import YandexPaySDK
extension ViewController: YandexPayButtonDelegate {
// Process the payment result
func yandexPayButton(_ button: YandexPayButton, didCompletePaymentWithResult result: YPPaymentResult) {
switch result {
case .succeeded(let paymentInfo):
// Payment was successful
case .failed(let paymentError):
// There was an error during payment
case .cancelled:
// The user closed/swiped away the YandexPay form?
}
}
// Provide UIViewController from which you need to show the YandexPay form by clicking the button
func yandexPayButtonDidRequestViewControllerForPresentation(_ button: YandexPayButton) -> UIViewController? {
return self
}
// Provide information about the merchant and the cart
func yandexPayButtonDidRequestPaymentSheet(_ button: YandexPayButton) -> YPPaymentSheet? {
return YPPaymentSheet(
// Country code
countryCode: .ru,
// Currency code
currencyCode: .rub,
// Order details
order: YPOrder(
// Order ID
id: "ORDER-ID",
// Order cost
amount: "10000.00"
),
// Available payment methods
paymentMethods: [
// Only payment by card is currently available
.card(
YPCardPaymentMethod(
// ID of the payment service provider
gateway: "test-gateway",
// ID of the merchant in the payment service provider system
gatewayMerchantId: "test-gateway-merchant-id",
// What will be contained in the payment token: encrypted bank card data or a tokenized card
allowedAuthMethods: [
.panOnly
],
// List of supported payment systems
allowedCardNetworks: [
.mastercard,
.visa,
.mir
]
)
)
]
)
}
}
Adding a YandexPay button to the screen in the Objective-C project
You can only use Yandex Pay SDK from Objective-C when initializing a YandexPaySDKApi
object and reporting the application lifecycle to the SDK. To create a button, YPPaymentSheet
object, and to start responding to the events emitted by the button, implement a layer in Swift that can be called from Objective-C.
To create such a layer:
- Make this layer available in Objective-C by inheriting this class from
NSObject
or an ancestor ofNSObject
. - Use the
@objc
attribute to label the properties and methods of this layer that should be available from Objective-C. - Make classes from Objective-C available in Swift, adding their header files to the bridging header of your project.
- Establish communication between the container and, for example, the
UIViewController
object written in Objective-C by using a delegate object or otherwise.
Example of the layer object:
import YandexPaySDK
// In this example, we use a delegate object to report events
// from the UIViewController container written in Objective-C.
// To make this protocol available in Objective-C, we label it using the @objc
// attribute.
@objc
protocol YandexPayButtonContainerDelegate: AnyObject {
// We will use this method to notify
// that the payment token was received successfully.
func yandexPayButtonDidSucceedWithPaymentToken(_ paymentToken: String)
// We will use this method to report an error
// that arose in the process.
func yandexPayButtonDidFail()
// We'll use this method to notify about cancellation.
func yandexPayButtonDidCancel()
// We'll use this method to get UIViewController
// of the object for which you need to show the Yandex Pay form.
func yandexPayButtonDidRequestViewController() -> UIViewController?
// In the example, we'll use a separate method to get
// the order data. The Order model is written in Objective-C and
// we use it here because its header file // has been added to the bridging
header file of the project.
func yandexPayButtonDidRequestOrder() -> Order
}
// A layer object that will include the Yandex Pay button and
// interact with the button and with the Objective-C code.
final class YandexPayButtonContainer: UIView {
// We label the properties that should be available from Objective-C
// with the @objc attribute.
@objc
weak var delegate: YandexPayButtonContainerDelegate?
private var payButton: YandexPayButton!
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
// This container only includes a button, so let's override
// intrinsicContentSize. If your container is more sophisticated,
// you can use any view layout for it at your discretion.
override var intrinsicContentSize: CGSize {
return payButton.intrinsicContentSize
}
private func setupView() {
// Check whether the SDK has been initialized.
guard YandexPaySDKApi.isInitialized else {
assertionFailure("YandexPaySDKApi is not initialized.")
return
}
// Specify a theme for the button.
let payButtonTheme = YandexPayButtonTheme(appearance: .dark, dynamic: true)
// Specify the button layout.
let payButtonConfiguration = YandexPayButtonConfiguration(theme: payButtonTheme)
// Create a button using YandexPaySDKApi.
payButton = YandexPaySDKApi.instance.createButton(configuration: payButtonConfiguration, delegate: self)
// Add the button to the hierarchy.
payButton.autoresizingMask = [.flexibleWidth, .flexibleHeight]
payButton.frame = bounds
addSubview(payButton)
}
}
// Implement the button's delegate to get events from it.
extension YandexPayButtonContainer: YandexPayButtonDelegate {
func yandexPayButton(_ button: YandexPayButton, didCompletePaymentWithResult result: YPPaymentResult) {
// Get events about a successfully received token or about an error.
switch result {
case .succeeded(let paymentInfo):
// Use the delegate object to notify the Objective-C code that we received the...
// payment token
delegate?.yandexPayButtonDidSucceedWithPaymentToken(paymentInfo.paymentToken)
case .failed:
// Use the delegate object to report the error to the Objective-C code.
delegate?.yandexPayButtonDidFail()
case .cancelled:
// Use the delegate object to notify the Objective-C code about the cancellation.
delegate?.yandexPayButtonDidCancel()
@unknown default:
break
}
}
func yandexPayButtonDidRequestViewControllerForPresentation(_ button: YandexPayButton) -> UIViewController? {
// Use the delegate object to request, from the Objective-C code, the UIViewController
// object in which the Yandex Pay form should be shown.
return delegate?.yandexPayButtonDidRequestViewController()
}
func yandexPayButtonDidRequestPaymentSheet(_ button: YandexPayButton) -> YPPaymentSheet? {
guard let delegate = delegate else {
assertionFailure("Delegate is missing.")
return nil
}
// Construct the YPCheckoutPaymentSheet object here and return it to
// the button. Naturally, when creating the YPPaymentSheet object,
// you need to use a certain business logic written in
// Objective-C. To interact with this business logic,
// you can use the same delegate object
// or other Objective-C
// classes (for example, some services or interactors)
// that encapsulate this logic. To do this,
// make these classes available in Swift by adding their
// header files to the bridging header file of your project.
// Note that if you implement your business logic in
// Objective-C, it will use models implemented in
// Objective-C. That's why here you can implement their mapping
// to YandexPaySDK models.
// In this example, we use a delegate object.
let order = delegate.yandexPayButtonDidRequestOrder()
// Construct YPPaymentSheet
return YPPaymentSheet(
countryCode: .ru,
currencyCode: .rub,
// Map the model written in Objective-C to the model in Swift.
order: YPOrder(
id: order.orderId!,
label: order.label,
amount: order.amount!,
items: order.items?.compactMap({ $0 as? OrderItem }).map({ YPOrderItem(label: $0.label, amount: $0.amount) })
),
paymentMethods: [
.card(
YPCardPaymentMethod(
gateway: "yandex-trust",
gatewayMerchantId: "MerchantGW1",
allowedAuthMethods: [
.panOnly
],
allowedCardNetworks: [
.visa,
.mastercard,
.mir
]
)
)
]
)
}
}
Here's an example of a UIViewController object interacting with the layer:
#import <YandexPaySDK/YandexPaySDK-Swift.h>
// Add conformance to the protocol.
@interface ViewController (ViewControllerYandexPayButtonContainer) <YandexPayButtonContainerDelegate>
@end
// Declare private methods.
@interface ViewController ()
- (void)showAlertWithMessage:(NSString *)message;
@end
// Implement the ViewController class.
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor systemBackgroundColor];
// Use the layer object to show the Yandex Pay button.
YandexPayButtonContainer *container = [[YandexPayButtonContainer alloc] init];
// Specify the delegate object for the layer.
container.delegate = self;
// Add the layer object to the hierarchy.
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)yandexPayButtonDidSucceedWithPaymentToken:(NSString *)paymentToken {
// We've got a token and can now check out
// using this token.
[self showAlertWithMessage:[NSString stringWithFormat:@"Payment complete: %@", paymentToken]];
// Access the payment gateway
// ...
}
- (void)yandexPayButtonDidFail {
// We received an error event.
[self showAlertWithMessage:@"Payment failed"];
}
- (void)yandexPayButtonDidCancel {
// We received a cancellation event.
[self showAlertWithMessage:@"Payment was cancelled by the user"];
}
- (UIViewController *)yandexPayButtonDidRequestViewController {
// Return the current UIViewController to render the form.
return self;
}
- (Order *)yandexPayButtonDidRequestOrder {
// If needed, transmit the order data and other
// data using the delegate or by any other convenient method.
NSMutableArray *items = [[NSMutableArray alloc] init];
[items addObject:[[OrderItem alloc] initWithLabel:@"Wellington boots" amount:@"7000"]];
[items addObject:[[OrderItem alloc] initWithLabel:@"Belt" amount:@"3000"]];
return [[Order alloc] initWithOrderId:@"ORDER1" label:@"ORDER1" amount:@"10000" items:[NSArray arrayWithArray:items]];
}
@end
As the Order
and OrderItem
structures, you can use any of your models implemented in Objective-C. Here we use sample objects for illustrative purposes.
Order.h file:
@interface Order : NSObject
@property (readonly, copy, nonatomic) NSString *orderId;
@property (readonly, copy, nonatomic) NSString *label;
@property (readonly, copy, nonatomic) NSString *amount;
@property (readonly, copy, nonatomic) NSArray *items;
- (instancetype)initWithOrderId:(NSString *)orderId
label:(NSString *)label
amount:(NSString *)amount
items:(NSArray *)items;
@end
OrderItem.h file:
@interface OrderItem : NSObject
@property (readonly, copy, nonatomic) NSString *label;
@property (readonly, copy, nonatomic) NSString *amount;
- (instancetype)initWithLabel:(NSString *)label
amount:(NSString *)amount;
@end
You can use an asynchronous delegate object if you cannot instantly provide YPPaymentSheet
:
-
Create a button using a specialized method of the
YandexPaySDKApi
class:import YandexPaySDK payButtonAsync = YandexPaySDKApi.instance.createButton(configuration: configuration, asyncDelegate: self)
-
Implement the delegate
YandexPayButtonAsyncDelegate
:import YandexPaySDK extension ViewController: YandexPayButtonAsyncDelegate func yandexPayButton(_ button: YandexPayButton, didCompletePaymentWithResult result: YPPaymentResult) { // ... } func yandexPayButtonDidRequestViewControllerForPresentation(_ button: YandexPayButton) -> UIViewController? { // ... } func yandexPayButtonDidRequestPaymentSheet(_ button: YandexPayButton, completion: @escaping (YPPaymentSheet?) -> Void) { // Show a loader on the button using the provided method or show your custom loader if needed payButtonAsync.setLoaderVisible(true, animated: true) // Make an asynchronous call DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) { [weak self] in // Get YPPaymentSheet let paymentSheet = YPPaymentSheet(...) DispatchQueue.main.async { // Hide the loader self?.payButtonAsync.setLoaderVisible(false, animated: true) // Call the completion block completion(paymentSheet) } } }