Skip to content

iOS SDK Onboarding Guide

SumUp provides a native iOS SDK that enables you to integrate SumUp's proprietary card terminal(s) and its payment platform to accept credit and debit card payments (incl. VISA, MasterCard, American Express and more) as well as Tap-to-Pay payments on iPhones. SumUp's SDK communicates transparently to the card terminal(s) via Bluetooth. Upon initiating a checkout, the SDK guides your user using appropriate screens through each step of the payment process. As part of the process, SumUp also provides the card terminal setup screen, along with the cardholder signature verification screen. The checkout result is returned with the relevant data for your records.

No sensitive card data is ever passed through to or stored on the merchant’s phone. All data is encrypted by the card terminal, which has been fully certified to the highest industry standards (PCI, EMV I & II, Visa, MasterCard & Amex).

SumUp iOS SDK is provided as an Objective C binary. However, when you use the SDK in Swift projects, Xcode uses automatic bridging to generate Swift-friendly interfaces from the Objective-C headers. For that reason, code samples in this guide are provided both in Swift and Objective C.

The iOS SDK includes a Sample App, which you can run out-of-the-box to immediately test the implementation in practice.

  • Registered for a merchant account via SumUp's country websites (or received a test account).
  • Received SumUp card terminal: Solo Lite, Solo, Air, 3G, PIN+.
  • Requested an Affiliate (Access) Key via SumUp Dashboard for Developers.
  • Deployment Target iOS 14.0 or later.
  • Recommended to use on Xcode 14.3.1 and iOS SDK 16 or later.
  • iPhone or iPad.
  • If your device is managed by an organization, make sure app installation is permitted.
  • From version 4.4.0 of the SDK, iOS 14 or later is required
  • The SDK supports all device orientations on iPad and portrait on iPhone. Feel free to support other orientations on iPhone but please keep in mind that the SDK's UI will be presented in portrait on iPhone. See UISupportedInterfaceOrientations in the sample app's Info.plist or the "General" tab in Xcode's Target Editor.

iOS SDK uses the Affiliate Key from your merchant account to authenticate your app.

  1. Log in to SumUp with your merchant account and open the Affiliate Key page.
  2. Create an Affiliate Key if you don't have one yet.
  3. Add your app's Bundle ID in the SumUp portal's Application ID field. This way, your app will be able to call SumUp APIs, which require the Affiliate Key.

The SumUp iOS SDK requires access to the user's location and Bluetooth peripherals. If your app has not asked for the user's permission, the SumUp iOS SDK will ask at the time of the first login or checkout attempt. Please add the following keys to your info.plist file and set some values:

NSLocationWhenInUseUsageDescription
NSBluetoothAlwaysUsageDescription
NSBluetoothPeripheralUsageDescription (unless your deployment target is at least iOS 13)

Check the Sample App property list for reference.

If you want to dive straight into implementation, carry out the following steps:

  1. Install the SDK.
  2. Import the SDK into your project file.
  3. Initialize the SDK with an Affiliate Key.
  4. Log the user in.
  5. Prepare user's device for checkout.
  6. Allow the user to select a card reader.
  7. Finally, implement the full checkout.

Please consider the following when building Tap-to-Pay solutions, as Apple reviews them with high scrutiny:

  1. Install the SDK.
  2. Import the SDK into your project file.
  3. Initialize the SDK with an Affiliate Key.
  4. Log the user in.
  5. Follow steps under Implementing Tap-to-Pay.

The SumUp iOS SDK is provided as an XCFramework SumUpSDK.xcframework that contains the headers and bundles containing resources such as images and localizations. You can add the SDK binary manually or use a package manager such as Swift Package Manager, Cocoapods, or Carthage. Please follow the relevant instructions below to prepare your project:

The latest Swift Package Manager version added support to distribute binary frameworks as Swift Packages.

If you're using Xcode 12.3 or later, this should work out of the box.

Unfortunately, Xcode 12 releases before that had an issue, (https://bugs.swift.org/browse/SR-13343), that added the framework as a static library, not as an embedded dynamic framework.

Follow this workaround to manage SumUp iOS SDK versions via Swift PM in those cases:

  1. Add the package dependency to the repository https://github.com/sumup/sumup-ios-sdk (File > Swift Packages > Add Package Dependency...) with the version Up to Next Major: 4.0.0
  2. Leave the checkbox unchecked for the SumUpSDK at the integration popup (Add Package to ...:)
  3. From the Project Navigator, drag and drop the SumUpSDK/Referenced Binaries/SumUpSDK.xcframework to your Xcode project's "Frameworks, Libraries, and Embedded Content" on the General settings tab.
  4. Make sure the required Info.plist keys are present.

To learn more about adding Swift Package dependencies, please refer to the official documentation.

To import the SDK in Objective-C source files, you can use #import <SumUpSDK/SumUpSDK.h>. If module support is enabled in your project, you can use @import SumUpSDK; instead.

In Swift, use import SumUpSDK. You do not have to add any headers to your bridging header.

SumUp iOS SDK supports either a modally presented login from a view controller (as you can see in the Sample App) or using an Access Token via the Authorization Code Flow.

/**
* Presents the login modally from the given view controller.
*
* The login is automatically dismissed if login was successful or cancelled by the user.
* If error is nil and success is NO, the user cancelled the login.
* Errors are handled internally and usually do not need any display to the user.
* Does nothing if merchant is already logged in (calls completion block with success=NO, error=nil).
*
* @param fromViewController The UIViewController instance from which the login should be presented modally.
* @param animated Pass YES to animate the transition.
* @param block The completion block is called after each login attempt.
*/
+ (void)presentLoginFromViewController:(UIViewController *)fromViewController
animated:(BOOL)animated
completionBlock:(nullable SMPCompletionBlock)block;

Before calling any additional feature of the SumUp iOS SDK, you are required to set up the SDK with your Affiliate Key. Call on the main thread. You may wish to defer calling setupWithAPIKey: until after app launch, as it requests the user's location permission.

Login screen
Login screen
import SumUpSDK
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
/*
* This will setup the SumUpSDK.
*
* You might consider moving this to a later point in your application's lifecycle,
* as this will start updating for locations.
*
* Also remember to provide the necessary usage descriptions in your info.plist
* and to properly localize it, see the
* Add Property List Keys to Project section.
*
* Ensure to add the Bundle Identifier of your iOS app to your
* Affiliate Key's Application identifiers in the SumUp developer portal.
*/
SumUpSDK.setup(withAPIKey: "sup_afk_abcqwerty")
return true
}
}

Following app authentication, a registered SumUp merchant account needs to be logged in. Present a login screen from your UIViewController:

Login screen
Login screen
private func presentLogin() {
// present login UI and wait for completion block to update button states
SumUpSDK.presentLogin(from: self, animated: true) { [weak self] (success: Bool, error: Error?) in
print("Did present login with success: \(success). Error: \(String(describing: error))")
guard error == nil else {
// errors are handled within the SDK, there should be no need
// for your app to display any error message
return
}
self?.updateCurrency()
self?.updateButtonStates()
}
}

Similarly, you can log the user out.

fileprivate func requestLogout() {
SumUpSDK.logout { [weak self] (success: Bool, error: Error?) in
print("Did log out with success: \(success). Error: \(String(describing: error))")
self?.updateButtonStates()
}
}
  • In order to prepare a SumUp card terminal for checkout, prepareForCheckout can be called in advance. A registered SumUp merchant account needs to be logged in, and the card terminal must already be setup. You should use this method to let the SDK know that the user is most likely starting a checkout attempt soon; for example when entering an amount or adding products to a shopping cart. This allows the SDK to take appropriate measures, like attempting to wake a connected card terminal.
  • When logged in you can let merchants check and update their checkout preferences. Merchants can select their preferred card terminal and set up a new one if needed. The preferences available to a merchant depend on their respective account settings.
  • Present Checkout View is the main checkout request definition.

Check these methods and included comments before moving on to implementation below.

/**
* Can be called in advance when a checkout is imminent and a user is logged in.
* You should use this method to let the SDK know that the user is most likely starting a
* checkout attempt soon, e.g. when entering an amount or adding products to a shopping cart.
* This allows the SDK to take appropriate measures, like attempting to wake a connected card terminal.
*/
+ (void)prepareForCheckout;

In this step, we implement the payment checkout.

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == self.textFieldTotal) {
// we assume a checkout is imminent
// let the SDK know to e.g. wake a connected terminal
[SMPSumUpSDK prepareForCheckout];
[self.textFieldTitle becomeFirstResponder];
} else if ([SMPSumUpSDK isLoggedIn]) {
[self buttonChargeTapped:nil];
} else {
[textField resignFirstResponder];
}
return YES;
}

Provides the user a way to search for nearby Bluetooth card readers and select one to be used. The selected card reader will be saved to UserDefaults and used for subsequent checkouts. Using this screen is optional. If a checkout is started but no card reader has been saved, the checkout itself will automatically present the screen to search for and select a card reader, and this will be saved for next time.

Search Bluetooth
Search Bluetooth
Terminal selection
Terminal selection
private func presentCheckoutPreferences() {
SumUpSDK.presentCheckoutPreferences(from: self, animated: true) { [weak self] (success: Bool, presentationError: Error?) in
print("Did present checkout preferences with success: \(success). Error: \(String(describing: presentationError))")
guard let safeError = presentationError as NSError? else {
// no error, nothing else to do
return
}
print("error presenting checkout preferences: \(safeError)")
let errorMessage: String
switch (safeError.domain, safeError.code) {
case (SumUpSDKErrorDomain, SumUpSDKError.accountNotLoggedIn.rawValue):
errorMessage = "not logged in"
case (SumUpSDKErrorDomain, SumUpSDKError.checkoutInProgress.rawValue):
errorMessage = "checkout is in progress"
default:
errorMessage = "general error"
}
self?.showResult(string: errorMessage)
}
}

Prepare a checkout request that encapsulates the information regarding the transaction.

Checkout screen
Checkout screen
SumUpSDK.checkout(with: request, from: self) { [weak self] (result: CheckoutResult?, error: Error?) in
if let safeError = error as NSError? {
print("error during checkout: \(safeError)")
if (safeError.domain == SumUpSDKErrorDomain) && (safeError.code == SumUpSDKError.accountNotLoggedIn.rawValue) {
self?.showResult(string: "not logged in")
} else {
self?.showResult(string: "general error")
}
return
}

Detailed information on the payment checkout.

There are three modes for tipping:

  1. No tipping. Leave tipAmount set to nil when creating the SMPCheckoutRequest object.

  2. Programmatic tipping via the tipAmount property. Ask the user in your own UI for an appropriate tip amount and then set the tipAmount property on SMPCheckoutRequest. This will be added to the total amount, but will be displayed to the user separately during checkout.

  3. Tip on Card Reader. TCR prompts the customer directly on the card reader's display for a tip amount, rather than prompting for a tip amount on the iPhone or iPad display.

You have an option to add the paymentMethod to checkout request, or skip it and let it default to Card Reader payment, as shown in the examples below. You can also familiarize yourself with the payment properties before moving on to implementation.

/**
* Creates a new checkout request.
*
* Be careful when creating the NSDecimalNumber to not falsely use the NSNumber class creator methods.
*
* @param totalAmount The total amount to be charged to a customer. Cannot be nil.
* @param title An optional title to be displayed in the merchant's history and on customer receipts.
* @param currencyCode Currency Code in which the total should be charged (ISO 4217 code, see SMPCurrencyCode). Cannot be nil, has to match the currency of the merchant logged in. Use [[[SMPSumUpSDK currentMerchant] currencyCode] and ensure its length is not 0.
*
* @return A new request object or nil if totalAmount or currencyCode are nil.
*/
+ (SMPCheckoutRequest *)requestWithTotal:(NSDecimalNumber *)totalAmount
title:(nullable NSString *)title
currencyCode:(NSString *)currencyCode
paymentMethod:(SMPPaymentMethod)paymentMethod;

In this step, we implement the checkout.

  1. Verify that Merchant is logged in and using a valid currency code.
  2. Define total amount to be charged. Please note that you need to pass an NSDecimalNumber as the total value. While NSDecimalNumber is a subclass of NSNumber it is not advised to use the convenience method of NSNumber to create an NSDecimalNumber.
  3. Set up the request.
  4. Add a tip if selected (see the section about tipping).
  5. Check if the option to skip receipt is enabled, if so, execute it.
  6. Check for foreignTransactionID.
  7. Execute the request with error handling and confirmation.
  8. Verify that the checkout started correctly.
fileprivate func requestCheckout() {
// ensure that we have a valid merchant
guard let merchantCurrencyCode = SumUpSDK.currentMerchant?.currencyCode else {
showResult(string: "not logged in")
return
}
guard let totalText = textFieldTotal?.text else {
return
}
// create an NSDecimalNumber from the totalText
// please be aware to not use NSDecimalNumber initializers inherited from NSNumber
let total = NSDecimalNumber(string: totalText)
guard total != NSDecimalNumber.zero else {
return
}
// setup payment request
let request = CheckoutRequest(total: total,
title: textFieldTitle?.text,
currencyCode: merchantCurrencyCode)
// add tip if selected
if let selectedTip = segmentedControlTipping?.selectedSegmentIndex,
selectedTip > 0,
tipAmounts.indices ~= selectedTip {
let tipAmount = tipAmounts[selectedTip]
request.tipAmount = tipAmount
}
// set screenOptions to skip if switch is set to on
if let skip = switchSkipReceiptScreen?.isOn, skip {
request.skipScreenOptions = .success
}
// the foreignTransactionID is an **optional** parameter and can be used
// to retrieve a transaction from SumUp's API. See -[SMPCheckoutRequest foreignTransactionID]
request.foreignTransactionID = "your-unique-identifier-\(ProcessInfo.processInfo.globallyUniqueString)"
SumUpSDK.checkout(with: request, from: self) { [weak self] (result: CheckoutResult?, error: Error?) in
if let safeError = error as NSError? {
print("error during checkout: \(safeError)")
if (safeError.domain == SumUpSDKErrorDomain) && (safeError.code == SumUpSDKError.accountNotLoggedIn.rawValue) {
self?.showResult(string: "not logged in")
} else {
self?.showResult(string: "general error")
}
return
}
guard let safeResult = result else {
print("no error and no result should not happen")
return
}
print("result_transaction==\(String(describing: safeResult.transactionCode))")
if safeResult.success {
print("success")
var message = "Thank you - \(String(describing: safeResult.transactionCode))"
if let info = safeResult.additionalInfo,
let tipAmount = info["tip_amount"] as? Double, tipAmount > 0,
let currencyCode = info["currency"] as? String {
message = message.appending("\ntip: \(tipAmount) \(currencyCode)")
}
self?.showResult(string: message)
} else {
print("cancelled: no error, no success")
self?.showResult(string: "No charge (cancelled)")
}
}
// after the checkout is initiated we expect a checkout to be in progress
if !SumUpSDK.checkoutInProgress {
// something went wrong: checkout was not started
showResult(string: "failed to start checkout")
}
}

Credit/debit selection (processAs property)

Section titled “Credit/debit selection (processAs property)”

Some countries require the customer to select Credit or Debit at the beginning of the checkout. This is because a payment card may contain multiple applications linked with different accounts, making it necessary for the customer to specify which application should be used to process the transaction.

For countries that do not require credit/debit selection, you can set the processAs property of SMPCheckoutRequest to SMPProcessAsNotSet.

To tell if the current country requires processAs to be set to a value other than SMPProcessAsNotSet, check SMPSumUpSDK.isProcessAsRequired.

If needed, your app should set the processAs property of SMPCheckoutRequest to SMPProcessAsCredit or SMPProcessAsDebit after showing its own UI that prompts the customer to select Credit or Debit.

SDK 6.0 and earlier presented two screens during the checkout that prompted the user to select Credit or Debit, and if Credit, to also choose the number of installments. This functionality is now deprecated. However, it is still available if you need more time to migrate your app to use the above programmatic method. To keep the old behavior, set processAs to SMPProcessAsPromptUser.

When SMPProcessAsCredit is used, you should obtain the number of installments from the customer using your own UI. Assign the positive, non-zero value to numberOfInstallments on SMPCheckoutRequest.

With Tap to Pay on iPhone merchants can accept contactless card payments on their iPhone without needing a card reader.

To add Tap to Pay on iPhone to your app:

  • Request the Tap to Pay on iPhone entitlement from Apple, receive approval, and then add the com.apple.developer.proximity-reader.payment.acceptance entitlement to your app. Setting up the entitlement.
  • This feature requires an iPhone XS or later, running iOS 16.4 (iOS 16.7 starting July 8th) or later (ideally 17.5 or later.) The feature does not work on iPad.
  • For debugging and testing you will need to be logged into an iPhone with a non-Sandbox Apple ID. Using a Sandbox Apple ID requires both Apple and SumUp implementations to connect to their respective non-production (test) backends, which the SDK does not support.
  • During testing use a SumUp test account, to avoid transactions going to the acquirer and transferring real money.

In your code:

  • Make a call to check feature availability: is the Tap to Pay on iPhone payment method available for the current merchant?
  • Trigger activation if needed. Activation sets up the iPhone to receive payments, shows the merchant how to use the feature, and links the SumUp account and Apple ID.
  • Start the checkout.
  • Call checkTapToPayAvailability on SMPSumUpSDK to check the availability of the Tap to Pay on iPhone payment method. This call, which requires the SDK to be in a logged-in state, may internally perform one or more network calls.
  • If the feature is not available, your app could, as an example, hide or disable a button or menu item representing the Tap to Pay on iPhone payment method.
  • The feature is generally available when the following criteria are fulfilled:
    • the iPhone model and iOS version requirements are met
    • the user logs in with a SumUp account registered in one of the countries where SumUp supports Tap to Pay on iPhone (temporarily with exception of Brazil)
  • Activation must be completed before the first transaction can be performed. Activation means:
    • the merchant links their Apple ID with their SumUp account
    • the iPhone is prepared, which can take 45 seconds or longer
  • This needs to be done once per merchant account, per device.
  • In addition to determining feature availability, checkTapToPayAvailability also indicates whether Tap to Pay on iPhone has been activated yet for the current merchant.
  • If it has not yet been activated then you should trigger activation by calling presentTapToPayActivation at a convenient time. Calling it more than once will still show the user education screens each time. Independently, the activation from the initial setup will remain valid.

These methods handle Tap to Pay processing. Note that the SDK supports both Completion Block and async implementation.

/**
* Checks whether the Tap to Pay on iPhone payment method is available for the current merchant and whether or
* not it requires activation to be performed via a call to
* `presentTapToPayActivationFromViewController:animated:completionBlock:`.
*
* For the merchant to be able to use this payment method the following must be true:
*
* - The feature must be available in the merchant's country
*
* - It must be activated. This is where the merchant's Apple ID is linked with their SumUp account and the
* iPhone is prepared to work as a card reader. As this can take a minute or so the first time, the
* merchant is shown a UI that introduces them to the feature as it initializes in the background.
*
* The merchant must be logged in before you call this method.
*
* @param availability YES if the feature is available for the current merchant and it's OK to start activation.
* @param isActivated YES if activation has already been done for this device and merchant account
*/
open class func checkTapToPayAvailability(completion block: @escaping (Bool, Bool, (any Error)?) -> Void)
/**
* Checks whether the Tap to Pay on iPhone payment method is available for the current merchant and whether or
* not it requires activation to be performed via a call to
* `presentTapToPayActivationFromViewController:animated:completionBlock:`.
*
* For the merchant to be able to use this payment method the following must be true:
*
* - The feature must be available in the merchant's country
*
* - It must be activated. This is where the merchant's Apple ID is linked with their SumUp account and the
* iPhone is prepared to work as a card reader. As this can take a minute or so the first time, the
* merchant is shown a UI that introduces them to the feature as it initializes in the background.
*
* The merchant must be logged in before you call this method.
*
* @param availability YES if the feature is available for the current merchant and it's OK to start activation.
* @param isActivated YES if activation has already been done for this device and merchant account
*/
open class func checkTapToPayAvailability() async throws -> (Bool, Bool)
/**
* Performs activation for Tap to Pay on iPhone. This prepares the device, introduces the merchant to the
* feature and links their Apple ID to their SumUp account (which will require confirmation from the merchant.)
*
* Call `checkTapToPayAvailability:` before calling this method to find out if this payment method is available
* and if activation is needed.
*
* The merchant must be logged in before you call this method.
*
* Tap to Pay on iPhone requirements:
*
* - The hosting app must have the `com.apple.developer.proximity-reader.payment.acceptance`
* entitlement.
*
* - The merchant must have an iPhone XS or later with iOS 16.4 or later (iOS 17 or later recommended.)
* The feature does not work with iPads.
*
* @param fromViewController The UIViewController instance from which the UI should be presented modally.
* @param animated Pass YES to animate the transition.
* @param block The completion block is called after the view controller has been dismissed.
*/
open class func presentTapToPayActivation(from fromViewController: UIViewController, animated: Bool, completionBlock block: SMPCompletionBlock? = nil)
/**
* Performs activation for Tap to Pay on iPhone. This prepares the device, introduces the merchant to the
* feature and links their Apple ID to their SumUp account (which will require confirmation from the merchant.)
*
* Call `checkTapToPayAvailability:` before calling this method to find out if this payment method is available
* and if activation is needed.
*
* The merchant must be logged in before you call this method.
*
* Tap to Pay on iPhone requirements:
*
* - The hosting app must have the `com.apple.developer.proximity-reader.payment.acceptance`
* entitlement.
*
* - The merchant must have an iPhone XS or later with iOS 16.4 or later (iOS 17 or later recommended.)
* The feature does not work with iPads.
*
* @param fromViewController The UIViewController instance from which the UI should be presented modally.
* @param animated Pass YES to animate the transition.
* @param block The completion block is called after the view controller has been dismissed.
*/
open class func presentTapToPayActivation(from fromViewController: UIViewController, animated: Bool) async throws -> Bool
SumUpSDK.checkTapToPayAvailability { isAvailable, isActivated, error in
if let error {
// An error occurred
return
}
if !isAvailable {
// Tap to Pay on iPhone is not available for the merchant
return
}
if !isActivated {
// Tap to Pay on iPhone needs activation - call presentTapToPayActivation
return
}
// The app is ready to take Tap to Pay on iPhone payments!
}

In your debug setup you can call +[SMPSumUpSDK testSDKIntegration] or SumUpSDK.testIntegration() in Swift. It will run various checks and print its findings to the console. Please do not call it in your Release build.

The SDK uses Objective C header files, but XCode can also display its public types as Swift. The table below outlines the interfaces in the SDK and their purpose.

Header (Swift alias)Purpose
SMPSumUpSDK.h (SumUpSDK)Includes methods and properties for handling authentication, initial SDK setup, presenting checkout view, and testing your integration. Bundles all other headers and serves as the main SDK interface.
SMPCheckoutRequest.h (CheckoutRequest)Includes methods and properties handling checkout requests, such as amounts, currencies, and payment methods
SMPCheckoutResult.h (CheckoutResult)Handles checkout result structure, including status and transaction code
SMPCurrencyCodes.hDefines available currency codes
SMPMerchant.h (Merchant)Describes a Merchant, including Merchant Code (identifier) and currency used by merchant
SMPSkipScreenOptions.h (SkipScreenOptions)Describes options allowing to skip transaction confirmation screen
SumUpSDK.hDeclares project version

SumUp provides a sample app which implements main SDK components in a sample App Delegate and View Controller. Check the examples below for highlights provided in both Swift and Objective C. Clone the provided repository and check the SampleApp directory to use it.

  • In Tap-to-Pay solutions, if entitlements are not correctly set up in your app, presentTapToPayActivation may show an error Alert with Failed to show Terms of Service.
  • Businesses using SumUp sub-accounts must first activate the feature on their main account before using it on devices logged in with sub-accounts, otherwise an error message will appear during activation for the sub-account user.
  • Bluetooth permissions will be requested even if the merchant does not plan to use any of the SumUp Bluetooth card readers. This will be fixed in an upcoming release 6.1.
  • Distributing XCFrameworks with the latest Carthage version (0.35.0) is not yet available. There is an open issue (#2799) to solve this.

Got questions or found a bug? Get in contact with our integration team by sending an email to integration@sumup.com.

The following functions are handled by the SumUp APIs:

Check other resources we have, such as: