# 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

|  |  |
| --- | --- |
| Distribution | Swift Package Manager |
| Authentication | Standalone login flow (merchant credentials) or Yavin API key |
| Naming convention | Swift API, amounts as integers in cents |
| Current version | v1.7.3 |
| Result delivery | Synchronous: 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:

- PROD: [https://gitlab.com/yavin-public/ios-sdk-spm.git](https://gitlab.com/yavin-public/ios-sdk-spm.git)

- SANDBOX: [https://gitlab.com/yavin-public/ios-sdk-spm-sandbox.git](https://gitlab.com/yavin-public/ios-sdk-spm-sandbox.git)

> ⚠️ 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.

_[Illustration]_

## Demo application

You can find a demo application on this repo:  

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

## Flow Chart

[Tap to Pay on iPhone: end-to-end flow chart (Figma)](https://www.figma.com/design/ep1T6iRarfsDspOnl2ZAOd/2025-Q2---Tap2Pay-on-Iphone?node-id=2001-25&t=STbYvPTNkKfCtElw-1)

## 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

1. Merchant logs in using Yavin’s credentials

1. Acceptation of Yavin’s terms regarding Tap to Pay

Here’s how it looks:

_[Illustration]_

_[Illustration]_

_[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.

```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
			}
	}
)
```

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.

### 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
}

```

### Navigate to EligibilityView

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

Example:

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

_[Illustration]_

### Tap to Pay Tutorial Presentation

```swift
Task {    
		await YavinManager.shared.showTapToPayTutorial(viewController: self)
}
```

_[Illustration]_

### 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:

```swift
YavinManager.shared.prepareProximityReader()
```

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.

## 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)
}
```

## Related pages

[In-store payment: Cloud API](https://app.notion.com/p/3bc9a8f4fd9a81518b1bda59179c4d1b)

[Webservices API](https://app.notion.com/p/3bc9a8f4fd9a81529b21d135de85f424)

[IdempotentUuid Management](https://app.notion.com/p/3bc9a8f4fd9a81d6a3cbfce4939ac0e9)
