> For the complete documentation index, see [llms.txt](https://docs.verifone.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.verifone.com/psdk-latest/psdk-user-guide-and-training/psdk-user-guide/pos_guide.md).

# Getting Started

{% embed url="<https://vimeo.com/1132591200/b191846d0a>" %}

### Definitions <a href="#definitions" id="definitions"></a>

* POS - The Android application using the SDK. This may or may not be an actual Point-of-Sale application. This application is generally used by a cashier or employee of the merchant.
* POI - The point of interaction for the end customer, this is the device that collects card information and other similar input.
* Payment Service - A background service running on the Android system that receives the calls from the SDK and either handles it directly or adapts it to the protocol used by the POI.
* Payment App - The application running on the POI that handles the card information and other payment / transaction details, communicating this to the host to authorize the payment, or storing the data for later and issuing a temporary approval if offline authorization is enabled.
* Host / Payment Host - Receives the card data and other information from the Payment App, and actually enables the funds to be transferred. This can be a payment gateway, processor, etc.
* Session - The period between [TransactionManager.startSession2(…)](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#startsession2-transaction) and [TransactionManager.endSession()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#endsession) where the POI is reserved for the POS, blocking anything else from performing operations with the POI, and keeping the screen in a ready state to display information from the POS.
* Order - A collection of basket items and payments that are related by the same Invoice, linking the original basket items, refunded/replaced items, and all of the associated payments such as sales, pre-auths, pre-auth completions, voids, refunds, etc. There is no explicit object in the SDK for an order, it is an abstract concept represented by a single Invoice ID.
* Transaction - A collection of basket items and payments that are related by the same Invoice for a specific purpose. Similar to an Order, except that it has a type which defines its purpose, such as Transaction.PAYMENT\_TYPE or Transaction.REFUND\_TYPE. There can be one or more transactions for an order. There is always one transaction object associated with a Session.

### PaymentDeviceState <a href="#paymentdevicestate" id="paymentdevicestate"></a>

PSDK maintains the last known connection state between POS and terminal state in [PaymentDeviceState](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentdevicestate). This can be queried using the API [PaymentSdk.getDeviceInformation()](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdkinterface#getdeviceinformation). The following table indicates possible device states and the follow up actions that POS can take.

<table data-full-width="true"><thead><tr><th width="48">Index</th><th width="267">PaymentDeviceState</th><th>Possible Actions from POS</th></tr></thead><tbody><tr><td>1</td><td>NOT_CONNECTED</td><td>POS should call <a href="/spaces/yzyMJNwlQJZoQOIC8vZ8/pages/rF9kFFC3tmyuHJg2UeuF#initialize">PaymentSdk.initialize(…)</a></td></tr><tr><td>2</td><td>CONNECTING</td><td>POS needs to wait for the callback to see the result of Initialize request.</td></tr><tr><td>3</td><td>CONNECTED</td><td>POS can request regular operation API like <a href="/spaces/yzyMJNwlQJZoQOIC8vZ8/pages/fEkkWxALVe77XyFhQtHt#loginwithcredentials-logincredentials">loginWithCredentials</a>, <a href="/spaces/yzyMJNwlQJZoQOIC8vZ8/pages/fEkkWxALVe77XyFhQtHt#startsession2-transaction">startsession2</a> etc.</td></tr><tr><td>4</td><td>DISCONNECTING</td><td>PSDK is in the process of teardown, POS needs to wait for the callback to see the result of TearDown request.</td></tr><tr><td>5</td><td>CONNECTION_LOST</td><td>Connection lost and cannot be recovered. <a href="/spaces/yzyMJNwlQJZoQOIC8vZ8/pages/rF9kFFC3tmyuHJg2UeuF#teardown">PaymentSDK.TearDown(…)</a> call required</td></tr><tr><td>6</td><td>MAINTENANCE_IN_PROGRESS</td><td>To use the terminal, POS needs to wait till it receives the END_MAINTENANCE notification event.</td></tr></tbody></table>

### PSDK State Transitions <a href="#psdk-state-transitions" id="psdk-state-transitions"></a>

***

The table below itemizes the available actions and subsequent transition states for each action.

<table data-full-width="true"><thead><tr><th width="47">Index</th><th width="264" valign="top">Actions</th><th valign="top">Allowed State</th><th valign="top">TransactionManager Transient State</th><th valign="top">TransactionManager Final State (Success/Failure)</th></tr></thead><tbody><tr><td>1</td><td valign="top">Initialize</td><td valign="top">NOT_LOGGED_IN<br>UNKNOWN</td><td valign="top">NA</td><td valign="top">NA</td></tr><tr><td>2</td><td valign="top">Login</td><td valign="top">NOT_LOGGED_IN</td><td valign="top">LOGGING_IN</td><td valign="top">LOGGED_IN<br>NOT_LOGGED_IN</td></tr><tr><td>3</td><td valign="top">StartSession</td><td valign="top">LOGGED_IN</td><td valign="top">SESSION_OPENING</td><td valign="top">SESSION_OPEN<br>LOGGED_IN</td></tr><tr><td>4</td><td valign="top">HandleLineItem</td><td valign="top">SESSION_OPEN</td><td valign="top">AWAITING_TERMINAL</td><td valign="top">No state change</td></tr><tr><td>5</td><td valign="top">FinalizeBasket</td><td valign="top">SESSION_OPEN</td><td valign="top">AWAITING_TERMINAL</td><td valign="top">No state change</td></tr><tr><td>6</td><td valign="top">RequestCardData</td><td valign="top">SESSION_OPEN</td><td valign="top">AWAITING_TERMINAL</td><td valign="top">No state change</td></tr><tr><td>7</td><td valign="top">PerformStoredValueAction</td><td valign="top">SESSION_OPEN</td><td valign="top">AWAITING_TERMINAL</td><td valign="top">No state change</td></tr><tr><td>8</td><td valign="top">PerformPayment</td><td valign="top">SESSION_OPEN</td><td valign="top">PAYMENT_PROCESSING</td><td valign="top">No state change</td></tr><tr><td>9</td><td valign="top">HandleResponse</td><td valign="top">LOGGED_IN<br>SESSION_OPEN</td><td valign="top">PAYMENT_PROCESSING</td><td valign="top">No state change</td></tr><tr><td>10</td><td valign="top">EndSession</td><td valign="top">LOGGED_IN<br>SESSION_OPEN</td><td valign="top">NA</td><td valign="top">LOGGED_IN</td></tr><tr><td>11</td><td valign="top">Reconcile</td><td valign="top">LOGGED_IN<br>SESSION_OPEN</td><td valign="top">AWAITING_TERMINAL</td><td valign="top">No state change</td></tr><tr><td>12</td><td valign="top">QueryTransactions</td><td valign="top">LOGGED_IN<br>SESSION_OPEN</td><td valign="top">AWAITING_TERMINAL</td><td valign="top">No state change</td></tr><tr><td>13</td><td valign="top">ReprintReceipt</td><td valign="top">LOGGED_IN<br>SESSION_OPEN</td><td valign="top">NA</td><td valign="top">No state change</td></tr><tr><td>14</td><td valign="top">Logout</td><td valign="top">LOGGED_IN<br>SESSION_OPEN</td><td valign="top">NA</td><td valign="top">NOT_LOGGED_IN</td></tr><tr><td>15</td><td valign="top">PresentContent</td><td valign="top">NOT_LOGGED_IN<br>LOGGED_IN<br>SESSION_OPEN</td><td valign="top">NA</td><td valign="top">No state change</td></tr><tr><td>16</td><td valign="top">RequestInput</td><td valign="top">NOT_LOGGED_IN<br>LOGGED_IN<br>SESSION_OPEN</td><td valign="top">AWAITING_TERMINAL</td><td valign="top">No state change</td></tr><tr><td>17</td><td valign="top">DeviceManagementCommand</td><td valign="top">NOT_LOGGED_IN<br>LOGGED_IN<br>SESSION_OPEN</td><td valign="top">AWAITING_TERMINAL</td><td valign="top">No state change</td></tr><tr><td>18</td><td valign="top">GetState</td><td valign="top">NOT_LOGGED_IN<br>LOGGED_IN<br>SESSION_OPEN<br>PAYMENT_PROCESSING<br>AWAITING_TERMINAL</td><td valign="top">NA</td><td valign="top">synchronous, no state change</td></tr><tr><td>19</td><td valign="top">Abort</td><td valign="top">NOT_LOGGED_IN<br>LOGGED_IN<br>SESSION_OPEN<br>PAYMENT_PROCESSING<br>AWAITING_TERMINAL</td><td valign="top">NA</td><td valign="top">No state change</td></tr><tr><td>20</td><td valign="top">Print</td><td valign="top">NOT_LOGGED_IN<br>SESSION_OPEN</td><td valign="top">NA</td><td valign="top">No state change</td></tr><tr><td>21</td><td valign="top">Encryption</td><td valign="top">NOT_LOGGED_IN<br>LOGGED_IN<br>SESSION_OPEN</td><td valign="top">AWAITING_TERMINAL</td><td valign="top">No state change</td></tr><tr><td>22</td><td valign="top">DeviceVitals</td><td valign="top">NOT_LOGGED_IN<br>LOGGED_IN<br>SESSION_OPEN</td><td valign="top">NA</td><td valign="top">No state change</td></tr><tr><td>23</td><td valign="top">Pin</td><td valign="top">LOGGED_IN<br>SESSION_OPEN</td><td valign="top">NA</td><td valign="top">No state change</td></tr><tr><td>24</td><td valign="top">Teardown</td><td valign="top">NOT_LOGGED_IN<br>LOGGED_IN<br>SESSION_OPEN<br>PAYMENT_PROCESSING<br>AWAITING_TERMINAL</td><td valign="top">NA</td><td valign="top">UNKNOWN</td></tr></tbody></table>

Logging in ([TransactionManager.loginWithCreadentials(…)](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#loginwithcredentials-logincredentials)) informs the POI that a cashier is present, and enables the POI to download/configure itself based on the cashier ID.

Starting a session ([TransactionManager.startSession2(…)](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#startsession2-transaction)) reserves the POI for use by the POS, enabling an order to be built and payments to be processed. There should only be one “order” per session, where an order is an optional collection of basket items and a set of payments/refunds/voids, all associated with a single Invoice ID. It is acceptable to manage a single order across multiple sessions, but it’s not recommended to handle multiple orders within a single session.

Starting a payment or performing some other payment-related action will move it to the payment processing state.

{% @plantuml/diagram content="      @startuml
hide footbox

```
  participant POS order 10
  participant PSDK order 20
  |||
  POS -> PSDK: PaymentSdk.getTransactionManager()
  POS ->> PSDK: transactionManager.login()
  POS <<- PSDK: TransactionEvent.LOGIN_COMPLETED
  group STATE_LOGGED_IN
    POS ->> PSDK: transactionManager.startSession()
    group STATE_SESSION_OPEN
      POS <<- PSDK: CommerceEvent.SESSION_STARTED
      POS <<->> PSDK: update POI display with basket items
      POS ->> PSDK: transactionManager.startPayment()
      group STATE_PAYMENT_PROCESSING
        POS <<- PSDK: TransactionEvent.TRANSACTION_PAYMENT_STARTED
        ...notifications/other messages...
        POS <<- PSDK: TransactionEvent.TRANSACTION_PAYMENT_COMPLETED
      end
      == Repeat payments until full payment is collected ==
      POS ->> PSDK: transactionManager.endSession()
      POS <<- PSDK: CommerceEvent.SESSION_ENDED
    end
    == Repeat sessions until cashier logs out ==
    ...
    POS ->> PSDK: transactionManager.logout()
    POS <<- PSDK: TransactionEvent.LOGOUT_COMPLETED
  end
  |||

  @enduml
```

" %}

{% hint style="info" %}
There are some actions that can be performed without a session active but after login, primarily actions on the [ReportManager](https://docs.verifone.com/psdk-api-latest/android-java/index/reportmanager). Other actions, related to setting parameters such as [PaymentSdk.getDeviceInformation()](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdkbase#getdeviceinformation) can be performed even before login.
{% endhint %}

### All calls are asynchronous (except a few) <a href="#all-calls-are-asynchronous-except-a-few" id="all-calls-are-asynchronous-except-a-few"></a>

Nearly all of the calls are asynchronous, returning an immediate value to indicate if the call could be successfully transmitted, and later returning the actual result to the listener. The immediate value returned only indicates if it was possible to send the call across the bound interface and that the bound interface is in a state to accept the call and queue it to be sent to the POI. Logically, it would seem that if this immediate result returns an error status, the listener would not receive any event for the call. In practice, however, PSDK does send an asynchronous event to the listener in these situations. This can cause confusion for the user, and will be addressed in future PSDK releases.

The asynchronous calls are placed into a queue, so if one were to call [loginWithCredentials()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#loginwithcredentials-logincredentials), [startSession2()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#startsession2-transaction), [startPayment()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#startpayment-payment), the payment would be properly handled if the preceding calls succeeded without error, otherwise they are going to fail due to a state issue (see [PSDK State Transitions](#state-matters)).

The exceptions to this rule do not require communication with the POI, allowing the result to be returned immediately. These are listed below.

* [TransactionManager.getState()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#getstate)
* [TransactionManager.isCapable(…)](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#iscapable-string)
* [TransactionManager.getBasketManager()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#getbasketmanager)
* [TransactionManager.getReportManager()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#getreportmanager)
* [BasketManager.getBasket()](https://docs.verifone.com/psdk-api-latest/android-java/index/basketmanager#getbasket)
* [BasketManager.getCurrentAmountTotals()](https://docs.verifone.com/psdk-api-latest/android-java/index/basketmanager#getcurrentamounttotals)
* [ReportManager.isCapable(…)](https://docs.verifone.com/psdk-api-latest/android-java/index/reportmanager#iscapable-string)

### Separate Processes means Separate Objects <a href="#separate-processes-means-separate-objects" id="separate-processes-means-separate-objects"></a>

The Services are separate processes, which means that the memory is separate. Each API call to a processes marshalls and unmarshalls the objects across the process boundary, meaning that each application has a copy of the objects being used. This can be confusing and lead to mistakes while programming, such as setting a field on an object within the POS and expecting it to be set within the Payment Service, or modifying an object locally without using the latest version of that object from the service, and causing a merge issue. The services can perform most of the basic merges necessary to run easily, but it’s important to remember that if an object is changed locally and the update needs to be reflected in the service, this object must be sent to the service using one of the API calls defined. It is also important to keep local copies updated with or replaced by the latest objects received by the listener.

## Best Practices <a href="#id3" id="id3"></a>

### User Inactivity <a href="#user-inactivity" id="user-inactivity"></a>

It is important that a session not be kept open without any interaction, as it blocks the POI from performing important internal updates and prevents any other application from being able to use the device. We recommend ending the session if the cashier is inactive somewhere between 10 and 30 seconds. It can be good to immediately start a session once a previous session is ended, this can help a cashier dealing with a long line and needing to key in orders rapidly, but if the cashier is not doing anything, please end the session.

### Threads of Execution <a href="#threads-of-execution" id="threads-of-execution"></a>

It is generally best to interact with the PSDK from some context which is different than the UI. While most calls in to the PSDK will not block for long, there are a couple that might delay UI execution, such as initialization.

It is also important that when events are received, the actual processing of the event happen in a separate context from the one which received the event. We only send events from one thread, so any long processing of one event will delay subsequent events from being received. This is generally not desirable behavior.

Finally, when running on Android or Java, it is best for all calls into the PSDK be performed from a single thread/context. On these platforms, calls into the PSDK attach to the JVM, which can be an expensive operation, but contexts which have already attached do not need to perform that operation on subsequent calls.

***

## Connection to Payment Application <a href="#connection-to-payment-application" id="connection-to-payment-application"></a>

### PSDK Entry Points <a href="#psdk-entry-points" id="psdk-entry-points"></a>

Create the PaymentSdk object to begin the process. Once created, it must be initialized. Initialization can fail for a few reasons, such as the Verifone terminal could not be found, but when first starting out the primary reason is that a Verifone terminal has not yet been paired, and must be selected.

Note

If a Verifone terminal is already paired, use the SDK’s getDeviceInformation() method to get these details and render them into a completely custom settings/information view.

If Payment SDK needs to be re-initialized for any reason including failed initialization while still retaining the initially created PaymentSdk object, executing tearDown() is mandated before every subsequent initialize method as to ensure that all objects have been cleared/reset.

### Initialize Payment SDK and Get Transaction Manager <a href="#initialize-payment-sdk-and-get-transaction-manager" id="initialize-payment-sdk-and-get-transaction-manager"></a>

The first step is to create and initialize the Payment SDK, after which an instance of the TransactionManager can be acquired. The Payment SDK may be initialized in server mode, in which it listens for a connection from the payment device, or client mode, in which it connects to a specified payment device. In client/server description, no context passed to getDeviceInformation() mode, the Payment SDK may be connecting to a payment device under the following three scenarios.

1. The first connection, in which the address information of the target device must be specified.
2. A subsequent connection, in which the previous connection information is persisted.
3. A changed target address, in which the persisted connection information must be wiped out, and replaced with the new connection information.

Each of these scenarios is depicted below in the code samples. These code samples are organized to easily perform the initialization from the background, but this can be done in a variety of ways, and should be consistent with the existing design instead of doing this exact code.

### Client Mode Initialization <a href="#client-mode-initialization" id="client-mode-initialization"></a>

{% tabs fullWidth="false" %}
{% tab title="Java" %}
{% code lineNumbers="true" fullWidth="true" %}

```java
import com.verifone.payment_sdk.*;

private PaymentSdk mPaymentSdk;

private final CommerceListenerAdapter mCommerceListenerAdapter = new CommerceListenerAdapter() {
    @Override
    public void handleStatus(Status status) {
        if (status.getStatus() == StatusCode.SUCCESS) {
            // Now you can get the transaction manager and log in.
        } else {
            // Initialization failed, look at the status code and message to determine why.
        }
    }
};

private void psdkInit() {
    new AsyncTask<Context, Void, Void>(){
        @Override
        protected Void doInBackground(Context... contexts) {
            // Create PaymentSdk
            mPaymentSdk = PaymentSdk.create(contexts[0]);
            HashMap<String, String> paramMap = new HashMap<>();

            // Specify the connection type and address
            paramMap.put(PsdkDeviceInformation.DEVICE_CONNECTION_TYPE_KEY, "tcpip");
            paramMap.put(PsdkDeviceInformation.DEVICE_ADDRESS_KEY, "192.168.1.10");
            mPaymentSdk.initializeFromValues(mCommerceListenerAdapter, paramMap);
        }
    }.execute(someContext);
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
import com.verifone.payment_sdk.*

private val mPaymentSdk: PaymentSdk

private val mCommerceListenerAdapter = object : CommerceListenerAdapter() {
    override fun handleStatus(status: Status) {
        if (status.getStatus() === StatusCode.SUCCESS) {
            // Now you can get the transaction manager and log in.
        } else {
            // Initialization failed, look at the status code and message to determine why.
        }
    }
}

suspend fun psdkInit(context: Context) {
    // Create PaymentSdk
    mPaymentSdk = PaymentSdk.create(context)

    // Specify the connection type and address
    val paramMap: HashMap<String, String> = hashMapOf(
        PsdkDeviceInformation.DEVICE_CONNECTION_TYPE_KEY to "tcpip",
        PsdkDeviceInformation.DEVICE_ADDRESS_KEY to "192.168.1.10"
    )
    mPaymentSdk.initializeFromValues(mCommerceListenerAdapter, paramMap)
}

private fun getStarted() {
    val job = Job()
    val scope = CoroutineScope(Dispatchers.IO + job)
    scope.launch {
        // first connection
        psdkInit(false, false, context)
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
// Overridden from the VFICommerceListenerAdapter
override func handle(_ status: VFIStatus?) {
    if (VFIStatusCodeSuccess == status?.getStatus()) {
        // Now you can get the transaction manager and log in.
    } else {
        // Initialization failed, look at the status code and message to determine why.
    }
}

func psdkInit() {
    // Create PaymentSdk
    let sdk = VFIPaymentSdk.createPaymentSdk()
    let adapter = CommerceListenerAdapter(listener: self)

    // Enable listening
    let connectConfig = [
        // Enable listening
        VFITransactionManagerDEVICELISTENKEY: VFITransactionManagerENABLEDVALUE,
        VFIPsdkDeviceInformationDEVICECONNECTIONTYPEKEY: "tcpip",
        VFIPsdkDeviceInformationDEVICESERIALNUMBERKEY: VFIPsdkDeviceInformationACCEPTANYDEVICEVALUE]
    sdk.initialize(fromValues: adapter, config: connectConfig)
}
```

{% endcode %}
{% endtab %}

{% tab title="C#" %}
{% code lineNumbers="true" fullWidth="true" %}

```csharp
using PaymentSDK;
using PaymentSdkUi;

private readonly PaymentSdk payment_sdk_;
private readonly TransactionManager transaction_manager;

// EventHandler
sdk_payment_event_handler_ = async (sender, args) =>
{
    if (args.Status == StatusCode.SUCCESS)
    {
        payment_sdk_.TransactionManager.HandleBasketEvent += basket_event_handler_;
        // Subscribe to other events
        // Now you can log in.
    }
    else
    {
        // Initialization failed, look at the status code and message to determine why.
    }
};

void PsdkInit()
{
    // Create PaymentSdk
    payment_sdk_ = new PaymentSdk();

    // Enable listening
    var param_map = new Windows.Foundation.Collections.StringMap
    {
        {TransactionManager.DEVICE_LISTEN_KEY, TransactionManager.ENABLED_VALUE},
        {PsdkDeviceInformation.DEVICE_CONNECTION_TYPE_KEY, "tcpip"},
        {PsdkDeviceInformation.DEVICE_SERIAL_NUMBER_KEY, PsdkDeviceInformation.ACCEPT_ANY_DEVICE_VALUE}
    };
    payment_sdk_.InitializeFromValues(param_map);
}
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
class CommerceAdapter : public verifone_sdk::CommerceListenerAdapter {
 public:
  ~CommerceAdapter() override = default;

  void handleStatus(const std::shared_ptr<verifone_sdk::Status>& status) override {
    if (status->getStatus() == verifone_sdk::StatusCode::SUCCESS) {
      // Now you can get the transaction manager and log in.
    } else {
      // Initialization failed, look at the status code and message to determine why.
    }
  }
};


void psdkInit() {
  // Create PaymentSdk
  payment_sdk_ = verifone_sdk::PaymentSdkInterface::create(
      verifone_sdk::CreatePlatformContextLinux(std::nullopt));
  auto adapter_ = std::make_shared<CommerceAdapter>();

  // Specify the connection type and address
  std::unordered_map<std::string, std::string> param_map{
      {verifone_sdk::PsdkDeviceInformation::DEVICE_ADDRESS_KEY, "192.168.1.10"},
      {verifone_sdk::PsdkDeviceInformation::DEVICE_CONNECTION_TYPE_KEY, "tcpip"}};

  payment_sdk_->initializeFromValues(adapter_, param_map);
}
```

{% endcode %}
{% endtab %}

{% tab title=".NET" %}
{% code lineNumbers="true" %}

```aspnet
using VerifoneSdk;

private readonly PaymentSdk payment_sdk_;

// EventHandler
public void HandleStatus(Status status) {
    if (status.StatusCode == 0)
    {
        // Subscribe to other events
        // Now you can log in.
    }
    else
    {
        // Initialization failed, look at the status code and message to determine why.
    }
};

void PsdkInit()
{
    // Create PaymentSdk
    payment_sdk_ = new PaymentSdk();

    // Specify the connection type and address
    var param_map = new Dictionary<string, string>
    {
        { "DEVICE_ADDRESS_KEY", "192.168.0.10" },
        { "DEVICE_CONNECTION_TYPE_KEY", "tcpip" }
    };
    payment_sdk_.InitializeFromValues(param_map);
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Server Mode Initialization <a href="#server-mode-initialization" id="server-mode-initialization"></a>

{% tabs fullWidth="false" %}
{% tab title="Java" %}
{% code lineNumbers="true" fullWidth="true" %}

```java
import com.verifone.payment_sdk.*;

private PaymentSdk mPaymentSdk;

private final CommerceListenerAdapter mCommerceListenerAdapter = new CommerceListenerAdapter() {
    @Override
    public void handleStatus(Status status) {
        if (status.getStatus() == StatusCode.SUCCESS) {
            // Now you can get the transaction manager and log in.
        } else {
            // Initialization failed, look at the status code and message to determine why.
        }
    }
};

private void psdkInit() {
    new AsyncTask<Context, Void, Void>(){
        @Override
        protected Void doInBackground(Context... contexts) {
            // Create PaymentSdk
            mPaymentSdk = PaymentSdk.create(contexts[0]);
            HashMap<String, String> paramMap = new HashMap<>();

            // Enable listening
            paramMap.put(TransactionManager.DEVICE_LISTEN_KEY, TransactionManager.ENABLED_VALUE);
            paramMap.put(PsdkDeviceInformation.DEVICE_CONNECTION_TYPE_KEY, "tcpip");
            paramMap.put(PsdkDeviceInformation.DEVICE_SERIAL_NUMBER_KEY, PsdkDeviceInformation.ACCEPT_ANY_DEVICE_VALUE);
            mPaymentSdk.initializeFromValues(mCommerceListenerAdapter, paramMap);
        }
    }.execute(someContext);
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
 import com.verifone.payment_sdk.*

private val mPaymentSdk: PaymentSdk

private val mCommerceListenerAdapter = object : CommerceListenerAdapter() {
    override fun handleStatus(status: Status) {
        if (status.getStatus() === StatusCode.SUCCESS) {
            // Now you can get the transaction manager and log in.
        } else {
            // Initialization failed, look at the status code and message to determine why.
        }
    }
}

suspend fun psdkInit(context: Context) {
    // Create PaymentSdk
    mPaymentSdk = PaymentSdk.create(context)

    // Enable listening
    val paramMap: HashMap<String, String> = hashMapOf(
        TransactionManager.DEVICE_LISTEN_KEY to TransactionManager.ENABLED_VALUE,
        PsdkDeviceInformation.DEVICE_CONNECTION_TYPE_KEY to "tcpip",
        PsdkDeviceInformation.DEVICE_SERIAL_NUMBER_KEY to PsdkDeviceInformation.ACCEPT_ANY_DEVICE_VALUE
    )

    mPaymentSdk.initializeFromValues(mCommerceListenerAdapter, paramMap)
}

private fun getStarted() {
    val job = Job()
    val scope = CoroutineScope(Dispatchers.IO + job)
    scope.launch {
        // first connection
        psdkInit(false, false, context)
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
// Overridden from the VFICommerceListenerAdapter
override func handle(_ status: VFIStatus?) {
    if (VFIStatusCodeSuccess == status?.getStatus()) {
        // Now you can get the transaction manager and log in.
    } else {
        // Initialization failed, look at the status code and message to determine why.
    }
}

func psdkInit() {
    // Create PaymentSdk
    let sdk = VFIPaymentSdk.createPaymentSdk()
    let adapter = CommerceListenerAdapter(listener: self)

    // Enable listening
    let connectConfig = [
        // Enable listening
        VFITransactionManagerDEVICELISTENKEY: VFITransactionManagerENABLEDVALUE,
        VFIPsdkDeviceInformationDEVICECONNECTIONTYPEKEY: "tcpip",
        VFIPsdkDeviceInformationDEVICESERIALNUMBERKEY: VFIPsdkDeviceInformationACCEPTANYDEVICEVALUE]
    sdk.initialize(fromValues: adapter, config: connectConfig)
}
```

{% endcode %}
{% endtab %}

{% tab title="C#" %}
{% code lineNumbers="true" fullWidth="true" %}

```csharp
 using PaymentSDK;
using PaymentSdkUi;

private readonly PaymentSdk payment_sdk_;
private readonly TransactionManager transaction_manager;

// EventHandler
sdk_payment_event_handler_ = async (sender, args) =>
{
    if (args.Status == StatusCode.SUCCESS)
    {
        payment_sdk_.TransactionManager.HandleBasketEvent += basket_event_handler_;
        // Subscribe to other events
        // Now you can log in.
    }
    else
    {
        // Initialization failed, look at the status code and message to determine why.
    }
};

void PsdkInit()
{
    // Create PaymentSdk
    payment_sdk_ = new PaymentSdk();

    // Enable listening
    var param_map = new Windows.Foundation.Collections.StringMap
    {
        {TransactionManager.DEVICE_LISTEN_KEY, TransactionManager.ENABLED_VALUE},
        {PsdkDeviceInformation.DEVICE_CONNECTION_TYPE_KEY, "tcpip"},
        {PsdkDeviceInformation.DEVICE_SERIAL_NUMBER_KEY, PsdkDeviceInformation.ACCEPT_ANY_DEVICE_VALUE}
    };
    payment_sdk_.InitializeFromValues(param_map);
}
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
class CommerceAdapter : public verifone_sdk::CommerceListenerAdapter {
 public:
  ~CommerceAdapter() override = default;

  void handleStatus(const std::shared_ptr<verifone_sdk::Status>& status) override {
    if (status->getStatus() == verifone_sdk::StatusCode::SUCCESS) {
      // Now you can get the transaction manager and log in.
    } else {
      // Initialization failed, look at the status code and message to determine why.
    }
  }
};

void psdkInit(bool use_last_device) {
  // Create PaymentSdk
  payment_sdk_ = verifone_sdk::PaymentSdkInterface::create(
      verifone_sdk::CreatePlatformContextLinux(std::nullopt));
  auto adapter_ = std::make_shared<CommerceAdapter>();

  // Specify the connection type and address
  std::unordered_map<std::string, std::string> param_map{
      {verifone_sdk::TransactonManager::DEVICE_LISTEN_KEY,
       verifone_sdk::TransactionManager::ENABLED_VALUE},
      {verifone_sdk::PsdkDeviceInformation::DEVICE_CONNECTION_TYPE_KEY, "tcpip"},
      {verifone_sdk::PsdkDeviceInformation::DEVICE_SERIAL_NUMBER_KEY,
       verifone_sdk::PsdkDeviceInformation::ACCEPT_ANY_DEVICE_VALUE}};
  payment_sdk_->initializeFromValues(adapter_, param_map);
}
```

{% endcode %}
{% endtab %}

{% tab title=".NET" %}
{% code lineNumbers="true" %}

```aspnet
 using PaymentSDK;

private readonly PaymentSdk payment_sdk_;

// EventHandler
public void HandleStatus(Status status) {
    if (status.StatusCode == 0)
    {
        // Subscribe to other events
        // Now you can log in.
    }
    else
    {
        // Initialization failed, look at the status code and message to determine why.
    }
};

void PsdkInit()
{
    // Create PaymentSdk
    payment_sdk_ = new PaymentSdk();

    // Enable listening
    var param_map = new Dictionary<string, string>
    {
        {TransactionManager.DEVICE_LISTEN_KEY, TransactionManager.ENABLED_VALUE},
        {PsdkDeviceInformation.DEVICE_CONNECTION_TYPE_KEY, "tcpip"},
        {PsdkDeviceInformation.DEVICE_SERIAL_NUMBER_KEY, PsdkDeviceInformation.ACCEPT_ANY_DEVICE_VALUE}
    };
    payment_sdk_.InitializeFromValues(param_map);
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Device setup via Bluetooth <a href="#device-setup-via-bluetooth" id="device-setup-via-bluetooth"></a>

To connect via Bluetooth or Bluetooth PAN using tethering, the Bluetooth Device Setup functionality will allow pairing with a new device and will prompt the user with messages if necessary bluetooth settings are not enabled.

{% code lineNumbers="true" %}

```swift
func psdkLaunchBluetoothDeviceSetup() {
    // Create PaymentSdk
    let sdk = VFIPaymentSdk.createPaymentSdk()

    // Launch the Bluetooth device setup flow.
    // Note: If callback received a successful status, the bluetooth device was paired
    // and saved successfully. Calling initializeFromValues() will
    // initialize PaymentSdk with the new device.
    // btpan: Using Bluetooth PAN?
    // sdi: Connecting to a SDI terminal?
    // caller: Calling view ViewController
    // listener: Handler that implements the callback
    sdk?.setupBluetoothDevice(btpan: true, sdi: true, caller: self, listener: self)
}

// Callback for handling Bluetooth device setup
extension ViewController: VFIBluetoothDeviceSetupListener {
    func foundBluetoothDevice(status: VFIBluetoothSetupStatus, device: VFIPsdkDeviceInformation?) {
        // handle different return status codes
    }
}
```

{% endcode %}

### Connecting to a New Device <a href="#connecting-to-a-new-device" id="connecting-to-a-new-device"></a>

In the situation in which a previously connected device is being replaced, if the logical ID of the new device as assigned by VHQ is different than the original logical ID, or if the original device did not have a logical ID, then the old device information must be wiped out of persistent storage and initialized with a configuration which contains the new information. The code sample below assumes a client mode initialization.

{% tabs fullWidth="false" %}
{% tab title="Java" %}
{% code lineNumbers="true" fullWidth="true" %}

```java
import com.verifone.payment_sdk.*;

private PaymentSdk mPaymentSdk;

private final CommerceListenerAdapter mCommerceListenerAdapter = new CommerceListenerAdapter() {
    @Override
    public void handleStatus(Status status) {
        if (status.getStatus() == StatusCode.SUCCESS) {
            // Now you can get the transaction manager and log in.
        } else {
            // Initialization failed, look at the status code and message to determine why.
        }
    }
};

private void psdkConnectToNewDevice() {
    new AsyncTask<Context, Void, Void>(){
        @Override
        protected Void doInBackground(Context... contexts) {
            // Create PaymentSdk
            mPaymentSdk = PaymentSdk.create(contexts[0]);
            PsdkDeviceInformation current = mPaymentSdk.getDeviceInformation();
            if (!current) {
                // there is no known last device
                return;
            }
            // Forget current device
            mPaymentSdk.UseDevice(current, false);
            HashMap<String, String> paramMap = new HashMap<>();
            // Specify the connection type and address
            paramMap.put(PsdkDeviceInformation.DEVICE_CONNECTION_TYPE_KEY, "tcpip");
            paramMap.put(PsdkDeviceInformation.DEVICE_ADDRESS_KEY, "192.168.1.10");
            mPaymentSdk.initializeFromValues(mCommerceListenerAdapter, paramMap);
        }
    }.execute(someContext);
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
 import com.verifone.payment_sdk.*

private val mPaymentSdk: PaymentSdk

private val mCommerceListenerAdapter = object : CommerceListenerAdapter() {
    override fun handleStatus(status: Status) {
        if (status.getStatus() === StatusCode.SUCCESS) {
            // Now you can get the transaction manager and log in.
        } else {
            // Initialization failed, look at the status code and message to determine why.
        }
    }
}

suspend fun psdkConnectToNewDevice(context: Context) {
    // Create PaymentSdk
    mPaymentSdk = PaymentSdk.create(context)
    val current = mPaymentSdk.getDeviceInformation()
    if (!current) {
        // there is no known last device
        return
    }
    // Forget current device
    mPaymentSdk.UseDevice(current, false)
    // Specify the connection type and address
    val paramMap: HashMap<String, String> = hashMapOf(
        PsdkDeviceInformation.DEVICE_CONNECTION_TYPE_KEY to "tcpip",
        PsdkDeviceInformation.DEVICE_ADDRESS_KEY to "192.168.1.10"
    )
    mPaymentSdk.initializeFromValues(mCommerceListenerAdapter, paramMap)
}

private fun getStarted() {
    val job = Job()
    val scope = CoroutineScope(Dispatchers.IO + job)
    scope.launch {
        // first connection
        psdkInit(false, false, context)
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
// Overridden from the VFICommerceListenerAdapter
override func handle(_ status: VFIStatus?) {
    if (VFIStatusCodeSuccess == status?.getStatus()) {
        // Now you can get the transaction manager and log in.
    } else {
        // Initialization failed, look at the status code and message to determine why.
    }
}

func psdkConnectToNewDevice() {
    // Create PaymentSdk
    let sdk = VFIPaymentSdk.createPaymentSdk()
    let adapter = CommerceListenerAdapter(listener: self)

    if let current = sdk.getDeviceInformation() {
        // Forget current device
        sdk.useDevice(current, saveDevice: false)
    } else {
        // there is no known last device
        return
    }
    let connectConfig = [
        // Specify the connection type and address
        VFIPsdkDeviceInformationDEVICECONNECTIONTYPEKEY: "tcpip",
        VFIPsdkDeviceInformationDEVICEADDRESSKEY: "192.168.1.10"]
    sdk.initialize(fromValues: adapter, config: connectConfig)
}
```

{% endcode %}
{% endtab %}

{% tab title="C#" %}

<pre class="language-csharp" data-line-numbers data-full-width="true"><code class="lang-csharp"><strong> using PaymentSDK;
</strong>using PaymentSdkUi;

private readonly PaymentSdk payment_sdk_;
private readonly TransactionManager transaction_manager;

// EventHandler
sdk_payment_event_handler_ = async (sender, args) =>
{
    if (args.Status == StatusCode.SUCCESS)
    {
        payment_sdk_.TransactionManager.HandleBasketEvent += basket_event_handler_;
        // Subscribe to other events
        // Now you can log in.
    }
    else
    {
        // Initialization failed, look at the status code and message to determine why.
    }
};

void PsdkConnectToNewDevice()
{
    // Create PaymentSdk
    payment_sdk_ = new PaymentSdk();

    var current = payment_sdk_.DeviceInformation;
    if (current == null)
    {
        // There is no known last device.
        return;
    }
    // Forget current device
    payment_sdk_.UseDevice(current, false);
    // Specify the connection type and address
    var param_map = new Windows.Foundation.Collections.StringMap
    {
        {PsdkDeviceInformation.DEVICE_CONNECTION_TYPE_KEY, "tcpip"},
        {PsdkDeviceInformation.DEVICE_ADDRESS_KEY, "192.168.1.10"}
    };
    payment_sdk_.InitializeFromValues(param_map);
}
</code></pre>

{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
class CommerceAdapter : public verifone_sdk::CommerceListenerAdapter {
 public:
  ~CommerceAdapter() override = default;

  void handleStatus(const std::shared_ptr<verifone_sdk::Status>& status) override {
    if (status->getStatus() == verifone_sdk::StatusCode::SUCCESS) {
      // Now you can get the transaction manager and log in.
    } else {
      // Initialization failed, look at the status code and message to determine why.
    }
  }
};


void psdkConnectToNewDevice() {
  // Create PaymentSdk
  payment_sdk_ = verifone_sdk::PaymentSdkInterface::create(
      verifone_sdk::CreatePlatformContextLinux(std::nullopt));
  auto adapter_ = std::make_shared<CommerceAdapter>();

  auto current = payment_sdk_->getDeviceInformation();
  if (!current) {
    // There is no known last device.
    return;
  }
  // Forget current device
  payment_sdk_->UseDevice(*current, false);

  // Specify the connection type and address
  std::unordered_map<std::string, std::string> param_map{
      {verifone_sdk::PsdkDeviceInformation::DEVICE_ADDRESS_KEY, "192.168.1.10"},
      {verifone_sdk::PsdkDeviceInformation::DEVICE_CONNECTION_TYPE_KEY, "tcpip"}};

  payment_sdk_->initializeFromValues(adapter_, param_map);
}
```

{% endcode %}
{% endtab %}

{% tab title=".NET" %}
{% code lineNumbers="true" %}

```aspnet
// To be defined
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="warning" %}
**Todo:** Provide link to instructions when initialization fails for requiring configuration.
{% endhint %}

{% hint style="info" %}
TransactionManager.getTransactionManager() is no longer available. Please see Migration from PSDK 2.x to 3.x.
{% endhint %}

{% hint style="info" %}
**See also**

* [PaymentSdk.getTransactionManager(…)](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdk#gettransactionmanager)
* [Threads of Execution](#threads-of-execution)
* [When the status code is non-zero after calling a method](/psdk-latest/psdk-user-guide-and-training/psdk-user-guide/error_handling.md)
  {% endhint %}

### Scanning For Devices on the Network <a href="#scanning-for-devices-on-the-network" id="scanning-for-devices-on-the-network"></a>

During PSDK initialization, the desired device must first be located in the network via network scanning. Shown below are the three possible network configurations for scanning.

1. Discovery: Uses Zero-configuration networking to identify devices on the network which can be connected.
2. Static: The scanner attempts to find the device address specified in the initialization parameters. If the designated device is not found after two attempts, then scanning is ended and an error is returned indicating that the device was not found.
3. Dynamic: This is the default network configuration if one is not specified in the initialization parameters. It differs from static mode in that if the preferred address is not found, the scanner cycles through the determined address range to find a device to connect to, while also retrying the preferred address several times.

{% hint style="info" %}
Example of discovery API usage can be found in C++ application (available for Linux/Windows).

{% tabs %}
{% tab title="Java" %}
{% code overflow="wrap" lineNumbers="true" %}

```java
public void performAction(JavaConsole console) {
    HashMap<String, String> paramMap = new HashMap<>();
    paramMap.put(PsdkInitializationConstants.NETWORK_CONFIGURATION_KEY,
                 PsdkInitializationConstants.NETWORK_CONFIGURATION_SERVICE_DISCOVERY_VALUE);
    DeviceScannerInterface scanner = console.mContext.psdk().getDeviceScanner();
    console.mPool.submit(() -> {
        scanner.StartScan(console.mDeviceScanListener, paramMap);
    });
    console.mDeviceScanListener.AwaitScanEnded();
    if (console.mDeviceScanListener.mScanEndedCount == 0) {
        // timed out, abort the scan
        console.mPool.submit(() -> {
            scanner.EndScan();
        });
    }
    int foundCount = console.mDeviceScanListener.mDeviceFoundCount;
    if (foundCount > 0) {
        log("Please select from the list of devices");
        Integer devices = getIntInput(null);
        if (devices == null) {
            log("No input received");
        } else {
            PsdkDeviceInformation device = console.mDeviceScanListener.mDevices[devices];
            paramMap.put(TransactionManager.DEVICE_PROTOCOL_KEY, device.getPaymentProtocol());
            if (device.getIsListening()) {
                paramMap.put(TransactionManager.DEVICE_LISTEN_KEY,
                             TransactionManager.ENABLED_VALUE);
            }
            paramMap.put(PsdkDeviceInformation.DEVICE_CONNECTION_TYPE_KEY, "tcpip");
            paramMap.put(PsdkDeviceInformation.DEVICE_ADDRESS_KEY, device.getAddress());
            console.initializePsdk(paramMap, true);
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code overflow="wrap" lineNumbers="true" %}

```cpp
param_map.insert({verifone_sdk::PsdkInitializationConstants::NETWORK_CONFIGURATION_KEY,
                verifone_sdk::PsdkInitializationConstants::NETWORK_CONFIGURATION_SERVICE_DISCOVERY_VALUE});
if (!scanner_)  scanner_ = verifone_sdk::DeviceScannerInterface::Create(sdk_);
auto listener = std::make_shared<DeviceScanListener>(scanner_);
scanner_->StartScan(listener, param_map);
listener->AwaitScanEnded();
// check if device scan timed out or reached end of range
if (listener->scan_ended_count_ == 0) {
// timed out - abort the scan
scanner_->EndScan();
}
auto found_count = listener->device_found_notification_count_.load();
std::cout << kGreen << "Device discovery completed:  found " << found_count << " devices."
    << kDefault << std::endl;
if (found_count > 0) {
std::cout << kBlue << "Please select from the following list of devices " << std::endl;
std::cout << "(Or choose 'incoming' for listening mode)" << kDefault << std::endl;
int device_index = 0;
auto devices = listener->devices_found_;
for (auto device : devices) {
  std::cout << kBlue << device_index << " - " << device.first << ":"
      << device.second << ":" << kDefault << std::endl;
  device_index++;
}
std::optional<int> selection;
bool valid_selection = false;
while (!valid_selection) {
  selection = GetIntInput("Select entry:");
  if ((selection >= 0) && (selection < devices.size())) {
    valid_selection = true;
  }
}
// set DEVICE_PROTOCOL_KEY
param_map.insert({verifone_sdk::TransactionManager::DEVICE_PROTOCOL_KEY,
      devices[*selection].second});
if (devices[*selection].first == kIncomingConnection) {
  param_map.insert({verifone_sdk::TransactionManager::DEVICE_LISTEN_KEY,
      verifone_sdk::TransactionManager::ENABLED_VALUE});
  param_map.insert({verifone_sdk::PsdkDeviceInformation::DEVICE_CONNECTION_TYPE_KEY, "tcpip"});
}
else {
  param_map.insert({verifone_sdk::PsdkDeviceInformation::DEVICE_ADDRESS_KEY, devices[*selection].first});
  param_map.insert({verifone_sdk::PsdkDeviceInformation::DEVICE_CONNECTION_TYPE_KEY, "tcpip"});
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

For full example please see [Reference Applications](https://github.com/iKettles/verifone-migration-test/blob/main/psdk/reference_applications.html#reference-applications).
{% endhint %}

### Create a Commerce Listener <a href="#create-a-commerce-listener" id="create-a-commerce-listener"></a>

A listener must be created so that the [Paymentsdk](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdk) Initialize and Teardown status are available for the POS. It also processes various events derived from [CommerceEvent](https://docs.verifone.com/psdk-api-latest/android-java/index/commerceevent). There must be at least one valid listener while a session is open, the last listener cannot be removed during this state. The listener must be passed to [PaymentSdk.initialize(…)](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdkbase#initialize), [PaymentSdk.initializeFromValues(…)](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdkbase#initializefromvalues) and [PaymentSdk.initializeFromFile(…)](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdkbase#initializefromfile)

Create the [CommerceListenerAdapter](https://app.gitbook.com/s/yzyMJNwlQJZoQOIC8vZ8/api/moduleindex-4/_commerce_listener_adapter_8java). The POS can override handle methods that it is interested in. The event type can be determined by calling the [CommerceEvent.getType()](https://docs.verifone.com/psdk-api-latest/android-java/index/commerceevent#gettype) method.

{% tabs fullWidth="false" %}
{% tab title="Java" %}
{% code lineNumbers="true" fullWidth="true" %}

```java
private final CommerceListenerAdapter mCommerceListenerAdapter = new CommerceListenerAdapter() {
    // Override only the methods that the POS is interested in.
    @Override
    public void handleStatus(Status status) {

    }

    @Override
    public void handleCommerceEvent(CommerceEvent event) {

    }

    @Override
    public void handleTransactionEvent(TransactionEvent event) {

    }

    ...

};
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
 private val mCommerceListenerAdapter = object : CommerceListenerAdapter() {
    // Override only the methods that the POS is interested in.
    override fun handleStatus(status: Status) {

    }

    override fun handleCommerceEvent(event: CommerceEvent) {

    }

    override fun handleTransactionEvent(event: TransactionEvent) {

    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
class PaymentSdkListener: VFICommerceListenerAdapter {
    // Override only the methods that the POS is interested in from the VFICommerceListenerAdapter.
    override func handle(_ status: VFIStatus?) {

    }

    override func handle(_ event: VFICommerceEvent?) {

    }

    override func handle(_ event: VFITransactionEvent?) {

    }

    ...
}
```

{% endcode %}
{% endtab %}

{% tab title="C#" %}

<pre class="language-csharp" data-line-numbers data-full-width="true"><code class="lang-csharp"><strong>private readonly EventHandler&#x3C;Status> sdk_payment_event_handler_;
</strong>private readonly EventHandler&#x3C;BasketEvent> basket_event_handler_;
private readonly EventHandler&#x3C;CommerceEvent> commerce_event_handler_;
private readonly EventHandler&#x3C;ReconciliationEvent> reconcilliation_event_handler_;
private readonly EventHandler&#x3C;TransactionEvent> transaction_event_handler_;
// Declare the remaining event handlers

sdk_payment_event_handler_ = async (sender, args) =>
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
        () => HandleEvent(args));
};

basket_event_handler_ = async (sender, args) =>
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
        () => HandleEvent(args));
};

commerce_event_handler_ = async (sender, args) =>
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
        () => HandleEvent(args));
};

transaction_event_handler_ = async (sender, args) =>
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
        () => HandleEvent(args));
};

reconciliation_event_handler_ = async (sender, args) =>
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
        () => HandleEvent(args));
};

// Continue creating the remaining event handlers

// Construct event handler for initialization
sdk_payment_event_handler_ = async (sender, args) =>
{
    if (args.Status == StatusCode.SUCCESS) {
        // Initialization succeeded, attach other event handlers.
        payment_sdk_.HandlePaymentSdkEvent += sdk_payment_event_handler_;
        payment_sdk_.HandleBasketEvent += basket_event_handler_;
        payment_sdk_.HandleCommerceEvent += commerce_event_handler_;
        payment_sdk_.HandleTransactionEvent += transaction_event_handler_;
        payment_sdk_.HandleReconciliationEvent += reconcilliation_event_handler_;
        // Attach the remaining event handlers.
    } // Else initialization failed.
};
</code></pre>

{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
class CommerceAdapter : public verifone_sdk::CommerceListenerAdapter {
// Override only the methods that the POS is interested in.
 public:
  void handleStatus(const std::shared_ptr<verifone_sdk::Status> & status) override {
    // Process status
  }

  void handleCommerceEvent(const std::shared_ptr<verifone_sdk::CommerceEvent>& event) override {
    // Process event
  }

  void handleTransactionEvent(const std::shared_ptr<verifone_sdk::TransactionEvent>& event) override {
    // Process event
  }

    ...

}
```

{% endcode %}
{% endtab %}

{% tab title=".NET" %}
{% code lineNumbers="true" %}

```aspnet
//Inherit and implement the methods in CommerceListener2
public class Listener : CommerceListener2 {

    public void HandleStatus(Status status) {
        // Process status
    }

    public void HandleCommerceEvent(CommerceEvent sdk_event) {
        // Process event
    }

    public void HandleDeviceManagementEvent(DeviceManagementEvent sdk_event) {
        // Process event
    }

      ...
}

```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}

* The Payment SDK does not keep the listeners from being destroyed. It is the\
  responsibility of the POS to maintain a valid reference to the listeners while they need to be active.
* CommerceListener and PsdkStatusListener are being deprecated.\
  Please see Migration from CommerceListener and PsdkStatusListener to [CommerceListenerAdapter](https://app.gitbook.com/s/yzyMJNwlQJZoQOIC8vZ8/api/moduleindex-4/_commerce_listener_adapter_8java).
  {% endhint %}

{% hint style="info" %}
**See also**:

* [CommerceListenerAdapter](https://app.gitbook.com/s/yzyMJNwlQJZoQOIC8vZ8/api/moduleindex-4/_commerce_listener_adapter_8java)
  {% endhint %}

## Login / Logout <a href="#id6" id="id6"></a>

After initialization is complete, the transaction manager can be retrieved. Once the transaction manager is obtained and bound, it’s time to login. The login and logout calls should match the presence of the cashier at the station, when the cashier logs in to the device, call [TransactionManager.loginWithCredentials()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#loginwithcredentials-logincredentials), if the cashier logs out or is logged out automatically, call [TransactionManager.logout()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#logout). If there is no authentication mechanism for the cashier, then login should be called when the device wakes, and logout when the device sleeps.

{% tabs fullWidth="false" %}
{% tab title="Java" %}
{% code lineNumbers="true" fullWidth="true" %}

```java
private final CommerceListenerAdapter mCommerceListenerAdapter = new CommerceListenerAdapter() {
    @Override
    public void handleTransactionEvent(TransactionEvent event) {
        if (TransactionEvent.LOGIN_COMPLETED.equals(event.getType())) {
          if (event.getStatus() == StatusCode.SUCCESS) {
              // Login is complete.
          } else {
              // Handle failure by examining the status code and message.
          }
        }
    }
}

// ...
// The PaymentSdk must be initialized to login and the CommerceListenerAdapter
// should be provided in the initialize APIs.

// Username, password, shift ID and Merchant ID are optional. Providing the username
// links any following transactions to that user/cashier and can be
// useful for reporting.
LoginCredentials credentials = LoginCredentials.createWith2("username", null, null, null);
mPaymentSdk.getTransactionManager().loginWithCredentials(credentials);
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
 private val mCommerceListenerAdapter = object : CommerceListenerAdapter() {
    override fun handleTransactionEvent(event: TransactionEvent) {
        if (TransactionEvent.LOGIN_COMPLETED.equals(event.getType())) {
            if (event.getStatus() == StatusCode.SUCCESS) {
                // Login is complete.
            } else {
                // Handle failure by examining the status code and message.
            }
        }
    }
}

suspend fun psdkTransactionManagerLogin()
{
    // Username, password, shift ID and Merchant ID are optional. Providing the username
    // links any following transactions to that user/cashier and can be
    // useful for reporting.
    val credentials =
    LoginCredentials.createWith2("username", null, null, null)
    mPaymentSdk.getTransactionManager()!!.loginWithCredentials(credentials)
}

// ...
// The PaymentSdk must be initialized to login and the CommerceListenerAdapter
// should be provided in the initialize APIs.

// Perform the login using a co-routine.
val job = Job()
val scope = CoroutineScope(Dispatchers.IO + job)
scope.launch {
    psdkTransactionManagerLogin()
}
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
// Overridden from the VFICommerceListenerAdapter
override func handle(_ event: VFITransactionEvent?) {
    if (VFITransactionEventLOGINCOMPLETED == event?.getType()) {
        if (VFIStatusCodeSuccess == event?.getStatus()) {
            // Login is complete.
        } else {
            // Handle failure by examining the status code and message.
        }
    }
}

let adapter = VFICommerceListenerAdapter()

// ...
// The PaymentSdk must be initialized to login and the VFICommerceListenerAdapter
// should be provided in the initialize APIs.

// Username, password, shift ID and Merchant ID are optional. Providing the username
// links any following transactions to that user/cashier and can be
// useful for reporting.
let loginCredentials = VFILoginCredentials.createWith2("user", password: nil, shiftNumber: nil, merchantId: nil)
let result = sdk.getTransactionManager()?.login(with: loginCredentials)
```

{% endcode %}
{% endtab %}

{% tab title="C#" %}

<pre class="language-csharp" data-line-numbers data-full-width="true"><code class="lang-csharp"><strong> private readonly EventHandler&#x3C;TransactionEvent> transaction_event_handler_;
</strong>
private void HandleEvent(TransactionEvent event)
{
    if (event.Type.Value == TransactionEvent.LOGIN_COMPLETED)
    {
        if (event.Status == 0)
        {
            // Login is complete.
        }
        else
        {
            // Handle failure by examining the status code and message.
        }
    }
}

// ...
// Link event handler after initialization is complete
transaction_event_handler_ = async (sender, args) =>
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
        () => HandleEvent(args));
};
payment_sdk_.HandleTransactionEvent += transaction_event_handler_;

// Username, password, and shift ID are optional. Providing the username
// links any following transactions to that user/cashier and can be
// useful for reporting.
var credentials = new LoginCredentials();
credentials.UserId = "username";
var result = payment_sdk_.TransactionManager.LoginWithCredentials(credentials);
</code></pre>

{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
class CommerceAdapter: public verifone_sdk::CommerceListenerAdapter {
 public:
  void handleTransactionEvent(
      const std::shared_ptr<verifone_sdk::TransactionEvent>& event) override {
    if (event->getType() == verifone_sdk::TransactionEvent::LOGIN_COMPLETED) {
      if (event->getStatus() == verifone_sdk::StatusCode::SUCCESS) {
        // Login is complete.
      } else {
        // Handle failure by examining the status code and message.
      }
    }
  }
  // ...
}

// ...
// The PaymentSdk must be initialized to login and the CommerceListenerAdapter
// should be provided in the initialize APIs.

if (auto transaction_manager = psdk->getTransactionManager()) {
  // Username, password, shift ID and Merchant ID are optional. Providing the username
  // links any following transactions to that user/cashier and can be
  // useful for reporting.
  auto credentials = verifone_sdk::LoginCredentials::createWith2("user", null, null, null);
  auto status = transaction_manager_->loginWithCredentials(credentials);
}
```

{% endcode %}
{% endtab %}

{% tab title=".NET" %}
{% code lineNumbers="true" %}

```aspnet
public void HandleTransactionEvent(TransactionEvent sdk_event)
{
    if (sdk_event.Type == TransactionEvent.LOGIN_COMPLETED)
    {
        if (sdk_event.Status == 0)
        {
            // Login is complete.
        }
        else
        {
            // Handle failure by examining the status code and message.
        }
    }
}

// Username, password, and shift ID are optional. Providing the username
// links any following transactions to that user/cashier and can be
// useful for reporting.
var credentials = LoginCredentials.Create();
credentials.UserId = "username";
credentials.Password = "password";
credentials.ShiftNumber = "shift";
var result = payment_sdk_.TransactionManager.LoginWithCredentials(credentials);
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% tabs fullWidth="false" %}
{% tab title="Java" %}
{% code lineNumbers="true" fullWidth="true" %}

```java
private final CommerceListenerAdapter mCommerceListenerAdapter = new CommerceListenerAdapter() {
    @Override
    public TransactionEventResponse handleTransactionEvent(TransactionEvent event) {
        if (TransactionEvent.LOGOUT_COMPLETED.equals(event.getType())) {
          if (event.getStatus() == StatusCode.SUCCESS) {
              // Logout is complete.
          } else {
              // Handle failure by examining the status code and message.
          }
        }
    }
}

// ...
// The PaymentSdk must be initialized and should be logged in already

// The listener provided during initialize will receive the event for logout
mPaymentSdk.getTransactionManager().logout();
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
 private val mCommerceListenerAdapter = object : CommerceListenerAdapter() {
    override fun handleTransactionEvent(event: TransactionEvent) :
                                        TransactionEventResponse {
        if (TransactionEvent.LOGOUT_COMPLETED.equals(event.getType())) {
            if (event.getStatus() == StatusCode.SUCCESS) {
                // Logout is complete.
            } else {
                // Handle failure by examining the status code and message.
            }
        }
    }
}

suspend fun psdkTransactionManagerLogout()
{
    // The listener provided during initialize will receive the event for logout
    mPaymentSdk.getTransactionManager()!!.logout()
}

// ...
// The PaymentSdk must be initialized and should be logged in already

// Perform the action using a co-routine.
val job = Job()
val scope = CoroutineScope(Dispatchers.IO + job)
scope.launch {
    psdkTransactionManagerLogout()
}
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
// Overridden from the VFICommerceListenerAdapter
override func handle(_ event: VFITransactionEvent?) {
    if (VFITransactionEventLOGOUTCOMPLETED == event?.getType()) {
        if (VFIStatusCodeSuccess == event?.getStatus()) {
            // Logout is complete.
        } else {
            // Handle failure by examining the status code and message.
        }
    }
}

let adapter = CommerceListenerAdapter(listener: self)

// ...
// The PaymentSdk must be initialized and should be logged in already

// The listener provided during initialize will receive the event for logout
sdk!.getTransactionManager()!.logout()
```

{% endcode %}
{% endtab %}

{% tab title="C#" %}

<pre class="language-csharp" data-line-numbers data-full-width="true"><code class="lang-csharp"><strong> private void HandleEvent(TransactionEvent event)
</strong>{
    if (event.Type == TransactionEvent.LOGOUT_COMPLETED)
    {
        if (event.Status == StatusCode.SUCCESS)
        {
            // Logout is complete.
        }
        else
        {
            // Handle failure by examining the status code and message.
        }
    }
}

// The listener provided during initialize will receive the event for logout
payment_sdk_.TransactionManager.Logout();
</code></pre>

{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
class CommerceAdapter: public verifone_sdk::CommerceListenerAdapter {
 public:
  void handleTransactionEvent(const std::shared_ptr<verifone_sdk::TransactionEvent>& event) override {
    if (event->getType() == verifone_sdk::TransactionEvent::LOGOUT_COMPLETED) {
      if (event->getStatus() == verifone_sdk::StatusCode::SUCCESS) {
        // Logout is complete.
      } else {
        // Handle failure by examining the status code and message.
      }
    }
  }
  // ...
}

// ...
// The PaymentSdk must be initialized and should be logged in already

if (auto transaction_manager = psdk->getTransactionManager()) {
  // The listener provided during initialize will receive the event for logout
  transaction_manager->logout();
}
```

{% endcode %}
{% endtab %}

{% tab title=".NET" %}
{% code lineNumbers="true" %}

```aspnet
public void HandleTransactionEvent(TransactionEvent sdk_event)
{
    if (sdk_event.Type == TransactionEvent.LOGOUT_COMPLETED)
    {
        if (sdk_event.Status == 0)
        {
            // Logout is complete.
        }
        else
        {
            // Handle failure by examining the status code and message.
        }
    }
}

// The listener provided during initialize will receive the event for logout
payment_sdk_.TransactionManager.Logout();
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**See also**

* [TransactionManager.loginWithCredentials(…)](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#loginwithcredentials-logincredentials)
* [TransactionManager.logout()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#logout)
* [PSDK State Transitions](#state-matters)
  {% endhint %}

## Session <a href="#session" id="session"></a>

### Start a Session <a href="#start-a-session" id="start-a-session"></a>

After login, it is necessary to start a session for the transaction. An instance of the [CommerceListenerAdapter](https://app.gitbook.com/s/yzyMJNwlQJZoQOIC8vZ8/api/moduleindex-4/_commerce_listener_adapter_8java) which was passed in the [PaymentSdk.initialize()](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdkbase#initialize) method acts as the listener for events passed back from [TransactionManager](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager). Building on the example above, after login is completed we can start the session. Once we have a session open, items can be added to the POI display and payments can be processed. The session is also needed in order to do certain operations like stored value operations, [TransactionManager.requestCardData()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#requestcarddata-string-arraylistpresentationmethod), [TransactionManager.requestCardData2()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#requestcarddata2-cardacquisitionrequest), [TransactionManager.requestCardDataWithAids()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#requestcarddatawithaids-string-arraylistpresentationmethod-arrayliststring), [TransactionManager.requestCardToken()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#requestcardtoken) etc.

{% tabs fullWidth="false" %}
{% tab title="Java" %}
{% code lineNumbers="true" fullWidth="true" %}

```java
private final CommerceListenerAdapter mCommerceListenerAdapter = new CommerceListenerAdapter() {
    @Override
    public void handleTransactionEvent(TransactionEvent event) {
        if (CommerceEvent.SESSION_STARTED.equals(event.getType())) {
          if (event.getStatus() == StatusCode.SUCCESS) {
              // Session is started
          } else {
              // Handle failure by examining the status code and message.
          }
        }
    }
    // ...
}

// ...
// Must be logged in to start a session

// Using the same listener for initialize.
// The transaction can also be modified before sending to configure the currency,
// change the shift ID or cashier ID, and configure manual prompt options to be
// used as the default for any following payments.
if (transactionManager.startSession2(Transaction.create())) {
    // Session start was sent successfully
} else {
    // Session start failed to send
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
 private val mCommerceListenerAdapter = object : CommerceListenerAdapter() {
    override fun handleTransactionEvent(event: TransactionEvent) {
        if (TransactionEvent.SESSION_STARTED.equals(event.getType())) {
            if (event.getStatus() == StatusCode.SUCCESS) {
                // Session is started
            } else {
                // Handle failure by examining the status code and message.
            }
        }
    }
}

let adapter = CommerceListenerAdapter()

suspend fun psdkTransactionManagerStartSession()
{
    // Using the same listener for initialize.
    // The transaction can also be modified before sending to configure the currency,
    // change the shift ID or cashier ID, and configure manual prompt options to be
    // used as the default for any following payments.
    if (mPaymentSdk.getTransactionManager()!!.startSession2(Transaction.create())) {
        // Session start was sent successfully
    } else {
        // Session start failed to send
    }
}

// ...
// Must be logged in to start a session

// Perform the login using a co-routine.
val job = Job()
val scope = CoroutineScope(Dispatchers.IO + job)
scope.launch {
    psdkTransactionManagerStartSession()
}
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
// Overridden from the VFICommerceListenerAdapter
override func handle(_ event: VFITransactionEvent?) {
    if (VFICommerceEventSESSIONSTARTED == event?.getType()) {
        if (VFIStatusCodeSuccess == event?.getStatus()) {
            // Session is started
        } else {
            // Handle failure by examining the status code and message.
        }
    }
}

let adapter = CommerceListenerAdapter(listener: self)

// ...
// Must be logged in to start a session

// Using the same listener for initialize.
// The transaction can also be modified before sending to configure the currency,
// change the shift ID or cashier ID, and configure manual prompt options to be
// used as the default for any following payments.
let status = sdk.getTransactionManager()?.startSession2(VFITransaction.create()) ?? false
if status {
    // Session start was sent successfully
} else {
    // Session start failed to send
}
```

{% endcode %}
{% endtab %}

{% tab title="C#" %}

<pre class="language-csharp" data-line-numbers data-full-width="true"><code class="lang-csharp"><strong> private readonly EventHandler&#x3C;TransactionEvent> transaction_event_handler_;
</strong>
private void HandleEvent(TransactionEvent event)
{
    if (event.Type == TransactionEvent.SESSION_STARTED)
    {
        if (event.Status == StatusCode.SUCCESS)
        {
            // Session is started
        }
        else
        {
            // Handle failure by examining the status code and message.
        }
    }
}

// ...
// Link event handler after initialization is complete
transaction_event_handler_ = async (sender, args) =>
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
        () => HandleEvent(args));
};
// It's possible to use the same transaction_event_handler_ from Login if desired.
payment_sdk_.HandleTransactionEvent += transaction_event_handler_;

// ...
// Must be logged in to start a session

// The transaction can also be modified before sending to configure the currency,
// change the shift ID or cashier ID, and configure manual prompt options to be
// used as the default for any following payments.
if (payment_sdk_.TransactionManager.StartSession2(new Transaction()))
{
    // Session start was sent successfully
}
else
{
    // Session start failed to send
}
</code></pre>

{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
class CommerceAdapter: public verifone_sdk::CommerceListenerAdapter {
 public:
  void handleTransactionEvent(const std::shared_ptr<verifone_sdk::TransactionEvent>& event)
      override {
    if (event->getType() == verifone_sdk::TransactionEvent::SESSION_STARTED) {
      if (event->getStatus() == verifone_sdk::StatusCode::SUCCESS) {
        // Session is started
      } else {
        // Handle failure by examining the status code and message.
      }
    }
  }
  // ...
}

// ...
// Must be logged in to start a session

// The same listener from initialize can be used.
// The transaction can also be modified before sending to configure the currency,
// change the shift ID or cashier ID, and configure manual prompt options to be
// used as the default for any following payments.
auto transaction_manager = psdk->getTransactionManager();
if (transaction_manager &&
    transaction_manager->startSession2(verifone_sdk::Transaction::create())) {
  // Session start was sent successfully
} else {
  // Session start failed to send
}
```

{% endcode %}
{% endtab %}

{% tab title=".NET" %}
{% code lineNumbers="true" %}

```aspnet
private readonly EventHandler<TransactionEvent> transaction_event_handler_;
public void HandleTransactionEvent(TransactionEvent sdk_event)
{
    if (sdk_event.Type == CommerceEvent.SESSION_STARTED)
        {
            if (sdk_event.Status == 0)
            {
                // Session is started
            }
            else
            {
                // Handle failure by examining the status code and message.
            }
        }
}


// ...
// Must be logged in to start a session

// The transaction can also be modified before sending to configure the currency,
// change the shift ID or cashier ID, and configure manual prompt options to be
// used as the default for any following payments.
if (payment_sdk_.TransactionManager.StartSession(new Transaction()))
{
    // Session start was sent successfully
}
else
{
    // Session start failed to send
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**See also**

* [TransactionManager.startSession2(…)](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#startsession2-transaction)
* [TransactionManager.endSession()](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#endsession)
* [PSDK State Transitions](#state-matters)
  {% endhint %}

### Best practices to manage session in various use cases. <a href="#best-practices-to-manage-session-in-various-use-cases" id="best-practices-to-manage-session-in-various-use-cases"></a>

1. In a 1-1 ECR-PED scenario, ECR can START a session, process however many tenders that are needed to satisfy that transaction amount and then FINISH the session.
2. In a multiple ECR to multiple PED scenario, any ECR that wants to do a transaction with a specific PED should START a session with that PED, process however many tenders that are needed to satisfy that transaction amount and then FINISH the session.
3. In a single ECR to multiple PED scenario, the ECR can create multiple instances of PSDK with each instance connecting to different PEDs. Each PSDK instance connected to a specific PED can START the session, process however many tenders that are needed to satisfy that transaction amount and then FINISH the session (OR) A single instance of PSDK, establish connection with specific PED, Login and START the session, complete the transaction, FINISH the session, Logout and TearDown the connection. If it wants to communicate with different PED repeat the same process.

***

{% columns %}
{% column width="25%" %}

<figure><img src="/files/9ezXRridnCaI1awLr3X9" alt="" width="150"><figcaption></figcaption></figure>
{% endcolumn %}

{% column width="25%" %}

{% endcolumn %}

{% column width="16.66666666666666%" %}
**Version:**\
**Date:**
{% endcolumn %}

{% column width="33.333333333333336%" %} <code class="expression">space.vars.psdk\_version</code>\ <code class="expression">space.vars.psdk\_date</code>
{% endcolumn %}
{% endcolumns %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.verifone.com/psdk-latest/psdk-user-guide-and-training/psdk-user-guide/pos_guide.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
