--- title: "Tap to Pay on iPhone" description: "Integrate your POS app with the iOS Mobile SDK to make Tap to Pay on iPhone payments." url: "https://docs.adyen.com/point-of-sale/mobile-ios/build/tap-to-pay" source_url: "https://docs.adyen.com/point-of-sale/mobile-ios/build/tap-to-pay.md" canonical: "https://docs.adyen.com/point-of-sale/mobile-ios/build/tap-to-pay" last_modified: "2024-07-05T10:12:00+02:00" language: "en" --- # Tap to Pay on iPhone Integrate your POS app with the iOS Mobile SDK to make Tap to Pay on iPhone payments. [View source](/point-of-sale/mobile-ios/build/tap-to-pay.md) With our Tap to Pay on iPhone solution you can accept contactless in-person payments using an iPhone as the payment interface, and process these payments on the Adyen payments platform. ## Requirements Before you begin, take into account the following requirements, limitations, and preparations. | Requirement | Description | | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Integration type** | Your POS app must be integrated with [Terminal API](/point-of-sale/design-your-integration/terminal-api/). | | **[API credentials](/development-resources/api-credentials/)** | You need the following API credentials:- To get the SDK, you must have an API credential with a basic authentication password and the **Allow SDK download for POS developers** role. - To establish a communication session, you must have an API credential with an API key, a client key, and the **Checkout webservice** role. | | **[Webhooks](/development-resources/webhooks)** | To learn the outcome of refunds, set up Standard webhooks (if this hasn't been done already). | | **Hardware** | An iPhone. See [iOS system requirements](/point-of-sale/mobile-ios/requirements) for the full hardware and software requirements. | | **Limitations** | Check the countries/regions, payment methods, and functionality that we [support](/point-of-sale/ipp-mobile) for Tap to Pay on iPhone. | | **Setup steps** | Before you begin:- Ask our [Support Team](https://ca-test.adyen.com/ca/ca/contactUs/support.shtml?form=other) to configure your account for Tap to Pay on iPhone payments. - [Order](/point-of-sale/managing-terminals/order-terminals#sales-order-steps) a test card. | ## How it works **Tutorial and app**\ Hit the ground running with our [integration tutorial](https://adyen.github.io/adyen-pos-mobile-ios-artifacts/2.0.1/tutorials/meet-adyenpos/) and [iOS sample app](https://github.com/Adyen/adyen-pos-mobile-ios/tree/main/POSSampleApp). To build a Tap to Pay on iPhone solution: 1. Get an entitlement from Apple for Tap to Pay on iPhone and configure your Xcode project accordingly. 2. Add the iOS Mobile SDK to your project, using basic authentication. Note that for [compatibility with Objective C](#objective-c), you need to prefix the public symbols with `ADY`. 3. Implement a server-to-server API request to establish a secure communication session. 4. In your POS app, enable the transaction functionality of the SDK. 5. Make sure that the Apple Terms of Conditions are accepted. 6. From your POS app, call the warmup function to speed up initiating transactions. 7. In your POS app, implement handling payments using the SDK. This creates the following flow: 1. Your iOS POS app creates a Terminal API payment request, or receives a Terminal API payment request from your backend. 2. The POS app passes the payment request to the iOS Mobile SDK. 3. The SDK passes the request to the Tap to Pay on iPhone component. 4. When the customer completes the payment by tapping their card or mobile device on the iPhone, the SDK passes the Terminal API payment response to the POS app. 8. In your POS app, implement handling refunds and diagnosing the state of the SDK. 9. If the same device will be used at multiple locations, implement clearing the communication session. ## 1. Get an entitlement from Apple To add Tap to Pay functionality to your iOS POS app through the iOS Mobile SDK, you need to apply for a specific entitlement and then configure your Xcode project to use the entitlement. You need to apply separately for a TEST and a LIVE entitlement. The approval process for the LIVE environment can take up to several weeks. 1. Make sure that you meet the requirements for submitting an entitlement request: * An Organization level Apple Developer account. * The Account Holder role for that Apple Developer account. 2. [Fill out the form](https://developer.apple.com/contact/request/contactless-payments/) to get the entitlement. 3. After you receive the entitlement to support Tap to Pay on iPhone, follow Apple's instructions to [configure your Xcode project](https://developer.apple.com/documentation/proximityreader/setting-up-the-entitlement-for-tap-to-pay-on-iphone) to use it. ## 2. Add the SDK to your project You can add the iOS Mobile SDK to your POS app using a Swift Package Manager remote package. To get access you need to have a basic authentication credential. You can share the basic authentication password with anybody in your team who needs to get the SDK or create multiple passwords for internal use. Do not share this password in any publicly accessible code or area where unauthorized users can find it. To add the iOS Mobile SDK to your project: 1. In your [Customer Area](https://ca-test.adyen.com/) create a basic authentication credential with a password: 1. Go to **Developers** > **API credentials**, and select **Create new credential**. 2. Under **Payments** > **Credential type** select **Web service user** and then select **Create credential**. 3. Under **Server settings** > **Authentication** select the **Basic auth** tab and then select **Generate password**. 4. Select the copy icon **and save your basic authentication password in a secure location. 5. Go to **Permissions** > **Roles** > **POS**, select **Allow SDK download for POS developers**, and deselect any other permissions and roles. Contact [Support Team](https://ca-test.adyen.com/ca/ca/contactUs/support.shtml?form=other) if you don't see the **Allow SDK download for POS developers** role. 6. Select **Save changes**. 2. Save the basic authentication password in a [.netrc](https://everything.curl.dev/usingcurl/netrc.html) file: 1. Check if you already have a `.netrc` file in your home directory. If you do not have it, create a plain text file with that name in your home directory (`~/.netrc`). 2. Add the following content to your `.netrc` file, where `login` is the API credential username, for example ws\_12345\@Company.\[YourCompanyAccount], and `password` is the basic authentication password. **.netrc content for TEST integration** ```raw machine pos-mobile-test.cdn.adyen.com login YOUR_BASIC_AUTH_USERNAME password YOUR_BASIC_AUTH_PASSWORD ``` 3. Make sure that the `.netrc` file has the following file system permission: **0600**. 3. Add the POS Mobile SDK as a package dependency to your Xcode project: It is not possible to use TEST and LIVE environments from a single app target. You need to setup your Xcode project to have separate app targets for Test and for Live. Each target needs to link to the respective AdyenPOS SDK version for its environment: `AdyenPOSTest` and `AdyenPOSLive`. 1. In your Xcode project or workspace, go to **File** > **Add Package Dependencies**. 2. Enter the **URL** `https://github.com/Adyen/adyen-pos-mobile-ios-test` and select your preferred **Dependency Rule**. 3. Select **Add Package**, and after the SDK is loaded add `AdyenPOSTEST` to your app target that connects to the Adyen test environment. 4. Select **Add Package**. 4. In your code, add `import AdyenPOS`, or for Objective-C compatibility add `#import "ADYPOS/ADYPOS.h"`. ### Compatibility with Objective-C If your POS app requires the iOS Mobile SDK to be compatible with Objective-C: * Link the `ADYPOSTEST` package product to your app target instead of `AdyenPOSTEST`. * Link the `ADYPOSLIVE` package product to your app target instead of `AdyenPOSLIVE`. It is not possible to use TEST and LIVE environments from a single app target. Each app target must connect to a specific environment. The integration process is the same. The only difference is that the public symbols are prefixed with `ADY`. For example, `PaymentService` is called `ADYPaymentService`. ## 3. Establish a session To ensure the Mobile SDK and the Adyen payments platform can collaborate in a secure way, you need to implement a server-to-server API request for establishing a communication session. ![Authentication flow](/user/pages/reuse/pos-mobile-sdk/ios-build/session/ipp-mobile-authentication-flow.svg?decoding=auto\&fetchpriority=auto) To authenticate this API request, you need to have an Adyen [API credential](/development-resources/api-credentials) in your [test Customer Area](https://ca-test.adyen.com/). This credential must have an [API key](/development-resources/api-credentials#generate-api-key) with the following [role](/development-resources/api-credentials#manage-api-permissions): * **Checkout webservice role**. This role is assigned by default when the API key is created. The credential also needs to have a client key. ** #### Create a client key If you want to use an existing API credential that does not have a client key yet, create a client key as follows: 1. Log in to your [Customer Area](https://ca-test.adyen.com/). 2. Go to **Developers** > **API credentials**, and select the credential username for your integration, for example **ws\@Company.****\[YourCompanyAccount]**. 3. Under **Client settings** > **Authentication** select the **Client key** tab. 4. Select **Generate client key**. 5. Select **Save changes**. The client key is part of the setup but is not used later on. Therefore, you do not need to specify allowed origins, and you do not need to save the client key in your system. #### Implement the request To establish a communication session: 1. From your backend, make a POST request to the applicable test or regional [/auth/certificate](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate) endpoint, specifying: | Parameter | Required | Description | | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [merchantAccount](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#request-merchantAccount) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | The unique identifier of your [merchant account](/point-of-sale/design-your-integration/determine-account-structure#request-merchant-accounts). | | [setupToken](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#request-setupToken) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | The setup token provided by the Mobile SDK through the `PaymentServiceDelegate.register(with:)` callback of `PaymentServiceDelegate` . | | [store](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#request-store) | Not a payment facilitator | The reference of the [store](/point-of-sale/design-your-integration/determine-account-structure#create-stores) that you want to process payments for. Do not include this parameter if your [account structure](/point-of-sale/design-your-integration/determine-account-structure#determine-account-structure) uses merchant accounts as stores, or if you are a registered payment facilitator. | | [subMerchantData](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#request-subMerchantData) | Payment facilitator | An object with the details of the sub-merchant. See the next table for the parameters. This object is required if you are a registered payment facilitator. If you are not a payment facilitator, do not include this object. | The `subMerchantData` object includes the following parameters: | Parameter | Required | Description | | ------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | Your unique identifier of the sub-merchant. | | `name` | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | The name of the sub-merchant. | | `displayName` | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | The name of the sub-merchant as it should appear on the display of the mobile device during transactions. | | `mcc` | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | The sub-merchant's four-digit Merchant Category Code (MCC). This parameter is used to correctly route the transaction. | | `street` | | The street name and house number of the sub-merchant's address. | | `city` | | The city of the sub-merchant's address. | | `postalCode` | | The postal code of the sub-merchant's address, without dashes. | | `country` | | The country/region of the sub-merchant's address, specified as the three-letter country code in [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) format. | | `state` | | The state code of the sub-merchant's address, if applicable for the country or region. | | `taxId` | | The tax ID of the sub-merchant. Required only in Brazil and for Cartes Bancaires in France. For Brazil, this is the 11-digit CPF or 14-digit CNPJ. For France, this is the SIRET, with a maximum of 14 digits. | | `email` | | The email address of the sub-merchant. Required for American Express. | | `phoneNumber` | | The phone number of the sub-merchant. Required for American Express. | ### Tab: Not a payment facilitator If you are *not* a registered payment facilitator, the `/auth/certificate` request to establish a communication session must include the `store` parameter, unless your account structure does not have stores and instead uses merchant accounts as stores. If previously you have used [/checkout/possdk/v68/session](https://docs.adyen.com/api-explorer/possdk/latest/post/sessions) test and live endpoints, we recommend you migrate to [new endpoints](#endpoints-to-use). **/auth/certificate request with store to process payments for** ```bash curl https://softposconfig-test.adyen.com/softposconfig/v3/auth/certificate \ -H 'content-type: application/json' \ -H 'x-API-key: ADYEN_API_KEY' \ -X POST \ -d '{ "merchantAccount": "YOUR_MERCHANT_ACCOUNT", "setupToken": "SETUP_TOKEN", "store": "YOUR_STORE_REFERENCE" }' ``` ### Tab: Payment facilitator If you are a payment facilitator, card schemes need to receive certain data about your sub-merchant with each transaction. That is because your sub-merchant is considered the Merchant of Record when a transaction is facilitated by you. Adyen automatically adds certain sub-merchant data to transactions that you facilitate. The remaining data must be supplied by you in the `subMerchantData` object of the `/auth/certificate` request to establish a communication session. You must **not** submit sub-merchant data in your Terminal API requests. **/auth/certificate request with sub-merchant to process payments for** ```bash curl https://softposconfig-test.adyen.com/softposconfig/v3/auth/certificate \ -H 'content-type: application/json' \ -H 'x-API-key: ADYEN_API_KEY' \ -X POST \ -d '{ "merchantAccount": "YOUR_MERCHANT_ACCOUNT", "setupToken": "SETUP_TOKEN", "subMerchantData": { "id": "123456", "name": "Test Merchant", "mcc": "5734", "street": "123 Sample Street", "city": "Minneapolis", "state": "MN", "postalCode": "94107", "country": "USA", "taxId": "TAX123456", "displayName": "MN Computer Software", "email": "test-merchant@example.com", "phoneNumber": "PHONE_NUMBER" } }' ``` 2. When you receive the response: * Check that you get a **201 Created** HTTP status code. * Return the [sdkData](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#responses-201-sdkData) to your POS app. * If you create the Terminal API request on your backend, save the [installationId](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#responses-201-installationId) and use this as the `POIID` in the [`MessageHeader` ](/point-of-sale/design-your-integration/terminal-api#request-message-header)of the payment request. **Successful /auth/certificate response** ```json { "id": "APP_SESSION_ID", "installationId": "INSTALLATION_ID", "merchantAccount": "YOUR_MERCHANT_ACCOUNT", "store": "YOUR_STORE_ID", "sdkData": "SDK_DATA_BLOB" } ``` If you receive a response with an error message similar to the following, ask our [Support Team](https://ca-test.adyen.com/ca/ca/contactUs/support.shtml?form=other) to configure the MCC that you specified in the request. **/auth/certificate error message** ```raw Refused (905_3 Could not find an acquirer account for the provided currency (USD).) ``` #### Endpoints to use * Test endpoint: `https://softposconfig-test.adyen.com/softposconfig/v3/auth/certificate` * Regional live endpoints: * Australia: `https://softposconfig-live-au.adyen.com/softposconfig/v3/auth/certificate` * Asia Pacific South East: `https://softposconfig-live-apse.adyen.com/softposconfig/v3/auth/certificate` * Europe: `https://softposconfig-live.adyen.com/softposconfig/v3/auth/certificate` * North East Asia: `https://softposconfig-live-nea.adyen.com/softposconfig/v3/auth/certificate` * United States: `https://softposconfig-live-us.adyen.com/softposconfig/v3/auth/certificate` If you are using old [/checkout/possdk/v68/sessions](https://docs.adyen.com/api-explorer/possdk/latest/post/sessions) test and live endpoints, we recommend you migrate to the latest endpoints mentioned above. You can still use the `/checkout/possdk/v68/sessions` endpoints, but we are deprecating them. ## 4. Enable transactions To enable the payments functionality of the Mobile SDK: 1. In your POS app, implement the `PaymentServiceDelegate` protocol. Below is an example of how you could do that. ```swift struct SessionsResponse: Decodable { let sdkData: String } class MyPaymentServiceDelegate: PaymentServiceDelegate { internal func register( with setupToken: String ) async throws -> String { /// Make a call to your backend to trigger a `/softposconfig/v{version}/auth/certificate` request, specifying the `setupToken` provided by the SDK. let request = URLRequest(url: URL(string: "{ADDRESS_OF_YOUR_BACKEND_API}")!) let (data, _) = try await URLSession.shared.data(for: request) let response = try JSONDecoder().decode(SessionsResponse.self, from: data) return response.sdkData } } ``` The actual structure of the `SessionsResponse` depends on your backend implementation. 2. Create an instance of `PaymentService` with the `PaymentService(delegate:)` initializer and pass the delegate object. Make sure that you keep a strong reference to the payment service instance so that it is retained for the duration of the transaction. Also make sure your `delegate` is strongly referenced, because the `PaymentService` keeps a weak reference to the `delegate`. ```swift let paymentService = PaymentService(delegate: myPaymentServiceDelegate) ``` 3. Make sure that the `PaymentServiceDelegate` can provide new `sdkData` at any time.\ If there is no session or the session has expired, the delegate is called using the `PaymentServiceDelegate.register(with:)` callback. Using the provided `setupToken` you need to get the `sdkData` through your backend and return it. For instructions, see [Establish a session](#session). 4. Optional. Verify that the callback works, by calling the warm-up function. The warm-up function checks for a session and any configuration changes, and prepares the proximity reader on the iPhone. If you haven't accepted [Apple's Terms and Conditions for Tap to Pay](#accept-terms), calling the warm-up function will return an error. ```swift try await paymentService.warmUp() ``` ## 5. Accept Apple Terms and Conditions You can accept the Apple Terms and Conditions for Tap to Pay in the Customer Area for your LIVE integration. This won't work in your TEST environment. Before making your first payment, you need to make sure that you have accepted the Apple Terms and Conditions for Tap to Pay for the merchant account or store that uses the Mobile SDK: 1. Call `paymentService.isAccountLinkedForTapToPay()` to check if you have already accepted the Terms and Conditions. If the return value is `true`, the terms have already been accepted, and you can move on to [use the warm-up function](#use-warm-up). If the return value is `false`, continue with the next step. 2. Call `paymentService.linkAccountForTapToPay()` to show a prompt on the iPhone to accept the Apple Terms of Condition before starting the first Tap to Pay transaction on that phone. ## 6. Use the warm-up function To speed up initiating transactions, you can use the warm-up function. This function checks for a session and any configuration changes, and prepares the proximity reader on the iPhone. As a best practice, call the warm-up function: * When the POS app starts. In other words, as soon as the app has the [active state](https://developer.apple.com/documentation/uikit/uiapplication/state). * When the POS app returns to the active state after running in the background. To call the warm-up function: ```swift try await paymentService.warmUp() ``` Similar to the warm-up function, you can optionally [prepare the card reader](/point-of-sale/mobile-ios/build/card-reader#prepare-the-card-reader-for-transaction) to perform hardware configuration updates and security checks outside the transaction flow. ## 7. Handle a payment In this step you add code to start a transaction with: * A [Terminal API](/point-of-sale/design-your-integration/terminal-api) payment request. **Tip**\ To help you create Terminal API requests, we provide a **TerminalAPIKit for iOS** on [GitHub](https://github.com/Adyen/adyen-terminal-api-ios). Installation and usage instructions are in the repository's README. * The iPhone as the payment interface to use. * The presentation mode you want to use. To enable the iOS Mobile SDK to handle transactions: 1. In your POS app, create a Terminal API payment request with: * `MessageHeader.POIID`: the installation ID of the SDK. * If you create the Terminal API diagnosis request in your POS app, use `PaymentService.installationId` as the `POIID` in the [MessageHeader](/point-of-sale/design-your-integration/terminal-api#request-message-header) of the request. * If you create the Terminal API diagnosis request in the backend, this uses the [installationId](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#responses-201-installationId) from the [`/auth/certificate` response](#session). * The remaining `MessageHeader` parameters and the request body. For details, see [Make a payment](/point-of-sale/basic-tapi-integration/make-a-payment) and [PaymentRequest](https://docs.adyen.com/api-explorer/terminal-api/latest/post/payment). * Create an instance of `Payment.Request` using `Payment.Request(data:)`, and pass the Terminal API payment request from your POS app or backend. ```swift let transaction = try Payment.Request(data: requestData) ``` * Get a `PaymentInterface` from an instance of `PaymentService`, using `PaymentService.getPaymentInterface(with: .tapToPay)`. ```swift let paymentService = PaymentService(...) let paymentInterface = try paymentService .getPaymentInterface(with: .tapToPay) ``` * Specify a `TransactionPresentationMode` value that matches the UI framework, SwiftUI or UIKit, of the POS app. | Value | Description | | -------------------------- | ------------------------------------------------------------------------------------------------ | | `viewModifier` | For use with a SwiftUI application. The UI is embedded in a `View` as a `ViewModifier`. | | `presentingViewController` | For use with a UIKit application. The UI is presented on top of the provided `UIViewController`. | []()Optionally use parameters to customize the user interface. | Parameter | Description | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `logo` | A bitmap image to show on your mobile device during the transaction flow. To ensure visibility in both dark mode and light mode, the bitmap image must have a transparent background. The logo is placed in a frame with a vertical height of 40 points and scaled to aspect ratio to fit in that frame. | | `successScreenTimeout` | Indicates how long the SDK shows the screen that indicates the transaction succeeded. If not specified, this success screen is dismissed after four seconds. You can set a time in seconds as a Double with a minimum of 0.5 seconds and a maximum of 4 seconds. | #### Using `viewModifier` (SwiftUI) If you use `TransactionPresentationMode` with `viewModifier`: 1. Set `presentationMode` as follows. ```swift let presentationMode: TransactionPresentationMode = .viewModifier ``` 2. Apply the presentation mode on your SwiftUI view. ```swift Button(...) { // code to start the transaction }) .transactionModal( with: {YOUR_INSTANCE_OF_PAYMENT_SERVICE} logo: logo, parameters: .init(successScreenTimeout: 2) ) ``` #### Using `presentingViewController` (UIKit) If you use `TransactionPresentationMode` with `presentingViewController(_:logo:parameters:)`, set `presentationMode` as follows. ```swift let presentationMode: TransactionPresentationMode = .presentingViewController( rootViewController, logo: logo, parameters: .init(successScreenTimeout: 2) ) ``` * Invoke `PaymentService.performTransaction(with:paymentInterface:presentationMode:)` on your instance of `PaymentService`. ```swift let transactionResponse = await paymentService.performTransaction( with: transaction, paymentInterface: paymentInterface, presentationMode: presentationMode ) ``` The Mobile SDK checks for a session and shows the transaction screen on your iPhone. * Check the `paymentResponse`. This is the Terminal API response with the transaction result and the data you can use to generate a receipt, or with any [errors](/point-of-sale/error-scenarios). * Pass the `paymentResponse` to your POS app. ## 8. Handle a refund There are two types of refund: [referenced](#referenced-refund) and [unreferenced](#unreferenced-refund). The main difference is that a referenced refund is connected to the original payment, and an unreferenced refund isn't. That makes unreferenced refunds a bit riskier. For an overview of the differences, see [Refund a payment](/point-of-sale/basic-tapi-integration/refund-payment). Refunds are usually *not* processed synchronously. When you send a request for a referenced or unreferenced refund, the Terminal API response only confirms we received the request. We inform you about the outcome of the refund asynchronously, through a webhook. * For a referenced refund, we return a **CANCEL\_OR\_REFUND** webhook. * For an unreferenced refund, we return a **REFUND\_WITH\_DATA** webhook. Depending on the card scheme and country/region where the card is used, unreferenced refunds are sometimes processed synchronously. In that case the Terminal API response includes an `acquirerResponseCode` to indicate the outcome. To learn the [outcome of a refund](/point-of-sale/basic-tapi-integration/refund-payment/refund-webhooks), you need to set up webhooks. ### Handle a referenced refund The Terminal API request for a referenced refund is a reversal request. The SDK contains a dedicated function for this. In your iOS POS app, add code for the following steps: 1. Create a Terminal API reversal request with: * `MessageHeader.POIID`: the installation ID of the SDK. * If you create the Terminal API diagnosis request in your POS app, use `PaymentService.installationId` as the `POIID` in the [MessageHeader](/point-of-sale/design-your-integration/terminal-api#request-message-header) of the request. * If you create the Terminal API diagnosis request in the backend, this uses the [installationId](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#responses-201-installationId) from the [`/auth/certificate` response](#session). * The remaining `MessageHeader` parameters and the request body. For details, see [Referenced refund](/point-of-sale/basic-tapi-integration/refund-payment/referenced) and [ReversalRequest](https://docs.adyen.com/api-explorer/terminal-api/latest/post/reversal). 2. Create an instance of `Reversal.Request` using `Reversal.Request(data:)`, and pass the Terminal API reversal request from your POS app or backend. ```swift let request = try Reversal.Request(data: requestData) ``` 3. Invoke `PaymentService.performReversal(with:)` on your instance of `PaymentService`. ```swift let reversalResponse = await paymentService.performReversal(with: request) ``` The Mobile SDK now checks for a session, and starts the transaction. 4. Check the `reversalResponse`. This is the Terminal API response with the transaction result and the data you can use to generate a receipt, or with any [errors](/point-of-sale/error-scenarios). 5. Pass the `reversalResponse` to your POS app. ### Handle an unreferenced refund The Terminal API request for an unreferenced refund is a payment request with an additional parameter: * `PaymentData.PaymentType`: **Refund** This means you can use the same code as for [handling a payment](#payment). The only difference is the structure of the Terminal API payment request that you pass as the `requestData` to the `Payment.Request`. For the structure of the Terminal API request, see [Unreferenced refund](/point-of-sale/basic-tapi-integration/refund-payment/unreferenced). ## 9. Diagnose the device We recommend implementing the Terminal API diagnosis request. This enables you to do the following: * Check if there are **stored offline payments** that have not been forwarded to Adyen yet. The SDK will try to go online and forward these transactions. Note that you do not need to send a diagnosis request to forward offline transactions, because forwarding happens automatically whenever the internet connection is restored after it had dropped. * Check for **security threats** that would block transactions. If threats are detected that an operator can solve, the response includes information about the cause and solution of the problem. * Check the **expiry date of the SDK** that is used on the mobile device. [Transactions will be blocked](/point-of-sale/mobile-ios/manage/#keep-the-mobile-sdk-up-to-date) if mandatory updates are not carried out. Be aware that the diagnosis can take a while if the SDK succeeds in going online and there is a large number of stored offline payments that are forwarded. In your iOS POS app, add code for the following steps: 1. Create a Terminal API diagnosis request with: * `MessageHeader.POIID`: the installation ID of the SDK. * If you create the Terminal API diagnosis request in your POS app, use `PaymentService.installationId` as the `POIID` in the [MessageHeader](/point-of-sale/design-your-integration/terminal-api#request-message-header) of the request. * If you create the Terminal API diagnosis request in the backend, this uses the [installationId](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#responses-201-installationId) from the [`/auth/certificate` response](#session). * The remaining `MessageHeader` parameters. * The request body consisting of `DiagnosisRequest.HostDiagnosisFlag`: set to **true**, so that the SDK will try to forward any stored offline transactions and perform a security scan. For details, see [Diagnose a Mobile SDK solution](/point-of-sale/diagnostics/request-diagnosis#diagnosis-request-mobile) and [DiagnosisRequest](https://docs.adyen.com/api-explorer/terminal-api/latest/post/diagnosis). 2. Call the `performDiagnosis` function, using the `request` parameter to pass the Terminal API diagnosis request. ```swift public func performDiagnosis( with request: Diagnosis.Request ) async -> Diagnosis.Response { await diagnosisPerformer.performDiagnosis(with: request) } ``` 3. When you get the Terminal API `DiagnosisResponse`, check the `Response.AdditionalResponse`: * See the `unconfirmedBatchCount` for the number of stored offline transactions that have not been forwarded to theAdyen payments platform yet. * Base64-decode the `storeAndForwardStatus` value for more information about offline transactions. * Base64-decode the `attestationStatus` value for information about any security issues. If issues are detected that can be resolved by the end user, the resulting JSON object includes messages with the details. These are the same [error messages](/point-of-sale/mobile-ios/troubleshooting/#error-messages) that we show automatically on the end user's mobile device when these issues are detected during a transaction. * See the `sdkExpiry` for the date when the installed SDK version expires. For a detailed explanation of the response, see [Diagnose a Mobile SDK solution](/point-of-sale/diagnostics/request-diagnosis#diagnosis-request-mobile). ## 10. (Optional) Clear the session token There are several situations when you need to clear the existing session to remove all session information from your mobile device. When you have cleared the session, configuration updates are fetched and a new session is established when you start a new transaction or call the warm-up function. As a best practice, clear the session to: * **Re-establish a session after switching between merchant accounts or stores in your POS app and your Customer Area**. If the device is reassigned from store A to store B and a transaction is started there, on the Adyen side the transaction will continue to appear to belong to store A instead of store B. Clearing the session prevents this issue. * **Force a refresh of the configuration**. After clearing the session, the latest configuration is fetched and stored on your mobile device the next time the iOS Mobile SDK connects to the Adyen backend. * **Test the session establishment flow in your POS app**, specifically how it interacts with your and Adyen's backend to securely establish a session with the iOS Mobile SDK. To clear the session: 1. Explicitly clear the communication session using `PaymentService.resetSession()` . 2. [Establish a new communication session](#session). ## Other supported features In addition to [payments](#payment), [refunds](#referenced-refund), and [diagnosis](#diagnosis), the Mobile solutions support other (payment) features. These are the same features that are supported in Terminal API integrations using Adyen-provided payment terminals. For some features you need to add parameters to your Terminal API payment request, similar to [unreferenced refunds](#unreferenced-refund) described above. Other features only require enabling the feature for your Adyen account. You can find the details on the pages dedicated to those features. Where the details differ between an integration using payment terminals and a mobile solution, this is clearly indicated. | Feature | Supported with Tap to Pay on iPhone | | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | Diagnosis | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | [Partial authorization](/point-of-sale/partial-authorizations/) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | Payment | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | [Pre-authorization](/point-of-sale/pre-authorisation/) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | Refund, referenced | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | Refund, unreferenced | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | [Store and forward](/point-of-sale/offline-payment/) offline payments | US only | | [Surcharge](/point-of-sale/surcharge/) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | [Tax-free shopping](/point-of-sale/shopper-recognition/tax-free-shopping/) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | ## Follow UI guidelines When using Tap to Pay on iPhone, the users of your POS app as well as the customers will interact with your app. There are some recommendations to follow for these interactions. ### POS app user screens It needs to be clear to users operating your POS app (like store staff) how they can activate Tap to Pay on iPhone. * Make the users aware they need to set up a device passcode to unlock the iPhone they use for Tap to Pay on iPhone. * Make sure that the user interface for Tap to Pay on iPhone is in accordance with Apple's [best practices and presentation guidelines](https://developer.apple.com/design/human-interface-guidelines/tap-to-pay-on-iphone).\ For example: * For a button, use the label **Tap to Pay on iPhone**, or **Tap to Pay** if there is little space. * Apple Pay and Tap to Pay on iPhone are two different things. So if you use an icon, use the Tap to Pay icon, not the Apple Pay icon. * Add education screens that instruct users on how to user Tap to Pay on iPhone. From iOS Mobile SDK version **3.9.0** and later you can call `showTapToPayEducationScreen()` to show education screens on devices on iOS version **18** or later. ```swift @available(iOS 18.4, *) func showTapToPayEducationScreen() async { do { try await myPaymentService.presentEducationForTapToPay(for: .tapContactless) } catch { print("Failed to display Tap to Pay Education: \(String(describing: error))") } } ``` ### Approval screen After reading a card, the iPhone automatically shows a **Done** screen first. This does not mean that the transaction is authorized yet. Only when the transaction is authorized and an **Approved** screen with a green check mark automatically appears, can you move on to the next step in the flow, which would be to get the receipt. ![](/user/pages/docs/03.point-of-sale/56.mobile-ios/06.build/04.tap-to-pay/screen-EN_TTPios-approval-flow.png) ## Test your solution To make test transactions: 1. Make sure you are using the test version of the Mobile SDK. 2. [Create a Sandbox Apple ID](https://developer.apple.com/help/app-store-connect/test-in-app-purchases/create-sandbox-apple-ids/). This is needed to test Tap to Pay on iPhone in our test environment. 3. Initiate a test transaction using the following Adyen point-of-sale test cards to complete the payment: * [White-green test card](/point-of-sale/testing-pos-payments/test-card-v3/) * [Blue-green test card](/point-of-sale/testing-pos-payments/test-card-v2/) version **2.4** or later The instructions are the same for both cards; see either of the pages mentioned above. ## Go live When you have finished testing your integration and are ready to go live: 1. If new to Adyen, get a [live account](/get-started-with-adyen/#apply-for-your-live-account). You need to have access to your organization's live Customer Area to generate API credentials for the live environment. 2. Ensure that you have a [LIVE entitlement from Apple](#get-entitlement). The approval process for a LIVE entitlement can take up to several weeks. 3. [Get the live SDK](#get-live-sdk). You need to generate a new, live basic authentication credential exclusively for downloading the SDK. 4. [Use the live endpoint for establishing a session](#establish-a-live-session). To access the live endpoint, you need to generate a new, live API key that is different from the API key used for downloading the SDK. 5. For Tap to Pay on iPhone, [accept the Apple Terms of Use](/point-of-sale/mobile-ios/manage#apple-terms-of-use). 6. Optionally [set a custom transaction screen text](/point-of-sale/mobile-ios/manage#set-the-transaction-screen-text). ### Get the live SDK To pull in the live version of the SDK: 1. Generate a new basic authentication credential in your [live Customer Area](https://ca-live.adyen.com/) and add it to your `.netrc` file. 2. In your `.netrc` file, change the value for `machine` to `pos-mobile.cdn.adyen.com`. 3. In your Xcode project or workspace, remove the TEST dependency and add the LIVE dependency using the **URL** `https://github.com/Adyen/adyen-pos-mobile-ios`. 4. Add `AdyenPOSLIVE` to your app target that connects to the Adyen Live environment. If your POS app requires the iOS Mobile SDK to be compatible with Objective-C: * Link the `ADYPOSLIVE` package product to your app target instead of `AdyenPOSLIVE`. ### Establish a live session When going live, you must change the [/auth/certificate](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate) endpoint where you send requests to establish a secure communication session, as well as the API key that you use to authenticate those requests. * To access the live endpoint, generate a new API key from your [live Customer Area](https://ca-live.adyen.com/). * The live endpoint URL to use depends on the region: * Australia: `https://softposconfig-live-au.adyen.com/softposconfig/v{version}/auth/certificate` * Asia Pacific South East: `https://softposconfig-live-apse.adyen.com/softposconfig/v{version}/auth/certificate` * Europe: `https://softposconfig-live.adyen.com/softposconfig/v{version}/auth/certificate` * North East Asia: `https://softposconfig-live-nea.adyen.com/softposconfig/v{version}/auth/certificate` * United States: `https://softposconfig-live-us.adyen.com/softposconfig/v{version}/auth/certificate` ## Next steps [required](/point-of-sale/mobile-ios/manage) [![](/user/themes/adyen/images/illustrations/settings.svg)](/point-of-sale/mobile-ios/manage) ###### [Manage your solution](/point-of-sale/mobile-ios/manage) [Make your solution available and keep the software up-to-date.](/point-of-sale/mobile-ios/manage) [![](/user/themes/adyen/images/illustrations/checkmark.svg)](/point-of-sale/mobile-ios/checklists) ###### [Checklists](/point-of-sale/mobile-ios/checklists) [Get a list of what needs to be done to get started and go live with a Mobile solution.](/point-of-sale/mobile-ios/checklists) [![](/user/themes/adyen/images/illustrations/close.svg)](/point-of-sale/mobile-ios/troubleshooting) ###### [Error handling](/point-of-sale/mobile-ios/troubleshooting) [Resolve errors that appear on the mobile iOS device.](/point-of-sale/mobile-ios/troubleshooting)