Local Integration with Android OS

Overview

This document serves as a complete reference guide for individuals seeking to utilize the Pine Labs Android PoSlib SDK. Its primary focus is to assist in the acceptance of payments, the processing of transactions, and the seamless return of payment results to the invoking Android application.

The billing system can be integrated Android platform in two different ways:

  • Off-Device Integration
  • On-Device Integration (App-to-App Integration)

In both integration approaches, the merchant billing application should use the PoSLib to communicate with the Pine Labs payment application via PoSBridge.

Also Merchant should ensure that PoSBridge is running on the Android payment terminal before initiating any transactions from the billing application.

Following sections provide detailed insights into both Off-Device and On-Device Integration processes.

Off-Device Integration

In this method, the merchant billing application, running on Android devices such as tablets or mobile phones, communicates with the Pine Labs payment application by integrating the Android PoSlib. The Android PoSlib facilitates communication with the Android payment application via PoSBridge.

The merchant billing application needs to integrate PoSlib to communicate with the PoSBridge application through PoSLib APIs. The message exchange between PoSLib and PoSBridge can occur over IP and Bluetooth (BT).

Screenshot

The following sequence diagram describes the transaction flow:

Screenshot

Merchant Billing Application should integrated PoSLib to perform transactions with Android Payment application

  1. Merchant Billing Application calls scan and connect IP and BT devices via PoSLib APIs.
  2. PoSLib send a device discovery request to connect with PoSBridge.
  3. PoSBridge responds with the device connected status.
  4. The merchant billing application fetches transaction details.
  5. The merchant billing application sends the transaction request to PoSLib by calling the doTransaction API.
  6. PoSLib processes the doTransaction and forwards the transaction request to PoSBridge.
  7. PoSBridge processes the doTransaction request and forwards the transaction request to the Pine Labs payment application.
  8. The Pine Labs payment application processes the transaction request and sends the payment request to the Pine Labs Payment Processing Engine for authorization.
  9. Pine Labs Payment Processing Engine send the payment authorization response to the Pine Labs payment application.
  10. The Pine Labs payment application processes the payment authorization response and sends the doTransaction response to PoSBridge.
  11. PoSBridge processes the doTransaction response and forwards the doTransaction response to PoSLib.
  12. PoSLib will process the doTransaction response and forward the doTransaction response to the merchant billing application.

Supported Features

The following features (APIs) are supported by Android PoSLib:

Supported APIs Functionality / Description
posLibInitialize This API will Initialize the POS lib. This should be the first API to be called.
scanOnlinePOSDevice This API will be used to auto discover/scan for PoS devices on the same network (Wi-Fi).
scanBTDevice This API will be used to scan the paired BT device.
setConfiguration This API will be used to set the required configuration in PoSlib.
getConfiguration This API will be used to get the configuration settings from the PoSlib.
testTCP This API will be used to check for a TCP/IP connection.
testBT This API will be used to check the BT connection.
checkBtComStatus This API will check the BT connectivity status every 30 seconds.
checkTcpComStatus This API will check the TCP/IP connectivity status every 30 seconds.
doTransaction This API will be used to perform transactions with the Payment application.

PoS Library Integration

This section describes the pre-requisites and steps to integrate the PoS library with the merchant billing system.

Pre-requisites
  • Ensure you have the latest version of POS Lib
  • Ensure you have the latest version of Android Studio installed on your system.

Steps to Integrate PoSLib with Billing System

  • Step 1: Download PoSLib from here and Extract.
  • Step 2: Copy the extracted .aar file into libs folder in your project. Screenshot
  • Step 3: Launch Android Studio.
  • Step 4: In build.gradle file under dependency include .aar file like below.
  • Screenshot

API Integration

The following section describes the the APIs exposed by PoSLib, and provides sample code snippets.

Note: The sample code snippets provided in this document are currently supported only in Java. These code snippets might have to be customized if you want to test them in Kotlin.

posLibInitialize

This API will Initialize the PoSlib. This should be the first API to be called

Signature boolean posLibInitialize(Context context)
Parameters context – Application context
Returns Success or Failure
Call-backs None
Error Condition null context

Code snippet for posLibInitialize:

Initialize PoSLib using the following code:

                        

Java


PosLibManager.getInstance().posLibInitialize(getApplicationContext());

scanOnlinePOSDevice

This API will auto-discover the Android payment terminals on the same network where PoSBridge is running and list them.

Signature

public void scanOnlinePOSDevice(ScanDeviceListener scanDeviceListener, Context context );

Parameters scanDeviceListener – Device listener
context – Application context
Returns None
Call-backs OnSucces( List< DeviceDetails > list)
-List of Devices discovered

OnFailure(String errorMsg, int errorCode)
-Error Message
-Error Code
Error Condition Response timeout
Device disconnected
Parsing error
Network down
Notes In response devicedetails isBtDevice set to false and btDeviceSsid and btDeviceName will be set to null for scanOnlinePOSDevice API call return

Code snippet for scanOnlinePOSDevice:

                    

Java


public class DeviceDetails { private boolean isBtDevice; private String deviceSlNo; private String deviceId; private String deviceIp; private String devicePort; private String btDeviceSsid; private String btDeviceName; } Button buttonScanDeviceViewTcp;// Button for scan online device buttonScanDeviceViewTcp.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { showProgress("Searching"); handler.postDelayed(() -> PosLibManager.getInstance().scanOnlinePOSDevice(scanDeviceListener, getApplicationContext()), 1000); } }); ScanDeviceListener scanDeviceListener = new ScanDeviceListener() { @Override public void onSuccess(List<DeviceDetails> list) { //"onSuccess" //Prompt device list and get the selected device from the user } @Override public void onFailure(String errorMsg, int errorCode) { //"on failure" //Handle error message and error code received } };

scanBTDevice

This API will scan and list the paired BT devices over Bluetooth.

Signature public void scanBTDevice(ScanDeviceListener scanDeviceListener, Context context);
Parameters scanDeviceListener – Device listener
context – Application context
Returns None
Call-backs OnSucces( List< DeviceDetails > list )
- List of Devices discovered

OnFailure(String errorMsg, int errorCode)
- Error Message
- Error Code
Error Condition • Response timeout
• Device disconnected
• Parsing error
• Network down
Notes In response devicedetails isBtDevice set to true and deviceIp,devicePort,deviceSlNo and deviceId will be set to null for scanBTDevice API call return

Code snippet for scanBTDevice:

                    

Java


Button buttonScanDeviceViewBt; //Button for scan BT devices buttonScanDeviceViewBt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //showDialog(AppSettingActivity.this); showProgress("Searching"); handler.postDelayed(() -> PosLibManager.getInstance().scanBTDevice(scanDeviceListener, getApplicationContext()), 1000); } }); ScanDeviceListener scanDeviceListener = new ScanDeviceListener() { @Override public void onSuccess(List<DeviceDetails> list) { //"onSuccess" //Prompt device list and get the selected device from the user } @Override public void onFailure(String errorMsg, int errorCode) { //"on failure" //Handle error message and error code received } };

setConfiguration

This API will set PoS Library configurations related to connectivity, priority settings, and logging options.

Signature public int setConfiguration(ConfigData configData,Context context);
Parameters configData - Configuration Data
context – Application context
Returns 0 - Success
-1 - Failure
Call-backs None
Error Condition Invalid IP
Invalid port
IP or Port not configured
PoSLib not initialized

Code snippet for setConfiguration:

                    

Java


public class ConfigData { private String tcpIP; private String tcpPort; private String commPortNumber; private String baudRate; private boolean isConnectivityFallBackAllowed; private String btSSID; private String btName; private int commP1;//1=TCPIP , 2=BT ,3=App2App private int commP2;//1=TCPIP , 2=BT ,3=App2App private int commP3;//1=TCPIP , 2=BT ,3=App2App private String connectionMode; private String logPath; private boolean isLogsEnabled; private int logLevel; private int dayToRetainLogs; private int retryCount; private int connectionTimeOut; private boolean isDemoMode; private String cashierID; private String cashierName; private String deviceSlNo; private String deviceId; } private void saveValues() { Log.d(TAG,"Entering saveValues()"); Log.i(TAG,"Saving values to ConfigData..."); ConfigData configData = new ConfigData(); PosLibManager.getInstance().getConfiguration(configData); if (scanDevicetcpIp != null) { configData.setTcpIP(scanDevicetcpIp.getText().toString()); Log.i(TAG,"TCP IP set: " + scanDevicetcpIp.getText().toString()); } if (scanDevicePort != null) { configData.setTcpPort(scanDevicePort.getText().toString()); Log.i(TAG,"TCP Port set: " + scanDevicePort.getText().toString()); } if (scanbtname != null) { configData.setBtName(scanbtname.getText().toString()); Log.i(TAG,"BT Name set: " + scanbtname.getText().toString()); } if (deviceID != null) { configData.setDeviceId(deviceID); Log.i(TAG,"Device Id set: " + deviceID); } if (deviceSlno != null) { configData.setDeviceSlNo(deviceSlno); Log.i(TAG,"Device Sl No set: " + deviceSlno); } if (scanbtssid != null) { configData.setBtSSID(scanbtssid.getText().toString().toUpperCase()); Log.i(TAG,"BT SSID set: " + scanbtssid.getText().toString()); } if (logPathEt.getText().length() > 0) { configData.setLogPath(logPathEt.getText().toString()); Log.i(TAG,"Log Path set: " + logPathEt.getText().toString()); } String logRetainValue = logRetainEt.getText().toString(); if (notNullNotEmpty(logRetainValue)) { configData.setDayToRetainLogs(Integer.parseInt((logRetainValue))); Log.i(TAG,"Day to Retain Logs set: " + logRetainValue); } String logLevelValue = logLevelEt.getText().toString(); if (notNullNotEmpty(logLevelValue)) { configData.setLogLevel(Integer.parseInt(logLevelValue)); Log.i(TAG,"Log Level set: " + logLevelValue); } if (connectionTimeOut.getText().length() > 0) { configData.setConnectionTimeOut(Integer.parseInt(connectionTimeOut.getText().toString())); Log.i(TAG,"Connection timeout set: " + connectionTimeOut.getText().toString()); } if (connectionRetry.getText().length() > 0) { configData.setRetryCount(Integer.parseInt(connectionRetry.getText().toString())); Log.i(TAG,"Connection retry set: " + connectionRetry.getText().toString()); } configData.setConnectivityFallBackAllowed(((CheckBox) findViewById(R.id.connection_FallBack_CheckBox)).isChecked()); if (prioritySpinnerTv.getSelectedItem().toString().contains("TCPIP")) { configData.setCommP1(Constants.TCP_IP); configData.setCommP2(Constants.BT); } else if (prioritySpinnerTv.getSelectedItem().toString().contains("BT")) { configData.setCommP1(Constants.BT); configData.setCommP2(Constants.TCP_IP); } else { configData.setCommP1(Constants.APPTOAPP); } configData.setLogsEnabled(logOptionCheckBox.isChecked()); Log.i(TAG,"Logs Enabled: " + logOptionCheckBox.isChecked()); PosLibManager.getInstance().setConfiguration(configData,this); Log.i(TAG,"Values saved successfully."); Log.d(TAG,"Exiting saveValues()"); }

getConfiguration

This API will be used to retrieve PoS Library configurations related to connectivity, priority settings, and logging options.

Signature int getConfiguration(ConfigData configData)
Parameters [out] configData - Configuration Data
Returns 0 - Success
-1 - Failure
Call-backs None
Error Condition POS Lib not initialized

Code snippet for getConfiguration:

                    

Java


public class ConfigData { private String tcpIP; private String tcpPort; private String commPortNumber; private String baudRate; private boolean isConnectivityFallBackAllowed; private String btSSID; private String btName; private int commP1;//1=TCPIP , 2=BT ,3=App2App private int commP2;//1=TCPIP , 2=BT ,3=App2App private int commP3;//1=TCPIP , 2=BT ,3=App2App private String connectionMode; private String logPath; private boolean isLogsEnabled; private int logLevel; private int dayToRetainLogs; private int retryCount; private int connectionTimeOut; private boolean isDemoMode; private String cashierID; private String cashierName; private String deviceSlNo; private String deviceId; } private void setSettingValues() { ConfigData configData = new ConfigData(); PosLibManager.getInstance().getConfiguration(configData); scanDevicetcpIp.setText(configData.getTcpIP()); scanDevicePort.setText(configData.getTcpPort()); scanbtname.setText(configData.getBtName()); scanbtssid.setText(configData.getBtSSID()); connectionTimeOut.setText(String.valueOf(configData.getConnectionTimeOut())); logOptionCheckBox.setChecked(configData.isLogsEnabled()); if (logOptionCheckBox.isChecked()) { logPathEt.setText(configData.getLogPath()); logRetainEt.setText(String.valueOf(configData.getDayToRetainLogs())); logLevelEt.setText(String.valueOf(configData.getLogLevel())); } connectionRetry.setText(String.valueOf(configData.getRetryCount())); ((CheckBox) findViewById(R.id.connection_FallBack_CheckBox)).setChecked(configData.isConnectivityFallBackAllowed()); if (configData.getCommP1() == Constants.TCP_IP) { prioritySpinnerTv.setSelection(0); } else if (configData.getCommP1() == Constants.BT) { prioritySpinnerTv.setSelection(1); } else { prioritySpinnerTv.setSelection(2); } enableDisableLogOptionComView(configData.isLogsEnabled()); }

testTCP

This API will verify connectivity between the merchant billing system and the Android payment terminal in IP connectivity mode.

Signature boolean testTCP(String IP, String port)
Parameters IP - Payment terminal IP
Port - Payment terminal port
Returns true - Success
false - Failure
Call-backs None

Code snippet for testTCP:

				

Java


Button buttonConnectTcp; // Button used to connect IP and port String scanDevicetcpIp; // 192.168.4.62 String scanDevicePort; // 6666 buttonConnectTcp.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { showProgress("Connecting"); ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.execute(() -> handleDeviceConnection(PosLibManager.getInstance().testTCP(scanDevicetcpIp.getText().toString(), scanDevicePort.getText().toString()))); executorService.shutdown(); } }); private void handleDeviceConnection(boolean isConnected) { runOnUiThread(() -> { dismissProgress(); if (isConnected) { // "Device Connected!!! } else { //"Failed to Connect" } }); Log.d(TAG,"Exiting connect()"); }

testBT

This API will verify connectivity between the merchant billing system and the Android payment terminal in BT connectivity mode.

Signature boolean testBT(String btSsid)
Parameters btSsid - Bluetooth Address
Returns true - Success
false - Failure
Call-backs None

Code snippet for testBT:

				

Java


Button buttonConnectBt; // Button used to connect Bluetooth device String btSsid = null ; // Bluetooth identifier ExecutorService executor = Executors.newSingleThreadExecutor(); btConnectBtn.setOnClickListener(view -> { btConnectBtn.setEnabled(false); String scanBtName = scanbtname.getText().toString().trim(); String scanBtSsid = scanbtssid.getText().toString().trim(); Log.i(TAG,"BT Connect button clicked. Initiating device connection..."); if (scanBtName.isEmpty() || scanBtSsid.isEmpty()) { Log.w(TAG,"Please select a device before connecting."); //Prompt for error message btConnectBtn.setEnabled(true); return; } showProgress("Connecting" executor.execute(() -> { boolean isConnected = PosLibManager.getInstance().testBT(scanBtSsid Handler mainHandler = new Handler(Looper.getMainLooper()); mainHandler.post(() -> { dismissProgress(); if(isConnected){ //"Device Connected" }else{ // "Failed to connect" } btConnectBtn.setEnabled(true); // Re-enable the button }); }); });

checkTcpComStatus

Signature

public void checkTcpComStatus(ComEventListener comEventListener, Context context, int timeInterval);

Parameters comEventListener - Com Event listener
context – Application context
timeInterval - The Parameter "timeInterval" is used to specify the time interval for sending health check status requests to the PoSBridge.
Returns None
Call-backs eventId = 3000 (TCP Connected and Payment app is Down)
eventId = 1000 (TCP Connected and Payment app is Up)
eventId = 1001 (TCP Disconnected)
eventId = 1002 (TCP Disconnected)
eventId = 1003 (TCP Disconnected)

Code snippet for checkTcpComStatus:

                

Java


runOnUiThread(() -> PosLibManager.getInstance().checkTcpComStatus(tcpComEventListener, getApplicationContext(), 30)); ComEventListener tcpComEventListener = eventId -> { Log.d(TAG,"Entering tcpComEventListener()"); Log.i(TAG,"Entering tcpComEventListener on onEvent : " + eventId); switch (eventId) { case 3000: //"TCP Connected and Payment app is down //Status: Inactive break; case 1000: //"TCP Connected and Payment app is UP // Status: Active break; case 1001: case 1002: case 1003: //"TCP Disconnected // Status: Inactive break; default: Log.i(TAG,"Unknown Event ID: " + eventId); break; } };

checkBtComStatus

Signature

public void checkBtComStatus(ComEventListener comEventListener, Context context, int timeInterval);

Parameters comEventListener – Com Event listener
context – Application context
timeInterval - The Parameter "timeInterval" is used to specify the time interval for sending health check status requests to the PoSBridge.
Returns None
Call-backs eventId = 3000 (BT Connected and Payment app down)
eventId = 2000 (BT Connected and Payment app UP)
eventId = 2001 (BT Disconnected)
eventId = 2002 (BT Disconnected)

Code snippet for checkBtComStatus:

                

Java


runOnUiThread(() -> PosLibManager.getInstance().checkBtComStatus(btComEventListener, getApplicationContext(), 30)); ComEventListener btComEventListener = eventId -> { Log.d(TAG,"Entering ComEventListener()"); Log.i(TAG,"Entering comEventListener on onEvent : " + eventId); switch (eventId) { case 3000: //"BT Connected Payment app is down // Status: Inactive break; case 2000: // "BT Connected and Payment app is UP // Status: Active break; case 2001: case 2002: //BT Disconnected // Status: Inactive break; default: Log.i(TAG,"Unknown Event ID: " + eventId); break; } };

doTransaction

This API will be used to perform transactions with Android Payment application.

Signature public void doTransaction(Context context, String paymentRequest, int transactionType, TransactionListener transactionListener);
Parameters

context – Application context
paymentRequest - Request message body from billing application. Refer APIs for details of the request payload for each transactions
transactionType = 6 // payment transaction type
transactionListener - transaction listiner

Returns None
Call-backs OnSuccess(String paymentResponse)
- paymentResponse data for all the supported transaction types

OnFailure(String errorMsg, int errorCode)
- Error Message
- Error Code
Error Condition • Response timeout
• Parsing error
• BT disconnected
• Network down
• Comm disconnected

Code snippet for doTransaction:

				

Java


String paymentRequest = null ; // Requered only for TxnType 6 (payment transaction) paymentRequest = "10000997001D343030312C545831323334353637382C3130313030302C2C2C2C2C2C2CFF"; // sale transaction re-quest paymentRequest = "10000997001D353130312C545831323334353637382C3130313030302C2C2C2C2C2C2CFF"; // Bank EMI transaction request paymentRequest = "10000997001D353030322C545831323334353637382C3130313030302C2C2C2C2C2C2CFF"; // Brand EMI transaction request paymentRequest = "10000997001D353132302C545831323334353637382C3130313030302C2C2C2C2C2C2CFF"; // UPI transaction request int txnType = 6; // Payment transaction new Handler(Looper.getMainLooper()).postDelayed(() -> { ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(() -> PosLibManager. getInstance().doTransaction(MainActivity.this, paymentRequest, TxnType, new TransactionListener() { @Override public void onSuccess(String paymentResponse) { // Success //Convert the payment response from CSV hex string format to byte array format and parse the CSV response. } @Override public void onFailure(String errorMsg, int errorCode) { // Failure //Handle error message and error coded received here } })); executor.shutdown(); }, 100);