API

Apple iOS SDK Tap to Pay

This documentation provides an overview and details for the core components of the YavinSDK, focusing on the YavinManager class and its related types. The SDK handles authentication, payment processing, and device eligibility for Tap to Pay functionality.

At a glance

DistributionSwift Package Manager
AuthenticationStandalone login flow (merchant credentials) or Yavin API key
Naming conventionSwift API, amounts as integers in cents
Current versionv1.7.3
Result deliverySynchronous: completion handlers on each SDK call

Overview

The SDK provides solutions for:

  • User Authentication: Handling login using either the Yavin API key or email/password.
  • Payment Processing: Initiating and completing payment requests.
  • Device Eligibility: Verifying that the device meets the necessary requirements (e.g., iOS version, hardware specifications, passcode).
  • User Interface: Presenting authentication and payment views with a full-screen modal presentation.
  • Error Handling: Managing and propagating errors related to authentication, payment, and device eligibility.

Requirements

  • Hardware: iPhone XS or newer (for Tap to Pay)
  • Device OS: iOS 17+ (for Tap to Pay)
  • Security: Device passcode must be set

Installation

Add dependency

Add the Swift package dependency in your Xcode project:

Project -> Package Dependencies -> Click + icon

In the search bar add the dependency depending on your usage:

In SANDBOX environment, you need a Sandbox iCloud account.

Ask for the Tap to Pay on iPhone capability to be added to your Apple Account.

Documentation illustration

Demo application

You can find a demo application on this repo:

https://gitlab.com/yavin-public/demo-ios

YavinManager usage

The YavinManager class is the central point of interaction for the SDK. It follows a singleton pattern and encapsulates the Yavin authentication and payment features.

Import the SDK

In order to start, first add the YavinSDK import:

Swift
import YavinSDK

Access the shared instance

Access the features using the YavinManager.shared instance:

Swift
let yavinSdk = YavinManager.shared

SwiftUI: get the UIViewController

If you’re using SwiftUI, some API methods require a UIViewController in order to work. The SDK provides a helper class ViewControllerAccessor to access the UIViewController from your SwiftUI View.

Swift
struct  MySwiftUIView: View {    
		@State  private var currentViewController: UIViewController?    
		var  body: some  View {        
				VStack {            
						// Use `currentViewController` to launch Yavin features        
				}        
				.background(            
						ViewControllerAccessor { vc in                
								self.currentViewController = vc
			      }        
		    )
		}
}

Authentication

There are two ways to log in the SDK:

  • Standalone Login Flow
  • Using Yavin API key

Standalone Login Flow

As the cash register may not have the Yavin API key for login in, we provide a standalone flow.

The standalone flow allows a cash register to provides merchants a fluid experience to enable Tap to Pay on iPhone.

It follows 3 steps:

  1. Display a “publicity” screen
  2. Merchant logs in using Yavin’s credentials
  3. Acceptation of Yavin’s terms regarding Tap to Pay

Here’s how it looks:

Documentation illustration
Documentation illustration
Documentation illustration

Once the terms have been accepted by the merchant, the SDK logs in, and on success payments can be processed.

The cash register can detect the SDK authentication using YavinManager.shared.isLoggedIn() (see more below)

Recommendations:

The cash register should display a button “Activate Tap to Pay on iPhone” if the SDK is not logged in. Upon click of the button, the cash register should call YavinManager.shared.showTapToPayActivationFlow() . This will show the “publicity” screen seen above.

When login is successful, cash register may hide the button, because at this point the Yavin SDK is logged in with the merchant and is able to process payments.

If the cash register can switch between several points of sale, it is cash register responsibility to logout and re-login the Yavin SDK before processing payments for the new selected point of sale.

Swift
YavinManager.shared.showTapToPayActivationFlow(
	viewController: currentViewController,
	onDismiss: {},
	returnApiKey: false,
  onCompletion: { result in
	  switch result {
				case .success(let loginResult):
					if loginResult.success {
							// SDK is logged in, payments can be processed
					}
				case .failure(let error):
					// handle error
			}
	}
)

Login using Yavin API Key

Partners may use this method if they are already handling the Yavin API key, otherwise we recommend using the Standalone Flow.

Swift
let request = YavinLoginRequest(    
		yavinApiKey: "your_yavin_api_key"
)

YavinManager.shared.login(    
		viewController: self,    
		request: request
) { result in    
		switch result {    
		case .success(let loginResult):        
				if loginResult.success {
            // Handle successful login
        }   
		case .failure(let error):        
				// Handle error    
		}
}

Refresh Login

This method is used to refresh the token the SDK is using internally for enabling payment processing.

You must be logged in in order to use this method.

Swift
YavinManager.shared.refreshLogin(viewController: self) { result in    
		switch result {    
		case .success(let loginResult):        
				if loginResult.success {
            // Handle successful login
        }  
		case .failure(let error):        
				// Handle error    
		}
}

Check if logged in

Swift
// Boolean check
let isLoggedIn = YavinManager.shared.isLoggedIn()

// Publisher for reactive updates
var cancellables = Set<AnyCancellable>()
YavinManager.shared.isLoggedIn()
    .sink { isLoggedIn in
        // Update UI
    }
    .store(in: &cancellables)

Logout

This method clears all data and set YavinManager.shared.isLoggedIn to false. You will need to login again in order to take payments.

Swift
YavinManager.shared.logout()

Payment

Once the SDK is logged in, payments are launched with a single call. Amounts are always integers in cents.

Launch payment API

Swift
let paymentRequest = YavinPaymentRequest(    
		amount: 1000, // In cents: 10€
		transactionType: .debit,
		reference: "my reference"
)

YavinManager.shared.launchPaymentRequest(    
		viewController: self,    
		request: paymentRequest
) { result in    
		switch result {    
		case .success(let paymentResult):        
				// Handle processed payment
        print("payment with id \(paymentResult.transactionId) processed")   
		case .failure(let error):        
				// Handle payment error    
			}
}

Cancel a payment by API

Set transactionType to reversal. When the payment is being processed, a pin code is required to confirm the cancellation.

The pin code is available on your company page in the Yavin backoffice, under Company Details > Pin code.

Swift
let paymentRequest = YavinPaymentRequest(    
		amount: 1000, // In cents: 10€
		transactionType: .reversal
)

YavinManager.shared.launchPaymentRequest(    
		viewController: self,    
		request: paymentRequest
) { result in    
		switch result {    
		case .success(let paymentResult):        
				// Handle processed payment
        print("payment with id \(paymentResult.transactionId) processed")   
		case .failure(let error):        
				// Handle payment error    
			}
}

Additional Features

Tips activation

In order to propose tips to your customer, use the following function:

Swift
YavinManager.shared.enableTipsScreen(enable: true)

Check eligibility programmatically

Swift
if YavinManager.shared.isDeviceEligible() {
    // Device supports Tap to Pay
} else {
    // Device doesn't meet requirements
}

In your app, you can navigate to YavinManager.shared.YavinEligibilityView() in order to see device eligibility in a standalone way.

Example:

Documentation illustration
Swift
NavigationLink(destination: YavinManager.shared.YavinEligibilityView()) {
    VStack(alignment: .leading) {
        Text("Eligibility")
            .foregroundStyle(.black)
        Text("SoftPos requirements")
            .font(.footnote)
            .foregroundStyle(.gray)
    }
}

Tap to Pay Tutorial Presentation

Documentation illustration
Swift
Task {    
		await YavinManager.shared.showTapToPayTutorial(viewController: self)
}

Prepare ProximityReader

In order to give your users a fluid experience, you need to prepare the proximity reader of the device before a payment (otherwise it may take few seconds before the payment actually starts).

Call prepareProximityReader() ahead of the payment:

The SDK calls this function internally while:

  • logging in
  • refreshing login
  • before payment started

Recommendation: prepare the proximity reader when typing an amount or products to pay.

Swift
YavinManager.shared.prepareProximityReader()

Data structure

YavinLoginRequest

Swift
public struct YavinLoginRequest {
    public let yavinApiKey: String?
}

YavinLoginResult

Swift
public struct YavinLoginResult {
    public let success: Bool
    public let apiKey: String? // filled when returnApiKey is true
}

YavinLoginError

Swift
public enum YavinLoginError: Error, Sendable, Hashable {
    case deviceNotEligible(message: String? = nil)
    case invalidCredentials(message: String? = nil)
    case loginYavinFailed(message: String? = nil)
    case loginGatewayFailed(message: String? = nil)
    case refreshLoginForbidden(message: String? = nil)
    case tapToPayDisabled(message: String? = nil)
    case invalidPaymentContract(message: String? = nil)
    case tapToPayTermsNotGranted(message: String? = nil)
}

YavinPaymentRequest

Swift
public struct YavinPaymentRequest {
    public let amount: Int
    public let transactionType: YavinTransactionType
    public let currency: String
    public let reference: String?
    public let idempotentUuid: String?
    
    public init(
        amount: Int,
        transactionType: YavinTransactionType,
        currency: String? = "EUR",
        reference: String? = nil,
        idempotentUuid: String? = nil
    ) {
        self.amount = amount
        self.transactionType = transactionType
        self.currency = currency ?? "EUR"
        self.reference = reference
        self.idempotentUuid = idempotentUuid
    }
}

YavinPaymentResult

Swift
public struct YavinPaymentResult: Hashable {
    public let success: Bool
    public let transactionId: String?
    public let amount: Int
    public let giftAmount: Int
    public let totalAmount: Int
    public let transactionType: YavinTransactionType
    public let currency: String
    public let requiresSignature: Bool
    public let deviceTimestamp: Int?
    public let ticketUrl: String?
    public let issuer: String?
    public let scheme: String?
    public let reference: String?
    public let idempotentUuid: String?
    public let cardToken: String?
    public let paymentApplication: String?
    public let clientTicket: String?
    public let companyTicket: String?
}

YavinPaymentError

Swift
public enum YavinPaymentError: Error, Sendable {
    case deviceNotEligible(message: String? = nil)
    case notLoggedIn(message: String? = nil)
    case cancelByUser(message: String? = nil)
    case illegalTransactionState(message: String? = nil)
    case paymentFailed(message: String? = nil)
}