--- title: "Building a local integration" description: "Learn how to build a Terminal API integration with local communications." url: "https://docs.adyen.com/point-of-sale/design-your-integration/choose-your-architecture/local" source_url: "https://docs.adyen.com/point-of-sale/design-your-integration/choose-your-architecture/local.md" canonical: "https://docs.adyen.com/point-of-sale/design-your-integration/choose-your-architecture/local" last_modified: "2025-11-12T10:08:00+01:00" language: "en" --- # Building a local integration Learn how to build a Terminal API integration with local communications. Before you start building a point-of-sale integration with Adyen-provided payment terminals, you need to [choose between local or cloud communications](/point-of-sale/design-your-integration/choose-your-architecture). This determines how your POS system —also referred to as cash register— connects to the Adyen payment terminals and the Adyen payments platform. This section of the documentation explains what to do if you have decided to use local communications. ## Requirements Before you begin, take into account the following information. | Requirement | Description | | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Integration type** | The information on this page is relevant for building a point-of-sale integration with the Adyen Terminal API and Adyen payment terminals using local communications. | | **[API credentials](/development-resources/api-credentials/)** | To set up a shared key using API calls, your API credential must have the following role:- Management API—Terminal Advanced settings read and write | | **[Customer Area roles](/account/user-roles)** | To set up a shared key using the Customer Area you must have the following user role:- Merchant POS Terminal Management Admin role/li> | ## How it works With local communications, your POS system sends an API request over the local network to the IP address of the payment terminal. When the shopper completes the transaction on the terminal, the terminal routes the request to the Adyen payments platform for processing. Optionally you can set up display notifications to inform your staff on the progress of the transaction and the result. You receive the API response synchronously. ![](/user/pages/docs/03.point-of-sale/04.design-your-integration/06.choose-your-architecture/01.local/local_flow.png) To build a local integration, you need to: 1. [Configure your network](#configure-network). To allow outgoing HTTPS traffic, you need to add Adyen domains to your firewall's allowlist, and you need to open certain network ports. 2. [Optionally install an API library](#install-library). This step can save you development time. 3. [Protect local communications](#protect-communications). This step involves validating the terminal certificate against the Adyen root certificate, and adding code to encrypt API requests and decrypt API responses. 4. [Build the payment flow](#build-payment-flow). For example, ensure your POS app can send Terminal API payment requests and other requests, and receive the synchronous response. 5. Learn how to [handle network issues](#handle-network-issues). ## 1. Configure your network To enable your POS app to connect directly to the terminal: 1. Add Adyen's domains to your firewall's allowlist.\ Configure your firewall to allow outgoing HTTPS traffic from the IP addresses of your POS apps and terminals to: * `*.adyen.com` * `*.adyenpayments.com` Allowlisting should be based on the DNS name of these URLs. Your firewall should dynamically check for IP address updates, at least every 60 seconds. Do not hard-code Adyen's IP addresses, because these can change over time. We do not share a list of our IP addresses publicly. 2. Open the ports: * **tcp/443** to the internet. * **tcp/8443** on your LAN. 3. Use the correct [Transport Layer Security (TLS) version and ciphers](/point-of-sale/design-your-integration/network-and-connectivity/network-configuration#communications-security). 4. To [assign IP addresses to your terminals](/point-of-sale/design-your-integration/network-and-connectivity/network-configuration#configuring-the-terminal-IP-address), use DHC reservation (recommended), or manually configure static IP addresses. Optionally you can get notified about changes in terminal IP addresses. For more information and recommendations, see [Network and connectivity](/point-of-sale/design-your-integration/network-and-connectivity). ## 2. (Optional) Install an API library On our [GitHub](https://github.com/adyen), we provide server-side API libraries in several languages. Installing a GitHub library is not required, but can save you development time. The libraries include all Terminal API models so you can use these to create requests. Most libraries also send requests to Adyen using a built-in HTTP client, construct the correct test and live endpoints, and help to [protect local communications](#protect-communications). The following Adyen GitHub libraries are available for a local Terminal API integration: * .NET - [adyen-dotnet-api-library](https://github.com/Adyen/adyen-dotnet-api-library) * iOS - [adyen-terminal-api-ios](https://github.com/Adyen/adyen-terminal-api-ios) * Java - [adyen-java-api-library](https://github.com/Adyen/adyen-java-api-library) * Node - [adyen-node-api-library](https://github.com/Adyen/adyen-node-api-library) These GitHub libraries work with Terminal API and are completely separate from the [classic libraries, which have been deprecated](/point-of-sale/classic-library-deprecation). ## 3. Protect local communications While you are building a **test** integration, we do not enforce this step. This allows you to develop the functional and security aspects in parallel. However, you will **not be able to process live transactions** until you have secured the requests between your POS app and your terminal. If your integration uses local communications, you need to protect your integration against [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack), eavesdropping, and tampering. The steps you need to take are: 1. [Install Adyen's root certificate](#install-root-cert). 2. [Set up a shared key](#set-up-shared-key). 3. [Add code](#add-code) to validate the terminal certificate against the root certificate and encrypt communications using the shared key. ### Install Adyen's root certificate The root certificate contains information about Adyen's public key, which is used to sign other certificates such as the certificate on the terminal. To verify the certificate on the terminal against the public key, you have to add Adyen's root certificate to a trust store that is available to your POS app. The instructions differ depending on the operating system. Select one of the tabs below. ### Tab: General instructions 1. Download the Adyen public root certificates: * [`adyen-terminalfleet-test.pem` ](/point-of-sale/design-your-integration/choose-your-architecture/local/adyen-terminalfleet-test.pem). This is the test root certificate for validating the certificate on **test** terminals. * [`adyen-terminalfleet-live.pem` ](/point-of-sale/design-your-integration/choose-your-architecture/local/adyen-terminalfleet-live.pem). This is the live root certificate for validating the certificate on **live** terminals. If your system requires another format, such as **.crt** or **.cer**, you can use OpenSSL commands to convert the certificates. For example: ```bash openssl x509 -outform der -in adyen-terminalfleet-test.pem -out adyen-terminalfleet-test.crt ``` 2. Verify that the **SHA-256 fingerprint** (also called thumbprint) of the root certificates matches the following: * **Test** root certificate: `3A 33 C3 34 C3 0F 69 46 E9 75 4B 6B B1 67 2B 54 6F BA A9 66 FB 6A 4B 58 AA 4E 3A BE 80 A7 EC BE` * **Live** root certificate: `06 D4 86 41 95 4B 95 7D 7A F5 F5 E4 5A 58 D8 61 DB 0D E3 CC ED BB 98 36 60 BB 01 6C E6 14 2D A1` To get the SHA-256 fingerprint, you can use the following OpenSSL command: **.pem files** ```bash openssl x509 -in adyen-terminalfleet-test.pem -noout -sha256 -fingerprint ``` **.crt or .cer files** ```bash openssl x509 -inform der -in adyen-terminalfleet-test.crt -noout -sha256 -fingerprint ``` 3. Install the Adyen root certificates in the trust store of your POS app, following your vendor's instructions. You can add root certificates to the user or system trust store. At the user level, it is easier to import a certificate. ### Tab: Windows 1. Click the following links to download the Adyen public root certificates: * [`adyen-terminalfleet-test.pem` ](/point-of-sale/design-your-integration/choose-your-architecture/local/adyen-terminalfleet-test.pem). This is the test root certificate for validating the certificate on **test** terminals. * [`adyen-terminalfleet-live.pem` ](/point-of-sale/design-your-integration/choose-your-architecture/local/adyen-terminalfleet-live.pem). This is the live root certificate for validating the certificate on **live** terminals. 2. In the Microsoft Management Console (MMC), add the Certificates snap-in: 1. In the **Run** window, enter **mmc**. 2. Select **File** > **Add/Remove Snap-in** and add the **Certificates** snap-in to your user or computer trust store (referred to as *account*). 3. Go to **Certificates** > **Trusted Root Certification Authorities** > **Certificates**. 4. Import the Adyen root certificate into the **Trusted Root Certification Authorities** trust store:\ Select **Action** > **All tasks** > **Import**, select the Adyen root certificate, and complete the wizard steps. 5. Double-click the imported root certificate and on the **Details** tab verify that the **Thumbprint** matches the following **SHA-1** fingerprint: * **Test** root certificate: `D5 02 7F A8 B3 93 96 DB 2A 4F B1 86 EF 61 E4 A4 40 A7 30 51` * **Live** root certificate: `62 61 0D 88 27 8E 95 B7 F8 57 9A 9B 5E 07 85 D7 72 87 66 42` 6. To verify that the root certificate is installed, in the **Run** window open `certmgr.msc` for a user trust store or `certlm.msc` for a system trust store and check that the Adyen root certificate is listed under **Trusted Root Certification Authorities** -> **Certificates**. Apart from using the [MMC Certificates snap-in](https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-view-certificates-with-the-mmc-snap-in) as described above, you could also use [PowerShell](https://docs.microsoft.com/en-us/powershell/module/pki/import-certificate?view=windowsserver2019-ps) to import the root certificates. ### Tab: iOS 1. Use the following links to download the Adyen public root certificates from our documentation website: * [`adyen-terminalfleet-test.pem` ](/point-of-sale/design-your-integration/choose-your-architecture/local/adyen-terminalfleet-test.pem). This is the test root certificate for validating the certificate on **test** terminals. * [`adyen-terminalfleet-live.pem` ](/point-of-sale/design-your-integration/choose-your-architecture/local/adyen-terminalfleet-live.pem). This is the live root certificate for validating the certificate on **live** terminals. 2. Use the following OpenSSL command to convert the certificates to **.crt** format: ```bash openssl x509 -outform der -in adyen-terminalfleet-test.pem -out adyen-terminalfleet-test.crt ``` 3. Add the Adyen public root certificate to the app bundle. 4. Add the following to your implementation of `URLSessionDelegate` that handles the communication with the payment terminals. ```swift class MyURLSessionDelegate: NSObject, URLSessionDelegate { func urlSession( _ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void ) { if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { if let certFile = Bundle.main.path(forResource: "adyen-terminalfleet-test", ofType: "crt"), let data = try? Data(contentsOf: URL(fileURLWithPath: certFile)), let cert = SecCertificateCreateWithData(nil, data as CFData), let trust = challenge.protectionSpace.serverTrust { SecTrustSetAnchorCertificates(trust, [cert] as CFArray) SecTrustSetAnchorCertificatesOnly(trust, true) var error: CFError? if SecTrustEvaluateWithError(trust, &error) { completionHandler(.useCredential, URLCredential(trust: trust)) } else { completionHandler(.cancelAuthenticationChallenge, nil) } } else { completionHandler(.cancelAuthenticationChallenge, nil) } } else { completionHandler(.performDefaultHandling, nil) } } } ``` ### Set up a shared key To prevent others from being able to read messages sent between your POS app and the payment terminal, you need to encrypt the communications. For that, you first need to create a key passphrase and share it with Adyen. Both your code (or the Adyen GitHub library you are using) and the terminal will apply a key derivation function to the passphrase, and use the resulting key material to encrypt communications. You can create a shared key manually in your Customer Area or with an API call. For both options, you can set up the shared key at the company, merchant, store, or terminal level. If your use case does not require a specific level, we recommend setting up a shared key at the highest possible level. For more information, see [Settings at different levels](/point-of-sale/design-your-integration/determine-account-structure/configure-features#settings-at-different-levels) and [Inheritance of settings](/point-of-sale/automating-terminal-management/configure-terminals-api#inheritance). ### Tab: Create a shared key in the Customer Area 1. Log in to your [Customer Area](https://ca-test.adyen.com/). 2. Go to the account level that you want to set up a shared key for. * For a company or merchant account, select the account and go to **In-person payments** > **Terminal settings**. * For a store, go to **In-person payments** > **Stores** and select the store. * For a terminal, go to **In-person payments** > **Payment devices**, select the **Terminals** tab, and then select your terminal. 3. On the **Settings** page, select **Integrations**. 4. Under **Encryption key**, select **Decrypted** and then complete the fields: * **Key identifier**: a description to recognize the key by. * **Key passphrase**: a string of random characters that must consist of: * At least 12 characters. * At least 1 uppercase letter: `[A-Z]`. * At least 1 lowercase letter: `[a-z]`. * At least 1 digit: `[0-9]`. * At least 1 special character. Limited to the following: `~`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `(`, `)`, `_`, `+`, `=`, `}`, `{`, `]`, `[`, `;`, `:`, `?`, `.`, `,`, `>`, `<`. * **Key version**: the version number of the key. For example, **1**. If this section is not visible or if you cannot edit the fields, ask our [Support Team](https://ca-test.adyen.com/ca/ca/contactUs/support.shtml?form=other) to set the required permission for your user. 5. Select **Save**. 6. Securely store the key identifier, passphrase, and version in your system. #### Look up your shared key You can later look up the details of the shared key that was created: 1. In your [Customer Area](https://ca-test.adyen.com/), under **In-person payments**, go to the **Terminal settings** for your company, merchant account, or store. 2. Select **Integrations** and under **Terminal API** go to **Encryption key**. 3. To see the key identifier, passphrase, and version values, select **Decrypted**. ### Tab: Create a shared key with an API call To create a shared key: 1. Use one of the following endpoints: * PATCH [/companies/{companyId}/terminalSettings](https://docs.adyen.com/api-explorer/Management/latest/patch/companies/\(companyId\)/terminalSettings) * PATCH [/merchants/{merchantId}/terminalSettings](https://docs.adyen.com/api-explorer/Management/latest/patch/merchants/\(merchantId\)/terminalSettings) * PATCH [/stores/{storeId}/terminalSettings](https://docs.adyen.com/api-explorer/Management/latest/patch/stores/\(storeId\)/terminalSettings) * PATCH [/terminals/{terminalId}/terminalSettings](https://docs.adyen.com/api-explorer/Management/latest/patch/terminals/\(terminalId\)/terminalSettings) Specify a [nexo](https://docs.adyen.com/api-explorer/Management/latest/patch/companies/\(companyId\)/terminalSettings#request-nexo) object with the following properties: | Parameter | Required | Type | Description | | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [identifier](https://docs.adyen.com/api-explorer/Management/latest/patch/companies/\(companyId\)/terminalSettings#request-nexo-encryptionKey-identifier) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | string | The unique identifier of the shared key. | | [passphrase](https://docs.adyen.com/api-explorer/Management/latest/patch/companies/\(companyId\)/terminalSettings#request-nexo-encryptionKey-passphrase) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | string | The secure passphrase to protect the shared key. Must consist of:- At least 12 characters. - At least 1 uppercase letter: `[A-Z]`. - At least 1 lowercase letter: `[a-z]`. - At least 1 digit: `[0-9]`. - At least 1 special character. Limited to the following: `~`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `(`, `)`, `_`, `+`, `=`, `}`, `{`, `]`, `[`, `;`, `:`, `?`, `.`, `,`, `>`, `<`. | | [version](https://docs.adyen.com/api-explorer/Management/latest/patch/companies/\(companyId\)/terminalSettings#request-nexo-encryptionKey-version) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | integer | The version number of the shared key. For example, **1**. | **/terminalSettings/encryptionKey request** #### curl ```bash curl https://management-test.adyen.com/v3/companies/{companyId}/terminalSettings \ -H 'x-API-key: ADYEN_API_KEY' \ -X PATCH \ -d '{ "nexo": { "encryptionKey" : { "identifier": "KEY_IDENTIFIER", "passphrase": "KEY_PASSPHRASE", "version": 1 } } }' ``` #### Java ```java // Adyen Java API Library v25.0.0 import com.adyen.Client; import com.adyen.enums.Environment; import com.adyen.model.management.*; import java.time.OffsetDateTime; import java.util.*; import com.adyen.service.management.*; Client client = new Client("ADYEN_API_KEY", Environment.TEST); // Request objects Key key = new Key() .identifier("KEY_IDENTIFIER") .passphrase("KEY_PASSPHRASE") .version(1); Nexo nexo = new Nexo() .encryptionKey(key); TerminalSettings terminalSettings = new TerminalSettings() .nexo(nexo); // Make the request TerminalSettingsCompanyLevelApi service = new TerminalSettingsCompanyLevelApi(client); TerminalSettings response = service.updateTerminalSettings("companyId", terminalSettings, null); ``` #### PHP ```php // Adyen PHP API Library v17.4.0 use Adyen\Client; use Adyen\Environment; use Adyen\Model\Management\Key; use Adyen\Model\Management\Nexo; use Adyen\Model\Management\TerminalSettings; use Adyen\Service\Management\TerminalSettingsCompanyLevelApi; $client = new Client(); $client->setXApiKey("ADYEN_API_KEY"); $client->setEnvironment(Environment::TEST); // Request objects $key = new Key(); $key ->setIdentifier("KEY_IDENTIFIER") ->setPassphrase("KEY_PASSPHRASE") ->setVersion(1); $nexo = new Nexo(); $nexo ->setEncryptionKey($key); $terminalSettings = new TerminalSettings(); $terminalSettings ->setNexo($nexo); // Make the request $service = new TerminalSettingsCompanyLevelApi($client); $response = $service->updateTerminalSettings('companyId', $terminalSettings); ``` #### C\# ```cs // Adyen .net API Library v14.3.0 using Adyen; using Environment = Adyen.Model.Environment; using Adyen.Model; using Adyen.Model.Management; using Adyen.Service.Management; var config = new Config() { XApiKey = "ADYEN_API_KEY", Environment = Environment.Test }; var client = new Client(config); // Fill in your request objects Key key = new Key { Identifier = "KEY_IDENTIFIER", Passphrase = "KEY_PASSPHRASE", Version = 1 }; Nexo nexo = new Nexo { EncryptionKey = key }; TerminalSettings terminalSettings = new TerminalSettings { Nexo = nexo }; // Make the request var service = new TerminalSettingsCompanyLevelService(client); var response = service.UpdateTerminalSettings("companyId", terminalSettings); ``` #### NodeJS (JavaScript) ```js // Adyen Node API Library v16.2.0 // Require the parts of the module you want to use const { Client, ManagementAPI } = require('@adyen/api-library'); // Initialize the client object const client = new Client({apiKey: "ADYEN_API_KEY", environment: "TEST"}); // Create the request object const terminalSettings = { nexo: { encryptionKey: { identifier: "KEY_IDENTIFIER", passphrase: "KEY_PASSPHRASE", version: 1 } } } // Make the request const managementAPI = new ManagementAPI(client); const response = managementAPI.TerminalSettingsCompanyLevelApi.updateTerminalSettings("companyId", terminalSettings); ``` #### Go ```go // Adyen Go API Library v9.2.0 import ( "context" "github.com/adyen/adyen-go-api-library/v9/src/common" "github.com/adyen/adyen-go-api-library/v9/src/adyen" "github.com/adyen/adyen-go-api-library/v9/src/management" ) client := adyen.NewClient(&common.Config{ ApiKey: "ADYEN_API_KEY", Environment: common.TestEnv, }) // Fill in your request objects key := management.Key{ Identifier: common.PtrString("KEY_IDENTIFIER"), Passphrase: common.PtrString("KEY_PASSPHRASE"), Version: common.PtrInt32(1), } nexo := management.Nexo{ EncryptionKey: &key, } terminalSettings := management.TerminalSettings{ Nexo: &nexo, } // Make the request service := client.Management() req := service.TerminalSettingsCompanyLevelApi.UpdateTerminalSettingsInput("companyId").TerminalSettings(terminalSettings) res, httpRes, err := service.TerminalSettingsCompanyLevelApi.UpdateTerminalSettings(context.Background(), req) ``` #### Python ```py # Adyen Python API Library v12.2.0 import Adyen adyen = Adyen.Adyen() adyen.client.xapikey = "ADYEN_API_KEY" adyen.client.platform = "test" # The environment to use library in. json_request = { "nexo": { "encryptionKey": { "identifier": "KEY_IDENTIFIER", "passphrase": "KEY_PASSPHRASE", "version": 1 } } } result = adyen.management.terminal_settings_company_level_api.update_terminal_settings(request=json_request, companyId="companyId") ``` #### Ruby ```rb # Adyen Ruby API Library v9.2.0 require "adyen-ruby-api-library" adyen = Adyen::Client.new adyen.api_key = 'ADYEN_API_KEY' adyen.env = :test # Set to "live" for live environment request_body = { :nexo => { :encryptionKey => { :identifier => 'KEY_IDENTIFIER', :passphrase => 'KEY_PASSPHRASE', :version => 1 } } } result = adyen.management.terminal_settings_company_level_api.update_terminal_settings(request_body, 'companyId') ``` #### NodeJS (TypeScript) ```ts // Adyen Node API Library v16.2.0 // Require the parts of the module you want to use import { Client, ManagementAPI, Types } from "@adyen/api-library"; // Initialize the client object const client = new Client({apiKey: "ADYEN_API_KEY", environment: "TEST"}); // Create the request objects const key: Types.management.Key = { identifier: "KEY_IDENTIFIER", passphrase: "KEY_PASSPHRASE", version: 1 }; const nexo: Types.management.Nexo = { encryptionKey: key }; const terminalSettings: Types.management.TerminalSettings = { nexo: nexo }; // Make the request const managementAPI = new ManagementAPI(client); const response = managementAPI.TerminalSettingsCompanyLevelApi.updateTerminalSettings("companyId", terminalSettings); ``` 2. The response returns all settings that your API credential has access to, including the shared key that you defined in the request: **/terminalSettings/encryptionKey response** ```json { "cardholderReceipt": { "headerForAuthorizedReceipt": "header1,header2,filler" }, "gratuities": [ { "currency": "EUR", "usePredefinedTipEntries": true, "predefinedTipEntries": [ "1000", "20%" ], "allowCustomAmount": true } ], "nexo": { "encryptionKey": { "identifier": "KEY_IDENTIFIER", "passphrase": "KEY_PASSPHRASE", "version": 1 }, "eventUrls": { "eventPublicUrls": [ { "password": "notification", "url": "...", "username": "adyen" } ] }, "nexoEventUrls": [ "..." ] }, "opi": { "enablePayAtTable": false }, "receiptPrinting": { "merchantApproved": true, "merchantRefused": true, "merchantCancelled": true, "merchantRefundApproved": true, "merchantRefundRefused": true, "merchantVoid": true, "shopperApproved": true, "shopperRefused": true, "shopperCancelled": true, "shopperRefundApproved": true, "shopperRefundRefused": true, "shopperVoid": true }, "signature": { "askSignatureOnScreen": true, "skipSignature": false }, "timeouts": { "fromActiveToSleep": 30 }, "offlineProcessing": { "chipFloorLimit": 0 }, "passcodes": { "adminMenuPin": "1111", "txMenuPin": "1111", "screenLockPin": "1111" }, "standalone": { "enableStandalone": false, "currencyCode": "EUR" } } ``` #### Look up your shared key You can later look up the details of the shared key that was created: 1. Make a GET call to one of the following endpoints: * [/companies/{companyId}/terminalSettings](https://docs.adyen.com/api-explorer/Management/latest/get/companies/\(companyId\)/terminalSettings) * [/merchants/{merchantId}/terminalSettings](https://docs.adyen.com/api-explorer/Management/latest/get/merchants/\(merchantId\)/terminalSettings) * [/stores/{storeId}/terminalSettings](https://docs.adyen.com/api-explorer/Management/latest/get/stores/\(storeId\)/terminalSettings) * [/terminals/{terminalId}/terminalSettings](https://docs.adyen.com/api-explorer/Management/latest/get/terminals/\(terminalId\)/terminalSettings) 2. In the response, see the [nexo.encryptionKey](https://docs.adyen.com/api-explorer/Management/latest/get/companies/\(companyId\)/terminalSettings#responses-200-nexo-encryptionKey) object for the key identifier, passphrase, and version of your shared key. ### Add code to protect local communications Your integration must: * **Validate the terminal certificate**: this confirms that your POS app is communicating directly with an Adyen-supplied payment terminal, and not an impostor. * **Encrypt communications**: this prevents intruders from reading the messages transmitted between your POS app and the terminal. There are two ways to get this done: use one of our [GitHub libraries](#install-library), or write your own code. For instructions, select one of the options below. [![](/user/themes/adyen/images/illustrations/library.svg)](/point-of-sale/design-your-integration/choose-your-architecture/local/protect-with-library) ###### [Use a GitHub library](/point-of-sale/design-your-integration/choose-your-architecture/local/protect-with-library) [Handle the protection of local communications through a library.](/point-of-sale/design-your-integration/choose-your-architecture/local/protect-with-library) [![](/user/themes/adyen/images/illustrations/digital-signature.svg)](/point-of-sale/design-your-integration/choose-your-architecture/local/protect) ###### [Do it yourself](/point-of-sale/design-your-integration/choose-your-architecture/local/protect) [Write your own code to handle the protection of local communications.](/point-of-sale/design-your-integration/choose-your-architecture/local/protect) ## 4. Build your payment flow The payment features that you minimally need to integrate, are the same regardless if you use local or cloud communications. These features, described under [Implement the payment flow](/point-of-sale/basic-tapi-integration), are: * Make a payment. * Issue a refund. * Cancel an in-progress payment. * Generate receipts. * Add details about your application to your requests. * Verify the status of a transaction (also see [5. Handle network issues](#handle-network-issues)). With local communications you additionally need to consider the following points: * Sending requests and handling the synchronous response:\ Adyen-supplied payment terminals listen for Terminal API POST requests to `/nexo` on port **8443**. For example, if your terminal has the IP address **198.51.100.1** you make API requests to `https://198.51.100.1:8443/nexo`. When you make a payment, the result is provided through a synchronous API response. To receive the synchronous response, your POS app needs to make HTTPS requests to the terminal using a time-out of more than 120 seconds. During this time the connection is kept alive and a synchronous response will follow. * Whether you want your POS app to show messages, called display notifications, that keep your staff up to date with the progress of the transaction. ### Showing display notifications in your POS app During a transaction or *[tender](/get-started-with-adyen/adyen-glossary/#tender)*, the payment terminal generates display notifications. You can show these in your POS app to keep your staff up-to-date on the progress of the tender. For example, whether the customer has selected Dynamic Currency Conversion, or finished entering their PIN. To set this up, see [Display notifications](/point-of-sale/design-your-integration/notifications/display-notifications). ## 5. Handle network issues To make your integration more resilient in case of connection issues, your integration should automatically [make a transaction status request](/point-of-sale/basic-tapi-integration/verify-transaction-status) when it does not receive a synchronous payment response from Adyen. Payment requests time out after 120 seconds. If you do not receive a payment response (or you receive a response indicating a time-out) after 150 seconds, your integration should automatically make a transaction status request. Showing the result of the transaction status request in your POS app reduces the risk that your store staff will unnecessarily try to cancel or refund the transaction, or make a duplicate transaction. You can optionally request the status of a transaction before 120 seconds. For example, you may also want to let store staff manually request the status of a payment at any time from the POS app. ## See also * [Terminal API integration](/point-of-sale) * [How to: View certificates with the MMC snap-in](https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-view-certificates-with-the-mmc-snap-in) * [Importing certificates with the PowerShell](https://docs.microsoft.com/en-us/powershell/module/pki/import-certificate?view=windowsserver2019-ps) * [Set up protection of local communications using an Adyen GitHub library](/point-of-sale/design-your-integration/choose-your-architecture/local/protect-with-library) * [Set up protection of local communications yourself](/point-of-sale/design-your-integration/choose-your-architecture/local/protect)