> 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/peripherals_guide.md).

# Peripherals Integration Guide

## Printing <a href="#id3" id="id3"></a>

To print, the application must first bind to the print service, then once bound, can use the APIs from the print service.

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

```
participant POS order 10
participant "Print Service" as PrintService order 20
activate POS
|||
POS ->> PrintService: Bind to service
activate PrintService
POS <<- PrintService: Service connected
...
POS ->> PrintService: Print
POS <<- PrintService: Print started
POS <<- PrintService: Print complete
...
POS ->> PrintService: Unbind
deactivate PrintService
|||
@enduml" %}
```

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

```java
/** Manages the connection to the service. */
private final ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        // We must use this to expose the service methods.
        mPrintService = IDirectPrintService.Stub.asInterface(iBinder);
        Log.d("PrintConnection", "Print service connected.");
    }
    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        Log.d("PrintConnection", "Print service disconnected.");
        mPrintService = null;
    }
};


/** Binds to the direct print service by dynamically locating it in the system. */
private static boolean bindToDirectPrintService(Context context, ServiceConnection serviceConnection) {
    Intent directPrintImplicitIntent = new Intent("com.verifone.intent.action.DIRECT_PRINT");
    directPrintImplicitIntent.addCategory("com.verifone.intent.category.DIRECT_PRINT");
    Intent directPrintIntent = createExplicitFromImplicitIntent(context, directPrintImplicitIntent);
    return directPrintIntent != null && context.bindService(directPrintIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}

/** Used when binding to the print service to turn a dynamic intent into an explicit intent. */
@Nullable
private static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
    //Retrieve all services that can match the given intent
    PackageManager pm = context.getPackageManager();
    List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);

    //Make sure only one match was found
    if (resolveInfo == null || resolveInfo.size() != 1) {
        Log.e(TAG, "Could not find service for intent with action " + implicitIntent.getAction() + ".");
        return null;
    }

    //Get component info and create ComponentName
    ResolveInfo serviceInfo = resolveInfo.get(0);
    String packageName = serviceInfo.serviceInfo.packageName;
    String className = serviceInfo.serviceInfo.name;
    ComponentName component = new ComponentName(packageName, className);

    //Create a new intent. Use the old one for extras and such reuse
    Intent explicitIntent = new Intent(implicitIntent);
    //Set the component to be explicit
    explicitIntent.setComponent(component);
    return explicitIntent;
}
```

{% endcode %}
{% endtab %}

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

```kotlin
 /** Manages the connection to the service.  */
private val mServiceConnection = object : ServiceConnection {
    override fun onServiceConnected(componentName: ComponentName, iBinder: IBinder) {
        // We must use this to expose the service methods.
        mPrintService = IDirectPrintService.Stub.asInterface(iBinder)
        Log.d("PrintConnection", "Print service connected.")
    }

    override fun onServiceDisconnected(componentName: ComponentName) {
        Log.d("PrintConnection", "Print service disconnected.")
        mPrintService = null
    }
}


/** Binds to the direct print service by dynamically locating it in the system.  */
private fun bindToDirectPrintService(
    context: Context,
    serviceConnection: ServiceConnection
): Boolean {
    val directPrintImplicitIntent = Intent("com.verifone.intent.action.DIRECT_PRINT")
    directPrintImplicitIntent.addCategory("com.verifone.intent.category.DIRECT_PRINT")
    val directPrintIntent = createExplicitFromImplicitIntent(context, directPrintImplicitIntent)
    return directPrintIntent != null && context.bindService(
        directPrintIntent,
        serviceConnection,
        Context.BIND_AUTO_CREATE
    )
}

/** Used when binding to the print service to turn a dynamic intent into an explicit intent.  */
@Nullable
private fun createExplicitFromImplicitIntent(context: Context, implicitIntent: Intent): Intent? {
    //Retrieve all services that can match the given intent
    val pm = context.getPackageManager()
    val resolveInfo = pm.queryIntentServices(implicitIntent, 0)

    //Make sure only one match was found
    if (resolveInfo == null || resolveInfo!!.size != 1) {
        Log.e(TAG, "Could not find service for intent with action " + implicitIntent.action + ".")
        return null
    }

    //Get component info and create ComponentName
    val serviceInfo = resolveInfo!!.get(0)
    val packageName = serviceInfo.serviceInfo.packageName
    val className = serviceInfo.serviceInfo.name
    val component = ComponentName(packageName, className)

    //Create a new intent. Use the old one for extras and such reuse
    val explicitIntent = Intent(implicitIntent)
    //Set the component to be explicit
    explicitIntent.component = component
    return explicitIntent
}
```

{% endcode %}
{% endtab %}

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

```swift
/** Manages the connection to the service. */
```

{% endcode %}
{% endtab %}

{% tab title="C#" %}

<pre class="language-csharp" data-line-numbers data-full-width="true"><code class="lang-csharp"><strong> /** Manages the connection to the service. */
</strong></code></pre>

{% endtab %}
{% endtabs %}

Here’s a sample print listener that receives back the different events for printing.

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

```java
/**
 * The listener which receives the callbacks from the print service for print job status
 * updates.
 */
private final IDirectPrintListener mPrintListener = new IDirectPrintListener.Stub() {
    /** Called when a print job has moved from the queue and is being processed. */
    @Override
    public void started(String printId) throws RemoteException {

    }

    /** Called when the print job cannot continue, but could be resumed later. */
    @Override
    public void block(String printId, String errorMessage) throws RemoteException {

    }

    /** Called when the print job has finished being cancelled. This is the final message. */
    @Override
    public void cancel(String printId) throws RemoteException {

    }

    /** Called when the print job has failed, and cannot be resumed. This is the final message. */
    @Override
    public void failed(String printId, String errorMessage) throws RemoteException {

    }

    /** Called when the print job is complete. */
    @Override
    public void complete(String printId) throws RemoteException {

    }
};
```

{% endcode %}
{% endtab %}

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

```kotlin
 /**
* The listener which receives the callbacks from the print service for print job status
* updates.
*/
private val mPrintListener = object : IDirectPrintListener.Stub() {
    /** Called when a print job has moved from the queue and is being processed.  */
    @Throws(RemoteException::class)
    fun started(printId: String) {

    }

    /** Called when the print job cannot continue, but could be resumed later.  */
    @Throws(RemoteException::class)
    fun block(printId: String, errorMessage: String) {

    }

    /** Called when the print job has finished being cancelled. This is the final message.  */
    @Throws(RemoteException::class)
    fun cancel(printId: String) {

    }

    /** Called when the print job has failed, and cannot be resumed. This is the final message.  */
    @Throws(RemoteException::class)
    fun failed(printId: String, errorMessage: String) {

    }

    /** Called when the print job is complete.  */
    @Throws(RemoteException::class)
    fun complete(printId: String) {

    }
}
```

{% endcode %}
{% endtab %}

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

```swift
/**
  * The listener which receives the callbacks from the print service for print job status
  * updates.
  */
```

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

An example of printing a receipt once the service is bound and the listener is set up.

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

```java
try {
    mPrintService.printString(mPrintListener, receipt.getAsHtml(), null, Printer.PRINTER_FULL_CUT);
} catch (RemoteException e) {
    e.printStackTrace();
}
```

{% endcode %}
{% endtab %}

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

```kotlin
try {
    mPrintService.printString(mPrintListener, receipt.getAsHtml(), null, Printer.PRINTER_FULL_CUT);
} catch (RemoteException e) {
    e.printStackTrace();
}
```

{% endcode %}
{% endtab %}

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

```swift
/** Code to be added */
```

{% endcode %}
{% endtab %}

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

```csharp
/** Code to be added */
```

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

***

## Barcode Scanning <a href="#barcode-scanning" id="barcode-scanning"></a>

Barcode scanning APIs are available for integrating if the BYOD device has a camera or when integrating off-device with e-series terminals that have a hardware barcode scanner. Currently these API’s are independent.

### Camera-based barcode scanning API <a href="#camera-based-barcode-scanning-api" id="camera-based-barcode-scanning-api"></a>

To setup the scanner first add a listener [PaymentSdk.initScanListener(scannerListener)](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdkbase#initscanlistener-scannerlistener) to receive the scan results. Then the scanning can be started using [PaymentSdk.startBarcodeScanner(attributes)](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdkbase#startbarcodescanner-hashmapstringobject). There are a few attributes like scan formats, display feed, scan area, front/rear camera etc that you can pass to the API. The scan formats are listed under the [ScannerBarcodeFormatEnum](https://docs.verifone.com/psdk-api-latest/android-java/index/scannerbarcodeformatenum) enumeration. For Android the attributes must contain an Activity or Fragment under the [ScannerConfiguration.ATTRIBUTE\_DISPLAY\_FEED\_PARENT](https://docs.verifone.com/psdk-api-latest/android-java/index/scannerconfiguration) key to work. The listener that was setup will receive the results as they are scanned.

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

* [PaymentSdk.initScanListener(scannerListener)](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdkbase#initscanlistener-scannerlistener)
* [PaymentSdk.startBarcodeScanner(attributes)](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdkbase#startbarcodescanner-hashmapstringobject)
  {% endhint %}

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

```java
private final ScannerListener mScannerListener = new ScannerListener() {
    @Override
    public void onBarcodeResult(String status, @Nullable HashMap<String, Object> attributes) {
        String barcode = "";
        String barcodeType = "";

        if (status == STATUS_BARCODE_DETECTED) {
            if (attributes.containsKey(ATTRIBUTE_BARCODE)) {
                barcode = (String) attributes.get(ATTRIBUTE_BARCODE);
            }
            if (attributes.containsKey(ATTRIBUTE_BARCODE_FORMAT)) {
                barcodeType = (String) attributes.get(ATTRIBUTE_BARCODE_FORMAT);
            }
            Log.d(TAG, "Scanner status:" + status + barcode + "-" + barcodeType);
        } else {
            Log.d(TAG, "Scanner status:" + status);
        }
    }
};

void initScanner() {
    mPsdk.initScanListener(mScannerListener);
    HashMap<String, Object> attributes = new HashMap<>();
    // Display the scanner at 90% of display view
    DisplayMetrics metrics = getResources().getDisplayMetrics();
    Rect rect = new Rect((int)((float)metrics.widthPixels * .1f),
            (int)((float)metrics.heightPixels * .1f),
            (int)((float)metrics.widthPixels * .9f),
            (int)((float)metrics.heightPixels * .9f));
    attributes.put(ScannerConfiguration.ATTRIBUTE_SCAN_AREA_LIMIT, rect);
    attributes.put(ScannerConfiguration.ATTRIBUTE_SET_DIRECTION, 2);
    attributes.put(ScannerConfiguration.ATTRIBUTE_ACTIVATE_LIGHT, false);
    attributes.put(ScannerConfiguration.ATTRIBUTE_PLAY_SOUND, true);
    attributes.put(ScannerConfiguration.ATTRIBUTE_DISPLAY_FEED_PARENT, this);
    // Optionally limit the barcode formats. Leaving this empty
    // will search for the default list of barcodes.
    ScannerBarcodeFormatEnum[] scanFormats = new ScannerBarcodeFormatEnum[] {
            ScannerBarcodeFormatEnum.UPCA,
            ScannerBarcodeFormatEnum.UPCE,
            ScannerBarcodeFormatEnum.QRCODE
    };
    attributes.put(ScannerConfiguration.ATTRIBUTE_SCANNING_FORMATS, scanFormats);
    mPsdk.startBarcodeScanner(attributes);
}

Following dependencies need to be added to the application’s build.gradle.
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation 'com.google.zxing:core:3.4.0'
    implementation 'androidx.preference:preference-ktx:1.1.1'
```

{% endcode %}
{% endtab %}

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

```kotlin
 private val mScannerListener = ScannerListener { status, attributes ->
    var barcode: String? = ""
    var barcodeType: String? = ""
    if (status === STATUS_BARCODE_DETECTED) {
        if (attributes.containsKey(ATTRIBUTE_BARCODE)) {
            barcode = attributes[ATTRIBUTE_BARCODE] as String?
        }
        if (attributes.containsKey(ATTRIBUTE_BARCODE_FORMAT)) {
            barcodeType = attributes[ATTRIBUTE_BARCODE_FORMAT] as String?
        }
        Log.d(TAG, "Scanner status:$status$barcode-$barcodeType")
    } else {
        Log.d(TAG, "Scanner status:$status")
    }
}

fun initScanner() {
    mPsdk.initScanListener(mScannerListener)
    val attributes: HashMap<String, Any> = HashMap()
    // Display the scanner at 90% of display view
    val metrics = resources.displayMetrics
    val rect = Rect(
            (metrics.widthPixels.toFloat() * .1f).toInt(),
            (metrics.heightPixels.toFloat() * .1f).toInt(),
            (metrics.widthPixels.toFloat() * .9f).toInt(),
            (metrics.heightPixels.toFloat() * .9f).toInt()
    )
    attributes[ScannerConfiguration.ATTRIBUTE_SCAN_AREA_LIMIT] = rect
    attributes[ScannerConfiguration.ATTRIBUTE_SET_DIRECTION] = 2
    attributes[ScannerConfiguration.ATTRIBUTE_ACTIVATE_LIGHT] = false
    attributes[ScannerConfiguration.ATTRIBUTE_PLAY_SOUND] = true
    attributes[ScannerConfiguration.ATTRIBUTE_DISPLAY_FEED_PARENT] = this
    // Optionally limit the barcode formats. Leaving this empty
    // will search for the default list of barcodes.
    val scanFormats = arrayOf(
            ScannerBarcodeFormatEnum.UPCA,
            ScannerBarcodeFormatEnum.UPCE,
            ScannerBarcodeFormatEnum.QRCODE
    )
    attributes[ScannerConfiguration.ATTRIBUTE_SCANNING_FORMATS] = scanFormats
    mPsdk.startBarcodeScanner(attributes)
}

Following dependencies need to be added to the application’s build.gradle.
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation 'com.google.zxing:core:3.4.0'
    implementation 'androidx.preference:preference-ktx:1.1.1'
```

{% endcode %}
{% endtab %}

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

```swift
```

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

To stop the scanner in Android there are few options

1. On the scanner UI, close button can be pressed.
2. If the POS wants to exit the scanner after a single scan, it could set the attribute [ScannerConfiguration.ATTRIBUTE\_CONTINUOUS\_SCAN](https://docs.verifone.com/psdk-api-latest/android-java/index/scannerconfiguration) to false when initiating the scanner.
3. [PaymentSdk.stopBarcodeScanner()](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdkbase#stopbarcodescanner) could be called based on an inactivity timeout that the POS can decide on.

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

```java
mPsdk.startBarcodeScanner(attributes);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
        mViewModel.mPaymentSdk.stopBarcodeScanner();
    }
}, 10000); // 10 seconds delay
```

{% endcode %}
{% endtab %}

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

```kotlin
 mPsdk.startBarcodeScanner(attributes)
val handler = Handler()
handler.postDelayed(java.lang.Runnable {
    mPsdk.stopBarcodeScanner()
}, 10000) // 10 seconds delay
```

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

### Hardware barcode scanner API <a href="#hardware-barcode-scanner-api" id="hardware-barcode-scanner-api"></a>

Hardware barcode scanner is available via an API in [ScannerManager](https://docs.verifone.com/psdk-api-latest/android-java/index/scannermanager). This interface is accessed using [PaymentSdk.getScannerManager()](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentsdkinterface#getscannermanager) and provides methods to start(optionally passing configuration) and stop the scanner. Scanning works asynchronously: Results of ScannerManager calls and every successful scan are returned to [CommerceListenerAdapter](https://app.gitbook.com/s/yzyMJNwlQJZoQOIC8vZ8/api/moduleindex-4/_commerce_listener_adapter_8java) via [ScannerStateEvent](https://docs.verifone.com/psdk-api-latest/android-java/index/scannerstateevent) and [ScannerDataEvent](https://docs.verifone.com/psdk-api-latest/android-java/index/scannerdataevent) respectively.

Limiting hardware barcode scanner to particular formats is WIP, thus third parameter to [BarcodeConfig](https://docs.verifone.com/psdk-api-latest/android-java/index/barcodeconfig) does not currently take effect. IMPORTANT: to receive [ScannerDataEvent](https://docs.verifone.com/psdk-api-latest/android-java/index/scannerdataevent) PSDK needs to be able to listen on “unsolicited notifications port”. By default, this port number is 5020 and can be modified via PsdkDeviceInformation::UNSOLICITED\_PORT\_KEY passed into InitializeFromValues routine. Please be aware that following a successful scanner configuration and start scan ([startScanner(BarcodeConfig) ](https://docs.verifone.com/psdk-api-latest/android-java/index/scannermanager#startscanner-barcodeconfig)is called), [stopScanner() ](https://docs.verifone.com/psdk-api-latest/android-java/index/scannermanager#stopscanner)must be called prior to configuring and starting the scanner again.

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

```java
 @Override // Overridden from the CommerceListenerAdapter
 public void handleScannerDataEvent(ScannerDataEvent event) {
    if (event.getStatus() == StatusCode.SUCCESS) {
         System.out.println("Scanned barcode data" + event.getDataAsString());
    }
 }

 @Override // Overridden from the CommerceListenerAdapter
 public void handleScannerStateEvent(ScannerStateEvent event) {
    if (event.getStatus() == StatusCode.SUCCESS) {
         System.out.println("Operation " + event.getType() + " completed with success");
    } else {
         System.out.println("Operation " + event.getType() + " completed with failure");
    }
}
...


BarcodeConfig scannerConfig = new BarcodeConfig(BarcodeTriggerModeEnum.TRIGGER_LEVEL,
                                                 BarcodeAutoBeepEnum.AUTOBEEP_ON,
                                                 null);
mPaymentSdk.getScannerManager().startScanner(scannerConfig);
```

{% endcode %}
{% endtab %}

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

```aspnet
 public override void HandleScannerDataEvent(ScannerDataEvent event) {
    if (event.Status == StatusCode.SUCCESS) {
        UpdateConsole("Scanned barcode data " + event.DataAsString);
    }
}
public override void HandleScannerStateEvent(ScannerStateEvent event) {
    if (event.Status == StatusCode.SUCCESS) {
        mw.UpdateConsole("Operation " + event.Type + " completed with success");
    } else
        mw.UpdateConsole("Operation " + event.Type + " completed with failure");
    }
}
...


// Configure scanner for edge trigger, beep on success and no limit on symbologies
var edge_trigger = BarcodeTriggerModeEnum.TRIGGER_EDGE;
var beep_on = BarcodeAutoBeepEnum.AUTOBEEP_ON;

var scanner_config = new BarcodeConfig(edge_trigger, beep_on, null);
mPaymentSdk.ScannerManager.StartScanner(scanner_config);
```

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

***

{% 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/peripherals_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.
