Introduction

The new generation of Handpoint APIs and SDKs are engineered to make your life simpler and happier.

Awesomely simple
Created for humans, coders, geeks, no need of a dark and complex knowledge of the payment industry.
Super secure
We take care of PCI compliance so you can be kept out of scope
The Handpoint terminals encrypt all sensitive cardholder data so your app does not have to deal with it.
Available through Maven Central
The Handpoint Android SDK is compatible with Android version 5.1.1 (API level 22) and up.
The latest version is compiled with java 1.8

Apache Maven:
                    
    <dependency>
      <groupId>com.handpoint.api</groupId>
      <artifactId>sdk</artifactId>
      <version>X.X.X (latest version)</version>
      <type>aar</type>
    </dependency>
                    
                
Gradle Groovy DSL:
                    
    implementation 'com.handpoint.api:sdk:X.X.X (latest version)'
                    
                
Follow one of the integration guides: or directly dive into the documentation!

If you have any questions, do not hesitate to contact us.

version

Get the SDK

API overview

Supported functionality

  • List the available payment terminals
  • Connect/disconnect to and from a payment terminal.
  • Automatically or manually reconnect to a payment terminal.
  • Execute financial transactions.
  • Get the status of transactions.
  • Control and access the device logs.

Processing Payments Simulation

Your test payments are sent against a test server on the Handpoint side which simulates the behavior of an acquiring bank. Funds are not moved and sensitive data from the card is fully encrypted. You can use trigger amounts to generate some specific responses from our server:

Sale amounts
Amount Behaviour
37.79 Issuer response code = 01 (Refer to issuer)
37.84 Issuer response code = 05 (Not authorized)
37.93 Issuer response code = 04 (Pick up card)
37.57 Request is partially approved
37.68 Request timeout


Integration Guide

Hilite & Hi5 - Bluetooth Integration

Introduction

This tutorial is guiding you through all the required steps to create a basic payment application for Android devices integrated with a HiLite or Hi5 payment terminal.

The new generation of Handpoint SDK's is designed to make your life easier. Simple and created for humans, it does not require any specific knowledge of the payment industry to be able to start accepting credit/debit card transactions.

At Handpoint we take care of securing every transaction so you don´t have to worry about it while creating your application. We encrypt data from the payment terminal to the bank with our point-to-point encryption solution. Our platform is always up to the latest PCI-DSS security requirements.

Let's start programming!

  1. Modify the AndroidManifest.xml
  2. The following lines have to be added inside the manifest in order to enable permissions:

    
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.INTERNET"/>
                            

    In order for the application to handle screen rotation the following line of code has to be added inside your manifest under <activity>:

    
    <activity android:configChanges="orientation|keyboardHidden|screenSize">
                            

    According to the android version of the device you are using you will have to modify the following line of your manifest:

    
    <uses-sdk android:minSdkVersion="14"/>//change the SDK version to the one corresponding to the device you are using
                            

    Your manifest should look like this:

    
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                package="com.example.androidGettingStarted"
                android:versionCode="1"
                android:versionName="1.0">
        <uses-permission android:name="android.permission.BLUETOOTH"/>
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
        <uses-permission android:name="android.permission.INTERNET"/>
        <uses-sdk android:minSdkVersion="14"/>
        <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
            <activity android:name="MyActivity"
                    android:configChanges="orientation|keyboardHidden|screenSize"
                    android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
            </activity>
        </application>
    </manifest>
                    
  3. Create a Java class
  4. Create a new java class called MyClass.java and include com.handpoint.api.* as a dependency:

    
    package com.example.androidGettingStarted;
    
    import com.handpoint.api.*;
    
    public class MyClass {
    }
                    
  5. Initialize the API
  6. 
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    import com.handpoint.api.*;
    
    public class MyClass  {
    
        Hapi api;
    
        public MyClass(Context context) {
            initApi(context);
        }
        //An Android Context is required to be able to handle bluetooth
        public void initApi(Context context) {
            String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
            HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret);
            Settings settings = new Settings();
            settings.automaticReconnection = true;
            this.api = HapiFactory.getAsyncInterface(this, this, handpointCredentials, settings);
            // Register class as listener for required events
            this.api.registerEventsDelegate(this);
            // The api is now initialized. Yay! we've even set a shared secret!
            // The shared secret is a unique string shared between the card reader and your mobile application.
            // It prevents other people to connect to your card reader.
            // You have to replace this default shared secret by the one sent by our support team.
        }
    }
                        

    Note about reconnections: By default, the SDK will automatically reconnect to the last known device when the connection is lost. If you want to change this behaviour set the property Settings.AutomaticReconnection in HapiManager to false.

  7. Implement the mandatory Events(Events.Required)
  8. 
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    import com.handpoint.api.*;
    
    import java.util.List;
    
    public class MyClass implements Events.Required {
    
        Hapi api;
        Device device;
    
        public MyClass(Context context) {
            initApi(context);
        }
        //An Android Context is required to be able to handle bluetooth
        public void initApi(Context context) {
            String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
            HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret);
            Settings settings = new Settings();
            settings.automaticReconnection = true;
            this.api = HapiFactory.getAsyncInterface(this, this, handpointCredentials, settings);
            // Register class as listener for required events
            this.api.registerEventsDelegate(this);
            // The api is now initialized. Yay! we've even set a shared secret!
            // The shared secret is a unique string shared between the card reader and your mobile application.
            // It prevents other people to connect to your card reader.
            // You have to replace this default shared secret by the one sent by our support team.
        }
    
        @Override
        public void deviceDiscoveryFinished(List<Device> list) {
            // here you get a list of Bluetooth devices paired with your android device
        }
    
        @Override
        public void signatureRequired(SignatureRequest signatureRequest, Device device) {
            // You'll be notified here if a sale process needs a signature verification
            // A signature verification is needed if the cardholder uses an MSR card or a chip & signature card
            // This method will not be invoked if a transaction is made with a Chip & PIN card
            // At this step, you should display the merchant receipt to the cardholder on the android device
            // The cardholder must have the possibility to accept or decline the transaction
            // If the cardholder clicks on decline, the transaction is VOID
            // If the cardholder clicks on accept he is then asked to sign electronically the receipt
            this.api.signatureResult(true);
            // This line means that the cardholder ALWAYS accepts to sign the receipt
            // For this sample app we are not going to implement the whole signature process
        }
    
        @Override
        public void endOfTransaction(TransactionResult transactionResult, Device device) {
            // The object TransactionResult holds the different receipts
            // Other information can be accessed through this object like the transaction ID, the amount...
        }
    
    }
                        
  9. Add a method to discover the surrounding bluetooth devices and connect to the card reader
    • Pre-requisite :
    • the bluetooth on your android device has to be activated
    • the card reader must be in bluetooth mode (default mode of the device)

    Note for PAX/Telpo devices: We still need to search for devices in order to keep consistency between different devices. In this case deviceDiscoveryFinished will return only one device which will be the underlying PAX/Telpo device.

    
    public void discoverDevices(){
        this.api.searchDevices(ConnectionMethod.BLUETOOTH);
        // This triggers the search for all the bluetooth devices around.
    }
    
    @Override
    public void deviceDiscoveryFinished(List<Device> devices) {
        for (Device device : devices) {
            if (device.getName() != null){
                if (device.getName().equals("PP0513901435")) {
                    // Put the name of your device, find it by doing C then up arrow on your card reader keypad
                    this.device = device;
                    this.api.connect(this.device);
                }
            }
        }
    }
                    
  10. You can also connect directly to a card reader without discovering the surrounding devices before
  11. 
    public void connect(){
    Device device = new Device("PP0513901435", "68:AA:D2:00:D5:27", "", ConnectionMethod.BLUETOOTH);
      //The address always has to be written in UPPER CASE
      //new Device("name", "address", "port", ConnectionMethod);
    
    this.api.connect(device);
    }
                    
  12. Add a method to take payments
  13. 
    public boolean pay() {
        return this.api.sale(new BigInteger("1000"), Currency.GBP);
        // Let´s start our first payment of 10 pounds
    }
                    
  14. Add a method to disconnect from the card reader
  15. 
    public void disconnect(){
        this.api.disconnect();
        //This disconnects the connection
    }
                    
  16. Eventually, MyClass.java must look like this after implementing all the necessary methods:
  17. 
    package com.example.androidGettingStarted;
    import android.content.Context;
    import com.handpoint.api.*;
    import java.math.BigInteger;
    import java.util.List;
    
    public class MyClass implements Events.Required {
    
        Hapi api;
        Device device;
    
        public MyClass(Context context) {
            initApi(context);
        }
        //An Android Context is required to be able to handle bluetooth
        public void initApi(Context context) {
            String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
            HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret);
            Settings settings = new Settings();
            settings.automaticReconnection = true;
            this.api = HapiFactory.getAsyncInterface(this, this, handpointCredentials, settings);
            // Register class as listener for required events
            this.api.registerEventsDelegate(this);
            // The api is now initialized. Yay! we've even set a shared secret!
            // The shared secret is a unique string shared between the card reader and your mobile application.
            // It prevents other people to connect to your card reader.
            // You have to replace this default shared secret by the one sent by our support team.
        }
    
        // Now  we need to connect to a device to start taking payments.
        // Let's search for them:
        public void discoverDevices(){
            this.api.searchDevicesc(ConnectionMethod.BLUETOOTH);
            // This triggers the search for all the bluetooth devices around.
        }
        public void connect(){
            Device device = new Device("PP0513901435", "68:AA:D2:00:D5:27", "", ConnectionMethod.BLUETOOTH);
            //The Address always has to be written in UPPER CASE
            //new Device("name", "address", "port", ConnectionMethod);
            this.api.connect(device);
        }
        @Override
        public void deviceDiscoveryFinished(List <Device> devices) {
            for (Device device : devices) {
                if (device.getName() != null){
                    if (device.getName().equals("PP0513901435")) {
                        // Put the name of your device, find it by doing C then up arrow on your card reader keypad
                        this.device = device;
                        this.api.connect(this.device);
                    }
                }
            }
        }
    
        public boolean pay() {
            return this.api.sale(new BigInteger("1000"), Currency.GBP);
            // Let´s start our first payment of 10 pounds
        }
    
        @Override
        public void signatureRequired(SignatureRequest signatureRequest, Device device) {
            // You'll be notified here if a sale process needs a signature verification
            // A signature verification is needed if the cardholder uses an MSR or a chip & signature card
            // This method will not be invoked if a transaction is made with a Chip & PIN card
            // At this step, you are supposed to display the merchant receipt to the cardholder on the android device
            // The cardholder must have the possibility to accept or decline the transaction
            // If the cardholder clicks on decline, the transaction is VOID
            // If the cardholder clicks on accept he is then asked to sign electronically the receipt
            this.api.signatureResult(true);
            // This line means that the cardholder ALWAYS accepts to sign the receipt
            // For this sample app we are not going to implement the whole signature process
        }
    
        @Override
        public void endOfTransaction(TransactionResult transactionResult, Device device) {
            // The object TransactionResult stores the different receipts
            // Other information can be accessed through this object like the transaction ID, the amount...
        }
    
        public void disconnect(){
            this.api.disconnect();
            //This disconnects the connection
        }
    }
                    

Let's create a User Interface!

  1. Create buttons
  2. Go to the layout folder of your project and add the following code to the xml file containing the code for your user interface:

    
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
        <Button
                android:id="@+id/payNowBtn"
                android:text="Pay Now"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
        <Button
                android:id="@+id/connectCardReader"
                android:text="Connect Card Reader"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
        <Button
                android:id="@+id/disconnectCardReader"
                android:text="Disconnect Card Reader"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
    
    </LinearLayout>
                    
  3. Initialize the buttons
  4. Go to your main activity (entry point of your program) and create new methods to initialize the buttons:

    
    package com.example.androidGettingStarted;
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    
    
    public class MyActivity extends Activity {
        private Button payNowButton;
        private Button connectButton;
        private Button disconnectButton;
        /**
         * Called when the activity is first created.
         */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            initializeButtons();
        }
        public void initializeButtons() {
            payNowButton = (Button) findViewById(R.id.payNowBtn);
            payNowButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    //Call an OnClick method
                }
            });
            connectButton = (Button) findViewById(R.id.connectCardReader);
            connectButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    //Call an OnClick method
                }
            });
            disconnectButton = (Button) findViewById(R.id.disconnectCardReader);
            disconnectButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    //Call an OnClick method
                }
            });
        }
    }
                    

Let's implement the android lifecycle methods!

    Implementing your activity lifecycle methods properly ensures your app behaves well in several ways, including that it:

    • Does not crash if the user receives a phone call or switches to another app while using your app
    • Does not consume valuable system resources when the user is not actively using it
    • Does not lose the user's progress if they leave your app and return to it at a later time
    • Does not crash or lose the user's progress when the screen rotates between landscape and portrait orientation
  1. In your main activity add the following lifecycle methods :
  2. 
    @Override
    public void onPause() {
    super.onPause();  // Always call the superclass method first
    myClass.disconnect(); // disconnects from the card reader if the application is paused
    }
    
    @Override
    public void onResume() {
    super.onResume();  // Always call the superclass method first
    }
    
    @Override
    public void onBackPressed() {
    moveTaskToBack(true); // When the user clicks the back button the application is moved in the background
    }
    
    @Override
    public void onStop() {
    super.onStop();  // Always call the superclass method first
    }
    
    @Override
    public void onRestart() {
    super.onRestart();  // Always call the superclass method first
    initializeButtons(); // initialize the buttons again after restarting the app
    }
    
    @Override
    public void onDestroy() {
    super.onDestroy();
    }
                    

Let´s display a receipt at the end of a transaction!

    in this example, you are going to display the customer receipt. However, to have your app accredited by Handpoint you need to display BOTH the customer receipt AND the merchant receipt at the end of a transaction. We decided to only display one receipt to simplify this basic application as much as possible.

  1. Fetch the cardholder's receipt from the method EndOfTransaction in MyClass
  2. 
    @Override
    public void endOfTransaction(TransactionResult transactionResult, Device device) {
        // The object TransactionResult stores a lot of information, for example, it holds the merchant and cardholder receipt
        // Other information can be accessed through this object like the transaction ID, the currency, the amount...
        UIClass.callReceiptDialog(transactionResult.getCustomerReceipt());
    }
                    
  3. Create an AlertDialog in your main activity to display the receipt on the android device
  4. 
    public void callReceiptDialog(final String customerReceipt) {
        this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                String positiveButton = "OK";
                WebView webView = new WebView(MyActivity.this); // Create a webView to display the receipt
                webView.loadData(customerReceipt, "text/html", "UTF-8");
                webView.getSettings().setDefaultFontSize(20);
                webView.setVerticalScrollBarEnabled(true);
                new AlertDialog.Builder(MyActivity.this)// Defines an AlertDialog that will popup
                    .setTitle("Transaction result")
                    .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            dialogInterface.dismiss();
                        }
                    })
                    .setView(webView)
                    .show();
            }
        });
    }
                    

Final Result!

Here is how MyClass and your main activity must eventually look like:

  1. main activity (entry point for your program):
  2. 
    package com.example.androidGettingStarted;
    
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.os.Bundle;
    import android.view.View;
    import android.webkit.WebView;
    import android.widget.Button;
    import com.handpoint.api.TransactionResult;
    
    
    public class MyActivity extends Activity{
        private Button payNowButton;
        private Button connectButton;
        private Button disconnectButton;
        MyClass myClass;
    
        /**
         * Called when the activity is first created.
         */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            myClass = new MyClass(this);
            initializeButtons();
        }
        public void initializeButtons() {
            payNowButton = (Button) findViewById(R.id.payNowBtn);
            payNowButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    myClass.pay();
                }
            });
            connectButton = (Button) findViewById(R.id.connectCardReader);
            connectButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                   myClass.discoverDevices();
                  // myClass.connect(); // For a direct connection to the card reader
                }
            });
            disconnectButton = (Button) findViewById(R.id.disconnectCardReader);
            disconnectButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    myClass.disconnect();
                }
            });
        }
        public void callReceiptDialog(final String customerReceipt) {
            this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    String positiveButton = "OK";
                    WebView webView = new WebView(MyActivity.this); // Create a webView to display the receipt
                    webView.loadData(customerReceipt, "text/html", "UTF-8");
                    webView.getSettings().setDefaultFontSize(20);
                    webView.setVerticalScrollBarEnabled(true);
                    new AlertDialog.Builder(MyActivity.this)// Defines an AlertDialog that will popup
                            .setTitle("Transaction result")
                            .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialogInterface, int i) {
                                    dialogInterface.dismiss();
                                }
                            })
                            .setView(webView)
                            .show();
                }
            });
        }
    
        @Override
        public void onPause() {
            super.onPause();  // Always call the superclass method first
            myClass.disconnect(); // disconnects from the card reader if the application is paused
        }
    
        @Override
        public void onResume() {
            super.onResume();  // Always call the superclass method first
        }
    
        @Override
        public void onBackPressed() {
            moveTaskToBack(true); // The application is moved in the background
        }
    
        @Override
        public void onStop() {
            super.onStop();  // Always call the superclass method first
        }
    
        @Override
        public void onRestart() {
            super.onRestart();  // Always call the superclass method first
            initializeButtons(); // initialize the buttons again after restarting the app
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
        }
    }
                
  3. My Class :
  4. 
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    import com.handpoint.api.*;
    import java.math.BigInteger;
    import java.util.List;
    
    public class MyClass implements Events.Required {
        Hapi api;
        Device device;
        MyActivity UIClass;
    
        public MyClass(MyActivity activity){
            initApi((Context) activity);
            UIClass = activity;
        }
        //An Android Context is required to be able to handle bluetooth
        public void initApi(Context context) {
            String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
            HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret);
            Settings settings = new Settings();
            settings.automaticReconnection = true;
            this.api = HapiFactory.getAsyncInterface(this, this, handpointCredentials, settings);
            // Register class as listener for required events
            this.api.registerEventsDelegate(this);
            // The api is now initialized. Yay! we've even set a shared secret!
            // The shared secret is a unique string shared between the card reader and your mobile application.
            // It prevents other people to connect to your card reader.
            // You have to replace this default shared secret by the one sent by our support team.
        }
    
        // Now  we need to connect to a device to start taking payments.
        // Let's search for them:
        public void discoverDevices(){
            this.api.searchDevices(ConnectionMethod.BLUETOOTH);
            // This triggers the search for all the bluetooth devices around.
        }
        public void connect(){
            Device device = new Device("PP0513901435", "68:AA:D2:00:D5:27", "", ConnectionMethod.BLUETOOTH);
            //The Address always has to be written in UPPER CASE
            //new Device("name", "address", "port", ConnectionMethod);
            this.api.connect(device);
        }
        @Override
        public void deviceDiscoveryFinished(List<Device> devices) {
            for (Device device : devices) {
                if (device.getName() != null){
                    if (device.getName().equals("PP0513901435")) {
                        // Put the name of your device, find it by doing C then up arrow on your card reader keypad
                        this.device = device;
                        this.api.connect(this.device);
                    }
                }
            }
        }
    
        public boolean pay() {
            return this.api.sale(new BigInteger("1000"), Currency.GBP);
            // Let´s start our first payment of 10 pounds
        }
    
        @Override
        public void signatureRequired(SignatureRequest signatureRequest, Device device) {
            // You'll be notified here if a sale process needs a signature verification
            // A signature verification is needed if the cardholder uses an MSR card or a chip & signature card
            // This method will not be invoked if a transaction is made with a Chip & PIN card
            // At this step, you should display the merchant receipt to the cardholder on the android device
            // The cardholder must have the possibility to accept or decline the transaction
            // If the cardholder clicks on decline, the transaction is VOID
            // If the cardholder clicks on accept he is then asked to sign electronically the receipt
            this.api.signatureResult(true);
            // This line means that the cardholder ALWAYS accepts to sign the receipt
            // For this sample app we are not going to implement the whole signature process
        }
    
        @Override
        public void endOfTransaction(TransactionResult transactionResult, Device device) {
            // The object TransactionResult holds the different receipts
            // Other information can be accessed through this object like the transaction ID, the amount...
            UIClass.callReceiptDialog(transactionResult.getCustomerReceipt());
        }
    
        public void disconnect(){
            this.api.disconnect();
            //This disconnects the connection
        }
    }
                

Let's run our program!

Run the program by clicking the "play" button, ensure that your android device is connected via USB and correctly recognized by your computer. If not, you can download specific drivers on the device manufacturer website and install them.

  1. Click on "Connect Card Reader", this can take a little bit of time (10 sec max) due to the fact that we are looking for all the devices around before connecting to a specific one. When the bluetooth sign is displayed on the card reader, it is connected with the application.
    If nothing happens, go in the settings of your android device, activate the bluetooth options and scan for new devices. When you see the name of the card reader try to pair with it. Once paired try to start again the application.
  2. Click "Pay Now"
  3. Follow the instructions on the card reader
  4. When the transaction is finished, the cardholder´s receipt should pop-up on the device screen
  5. Click on "Disconnect From Card Reader" to stop the connection with the card reader

PAX & Telpo - Cloud Integration

Introduction

This tutorial is guiding you through all the required steps to integrate with a PAX or Telpo payment terminal, from your Android application, through the internet. CLOUD will be the ConnectionMethod of choice for this guide.

The new generation of Handpoint SDK's is designed to make your life easier. Simple and created for humans, it does not require any specific knowledge of the payment industry to be able to start accepting credit/debit card transactions.

At Handpoint we take care of securing every transaction so you don´t have to worry about it while creating your application. We encrypt data from the payment terminal to the bank with our point-to-point encryption solution. The platform is always up to the latest PCI-DSS security requirements.

Let's start programming!

  1. Modify the AndroidManifest.xml
  2. The following lines have to be added inside the manifest in order to enable the required permissions:

                        
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
                        
                    

    In order for the application to correctly handle screen rotation, the following line of code should be added inside your manifest under <activity>:

                        
    <activity android:configChanges="orientation|keyboardHidden|screenSize">
                        
                    

    The min SDK version recommended for a PAX or Telpo integration is 22:

                        
    <uses-sdk android:minSdkVersion="22"/>
                        
                    

    Finally, your manifest should look like this:

                        
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.androidGettingStarted">
      <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
        </activity>
      </application>
      <uses-permission android:name="android.permission.INTERNET"/>
      <uses-permission android:name="android.permission.WAKE_LOCK"/>
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
      <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    </manifest>
                        
                    
  3. Configure your project:
  4. ONLY for this integration example Handpoint uses the okhttp3 library to handle data through the network (receipt download requests), therefore add to your build.gradle the following line in the dependencies section:
    implementation 'com.squareup.okhttp3:okhttp:4.2.1'.
    The gradle version used to compile this getting started application is 3.4.0, so please add the corresponding classpath dependency:
    classpath 'com.android.tools.build:gradle:3.4.0'

  5. Create a Java class:
  6. Create a new java class called MyClass.java and include com.handpoint.api.* , com.handpoint.api.shared.* and okhttp3.* as dependencies.

                        
    package com.example.androidGettingStarted;
    
    import com.handpoint.api.*;
    import com.handpoint.api.shared.*;
    import okhttp3.*;
    
    public class MyClass {
    
    }
                        
                    
  7. Initialize the API
  8.                     
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    
    import com.handpoint.api.*;
    import com.handpoint.api.shared.*;
    import okhttp3.*;
    
    public class MyClass  {
    
        Hapi api;
        Device device;
        MainActivity myActivity;
    
        public MyClass(MainActivity myActivity) {
          this.myActivity = myActivity;
          initApi(myActivity);
        }
    
        public void initApi(Context context) {
            String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
            String merchantApiKey = "This-is-my-api-key-provided-by-Handpoint";
            HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret, merchantApiKey);
    
            Settings settings = new Settings();
            settings.getReceiptsAsURLs = true;
            this.api = HapiFactory.getAsyncInterface(this, this, handpointCredentials, settings);
            // Register class as listener for required events
            this.api.registerEventsDelegate(this);
            // The api is now initialized. Yay! we've even set default credentials.
            // The shared secret is a unique string shared between the payment terminal and your application, it is a free field.
            // The Api key is a unique key per merchant used to authenticate the terminal against the Cloud.
            // You should replace the API key with the one sent by the Handpoint support team.
        }
    }
                        
                    
  9. Implement the mandatory events(Events.Required), the connection status events (Events.ConnectionStatusChanged) as well as the transaction status events (Events.CurrentTransactionStatus)
  10.                     
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    
    import com.handpoint.api.*;
    import com.handpoint.api.shared.*;
    import okhttp3.*;
    import java.util.List;
    
    public class MyClass implements Events.Required, Events.ConnectionStatusChanged, Events.CurrentTransactionStatus {
    
        Hapi api;
        Device device;
        MainActivity myActivity;
    
        public MyClass(MainActivity myActivity) {
          this.myActivity = myActivity;
          initApi(myActivity);
        }
    
        public void initApi(Context context) {
          String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
          String merchantApiKey = "This-is-my-api-key-provided-by-Handpoint";
          HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret, merchantApiKey);
    
            Settings settings = new Settings();
            settings.getReceiptsAsURLs = true;
            this.api = HapiFactory.getAsyncInterface(this, this, handpointCredentials, settings);
            this.api.registerEventsDelegate(this);
        }
    
        @Override
        public void deviceDiscoveryFinished(List<Device> devices) {
            // Here you get a list of devices associated with the merchant API key
        }
    
        @Override
        public void connectionStatusChanged( ConnectionStatus status,  Device device) {
            // Here you get the connection status with the payment terminal
        }
    
        @Override
        public void endOfTransaction(TransactionResult transactionResult, Device device) {
            // The TransactionResult object holds details about the transaction as well as the receipts
            // Useful information can be accessed through this object like the transaction ID, the amount, etc.
        }
    
        @Override
        public void currentTransactionStatus(StatusInfo statusInfo, Device device) {
            // The StatusInfo object holds the different transaction statuses like reading card, pin entry, etc.
        }
    
        @Override
        public void transactionResultReady(TransactionResult transactionResult, Device device) {
            // Pending TransactionResult objects will be received through this event if the EndOfTransaction
            // event was not delivered during the transaction, for example because of a network issue.
            // For this sample app we are not going to implement this event.
        }
    
        @Override
        public void signatureRequired(SignatureRequest request, Device device) {
           // This event can be safely ignored for a PAX/Telpo integration. The complete signature capture process
           // is already handled in the sdk, a dialog will prompt the user for a signature if required.
           // If a signature was entered, it should be printed on the receipts.
        }
    }
                        
                    
  11. Add a method to discover the payment terminal(s) associated with the API key:
  12.                     
    public void discoverDevices(){
        this.api.searchDevices(ConnectionMethod.CLOUD);
        // This triggers the search for all the CLOUD devices available under the merchant Api key.
    }
    
    @Override
    public void deviceDiscoveryFinished(List<Device> devices) {
        for (Device device : devices) {
            if (device.getName() != null){
                if (device.getName().equals("9822032398-PAXA920")) {
                // Put the name of your device, it is the aggregation of the serial number and device model.
                // Example for a PAX A920 device: serial_number - model -> 9822032398-PAXA920
                    this.device = device;
                    this.api.connect(this.device);
                }
            }
        }
    }
                        
                    
  13. You can also connect directly to a payment terminal
  14.           
    public void connect(){
        Device device = new Device("CloudDevice", "9822032398-PAXA920", "", ConnectionMethod.CLOUD);
        // new Device("name", "cloud address", "port (optional)", ConnectionMethod);
        // The cloud address always has to be written in UPPER CASE
        // It is the composition of the serial number and model ot the target device.
        // Example for a PAX A920 device: serial_number - model  -> 9822032398-PAXA920
    
        this.api.connect(device);
    }
              
            
  15. Add a method to take payments
  16.                     
    public boolean pay() {
        return this.api.sale(new BigInteger("1000"), Currency.GBP);
        // Let´s start our first transaction for 10 pounds
        // The amount should always be in the minor unit of the currency
    }
                        
                    
  17. Add a method to disconnect from the payment terminal
  18.                     
    public void disconnect(){
        this.api.disconnect();
    }
                        
                    
  19. Eventually, MyClass.java must look like this after implementing all the necessary methods:
  20.                     
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    
    import com.handpoint.api.*;
    import com.handpoint.api.shared.*;
    import okhttp3.*;
    
    import java.math.BigInteger;
    import java.util.List;
    
    public class MyClass implements Events.Required, Events.ConnectionStatusChanged, Events.CurrentTransactionStatus {
    
        Hapi api;
        Device device;
        MainActivity myActivity;
    
        public MyClass(MainActivity myActivity) {
          this.myActivity = myActivity;
          initApi(myActivity);
        }
    
        public void initApi(Context context) {
          String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
          String merchantApiKey = "This-is-my-api-key-provided-by-Handpoint";
          HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret, merchantApiKey);
    
          Settings settings = new Settings();
          settings.getReceiptsAsURLs = true;
          this.api = HapiFactory.getAsyncInterface(this, this, handpointCredentials, settings);
          this.api.registerEventsDelegate(this);
        }
    
        public void discoverDevices() {
          this.api.searchDevices(ConnectionMethod.CLOUD);
        }
    
        @Override
        public void deviceDiscoveryFinished(List<Device> devices) {
            for (Device device : devices) {
                if (device.getName() != null){
                    if (device.getName().equals("9822032398-PAXA920")) {
                        this.device = device;
                        this.api.connect(this.device);
                    }
                }
            }
        }
    
        public void connect() {
          Device device = new Device("CloudDevice", "0821032398-PAXA920", "", ConnectionMethod.CLOUD);
          this.api.connect(device);
        }
    
        public boolean pay() {
            return this.api.sale(new BigInteger("1000"), Currency.GBP);
        }
    
        @Override
        public void connectionStatusChanged(ConnectionStatus status, Device device) {
    
        }
    
        @Override
        public void endOfTransaction(TransactionResult transactionResult, Device device) {
    
        }
    
        @Override
        public void currentTransactionStatus(StatusInfo statusInfo, Device device) {
    
        }
    
        @Override
        public void transactionResultReady(TransactionResult transactionResult, Device device) {
    
        }
    
        @Override
        public void signatureRequired(SignatureRequest request, Device device) {
    
        }
    
        public void disconnect(){
            this.api.disconnect();
    
        }
    }
                        
                    

Let's create a User Interface!

  1. Create buttons
  2. Go to the layout folder of your project and add the following code to the xml file:

                        
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
        <Button
                android:id="@+id/payNowBtn"
                android:text="Pay Now"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
        <Button
                android:id="@+id/connectCardReader"
                android:text="Connect Card Reader"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
        <Button
                android:id="@+id/disconnectCardReader"
                android:text="Disconnect Card Reader"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
        <EditText
                android:id="@+id/textConsole"
                android:layout_width="match_parent"
                android:layout_height="211dp"
                android:layout_margin="@dimen/dp2"
                android:background="#000000"
                android:ems="10"
                android:enabled="false"
                android:gravity="start|center_horizontal|top"
                android:textColor="#FFFFFF" />
    
    </LinearLayout>
                        
                    
  3. Initialize the UI components
  4. Go to your main activity (entry point of your program) and create new methods to initialize the buttons:

                        
    package com.example.androidGettingStarted;
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    
    public class MainActivity extends Activity {
        private Button payNowButton;
        private Button connectButton;
        private Button disconnectButton;
        private EditText textConsole;
        /**
         * Called when the activity is first created.
         */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            initializeComponents();
        }
        public void initializeComponents() {
            payNowButton = (Button) findViewById(R.id.payNowBtn);
            payNowButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    //Call an OnClick method
                }
            });
            connectButton = (Button) findViewById(R.id.connectCardReader);
            connectButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    //Call an OnClick method
                }
            });
            disconnectButton = (Button) findViewById(R.id.disconnectCardReader);
            disconnectButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    //Call an OnClick method
                }
            });
    
            textConsole = (EditText) findViewById(R.id.textConsole);
        }
    }
                        
                    

Let's implement the android lifecycle methods!

    Implementing your activity lifecycle methods properly ensures your app behaves well in several ways, including that it:

    • Does not crash if the user receives a phone call or switches to another app while using your app
    • Does not consume valuable system resources when the user is not actively using it
    • Does not lose the user's progress if they leave your app and return to it at a later time
    • Does not crash or lose the user's progress when the screen rotates between landscape and portrait orientation
  1. In your main activity add the following lifecycle methods :
  2.                 
    @Override
    public void onPause() {
    super.onPause();  // Always call the superclass method first
    myClass.disconnect(); // disconnects from the card reader if the application is paused
    }
    
    @Override
    public void onResume() {
    super.onResume();  // Always call the superclass method first
    }
    
    @Override
    public void onBackPressed() {
    moveTaskToBack(true); // When the user clicks the back button the application is moved in the background
    }
    
    @Override
    public void onStop() {
    super.onStop();  // Always call the superclass method first
    }
    
    @Override
    public void onRestart() {
    super.onRestart();  // Always call the superclass method first
    initializeButtons(); // initialize the buttons again after restarting the app
    }
    
    @Override
    public void onDestroy() {
    super.onDestroy();
    }
                    
                

Let’s notify the user about the state of the connection with the payment terminal

  1. Trigger the ConnectionStatusChanged notifications in MyClass
  2.                     
    @Override
    public void connectionStatusChanged(ConnectionStatus status, Device device) {
      if (status == ConnectionStatus.CloudConnected) {
        myActivity.notifyConnectionStatus(true);
      } else if (status == ConnectionStatus.CloudDisconnected) {
        myActivity.notifyConnectionStatus(false);
      }
    }
                        
                    
  3. Create an AlertDialog in your main activity to display the connection status of the payment terminal
  4.                     
    public void notifyConnectionStatus(boolean connected) {
      this.runOnUiThread(new Runnable() {
        @Override
        public void run() {
          String positiveButton = "OK";
          WebView webView = new WebView(MainActivity.this);
          AlertDialog.Builder alert = new AlertDialog.Builder(MainActivity.this)// Defines an AlertDialog that will popup
            .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
              @Override
              public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.dismiss();
              }
            })
            .setView(webView);
          if (connected) {
            alert.setTitle("Connected to Device");
          } else {
            alert.setTitle("Disconnected from Device");
          }
          alert.show();
        }
      });
    }             
                    

Let’s notify the cardholder of the different transaction statuses

  1. Trigger the CurrentTransactionStatus notifications in MyClass
  2.                     
    @Override
    public void currentTransactionStatus(StatusInfo statusInfo, Device device) {
        myActivity.printTransactionStatus(statusInfo.getMessage());
    }
                        
                    
  3. Print the transaction state in the test Application console
  4.                     
      public void printTransactionStatus(String message) {
        this.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            textConsole.setText("Status Received -> " + message);
          }
        });
      }
                        
                    

Let´s display a receipt at the end of a transaction!

    in this example, you are going to display the customer receipt. However, to have your app accredited by Handpoint you need to display BOTH the customer receipt AND the merchant receipt at the end of a transaction. We decided to only display one receipt to simplify this basic application as much as possible.

  1. Fetch the cardholder's receipt from the method EndOfTransaction in MyClass
  2.                     
    @Override
    public void endOfTransaction(TransactionResult transactionResult, Device device) {
        // The object TransactionResult holds the url to retrieve the merchant and cardholder receipt from a cloud server.
        // Other information can be accessed through this object like the transaction ID, the currency, the amount, etc.
        byte[] response = downloadReceipt(result.getCustomerReceipt());
        String receipt = new String(response);
        myActivity.printTransactionStatus("Transaction result received");
        myActivity.callReceiptDialog(receipt);
    }
    
    private byte[] downloadReceipt(String url) {
        try {
            Call call = buildGETCall(url);
            Response response = call.execute();
            ResponseBody responseBody = response.body();
            if (responseBody != null) {
                return responseBody.bytes();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "Something went wrong".getBytes();
    }
    
    private Call buildGETCall(String url) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .build();
    
        return client.newCall(request);
    }
                        
                    
  3. Create an AlertDialog in your main activity to display the receipt on the android device
  4.                     
    public void callReceiptDialog(final String customerReceipt) {
        this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                String positiveButton = "OK";
                WebView webView = new WebView(MainActivity.this); // Create a webView to display the receipt
                webView.loadData(customerReceipt, "text/html", "UTF-8");
                webView.getSettings().setDefaultFontSize(20);
                webView.setVerticalScrollBarEnabled(true);
                new AlertDialog.Builder(MainActivity.this)// Defines an AlertDialog that will popup
                    .setTitle("Transaction result")
                    .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            dialogInterface.dismiss();
                        }
                    })
                    .setView(webView)
                    .show();
            }
        });
    }
                        
                    

Final Result!

Here is how MyClass and your main activity must eventually look like:

  1. Main Activity (entry point for your program):
  2.                 
    package com.example.androidGettingStarted;
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.os.Bundle;
    import android.view.View;
    import android.webkit.WebView;
    import android.widget.Button;
    import android.widget.EditText;
    
    public class MainActivity extends Activity {
      private Button payNowButton;
      private Button connectButton;
      private Button disconnectButton;
      private EditText textConsole;
      MyClass myClass;
    
      public MainActivity() {
    
      }
    
      /**
       * Called when the activity is first created.
       */
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myClass = new MyClass(this);
        initializeComponents();
      }
    
      public void initializeComponents() {
        payNowButton = (Button) findViewById(R.id.payNowBtn);
        payNowButton.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
            myClass.pay();
          }
        });
        connectButton = (Button) findViewById(R.id.connectCardReader);
        connectButton.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
            myClass.discoverDevices();
            // myClass.connect(); // For a direct connection to the payment terminal
          }
        });
        disconnectButton = (Button) findViewById(R.id.disconnectCardReader);
        disconnectButton.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
            myClass.disconnect();
          }
        });
    
        textConsole = (EditText) findViewById(R.id.textConsole);
      }
    
      public void callReceiptDialog(final String customerReceipt) {
        this.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            String positiveButton = "OK";
            WebView webView = new WebView(MainActivity.this); // Create a webView to display the receipt
            webView.loadData(customerReceipt, "text/html", "UTF-8");
            webView.getSettings().setDefaultFontSize(20);
            webView.setVerticalScrollBarEnabled(true);
            new AlertDialog.Builder(MainActivity.this)// Defines an AlertDialog that will popup
              .setTitle("Transaction result")
              .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                  dialogInterface.dismiss();
                }
              })
              .setView(webView)
              .show();
          }
        });
      }
    
      public void notifyConnectionStatus(boolean connected) {
        this.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            String positiveButton = "OK";
            WebView webView = new WebView(MainActivity.this);
    
            AlertDialog.Builder alert = new AlertDialog.Builder(MainActivity.this)// Defines an AlertDialog that will popup
              .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                  dialogInterface.dismiss();
                }
              })
              .setView(webView);
    
            if (connected) {
              alert.setTitle("Connected to Device");
            } else {
              alert.setTitle("Disconnected from Device");
            }
    
            alert.show();
          }
        });
      }
    
      @Override
      public void onPause() {
        super.onPause();  // Always call the superclass method first
        myClass.disconnect(); // disconnects from the card reader if the application is paused
      }
    
      @Override
      public void onResume() {
        super.onResume();  // Always call the superclass method first
      }
    
      @Override
      public void onBackPressed() {
        moveTaskToBack(true); // The application is moved in the background
      }
    
      @Override
      public void onStop() {
        super.onStop();  // Always call the superclass method first
      }
    
      @Override
      public void onRestart() {
        super.onRestart();  // Always call the superclass method first
        initializeComponents(); // initialize the buttons again after restarting the app
      }
    
      @Override
      public void onDestroy() {
        super.onDestroy();
      }
    
      public void printTransactionStatus(String message) {
        this.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            textConsole.setText("Status Received -> " + message);
          }
        });
      }
    }
                
  3. My Class:
  4.                 
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    
    import com.handpoint.api.HandpointCredentials;
    import com.handpoint.api.Hapi;
    import com.handpoint.api.HapiFactory;
    import com.handpoint.api.Settings;
    import com.handpoint.api.shared.ConnectionMethod;
    import com.handpoint.api.shared.ConnectionStatus;
    import com.handpoint.api.shared.Currency;
    import com.handpoint.api.shared.Device;
    import com.handpoint.api.shared.Events;
    import com.handpoint.api.shared.SignatureRequest;
    import com.handpoint.api.shared.StatusInfo;
    import com.handpoint.api.shared.TransactionResult;
    
    import java.math.BigInteger;
    import java.util.List;
    
    import okhttp3.Call;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.Response;
    import okhttp3.ResponseBody;
    
    
    public class MyClass implements Events.Required, Events.ConnectionStatusChanged, Events.CurrentTransactionStatus {
    
      Hapi api;
      Device device;
      MainActivity myActivity;
    
      public MyClass(MainActivity myActivity) {
        this.myActivity = myActivity;
        initApi(myActivity);
      }
    
      public void initApi(Context context) {
        String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
        HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret);
    
        Settings settings = new Settings();
        settings.getReceiptsAsURLs = true;
        this.api = HapiFactory.getAsyncInterface(this, context, handpointCredentials, settings);
        this.api.registerEventsDelegate(this);
      }
    
      public void discoverDevices() {
        this.api.searchDevices(ConnectionMethod.CLOUD);
      }
    
      @Override
      public void deviceDiscoveryFinished(List<Device> devices) {
          for (Device device : devices) {
              if (device.getName() != null){
                  if (device.getName().equals("9822032398-PAXA920")) {
                  // Put the name of your device, it is the aggregation of the serial number and device model.
                  // Example for a PAX A920 device: serial_number - model -> 9822032398-PAXA920
                      this.device = device;
                      this.api.connect(this.device);
                  }
              }
          }
      }
    
      @Override
      public void connectionStatusChanged(ConnectionStatus status, Device device) {
        if (status == ConnectionStatus.CloudConnected) {
          myActivity.notifyConnectionStatus(true);
        } else if (status == ConnectionStatus.CloudDisconnected) {
          myActivity.notifyConnectionStatus(false);
        }
      }
    
      public void connect() {
        Device device = new Device("CloudDevice", "0821032398-PAXA920", "", ConnectionMethod.CLOUD);
        this.api.connect(device);
      }
    
      public boolean pay() {
        return this.api.sale(new BigInteger("1000"), Currency.GBP);
      }
    
      @Override
      public void endOfTransaction(TransactionResult transactionResult, Device device) {
        byte[] response = downloadReceipt(transactionResult.getCustomerReceipt());
        String receipt = new String(response);
        myActivity.printTransactionStatus("Transaction result received");
        myActivity.callReceiptDialog(receipt);
      }
    
      private byte[] downloadReceipt(String url) {
        try {
          Call call = buildGETCall(url);
          Response response = call.execute();
          ResponseBody responseBody = response.body();
          if (responseBody != null) {
            return responseBody.bytes();
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
        return "Something went wrong".getBytes();
      }
    
      private Call buildGETCall(String url) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
          .url(url)
          .build();
    
        return client.newCall(request);
      }
    
      @Override
      public void transactionResultReady(TransactionResult transactionResult, Device device) {
      }
    
      @Override
      public void signatureRequired(SignatureRequest request, Device device) {
      }
    
      public void disconnect() {
        this.api.disconnect();
      }
    
      @Override
      public void currentTransactionStatus(StatusInfo statusInfo, Device device) {
        myActivity.printTransactionStatus(statusInfo.getMessage());
      }
    }
    
                    
                

Let's run our program!

Run the program by clicking the "play" button, ensure that your android device is connected via USB and correctly recognized by your computer. If not, you can download specific drivers on the device manufacturer website and install them.

  1. Click on "Connect Card Reader" (ConnectionMethod.CLOUD), this can take a little bit of time (5 sec max) due to the fact that the payment terminal is downloading its configuration. With the reception of the connected state, you will get a notification of the successful connection.
  2. Click "Pay Now"
  3. Follow the instructions on the payment terminal
  4. When the transaction is finished, the cardholder´s receipt should pop-up on the device screen
  5. Click on "Disconnect From Card Reader" to stop the connection with the card reader

PAX & Telpo - Direct Integration

Introduction

This tutorial is guiding you through all the required steps to run your application directly on a PAX or Telpo payment terminal. ANDROID_PAYMENT will be the ConnectionMethod of choice for this guide.

The new generation of Handpoint SDK's is designed to make your life easier. Simple and created for humans, it does not require any specific knowledge of the payment industry to be able to start accepting credit/debit card transactions.

At Handpoint we take care of securing every transaction so you don´t have to worry about it while creating your application. We encrypt data from the payment terminal to the bank with our point-to-point encryption solution. The platform is always up to the latest PCI-DSS security requirements.

Let's start programming!

  1. Modify the AndroidManifest.xml
  2. The following lines have to be added inside the manifest in order to enable the required permissions:

                        
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
                        
                    

    In order for the application to correctly handle screen rotation the following line of code can be added inside your manifest under <activity>:

                        
    <activity android:configChanges="orientation|keyboardHidden|screenSize">
                        
                    

    The min SDK version recommended for a PAX or Telpo integration is 22:

                        
    <uses-sdk android:minSdkVersion="22"/>
                        
                    

    Finally, your manifest should look like this:

                        
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.androidGettingStarted">
      <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
        </activity>
      </application>
      <uses-permission android:name="android.permission.INTERNET"/>
      <uses-permission android:name="android.permission.WAKE_LOCK"/>
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
      <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    </manifest>
                        
                    
  3. Configure your project:
  4. ONLY for this integration example Handpoint uses the okhttp3 library to handle data through the network (receipt download requests), therefore add to your build.gradle the following line in the dependencies section:
    implementation 'com.squareup.okhttp3:okhttp:4.2.1'.
    The gradle version used to compile this getting started application is 3.4.0, so please add the corresponding classpath dependency:
    classpath 'com.android.tools.build:gradle:3.4.0'

  5. Create a Java class:
  6. Create a new java class called MyClass.java and include com.handpoint.api.* , com.handpoint.api.shared.* and okhttp3.* as dependencies.

                        
    package com.example.androidGettingStarted;
    
    import com.handpoint.api.*;
    import com.handpoint.api.shared.*;
    import okhttp3.*;
    
    public class MyClass {
    
    }
                        
                    
  7. Initialize the API
  8.                     
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    
    import com.handpoint.api.*;
    import com.handpoint.api.shared.*;
    import okhttp3.*;
    
    public class MyClass  {
    
        Hapi api;
        Device device;
        MainActivity myActivity;
    
        public MyClass(MainActivity myActivity) {
          this.myActivity = myActivity;
          initApi(myActivity);
        }
    
        public void initApi(Context context) {
            String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
            HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret);
    
            Settings settings = new Settings();
            settings.getReceiptsAsURLs = true;
            this.api = HapiFactory.getAsyncInterface(this, this, handpointCredentials, settings);
            // Register class as listener for required events
            this.api.registerEventsDelegate(this);
            // The api is now initialized. Yay! we've even set default credentials.
            // The shared secret is a unique string shared between the payment terminal and your application, it is unique per merchant.
            // You should replace this default shared secret with the one sent by the Handpoint support team.
        }
    }
                        
                    
  9. Implement the mandatory events(Events.Required), the connection status events (Events.ConnectionStatusChanged) as well as the transaction status events (Events.CurrentTransactionStatus)
  10.                     
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    
    import com.handpoint.api.*;
    import com.handpoint.api.shared.*;
    import okhttp3.*;
    import java.util.List;
    
    public class MyClass implements Events.Required, Events.ConnectionStatusChanged, Events.CurrentTransactionStatus {
    
        Hapi api;
        Device device;
        MainActivity myActivity;
    
        public MyClass(MainActivity myActivity) {
          this.myActivity = myActivity;
          initApi(myActivity);
        }
    
        public void initApi(Context context) {
            String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
            HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret);
    
            Settings settings = new Settings();
            settings.getReceiptsAsURLs = true;
            this.api = HapiFactory.getAsyncInterface(this, this, handpointCredentials, settings);
            this.api.registerEventsDelegate(this);
        }
    
        @Override
        public void deviceDiscoveryFinished(List<Device> list) {
          // Here you trigger the identification of the payment terminal on which your application is running.
        }
    
        @Override
        public void connectionStatusChanged( ConnectionStatus status,  Device device) {
            // Here you get the connection status with the payment terminal
        }
    
        @Override
        public void endOfTransaction(TransactionResult transactionResult, Device device) {
          // The TransactionResult object holds details about the transaction as well as the receipts
          // Useful information can be accessed through this object like the transaction ID, the amount, etc.
        }
    
        @Override
        public void currentTransactionStatus(StatusInfo statusInfo, Device device) {
          // The StatusInfo object holds the different transaction statuses like reading card, pin entry, etc.
        }
    
        @Override
        public void transactionResultReady(TransactionResult transactionResult, Device device) {
          // Pending TransactionResult objects will be received through this event if the EndOfTransaction
          // event was not delivered during the transaction, for example because of a network issue.
          // For this sample app we are not going to implement this event.
        }
    
        @Override
        public void signatureRequired(SignatureRequest request, Device device) {
          // This event can be safely ignored for a PAX/Telpo integration. The complete signature capture process
          // is already handled in the sdk, a dialog will prompt the user for a signature if required.
          // If a signature was entered, it should be printed on the receipts.
        }
    }
                        
                    
  11. Add a method to discover the payment terminal
  12.                     
    // If your application is running directly on the payment terminal we will use ConnectionMethod.ANDROID_PAYMENT
    public void discoverDevices(){
        this.api.searchDevices(ConnectionMethod.ANDROID_PAYMENT);
        // Here you trigger the identification of the payment terminal on which your application is running.
    }
    
    @Override
    public void deviceDiscoveryFinished(List<Device> devices) {
        for (Device device : devices) {
            // Only the local device will be returned, so no filtering is needed, just connect directly to it.
            this.device = device;
            this.api.connect(this.device);
        }
    }
                        
                    
  13. You can also connect directly to a payment terminal
  14.                     
    public void connect(){
        Device device = new Device("LocalDevice", "LocalHost", "", ConnectionMethod.ANDROID_PAYMENT);
        // new Device("name", "address", "port (optional)", ConnectionMethod);
        this.api.connect(device);
    }
                        
                    
  15. Add a method to take payments
  16.                     
    public boolean pay() {
        return this.api.sale(new BigInteger("1000"), Currency.GBP);
        // Let´s start our first transaction for 10 pounds
        // The amount should always be in the minor unit of the currency
    }
                        
                    
  17. Add a method to disconnect from the card reader
  18.                     
    public void disconnect(){
        this.api.disconnect();
    
    }
                        
                    
  19. Eventually, MyClass.java must look like this after implementing all the necessary methods:
  20.                     
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    
    import com.handpoint.api.*;
    import com.handpoint.api.shared.*;
    import okhttp3.*;
    
    import java.math.BigInteger;
    import java.util.List;
    
    public class MyClass implements Events.Required, Events.ConnectionStatusChanged, Events.CurrentTransactionStatus {
    
        Hapi api;
        Device device;
        MainActivity myActivity;
    
        public MyClass(MainActivity myActivity) {
          this.myActivity = myActivity;
          initApi(myActivity);
        }
    
        public void initApi(Context context) {
          String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
          HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret);
    
          Settings settings = new Settings();
          settings.getReceiptsAsURLs = true;
          this.api = HapiFactory.getAsyncInterface(this, this, handpointCredentials, settings);
          this.api.registerEventsDelegate(this);
        }
    
        public void discoverDevices() {
          this.api.searchDevices(ConnectionMethod.ANDROID_PAYMENT);
    
        }
    
        @Override
        public void deviceDiscoveryFinished(List<Device> devices) {
          for (Device device : devices) {
            if (device.getName() != null) {
              this.device = device;
              this.api.connect(this.device);
            }
          }
        }
    
        @Override
        public void connectionStatusChanged(ConnectionStatus status, Device device) {
          if (status == ConnectionStatus.Connected) {
            myActivity.notifyConnectionStatus(true);
          } else if (status == ConnectionStatus.Disconnected) {
            myActivity.notifyConnectionStatus(false);
          }
        }
    
        public void connect() {
          Device device = new Device("LocalDevice", "0821032398-PAXA920", "", ConnectionMethod.ANDROID_PAYMENT);
          this.api.connect(device);
        }
    
        public boolean pay() {
            return this.api.sale(new BigInteger("1000"), Currency.GBP);
        }
    
        @Override
        public void endOfTransaction(TransactionResult transactionResult, Device device) {
        }
    
        @Override
        public void currentTransactionStatus(StatusInfo statusInfo, Device device) {
        }
    
        @Override
        public void transactionResultReady(TransactionResult transactionResult, Device device) {
        }
    
        @Override
        public void signatureRequired(SignatureRequest request, Device device) {
        }
    
        public void disconnect(){
            this.api.disconnect();
        }
    }
                        
                    

Let's create a User Interface!

  1. Create buttons
  2. Go to the layout folder of your project and add the following code to the xml file:

                        
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
        <Button
                android:id="@+id/payNowBtn"
                android:text="Pay Now"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
        <Button
                android:id="@+id/connectCardReader"
                android:text="Connect Card Reader"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
        <Button
                android:id="@+id/disconnectCardReader"
                android:text="Disconnect Card Reader"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
        <EditText
                android:id="@+id/textConsole"
                android:layout_width="match_parent"
                android:layout_height="211dp"
                android:layout_margin="@dimen/dp2"
                android:background="#000000"
                android:ems="10"
                android:enabled="false"
                android:gravity="start|center_horizontal|top"
                android:textColor="#FFFFFF" />
    
    </LinearLayout>
                        
                    
  3. Initialize the UI components
  4. Go to your main activity (entry point of your program) and create new methods to initialize the buttons:

                        
    package com.example.androidGettingStarted;
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    
    public class MainActivity extends Activity {
        private Button payNowButton;
        private Button connectButton;
        private Button disconnectButton;
        private EditText textConsole;
        /**
         * Called when the activity is first created.
         */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            initializeComponents();
        }
        public void initializeComponents() {
            payNowButton = (Button) findViewById(R.id.payNowBtn);
            payNowButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    //Call an OnClick method
                }
            });
            connectButton = (Button) findViewById(R.id.connectCardReader);
            connectButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    //Call an OnClick method
                }
            });
            disconnectButton = (Button) findViewById(R.id.disconnectCardReader);
            disconnectButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    //Call an OnClick method
                }
            });
    
            textConsole = (EditText) findViewById(R.id.textConsole);
        }
    }
                        
                    

Let's implement the android lifecycle methods!

    Implementing your activity lifecycle methods properly ensures your app behaves well in several ways, including that it:

    • Does not crash if the user receives a phone call or switches to another app while using your app
    • Does not consume valuable system resources when the user is not actively using it
    • Does not lose the user's progress if they leave your app and return to it at a later time
    • Does not crash or lose the user's progress when the screen rotates between landscape and portrait orientation
  1. In your main activity add the following lifecycle methods :
  2.                 
    @Override
    public void onPause() {
    super.onPause();  // Always call the superclass method first
    myClass.disconnect(); // disconnects from the card reader if the application is paused
    }
    
    @Override
    public void onResume() {
    super.onResume();  // Always call the superclass method first
    }
    
    @Override
    public void onBackPressed() {
    moveTaskToBack(true); // When the user clicks the back button the application is moved in the background
    }
    
    @Override
    public void onStop() {
    super.onStop();  // Always call the superclass method first
    }
    
    @Override
    public void onRestart() {
    super.onRestart();  // Always call the superclass method first
    initializeButtons(); // initialize the buttons again after restarting the app
    }
    
    @Override
    public void onDestroy() {
    super.onDestroy();
    }
                    
                

Let’s notify the user about the state of the connection with the payment terminal

  1. Trigger the ConnectionStatusChanged notifications in MyClass
  2.                     
    // If your application is running directly on the payment terminal we will use (ConnectionMethod.ANDROID_PAYMENT)
    @Override
    public void connectionStatusChanged(ConnectionStatus status, Device device) {
      if (status == ConnectionStatus.Connected) {
        myActivity.notifyConnectionStatus(true);
      } else if (status == ConnectionStatus.Disconnected) {
        myActivity.notifyConnectionStatus(false);
      }
    }
                        
                    
  3. Create an AlertDialog in your main activity to display the connection status of the payment terminal
  4.                     
    public void notifyConnectionStatus(boolean connected) {
      this.runOnUiThread(new Runnable() {
        @Override
        public void run() {
          String positiveButton = "OK";
          WebView webView = new WebView(MainActivity.this);
          AlertDialog.Builder alert = new AlertDialog.Builder(MainActivity.this)// Defines an AlertDialog that will popup
            .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
              @Override
              public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.dismiss();
              }
            })
            .setView(webView);
          if (connected) {
            alert.setTitle("Connected to Device");
          } else {
            alert.setTitle("Disconnected from Device");
          }
          alert.show();
        }
      });
    }             
                    

Let’s notify the cardholder of the different transaction statuses

  1. Trigger the CurrentTransactionStatus notifications in MyClass
  2.                     
    @Override
    public void currentTransactionStatus(StatusInfo statusInfo, Device device) {
        myActivity.printTransactionStatus(statusInfo.getMessage());
    }
                        
                    
  3. Print in the Test App console, the transaction state.
  4.                     
      public void printTransactionStatus(String message) {
        this.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            textConsole.setText("Status Received -> " + message);
          }
        });
      }
                        
                    

Let´s display a receipt at the end of a transaction!

    in this example, you are going to display the customer receipt. However, to have your app accredited by Handpoint you need to display BOTH the customer receipt AND the merchant receipt at the end of a transaction. We decided to only display one receipt to simplify this basic application as much as possible.

  1. Fetch the cardholder's receipt from the method EndOfTransaction in MyClass
  2.                     
    @Override
    public void endOfTransaction(TransactionResult transactionResult, Device device) {
        // The object TransactionResult holds the url to retrieve the merchant and cardholder receipt from a cloud server.
        // Other information can be accessed through this object like the transaction ID, the currency, the amount, etc.
        byte[] response = downloadReceipt(result.getCustomerReceipt());
        String receipt = new String(response);
        myActivity.printTransactionStatus("Transaction result received");
        myActivity.callReceiptDialog(receipt);
    }
    
    private byte[] downloadReceipt(String url) {
        try {
            Call call = buildGETCall(url);
            Response response = call.execute();
            ResponseBody responseBody = response.body();
            if (responseBody != null) {
                return responseBody.bytes();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "Something went wrong".getBytes();
    }
    
    private Call buildGETCall(String url) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .build();
    
        return client.newCall(request);
    }
                        
                    
  3. Create an AlertDialog in your main activity to display the receipt on the android device
  4.                     
    public void callReceiptDialog(final String customerReceipt) {
        this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                String positiveButton = "OK";
                WebView webView = new WebView(MainActivity.this); // Create a webView to display the receipt
                webView.loadData(customerReceipt, "text/html", "UTF-8");
                webView.getSettings().setDefaultFontSize(20);
                webView.setVerticalScrollBarEnabled(true);
                new AlertDialog.Builder(MainActivity.this)// Defines an AlertDialog that will popup
                    .setTitle("Transaction result")
                    .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            dialogInterface.dismiss();
                        }
                    })
                    .setView(webView)
                    .show();
            }
        });
    }
                        
                    

Final Result!

Here is how MyClass and your main activity must eventually look like:

  1. Main Activity (entry point for your program):
  2.                 
    package com.example.androidGettingStarted;
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.os.Bundle;
    import android.view.View;
    import android.webkit.WebView;
    import android.widget.Button;
    import android.widget.EditText;
    
    public class MainActivity extends Activity {
      private Button payNowButton;
      private Button connectButton;
      private Button disconnectButton;
      private EditText textConsole;
      MyClass myClass;
    
      public MainActivity() {
    
      }
    
      /**
       * Called when the activity is first created.
       */
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myClass = new MyClass(this);
        initializeComponents();
      }
    
      public void initializeComponents() {
        payNowButton = (Button) findViewById(R.id.payNowBtn);
        payNowButton.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
            myClass.pay();
          }
        });
        connectButton = (Button) findViewById(R.id.connectCardReader);
        connectButton.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
            myClass.discoverDevices();
            // myClass.connect(); // For a direct connection to the card reader
          }
        });
        disconnectButton = (Button) findViewById(R.id.disconnectCardReader);
        disconnectButton.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
            myClass.disconnect();
          }
        });
    
        textConsole = (EditText) findViewById(R.id.textConsole);
      }
    
      public void callReceiptDialog(final String customerReceipt) {
        this.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            String positiveButton = "OK";
            WebView webView = new WebView(MainActivity.this); // Create a webView to display the receipt
            webView.loadData(customerReceipt, "text/html", "UTF-8");
            webView.getSettings().setDefaultFontSize(20);
            webView.setVerticalScrollBarEnabled(true);
            new AlertDialog.Builder(MainActivity.this)// Defines an AlertDialog that will popup
              .setTitle("Transaction result")
              .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                  dialogInterface.dismiss();
                }
              })
              .setView(webView)
              .show();
          }
        });
      }
    
      public void notifyConnectionStatus(boolean connected) {
        this.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            String positiveButton = "OK";
            WebView webView = new WebView(MainActivity.this);
    
            AlertDialog.Builder alert = new AlertDialog.Builder(MainActivity.this)// Defines an AlertDialog that will popup
              .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                  dialogInterface.dismiss();
                }
              })
              .setView(webView);
    
            if (connected) {
              alert.setTitle("Connected to Device");
            } else {
              alert.setTitle("Disconnected from Device");
            }
    
            alert.show();
          }
        });
      }
    
      @Override
      public void onPause() {
        super.onPause();  // Always call the superclass method first
        myClass.disconnect(); // disconnects from the card reader if the application is paused
      }
    
      @Override
      public void onResume() {
        super.onResume();  // Always call the superclass method first
      }
    
      @Override
      public void onBackPressed() {
        moveTaskToBack(true); // The application is moved in the background
      }
    
      @Override
      public void onStop() {
        super.onStop();  // Always call the superclass method first
      }
    
      @Override
      public void onRestart() {
        super.onRestart();  // Always call the superclass method first
        initializeComponents(); // initialize the buttons again after restarting the app
      }
    
      @Override
      public void onDestroy() {
        super.onDestroy();
      }
    
      public void printTransactionStatus(String message) {
        this.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            textConsole.setText("Status Received -> " + message);
          }
        });
      }
    }
                
  3. My Class:
  4.                 
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    
    import com.handpoint.api.HandpointCredentials;
    import com.handpoint.api.Hapi;
    import com.handpoint.api.HapiFactory;
    import com.handpoint.api.Settings;
    import com.handpoint.api.shared.ConnectionMethod;
    import com.handpoint.api.shared.ConnectionStatus;
    import com.handpoint.api.shared.Currency;
    import com.handpoint.api.shared.Device;
    import com.handpoint.api.shared.Events;
    import com.handpoint.api.shared.SignatureRequest;
    import com.handpoint.api.shared.StatusInfo;
    import com.handpoint.api.shared.TransactionResult;
    
    import java.math.BigInteger;
    import java.util.List;
    
    import okhttp3.Call;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.Response;
    import okhttp3.ResponseBody;
    
    
    public class MyClass implements Events.Required, Events.ConnectionStatusChanged, Events.CurrentTransactionStatus {
    
      Hapi api;
      Device device;
      MainActivity myActivity;
    
      public MyClass(MainActivity myActivity) {
        this.myActivity = myActivity;
        initApi(myActivity);
      }
    
      public void initApi(Context context) {
        String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
        HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret);
    
        Settings settings = new Settings();
        settings.getReceiptsAsURLs = true;
        this.api = HapiFactory.getAsyncInterface(this, context, handpointCredentials, settings);
        this.api.registerEventsDelegate(this);
      }
    
      public void discoverDevices() {
        this.api.searchDevices(ConnectionMethod.ANDROID_PAYMENT);
      }
    
      @Override
      public void deviceDiscoveryFinished(List devices) {
        for (Device device : devices) {
          this.device = device;
          this.api.connect(this.device);
        }
      }
    
      @Override
      public void connectionStatusChanged(ConnectionStatus status, Device device) {
        if (status == ConnectionStatus.Connected) {
          myActivity.notifyConnectionStatus(true);
        } else if (status == ConnectionStatus.Disconnected) {
          myActivity.notifyConnectionStatus(false);
        }
      }
    
      public void connect() {
        Device device = new Device("LocalDevice", "0821032398-PAXA920", "", ConnectionMethod.ANDROID_PAYMENT);
        this.api.connect(device);
      }
    
      public boolean pay() {
        return this.api.sale(new BigInteger("1000"), Currency.GBP);
      }
    
      @Override
      public void endOfTransaction(TransactionResult transactionResult, Device device) {
        byte[] response = downloadReceipt(transactionResult.getCustomerReceipt());
        String receipt = new String(response);
        myActivity.printTransactionStatus("Transaction result received");
        myActivity.callReceiptDialog(receipt);
      }
    
      private byte[] downloadReceipt(String url) {
        try {
          Call call = buildGETCall(url);
          Response response = call.execute();
          ResponseBody responseBody = response.body();
          if (responseBody != null) {
            return responseBody.bytes();
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
        return "Something went wrong".getBytes();
      }
    
      private Call buildGETCall(String url) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
          .url(url)
          .build();
    
        return client.newCall(request);
      }
    
      @Override
      public void transactionResultReady(TransactionResult transactionResult, Device device) {
      }
    
      @Override
      public void signatureRequired(SignatureRequest request, Device device) {
      }
    
      public void disconnect() {
        this.api.disconnect();
      }
    
      @Override
      public void currentTransactionStatus(StatusInfo statusInfo, Device device) {
        myActivity.printTransactionStatus(statusInfo.getMessage());
      }
    }
    
                    
                

Let's run our program!

Run the program by clicking the "play" button, ensure that your android device is connected via USB and correctly recognized by your computer. If not, you can download specific drivers on the device manufacturer website and install them.

  1. Click on "Connect Card Reader" (ConnectionMethod.ANDROID_PAYMENT) this can take a little bit of time (5 sec max) due to the fact that the payment terminal is downloading its configuration. With the reception of the connected state, you will get a notification of the successful connection.
  2. Click "Pay Now"
  3. Follow the instructions on the payment terminal
  4. When the transaction is finished, the cardholder´s receipt should pop-up on the device screen
  5. Click on "Disconnect From Card Reader" to stop the connection with the payment terminal

Terminal Simulator Integration

Introduction

This tutorial is guiding you through all the steps to create a basic payment application for Android devices using a card reader simulator. The simulator only has limited capabilities and we highly recommend that you order a development kit if you want to carry a full integration. The development kit contains a card reader as well as a test card and will allow you to test your integration from end to end.

The new generation of Handpoint SDK's is designed to make your life easier. Simple and created for humans, it does not require any specific knowledge of the payment industry to be able to start accepting credit/debit card transactions.

At Handpoint we take care of securing every transaction so you don´t have to worry about it while creating your application. We encrypt data from the payment terminal to the bank with our point-to-point encryption solution. Our platform is always up to the latest PCI-DSS security requirements.

Connecting to the simulator

The SDK offers a method in which you will need to specify the card reader to be used:


hapi.connect(new Device("Name", "Port", "Address", ConnectionMethod.****))
                

Simply set the ConnectionMethod to Simulator, i.e. ConnectionMethod.Simulator. The SDK does the rest. You don't need to search via bluetooth for surrounding card readers when using the simulator.


hapi.connect(new Device("Name", "Port", "Address", ConnectionMethod.Simulator))
            

Controlling responses

The simulator mimics the card reader as much as possible regarding information flow from the SDK interface to your application. It will return all the transaction statuses, transaction results and receipts.

Results of a transaction are controlled by the amount sent into the sale function:
The 3rd position from the right sets the desired financial status, 0 = Authorized and 1 = Declined.
The 4th position from the right sets the desired verification method, 0 = Signature and 1 = PIN.

hapi.Sale(X10XX, Currency.GBP); // amount = X 10 XX - where X represents an integer [0;9]

  • X 00 XX = Signature authorized
  • X 01 XX = Signature declined
  • X 10 XX = Pin authorized
  • X 11 XX = Pin declined

Let's start programming!

  1. Modify the AndroidManifest.xml
  2. The following lines have to be added inside the manifest in order to enable permissions:

    
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.INTERNET"/>
                            

    In order for the application to handle screen rotation the following line of code has to be added inside your manifest under <activity>:

    
    <activity android:configChanges="orientation|keyboardHidden|screenSize">
                            

    According to the android version of the device you are using you will have to modify the following line of your manifest:

    
    <uses-sdk android:minSdkVersion="14"/>//change the SDK version to the one corresponding to the device you are using
                            

    Your manifest should look like this:

    
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                package="com.example.androidGettingStarted"
                android:versionCode="1"
                android:versionName="1.0">
        <uses-permission android:name="android.permission.BLUETOOTH"/>
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
        <uses-permission android:name="android.permission.INTERNET"/>
        <uses-sdk android:minSdkVersion="14"/>
        <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
            <activity android:name="MyActivity"
                    android:configChanges="orientation|keyboardHidden|screenSize"
                    android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
            </activity>
        </application>
    </manifest>
                    
  3. Create a Java class
  4. Create a new java class called MyClass.java and include com.handpoint.api.* as a dependency:

    
    package com.example.androidGettingStarted;
    
    import com.handpoint.api.*;
    
    public class MyClass {
    }
                    
  5. Initialize the API
  6. 
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    import com.handpoint.api.*;
    
    public class MyClass  {
    
        Hapi api;
    
        public MyClass(Context context) {
            initApi(context);
        }
    
        public void initApi(Context context) {
            String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
            this.api = HapiFactory.getAsyncInterface(this, context).sharedSecret(sharedSecret);
            // Register class as listener for required events
            this.api.registerEventsDelegate(this);
            // The api is now initialized. Yay! we've even set a shared secret!
            // The shared secret is a unique string shared between the card reader and your mobile application.
            // It prevents other people to connect to your card reader.
        }
    }
                    
  7. Implement the mandatory Events(Events.Required)
  8. 
    package com.example.androidGettingStarted;
    
    import android.content.Context;
    import com.handpoint.api.*;
    import java.util.List;
    
    public class MyClass implements Events.Required {
    
        Hapi api;
    
        public MyClass(Context context) {
            initApi(context);
        }
        public void initApi(Context context) {
            String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
            this.api = HapiFactory.getAsyncInterface(this, context).sharedSecret(sharedSecret);
            // Register class as listener for required events
            this.api.registerEventsDelegate(this);
            // The api is now initialized. Yay! we've even set a shared secret!
            // The shared secret is a unique string shared between the card reader and your mobile application.
            // It prevents other people to connect to your card reader.
        }
    
        @Override
        public void deviceDiscoveryFinished(List<Device> list) {
            // Only needed when using a card reader
            // Here you get a list of Bluetooth devices discovered around your android device
        }
    
        @Override
        public void signatureRequired(SignatureRequest signatureRequest, Device device) {
            // You'll be notified here if a sale process needs a signature verification
            // A signature verification is needed if the cardholder uses an MSR card or a chip & signature card
            // This method will not be invoked if a transaction is made with a Chip & PIN card
            // At this step, you are supposed to display the merchant receipt to the cardholder on the android device
            // the cardholder must have the possibility to accept or decline the transaction
            // If the cardholder clicks on decline, the transaction is VOID
            // If the cardholder clicks on accept he is then asked to sign electronically the receipt
            this.api.signatureResult(true);// This line means that the cardholder ALWAYS accepts to sign the receipt
            // For this sample app we are not going to implement the whole signature process
        }
    
        @Override
        public void endOfTransaction(TransactionResult transactionResult, Device device) {
            // The object TransactionResult stores a lot of information
            // For example, it holds the merchant receipt as well as the cardholder receipt
            // Other information can be accessed through this object like the transaction ID, the amount...
        }
    
    }
                    
  9. Add a method to connect to the simulator
  10. 
    public void connect() {
        Device device = new Device("name", "address", "port", ConnectionMethod.SIMULATOR);
        this.api.connect(device);
    }
                    
  11. Add a method to take payments with the simulator
  12. 
    hapi.Sale(X10XX, Currency.GBP); // amount = X 10 XX - where X represents an integer [0;9]
                    

    Let´s add 4 different functions to MyClass.java in order to represent the 4 main payment scenarios:

    
    public boolean payWithSignatureAuthorized() {
        return this.api.sale(new BigInteger("10000"), Currency.GBP);
        // amount X00XX where X represents an integer [0;9] --> Signature authorized
    }
    public boolean payWithSignatureDeclined() {
        return this.api.sale(new BigInteger("10100"), Currency.GBP);
        // amount X01XX where X represents an integer [0;9] --> Signature declined
    }
    public boolean payWithPinAuthorized() {
        return this.api.sale(new BigInteger("11000"), Currency.GBP);
        // amount X10XX where X represents an integer [0;9] --> PIN authorized
    }
    public boolean payWithPinDeclined() {
        return this.api.sale(new BigInteger("11100"), Currency.GBP);
        // amount X11XX where X represents an integer [0;9] --> PIN declined
    }
                    

Let's create a User Interface!

  1. Create buttons
  2. Go to the layout folder of your project and add the following code to the xml file containing the code for your user interface:

    
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:orientation="vertical"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent">
    <Button
                android:id="@+id/connectSimulator"
                android:text="Connect To Simulator"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
    <Button
                android:id="@+id/payWithSignatureAuthorized"
                android:text="Pay With Signature Authorized"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
    <Button
                android:id="@+id/payWithSignatureDeclined"
                android:text="Pay With Signature Declined"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
    <Button
                android:id="@+id/payWithPinAuthorized"
                android:text="Pay With Pin Authorized"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
    <Button
                android:id="@+id/payWithPinDeclined"
                android:text="Pay With PIN Declined"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" />
    
    </LinearLayout>
                    
  3. Initialize the buttons
  4. Go to your main activity (entry point of your program) and create new methods to initialize the buttons:

    
    package com.example.androidGettingStarted;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    
    
    public class MyActivity extends Activity {
        private Button connectToSimulatorButton;
        private Button payWithSignatureAuthorizedButton;
        private Button payWithSignatureDeclinedButton;
        private Button payWithPinAuthorizedButton;
        private Button payWithPinDeclinedButton;
        /**
        * Called when the activity is first created.
        */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            initializeButtons();
        }
        public void initializeButtons() {
        connectToSimulatorButton = (Button) findViewById(R.id.connectSimulator);
        connectToSimulatorButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
            //Call an OnClick method
            }
        });
    
        payWithSignatureAuthorizedButton = (Button) findViewById(R.id.payWithSignatureAuthorized);
        payWithSignatureAuthorizedButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
            //Call an OnClick method
            }
        });
    
        payWithSignatureDeclinedButton = (Button) findViewById(R.id.payWithSignatureDeclined);
        payWithSignatureDeclinedButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
            //Call an OnClick method
            }
        });
    
        payWithPinAuthorizedButton = (Button) findViewById(R.id.payWithPinAuthorized);
        payWithPinAuthorizedButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
            //Call an OnClick method
            }
        });
    
        payWithPinDeclinedButton = (Button) findViewById(R.id.payWithPinDeclined);
        payWithPinDeclinedButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
            //Call an OnClick method
            }
        });
    }
                    

Let's link our user interface with methods!

  1. Referencing your main activity in MyClass
  2. To be able to call methods from your main activity in MyClass you need to define your main activity as a Context in MyClass and then define a new instance of it:

    
    public class MyClass implements Events.Required {
        Hapi api;
        MyActivity UIClass;
    
    public MyClass(MyActivity activity){
        initApi((Context) activity);
        UIClass = activity;
    }
                    
  3. Referencing Myclass in your main activity
  4. To be able to call methods from MyClass to your main activity you need to instantiate MyClass in your main activity:

    
    public class MyActivity extends Activity{
        private Button connectToSimulatorButton;
        private Button payWithSignatureAuthorizedButton;
        private Button payWithSignatureDeclinedButton;
        private Button payWithPinAuthorizedButton;
        private Button payWithPinDeclinedButton;
        MyClass myClass;
    
        /**
        * Called when the activity is first created.
        */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            myClass = new MyClass(this);
            initializeButtons();
        }
                    
  5. Add OnClick methods when using the buttons
  6. Let's call specific methods when clicking on the different buttons:

    
    public void initializeButtons() {
        connectToSimulatorButton = (Button) findViewById(R.id.connectSimulator);
        connectToSimulatorButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
            myClass.connect();
            }
        });
        payWithSignatureAuthorizedButton = (Button) findViewById(R.id.payWithSignatureAuthorized);
        payWithSignatureAuthorizedButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
            myClass.payWithSignatureAuthorized();
            }
        });
        payWithSignatureDeclinedButton = (Button) findViewById(R.id.payWithSignatureDeclined);
        payWithSignatureDeclinedButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
            myClass.payWithSignatureDeclined();
            }
        });
        payWithPinAuthorizedButton = (Button) findViewById(R.id.payWithPinAuthorized);
        payWithPinAuthorizedButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
            myClass.payWithPinAuthorized();
            }
        });
        payWithPinDeclinedButton = (Button) findViewById(R.id.payWithPinDeclined);
        payWithPinDeclinedButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
            myClass.payWithPinDeclined();
            }
        });
    }
                    

Let's implement the android lifecycle methods!

    Implementing your activity lifecycle methods properly ensures your app behaves well in several ways, including that it:

    • Does not crash if the user receives a phone call or switches to another app while using your app
    • Does not consume valuable system resources when the user is not actively using it
    • Does not lose the user's progress if they leave your app and return to it at a later time
    • Does not crash or lose the user's progress when the screen rotates between landscape and portrait orientation
  1. Add the lifecycle methods in your main activity
  2. 
    @Override
    public void onPause() {
        super.onPause();  // Always call the superclass method first
        myClass.disconnect(); // disconnects from the card reader if the application is paused
    }
    
    @Override
    public void onResume() {
        super.onResume();  // Always call the superclass method first
    }
    
    @Override
    public void onBackPressed() {
        moveTaskToBack(true); // When the user clicks the back button the application is moved in the background
    }
    
    @Override
    public void onStop() {
        super.onStop();  // Always call the superclass method first
    }
    
    @Override
    public void onRestart() {
        super.onRestart();  // Always call the superclass method first
        initializeButtons(); // initialize the buttons again after restarting the app
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
                

Let´s display a receipt at the end of a transaction!

    in this example, you are going to display the customer receipt. However, to have your app accredited by Handpoint you need to display BOTH the customer receipt AND the merchant receipt at the end of a transaction. We decided to only display one receipt to simplify this basic application as much as possible.

  1. Fetch the cardholder's receipt from the method EndOfTransaction in MyClass
  2. 
    @Override
    public void endOfTransaction(TransactionResult transactionResult, Device device) {
        // The object TransactionResult stores a lot of information, for example, it holds the different receipts
        // Other information can be accessed through this object like the transaction ID, the currency, the amount...
        UIClass.callReceiptDialog(transactionResult.getCustomerReceipt());
    }
                    
  3. Create an AlertDialog in your main activity to display the receipt on the android device
  4. 
    public void callReceiptDialog(final String customerReceipt) {
        this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                String positiveButton = "OK";
                WebView webView = new WebView(MyActivity.this); // Create a webView to display the receipt
                webView.loadData(customerReceipt, "text/html", "UTF-8");
                webView.getSettings().setDefaultFontSize(20);
                webView.setVerticalScrollBarEnabled(true);
                new AlertDialog.Builder(MyActivity.this)// Defines an AlertDialog that will popup
                    .setTitle("Transaction result")
                    .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            dialogInterface.dismiss();
                        }
                    })
                    .setView(webView)
                    .show();
            }
        });
    }
                    

Final Result!

Here is how MyClass and your main activity must eventually look like:

  1. main activity (entry point for your program):
  2. 
    package com.example.androidGettingStarted;
    
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.os.Bundle;
    import android.view.View;
    import android.webkit.WebView;
    import android.widget.Button;
    import com.handpoint.api.TransactionResult;
    
    
    public class MyActivity extends Activity{
        private Button connectToSimulatorButton;
        private Button payWithSignatureAuthorizedButton;
        private Button payWithSignatureDeclinedButton;
        private Button payWithPinAuthorizedButton;
        private Button payWithPinDeclinedButton;
        MyClass myClass;
    
        /**
        * Called when the activity is first created.
        */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            myClass = new MyClass(this);
            initializeButtons();
        }
        public void initializeButtons() {
            connectToSimulatorButton = (Button) findViewById(R.id.connectSimulator);
            connectToSimulatorButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                myClass.connect();
                }
            });
            payWithSignatureAuthorizedButton = (Button) findViewById(R.id.payWithSignatureAuthorized);
            payWithSignatureAuthorizedButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                myClass.payWithSignatureAuthorized();
                }
            });
            payWithSignatureDeclinedButton = (Button) findViewById(R.id.payWithSignatureDeclined);
            payWithSignatureDeclinedButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                myClass.payWithSignatureDeclined();
                }
            });
            payWithPinAuthorizedButton = (Button) findViewById(R.id.payWithPinAuthorized);
            payWithPinAuthorizedButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                myClass.payWithPinAuthorized();
                }
            });
            payWithPinDeclinedButton = (Button) findViewById(R.id.payWithPinDeclined);
            payWithPinDeclinedButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                myClass.payWithPinDeclined();
                }
            });
        }
        public void callReceiptDialog(final String customerReceipt) {
            this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    String positiveButton = "OK";
                    WebView webView = new WebView(MyActivity.this); // Create a webView to display the receipt
                    webView.loadData(customerReceipt, "text/html", "UTF-8");
                    webView.getSettings().setDefaultFontSize(20);
                    webView.setVerticalScrollBarEnabled(true);
                    new AlertDialog.Builder(MyActivity.this)// Defines an AlertDialog that will popup
                        .setTitle("Transaction result")
                        .setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                dialogInterface.dismiss();
                            }
                        })
                        .setView(webView)
                        .show();
                }
            });
        }
    
        @Override
        public void onPause() {
            super.onPause();  // Always call the superclass method first
            myClass.disconnect(); // disconnects from the card reader if the application is paused
        }
    
        @Override
        public void onResume() {
            super.onResume();  // Always call the superclass method first
        }
    
        @Override
        public void onBackPressed() {
            moveTaskToBack(true); // the application is moved to the background
        }
    
        @Override
        public void onStop() {
            super.onStop();  // Always call the superclass method first
        }
    
        @Override
        public void onRestart() {
            super.onRestart();  // Always call the superclass method first
            initializeButtons(); // initialize the buttons again after restarting the app
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
        }
    }
                
  3. My Class :
  4. 
    package com.example.androidGettingStarted;
    import java.math.BigInteger;
    import android.content.Context;
    import com.handpoint.api.*;
    
    import java.util.List;
    
    public class MyClass implements Events.Required {
        Hapi api;
        MyActivity UIClass;
    
        public MyClass(MyActivity activity){
            initApi((Context) activity);
            UIClass = activity;
        }
        public void initApi(Context context) {
            String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
            this.api = HapiFactory.getAsyncInterface(this, context).sharedSecret(sharedSecret);
            // Register class as listener for required events
            this.api.registerEventsDelegate(this);
            // The api is now initialized. Yay! we've even set a shared secret!
            // The shared secret is a unique string shared between the card reader and your mobile application.
            // It prevents other people to connect to your card reader.
        }
    
        @Override
        public void deviceDiscoveryFinished(List<Device> list) {
            // Only needed when using a payment terminal, here you get a list of Bluetooth devices discovered
        }
    
        public void connect() {
            Device device = new Device("name", "address", "port", ConnectionMethod.SIMULATOR);
            this.api.connect(device);
        }
    
    
        public boolean payWithSignatureAuthorized() {
            return this.api.sale(new BigInteger("10000"), Currency.GBP);
            // amount X00XX where X represents an integer [0;9] --> Signature authorized
        }
    
        public boolean payWithSignatureDeclined() {
            return this.api.sale(new BigInteger("10100"), Currency.GBP);
            // amount X01XX where X represents an integer [0;9] --> Signature declined
        }
    
        public boolean payWithPinAuthorized() {
            return this.api.sale(new BigInteger("11000"), Currency.GBP);
            // amount X10XX where X represents an integer [0;9] --> PIN authorized
        }
    
        public boolean payWithPinDeclined() {
            return this.api.sale(new BigInteger("11100"), Currency.GBP);
            // amount X11XX where X represents an integer [0;9] --> PIN declined
        }
    
        @Override
        public void signatureRequired(SignatureRequest signatureRequest, Device device) {
            // You'll be notified here if a sale process needs a signature verification
            // A signature verification is needed if the cardholder uses an MSR card or a chip & signature card
            // This method will not be invoked if a transaction is made with a Chip & PIN card
            // At this step, you should display the merchant receipt to the cardholder on the android device
            // The cardholder must have the possibility to accept or decline the transaction
            // If the cardholder clicks on decline, the transaction is VOID
            // If the cardholder clicks on accept he is then asked to sign electronically the receipt
            this.api.signatureResult(true);
            // This line means that the cardholder ALWAYS accepts to sign the receipt
            // For this sample app we are not going to implement the whole signature process
        }
    
        @Override
        public void endOfTransaction(TransactionResult transactionResult, Device device) {
            // The object TransactionResult stores holds the different receipts
            // Other information can be accessed through this object like the transaction ID, the amount...
            UIClass.callReceiptDialog(transactionResult.getCustomerReceipt());
        }
    
    }
            

Let's run our program!

Run the program by clicking the "play" button, ensure that your android device is connected via USB and correctly recognized by your computer. If not, you can download specific drivers on your device manufacturer website and install them.

On your android device, click on the "Connect to simulator" button and then click on one of the payment types available and have a look at the receipts! Voila!

Transactions

Sale

method Available since 5.0.0

sale

A sale initiates a payment operation to the card reader. In it's simplest form you only have to pass the amount and currency but it also accepts a map with extra parameters.

Parameters

Parameter Notes
amount *
BigInteger
Amount of funds to charge - in the minor unit of currency (f.ex. 1000 is 10.00 GBP)
currency *
Currency
Currency of the charge
map
Map
A map including extra optional transaction parameters.


Events invoked

currentTransactionStatus
Invoked during a transaction, it fetches statuses coming from the terminal (ex : 'waiting for card' or 'waiting for PIN entry').
signatureRequired
Invoked if card verification requires signature.
endOfTransaction
Invoked when the terminal finishes processing the transaction.

Returns

Boolean
true if the operation was successfully sent to the terminal

Code example

//Initiate a sale for 10.00 in Great British Pounds
api.sale(new BigInteger("1000"),Currency.GBP);

Sale And Tokenize Card

method Available since 5.0.0

saleAndTokenizeCard

A sale operation which also returns a card token. (not available for all acquirers, please check with Handpoint to know if tokenization is supported for your acquirer of choice)

Parameters

Parameter Notes
amount *
BigInteger
Amount of funds to charge - in the minor unit of currency (f.ex. 1000 is 10.00 GBP)
currency *
Currency
Currency of the charge
map
Map
A map including extra optional transaction parameters.


Events invoked

currentTransactionStatus
Invoked during a transaction, it fetches statuses coming from the terminal (ex : 'waiting for card' or 'waiting for PIN entry').
signatureRequired
Invoked if card verification requires signature.
endOfTransaction
Invoked when the terminal finishes processing the transaction.

Returns

Boolean
true if the operation was successfully sent to the terminal

Code example

//Initiate a sale for 10.00 in Great British Pounds
api.saleAndTokenizeCard(new BigInteger("1000"),Currency.GBP);

Sale Reversal

method Available since 5.0.0

saleReversal

A sale reversal, also called sale VOID allows the user to reverse a previous sale operation. This operation reverts (if possible) a specific sale identified with a transaction id. In its simplest form you only have to pass the amount, currency and originalTransactionID but it also accepts a map with extra parameters. Note that transactions can only be reversed within a 24 hours timeframe or until the daily batch of transactions has been sent for submission.

Parameters

Parameter Notes
amount *
BigInteger
Amount of funds to charge - in the minor unit of currency (f.ex. 1000 is 10.00 GBP)
currency *
Currency
Currency of the charge
originalTransactionID *
String
Id of the original sale transaction
map
Map
A map including extra optional transaction parameters.
device
Device
This parameter specifies to the system which terminal you want to use for the operations. If none is supplied the system will attempt to use a default terminal, if any.


Events invoked

currentTransactionStatus
Invoked during a transaction, it fetches statuses coming from the terminal (ex : 'waiting for card' or 'waiting for PIN entry').
signatureRequired
Invoked if card verification requires signature.
endOfTransaction
Invoked when the terminal finishes processing the transaction.

Returns

Boolean
true if the operation was successfully sent to the terminal

Code example

//Initiate a reversal for 10.00 in Great British Pounds
api.saleReversal(new BigInteger("1000"),Currency.GBP,"00000000-0000-0000-0000-000000000000");

Refund

method Available since 5.0.0

refund

A refund operation moves funds from the merchant account to the cardholder´s credit card. In it's simplest form you only have to pass the amount and currency but it also accepts a map with extra parameters. Note that a card is required to be swiped, dipped or tapped for this operation.

Parameters

Parameter Notes
amount *
BigInteger
Amount of funds to charge - in the minor unit of currency (f.ex. 1000 is 10.00 GBP)
currency *
Currency
Currency of the charge
map
Map
A map including extra optional transaction parameters.
device
Device
This parameter specifies to the system which terminal you want to use for the operations. If none is supplied the system will attempt to use a default terminal, if any.


Events invoked

currentTransactionStatus
Invoked during a transaction, it fetches statuses coming from the terminal (ex : 'waiting for card' or 'waiting for PIN entry')
signatureRequired
Invoked if card verification requires signature.
endOfTransaction
Invoked when the terminal finishes processing the transaction

Returns

Boolean
true if the operation was successfully sent to terminal

Code example

//Initiate a refund for 10.00 in Great British Pounds
api.refund(new BigInteger("1000"),Currency.GBP);

Refund reversal

method Available since 5.0.0

refundReversal

A refund reversal, also called refund VOID allows the merchant to reverse a previous refund operation. This operation reverts (if possible) a specific refund identified with a transaction id. In it's simplest form you only have to pass the amount, currency and originalTransactionID but it also accepts a map with extra parameters. Note that transactions can only be reversed within a 24 hours timeframe or until the daily batch of transactions has been sent for submission.

Parameters

Parameter Notes
amount *
BigInteger
Amount of funds to charge - in the minor unit of currency (f.ex. 1000 is 10.00 GBP)
currency *
Currency
Currency of the charge
originalTransactionID *
String
transaction id of the original refund
map
Map
A map including extra optional transaction parameters.
device
Device
This parameter specifies to the system which terminal you want to use for the operations. If none is supplied the system will attempt to use a default terminal, if any.


Events invoked

currentTransactionStatus
Invoked during a transaction, it fetches statuses coming from the terminal (ex : 'waiting for card' or 'waiting for PIN entry')
signatureRequired
Invoked if card verification requires signature.
endOfTransaction
Invoked when the terminal finishes processing the transaction

Returns

Boolean
true if the operation was successfully sent to the terminal

Code example

//Initiate a refund reversal for 10.00 in Great British Pounds
api.refundReversal(new BigInteger("1000"),Currency.GBP,"00000000-0000-0000-0000-000000000000");

Signature result

method Available since 5.0.0

signatureResult

A signatureRequired event is invoked during a transaction when a signature verification is required (f.ex when a payment is done with a swiped or chip and sign card). The merchant is required to ask the cardholder for signature and approve (or decline) the signature. signatureResult tells the card reader if the signature was approved by passing the value true in the method. To decline a signature event then false should be passed to the card reader. Note that this event is only required for an HiLite or Hi5 integration and can be safely ignored for a PAX or Telpo integration.

Parameters

Parameter Notes
accepted *
Boolean
pass true if merchant accepts cardholder signature
device
Device
This parameter specifies to the system which terminal you want to use for the operations. If none is supplied the system will attempt to use a default terminal, if any.


Events invoked

currentTransactionStatus
Invoked during a transaction, it fetches statuses coming from the terminal (ex : 'waiting for card' or 'waiting for PIN entry').
endOfTransaction
Invoked when the terminal finishes processing the transaction.

Returns

Boolean
true if the operation was successfully sent to the terminal.

Code example

//Approves signature automatically in signatureRequired event
@Override
public void signatureRequired(SignatureRequest signatureRequest, Device device){
	api.signatureResult(true);
}

Tip Adjustment

method Available since 3.3.0

TipAdjustment

A tip adjustment operation allows merchants to adjust the tip amount of a sale transaction before the batch of transactions is settled by the processor at the end of the day.
Note: This functionality is only available for the restaurant industry in the United States and the processors currently supporting this functionality are TSYS and VANTIV.

Dependencies:
The code example provided depends on RxJava, take a look a their documentation to see how to easily include this dependency in your android project. If you do not want to use RxJava or any additional dependencies then AsyncTask, provided by android, can be used instead for this asynchronous processing. Still we recommend using RxJava as it improves readability and maintainability.

Parameters

Parameter Notes
tipAmount *
BigDecimal
Tip amount added to the original (base) transaction amount - in the minor unit of currency (f.ex. 1000 is 10.00 GBP)
originalTransactionID *
String
Unique id of the original sale transaction as received from the card reader (EFTTransactionID)


Returns

FinancialStatus

Result of the tip adjustment transaction, it returns a FinancialStatus, the possible values are :

- FinancialStatus.AUTHORISED (tip adjustment approved by the processor)
- FinancialStatus.FAILED (system error or timeout)
- FinancialStatus.DECLINED (tip adjustment declined by the processor)

If two tip adjustments are sent for the same sale transaction, the second tip adjustment will override the first one. In case the transaction fails (not declined) we recommend that you prompt the user of the POS to retry the adjustment.

Code example

Observable.fromCallable(new Callable() {
	@Override 
	public FinancialStatus call() throws Exception { 
		return api.tipAdjustment(new BigDecimal(1000), "2bc23910-c3b3-11e6-9e62-07b2a5f091ec"); 
	}
}) 
.subscribeOn(Schedulers.io()) 
.observeOn(AndroidSchedulers.mainThread()) 
.subscribe(new Consumer() { 
	@Override 
	public void accept(@NonNull FinancialStatus status) throws Exception {
		if (status == FinancialStatus.AUTHORISED) { 
			//SUCCESS
		} else if (status == FinancialStatus.DECLINED) {
			//DECLINED
		} else {
			//FAILED
	}
});

Tokenize Card

method Available since 4.4.0

tokenizeCard

Returns the tokenized version of the card used if successful (not available for all acquirers, please check with Handpoint to know if tokenization is supported for your acquirer of choice)

Parameters

Parameter Notes
map
Map
A map including extra optional transaction parameters.


Events invoked

currentTransactionStatus
Invoked during a transaction, it fetches statuses coming from the card reader (ex : 'waiting for card' or 'waiting for PIN entry').
signatureRequired
Invoked if card verification requires signature.
endOfTransaction
Invoked when the card reader finishes processing the transaction.

Returns

Boolean
true if the operation was successfully sent to device

Code example

//Tokenize a card
api.tokenizeCard();

Card PAN

method Available since 5.10.0

cardPan

A cardPan request will return the full PAN of the card being swiped, dipped or tapped. Only the PANs of whitelisted card ranges will be returned by the Handpoint systems. This operation is mostly used to be able to process funds or points from loyalty cards.

Parameters

Parameter Notes
map
Map
A map including extra optional transaction parameters.


Events invoked

currentTransactionStatus
Invoked during a transaction, it fetches statuses coming from the terminal (ex : 'waiting for card' or 'waiting for PIN entry').
endOfTransaction
Invoked when the terminal finishes processing the transaction.

Returns

Boolean
true if the operation was successfully sent to the terminal

Code example

//Gets the PAN of a card
api.cardPan();

Device management

Connect

method Available since 5.0.0

connect

Connects to a device. Whenever the connection to the device is lost, the SDK will keep on trying to establish a connection until it’s re-established. No special actions are needed.

Parameters

Parameter Notes
device *
Device
This parameter specifies which device type you want to connect to.


Events invoked

connectionStatusChanged
Each time the card reader state changes (ex : going from Connected to Disconnected) the ConnectionStatusChanged event is called. It causes the connection manager to invoke this event with the appropriate information.

Returns

Boolean
true if the operation was successful.

Code example

//Connect to a device
Device device = new Device("CardReader7", "08:00:69:02:01:FC", "1", ConnectionMethod.BLUETOOTH);
Device device = new Device("LocalDevice", "0821032398-PAXA920", "", ConnectionMethod.ANDROID_PAYMENT);
Device device = new Device("CloudDevice", "0821032398-PAXA920", "", ConnectionMethod.CLOUD);
api.connect(device);

Disconnect

method Available since 5.0.0

disconnect

Disconnect will stop the active connection (or reconnection process). Please note that the method ignores the current state of the terminal and just stops the connection. Calling disconnect might result in a commmunication error if triggered during a transaction.

Events invoked

connectionStatusChanged
Causes the connection manager to invoke this event with the appropriate information.

Returns

Boolean
true if the operation was successful.

Code example

//Disconnect from current device
api.Disconnect();

Get Paired Devices

method Available since 5.0.0

getPairedDevices

Returns the payment terminals associated with the specified ConnectionMethod

Parameters

Parameter Notes
method *
ConnectionMethod
The type of connection with the payment terminal (Bluetooth, Cloud, etc.).


Returns

List<Device>
The list of payment terminals.

Code example

// Get paired terminals
List<Device> devices = api.getPairedDevices(ConnectionMethod.XXX);

Print Receipt

method Available since 5.0.0

printReceipt

Print on demand functionality allowing the merchant to print any HTML formatted receipt. It is possible to print images or barcodes. A bitmap can also be printed, in order to do so it needs to be rendered as an image and inserted into the html.

Parameters

Parameter Notes
receipt *
String
The receipt must match the following HTML Print Format. The Transaction Report (also called End of Day Report) can be printed from the string returned in the ReportResult event.


Returns

Boolean
true if the receipt was sent to the printer, false otherwise

Code example

//Print a receipt with Handpoint logo (bitmap format)
String validReceipt = "<html><body><img src='data:image/bmp;base64,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'></body></html>";
boolean success = api.printReceipt(validReceipt);

Search Devices

method Available since 5.0.0

searchDevices

Starts the search of payment terminals associated with the specified ConnectionMethod

Parameters

Parameter Notes
method *
ConnectionMethod
The type of connection with the payment terminal (Bluetooth, Cloud, etc.).


Events invoked

deviceDiscoveryFinished
Returns a list of payment terminals.

Code example

// Starts the search for payment terminals.
// You must implement Events.DeviceDiscoveryFinished and subscribe
// to the event delegate in order to receive the result
api.searchDevices(ConnectionMethod.XXX);

Set Locale

method Available since 5.0.0

setLocale

Sets the SDK Locale (language). It is used to set the SDK language as well as the associated date and number formatting.

Parameters

Parameter Notes
locale *
SupportedLocales
The locale to be set. Supported locales are: SupportedLocales.en_CA, SupportedLocales.en_UK, SupportedLocales.en_US, SupportedLocales.hr_HR, SupportedLocales.is_IS.


Code example

// Set canadian english
api.setLocale(SupportedLocales.en_CA);

Set logging level

method Available since 3.2.4

setLogLevel

Sets the log level (info, debug...) for both the payment terminal and the SDK.

Parameters

Parameter Notes
level *
LogLevel
The desired log level. Can be LogLevel.None, LogLevel.Info, LogLevel.Full, LogLevel.Debug
device
Device
This parameter specifies to the system which device should be used for the operations. If no device is supplied, the system will attempt to use a default one.


Events invoked

None
No events are invoked.

Returns

Boolean
true if the operation was successfully sent to device

Code example

//Sets the log level to info
api.setLogLevel(LogLevel.info);

Stop current transaction

method Available since 5.0.0

stopCurrentTransaction

Stops the current transaction. A transaction can be stopped only if the last currentTransactionStatus event reported has the property isCancelAllowed set to true.
NOTE: this operation is Not supported on Datecs devices.

Events invoked

currentTransactionStatus
Invoked after stop transaction. Status UserCancelled will be reported
endOfTransaction
Transaction will fail with status CANCELLED

Returns

Boolean
true if the transaction was successfully stopped, false otherwise

Code example

// Stops current transaction 
if (api.stopCurrentTransaction()) { 
	...
} else { 
	...
}

Update device

method Available since 3.2.4

update

The update operation checks for new software or configuration updates and initiates a download if required.

Parameters

Parameter Notes
device
Device
This parameter specifies to the system which device should be used for the operations. If no device is supplied, the system will attempt to use a default one.


Events invoked

None
The merchant should be notified about the update process.

Returns

Boolean
true if the operation was successfully sent to device

Code example

//Check for card reader update
api.update();

Events subscribers

Register events delegate

method Available since 5.0.0

registerEventsDelegate

Registers a delegate for the SDK events.

Parameters

Parameter Notes
listener *
Object
Any Object implementing one or more of the available delegate interfaces.


Returns

Boolean
True if the new delegate was added successfully

Code example

public class ObjectHelper implements Events.Required, Events.Status, Events.Log, Events.PendingResults, Events.TransactionStarted {
	...
	private void setEventsHandler() {
		// Register this class as listener for events 
		this.api.registerEventsDelegate(this);
		...
	}

}

Unregister events delegate

method Available since 5.0.0

unregisterEventsDelegate

Unregisters an object from SDK events.

Parameters

Parameter Notes
listener *
Object
Any Object implementing one or more of the available delegate interfaces.


Returns

Boolean
True if the new delegate was removed successfully

Code example

public class ObjectHelper implements Events.Required, Events.Status, Events.Log, Events.PendingResults, Events.TransactionStarted {
	...
	private void unsubscribeEventsDelegate() {
	// Stop receiving events
	this.api.unregisterEventsDelegate(this);
	...
}

Events listeners

All

interface Available since 5.0.0

Events.All

An interface which needs to be implemented and added as a listener to receive all available events.

Extends

Required Log Status PendingResults ReportResult PrinterEvents TransactionStarted MessageHandling

Basic

interface Available since 5.0.0

Events.Basic

An interface which needs to be implemented and added as a listener to receive basic events.

Extends

Required Log Status PendingResults ReportResult

Connection status changed

interface Available since 5.10.0

Events.ConnectionStatusChanged

Implement this interface in order to receive connection status changes.

Methods

connectionStatusChanged( ConnectionStatus status , Device device );

Parameter Notes
status *
ConnectionStatus
New status of the connection
device *
Device
The terminal which sent this information.

Code example

public final class EventHandler implements Events.ConnectionStatusChanged {

	@Override
	public void connectionStatusChanged(ConnectionStatus status, Device device) { ... }

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Current transaction status

interface Available since 5.0.0

Events.CurrentTransactionStatus

Implement this interface in order to receive events about the current transaction.

Methods

currentTransactionStatus( StatusInfo status , Device device );

Parameter Notes
status *
StatusInfo
The StatusInfo of the current transaction
device *
Device
The terminal the request is sent from.

Code example

public final class EventHandler implements Events.CurrentTransactionStatus {

	@Override
	public void currentTransactionStatus(StatusInfo info, Device device) { ... }

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Device capabilities ready

interface Available since 5.0.0

Events.DeviceCapabilitiesReady

Implement this interface in case the terminal needs to notify the SDK of its capabilities

Methods

deviceCapabilities( DeviceCapabilities capabilities , Device device );

Parameter Notes
capabilities *
DeviceCapabilities
The capabilities of the terminal
device *
Device
The terminal sending its capabilities

Code example

public final class EventHandler implements Events.DeviceCapabilitiesReady {

	@Override
	public void deviceCapabilities(DeviceCapabilities capabilities, Device device) { ... }

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Device discovery finished

interface Available since 5.0.0

Events.DeviceDiscoveryFinished

Implement this interface in order to receive a list of available terminals. The event handler defined in this interface is invoked after calling the method searchDevices

Methods

deviceDiscoveryFinished( List<Device> devices );

Parameter Notes
devices *
List<Device>
A list of available devices.

Code example

public final class EventHandler implements Events.DeviceDiscoveryFinished {

	@Override
	public void deviceDiscoveryFinished(List<Device> devices) {
		// Receiving a list of connectable devices
		foreach(Device device in devices) { ... }

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Device logs ready

interface Available since 5.0.0 Deprecated since 5.3.1

Warning!

This interface has been marked as deprecated. We strongly discourage you to use it.



Events.DeviceLogsReady

Implement this interface to receive logs from the terminal. You must call getDeviceLogs to request the logs.

Methods

deviceLogsReady( String logs , Device device );

Parameter Notes
logs *
String
A String containing the device logs.
device *
Device
The device the logs belong to

Code example

public final class EventHandler implements Events.DeviceLogsReady {

	@Override
	public void deviceLogsReady(String logs, Device device) {
		// Process logs 
	}

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

End of transaction

interface Available since 5.0.0

Events.EndOfTransaction

Implement this interface to receive an event when a transaction is complete.

Methods

endOfTransaction( TransactionResult result , Device device );

Parameter Notes
result *
TransactionResult
Holds all the information about the transaction.
device *
Device
The payment terminal.

Code example

public final class EventHandler implements Events.EndOfTransaction {

	@Override
	public void endOfTransaction(TransactionResult result, Device device) {
		// Check the status of the transaction, save it, ... 
	}

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Hardware status changed

interface Available since 5.0.0

Events.HardwareStatusChanged

Implement this interface in order to receive events when the hardware status changes.

Methods

hardwareStatusChanged( HardwareStatus status , Device device );

Parameter Notes
status *
HardwareStatus
New status of the hardware.
device *
Device
The payment terminal.

Code example

public final class EventHandler implements Events.HardwareStatusChanged {

	@Override
	public void hardwareStatusChanged(HardwareStatus status, Device device) { ... }

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Log

interface Available since 5.3.1

Events.Log

An interface which needs to be implemented and added as a listener to receive logging information.

Extends

OnMessageLogged

Code example

public final class EventHandler implements Events.Log {

	@Override	
	public void deviceLogsReady(String logs, Device device) { ... }
	@Override
	public void onMessageLogged(LogLevel level , String message) { ... }

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

MessageHandling

interface Available since 5.8.0

Events.MessageHandling

An interface which needs to be implemented and added as a listener to get events which are called when the sdk asks the application to display or hide a message.

Methods

showMessage( String message , Boolean dismissible , int duration );

Parameter Notes
message *
String
Message to display
dismissible *
Boolean
A flag that indicates whether the message can be dismissed
duration *
int
The timeout to hide the message. In milliseconds, if 0 is sent, the message should not auto dismiss.

hideMessage( String message );

Parameter Notes
message *
String
Message to hide

Code example

public final class EventHandler implements Events.MessageHandling {

	@Override
	public void showMessage(String message, Boolean dismissible, int duration) {
		// Show message for a 'duration' period (if duration = 0 DO NOT dismiss until hideMessage(String message) event is received) and make it dismissible if the input marks it as possible 
	}

	@Override
	public void hideMessage(String message) {
		// Hide the message 
	}

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

On message logged

interface Available since 5.0.0

Events.OnMessageLogged

Implement this interface to receive logs from the payment terminal. You must call getDeviceLogs to request the logs.

Methods

onMessageLogged( LogLevel level , String message );

Parameter Notes
level *
LogLevel
The LogLevel of the logging
message *
String
The log trace which was logged by the SDK.

Code example

public final class EventHandler implements Events.OnMessageLogged {

	@Override
	public void onMessageLogged(LogLevel level, String message) {
		// Process log trace 
	}

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

PaymentProvider

interface Available since 5.0.0

Events.PaymentProvider

An interface which needs to be implemented and added as a listener to receive all available events related to financial operations.

Extends

SignatureRequired EndOfTransaction OnMessageLogged CurrentTransactionStatus

PendingResults

interface Available since 5.0.0

Events.PendingResults

An interface which needs to be implemented and added as a listener to receive information about pending TransactionResults. In case of a communication failure between the SDK and the payment terminal there might be a result pending from the transaction which did not get sent to the SDK.

Extends

TransactionResultReady

Code example

public final class EventHandler implements Events.PendingResults {

	@Override
	public void transactionResultReady(TransactionResult transactionResult, Device device) { ... }

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

PrinterEvents

interface Available since 5.8.0

Events.PrinterEvents

An interface which needs to be implemented and added as a listener to get events coming from the printer.

Methods

printSuccess( );

Parameter Notes

printError( PrintError error );

Parameter Notes
error *
PrintError
Enum detailing the reason of the error

Code example

public final class EventHandler implements Events.PrinterEvents {

	@Override
	public void printSuccess() {
		// Successful print action 
	}

	@Override
	public void printError(PrintError error) {
		// Unable to perform print action due to error 
	}

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Report result

interface Available since 5.0.0

Events.ReportResult

Implement this interface to receive an event when a report result from a getReport is returned

Methods

reportResult( TypeOfResult type , String report , DeviceStatus status , Device device );

Parameter Notes
type *
TypeOfResult
The type of the report
report *
String
The text of the report
status *
DeviceStatus
The status of the device
device *
Device
The terminal sending the report

Code example

public final class EventHandler implements Events.ReportResult {

	@Override
	public void reportResult(TypeOfResult type, String report, DeviceStatus status, Device device) { ... }

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Required

interface Available since 5.0.0

Events.Required

You must provide a class implementing this interface when initializing the SDK.

Extends

SignatureRequired EndOfTransaction DeviceDiscoveryFinished PendingResults

Code example

public final class EventHandler implements Events.Required {

	@Override	
	public void signatureRequired(SignatureRequest signatureRequest, Device device) { ... }
	@Override
	public void endOfTransaction(TransactionResult transactionResult, Device device) { ... }
	@Override
	public void deviceDiscoveryFinished(List devices) { ... }
	@Override
	public void transactionResultReady(TransactionResult transactionResult, Device device) { ... }

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Signature required

interface Available since 5.0.0

Events.SignatureRequired

The SignatureRequired interface must be implemented in order to receive an event when a card requires a signature as a verification method. This interface is only required for an Hi5 or Hilite integration, PAX and Telpo terminals automatically prompt for signature capture on the terminal.

Methods

signatureRequired( SignatureRequest request , Device device );

Parameter Notes
request *
SignatureRequest
Holds the signature request.
device *
Device
The payment terminal.

Code example

public final class EventHandler implements Events.SignatureRequired {

	@Override	
public void signatureRequired(SignatureRequest signatureRequest, Device device) {
		// Save merchant receipt 
		String merchantReceipt = signatureRequest.getMerchantReceipt();
		api.signatureResult(true);
	}

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Status

interface Available since 5.0.0

Events.Status

An interface which needs to be implemented and added as a listener to receive connection and transaction statuses.

Extends

ConnectionStatusChanged HardwareStatusChanged CurrentTransactionStatus

Code example

public final class EventHandler implements Events.Status {

	@Override	
	public void connectionStatusChanged(ConnectionStatus status, Device device) { ... }
	@Override
	public void hardwareStatusChanged(HardwareStatus status, Device device) { ... }
	@Override
	public void currentTransactionStatus(StatusInfo info, Device device) { ... }

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Transaction result ready

interface Available since 5.0.0

Events.TransactionResultReady

Implement this interface in order to receive an event after a pending TransactionResult has been recovered from the payment terminal.

Methods

transactionResultReady( TransactionResult transactionResult , Device device );

Parameter Notes
transactionResult *
TransactionResult
A TransactionResult is containing all information about the recovered transaction.
device *
Device
The payment terminal.

Code example

public final class EventHandler implements Events.TransactionResultReady {

	@Override
	public void transactionResultReady(TransactionResult transactionResult, Device device) { ... }

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Transaction started

interface Available since 5.0.0

Events.TransactionStarted

Implement this interface in order to receive an event when a transaction is started through the Cloud API channel.
IMPORTANT NOTE: This interface is only available for cloud-enabled devices. See DeviceCapabilitiesReady interface.

Methods

transactionStarted( TransactionType transactionType , BigInteger amount , Currency currency );

Parameter Notes
transactionType *
TransactionType
Type of transaction started
amount *
BigInteger
Amount in the minor unit of currency (f.ex. 1000 is 10.00 GBP)
currency *
Currency
Currency of the transaction started

Code example

public final class EventHandler implements Events.TransactionStarted {

	@Override	
public void transactionStarted(TransactionType type, BigInteger amount, Currency currency) {
		// Notify the app user transaction has been started ...
	}

}

// Remember to register the instance of this EventHandler:
this.api.registerEventsDelegate(eventHandlerInstance);

Objects

Transaction Result

object Available since 5.10.0

TransactionResult

An object holding information about the result of a transaction.

Properties

Property Description
StatusMessage
String
Gets the status message from the TransactionResult. The status message is a human readable string from our servers which contains a value representing the result from the server side. "AUTH CODE 12345" for example.
Type
TransactionType
Gets the transaction type from the TransactionResult. The transaction type represents which type of transaction was done. "SALE" for example.
FinStatus
FinancialStatus
Gets the financial status from the TransactionResult. The financial status describes the conclusion of the transaction as received from the card reader. "AUTHORISED" for example.
RequestedAmount
BigInteger
Gets the requested amount from the TransactionResult. The requested amount is the payment amount sent in the original request to the card reader, i.e. the amount which was to charge the card with.
GratuityAmount
BigInteger
Gets the gratuity amount from the TransactionResult. The gratuity amount is an additional amount added to the requested payment amount which represents additional fee added to the requested amount. This is used when the card reader is supporting the tipping functionality. An example: A sale is started with the amount 1000 and the card reader is set to support tipping. The card reader asks if a tip should be applied by the customer. The customer inputs an additional amount as a tip, lets say 100. The card is then charged for the requested amount, 1000, as well as the additional gratuity amount, 100. The result will be that the card will be charged for 1100. A calculated gratuity percentage will also be returned.
GratuityPercentage
double
Gets the gratuity percentage from the TransactionResult The gratuity percentage is used to calculate an additional amount to the requested amount. The card reader calculates that amount, rounded up to the closest whole number. This is used when the card reader is supporting the tipping functionality. An example: A sale is started with the amount 1000 and the card reader is set to support tipping. The card reader asks if a tip should be applied by the customer. Instead of the customer adding a value he selects the percentage of the requested amount to be applied as a tip, lets say 10%. The card is then charged for the requested amount, 1000, as well as the additional gratuity percentage, 10%. The result will be that the card will be charged for 1100. A calculated gratuity amount will also be returned.
TotalAmount
BigInteger
Gets the total amount from the TransactionResult. The total amount is the amount the card was charged for. It is possible that the total amount is not the same as the requested amount since an additional fee can be added, with the customer's approval, via the tipping functionality.
Currency
Currency
Gets the currency from the TransactionResult. The currency used in this transaction.
TransactionID
String
Gets the device transaction id from the TransactionResult. The transaction id is an internal increasing counter in the card reader which is incremented in each transaction.
eFTTransactionID
String
Gets the device transaction id from the TransactionResult. The EFT (electronic funds transfer) transaction id is a unique GUID from the servers which is linked to this transaction in order to search for a particual transaction. This id is used if a transaction is to be reversed.
OriginalEFTTransactionID
String
Gets the original EFT transaction id from the TransactionResult. The original EFT (electronic funds transfer) transaction id is a unique GUID previously received from the servers in order to reverse a transaction. This id is sent with the new eFTTransactionID in order to reference the original transaction. An example: A transaction is made. An eFTTransactionID is received. That transaction has to be reversed. A new transaction is started, now a reverse transaction, with the previously received eFTTransactionID as a parameter in the transaction. In the end result there will be a new eFTTransactionID, for the reverse transaction, and an originalEFTTransactionID referencing the original transaction.
eFTTimestamp
Date
Gets the time stamp from the TransactionResult. The eFTTimestamp represents the time when the transaction was done. This time is set by the device communicating to the card reader when the connection is established to the card reader.
AuthorisationCode
String
Gets the authorisation code from the TransactionResult. If the transaction was authorised the value represented can be used to search for a transaction in our system.
VerificationMethod
VerificationMethod
Gets the verification method from the TransactionResult. The verification method represents the card holder verification method used to allow the payment. "PIN" for example.
CardEntryType
CardEntryType
Gets the card entry type from the TransactionResult. The card entry type is the method the card information was input to card reader. "ICC" for example represents "Integrated Circuit Card" i.e. the information was read from the chip of the card.
CardSchemeName
String
Gets the card scheme name from the TransactionResult. The scheme which was used when the transaction was made.
ErrorMessage
String
Gets the error message from the TransactionResult. If there was an error during the transaction it is represented here in a human readable text.
CustomerReference
String
Gets the customer reference from the TransactionResult. If a customer reference was added, as an optional parameter, when the transaction was started. It is received here, unaltered. The customer reference can be used to reference in internal systems.
BudgetNumber
String
Gets the budget number from the TransactionResult. If a budget number was added, as an optional parameter, when the transaction was started. It is received here, unaltered. The budget number can be used to split payments over a period of months.
RecoveredTransaction
boolean
Gets the flag recovered transaction from the TransactionResult. This flag is true if the transaction result is from a previous transaction which failed to get sent from the card reader, false otherwise. In the case that the communication between the device and the card reader breaks down, the card reader will attempt to send the result of the previous transaction as an immediate reply when the next transaction is started. If this happens the transaction is flagged as a "RecoveredTransaction". This should be displayed very well in the UI since this is *NOT* the result from the transaction just started.
CardTypeId
String
Same as CardSchemeName.
MerchantReceipt
String
Gets the merchant receipt from the TransactionResult. An HTML containing the merchant receipt.
CustomerReceipt
String
Gets the customer receipt from the TransactionResult. An HTML containing the customer receipt.
DeviceStatus
DeviceStatus
Gets the device status from the TransactionResult.
CardToken
String
Gets the card token if it's available, null otherwise.
ExpiryDateMMYY
String
Gets the expiry date of the card used for the operation.
MaskedCardNumber
String
Gets the masked card number of the card used for the operation.
UnmaskedCardNumber
String
Gets the unmasked card number of the card used for the operation. Only available for a subset of whitelisted loyalty cards.
TenderType
TenderType
Gets the transaction tender type.
PaymentScenario
PaymentScenario
Gets the transaction payment scenario.

Card Entry Type

enum Available since 3.0.0

CardEntryType

An enum representing different card entry types.

Possible values

UNDEFINED MSR ICC CNP

Connection Method

enum Available since 5.3.1

ConnectionMethod

An enum representing different connection methods with the payment terminal.

Currently BLUETOOTH, SIMULATOR, CLOUD and ANDROID_PAYMENT are supported types.

Possible values

USB SERIAL BLUETOOTH CLOUD ANDROID_PAYMENT HTTPS WIFI ETHERNET SIMULATOR

Code example

//Currently BLUETOOTH, SIMULATOR, CLOUD and ANDROID_PAYMENT are the only ConnectionMethod available.
public enum ConnectionMethod {
	USB,
	SERIAL,
	BLUETOOTH,
	HTTPS,
	WIFI,
	ETHERNET,
	SIMULATOR,
	CLOUD,
	ANDROID_PAYMENT
}

Connection Status

enum Available since 5.3.1

ConnectionStatus

A list of connection statuses. Note: the events starting with Cloud[...] are exclusively for devices linked to merchants with CLOUD Api key (CLOUD mode enable).

Possible values

Connected Connecting Disconnected Disconnecting NotConfigured Initializing CloudConnected CloudInitialized CloudAvailable CloudDisconnected CloudUnavailable

Currency

enum Available since 3.0.0

Currency

An enum of currencies.

It contains the name of the currency, its ISO code, as well as information about how many decimals the currency uses.

Possible values

AED AFN ALL AMD ANG AOA ARS AUD AWG AZN BAM BBD BDT BGN BHD BIF BMD BND BOB BOV BRL BSD BTN BWP BYR BZD CAD CDF CHF CLP CNY COP COU CRC CUC CUP CVE CZK DJF DKK DOP DZD EEK EGP ERN ETB EUR FJD FKP GBP GEL GHS GIP GMD GNF GTQ GYD HKD HNL HRK HTG HUF IDR ILS INR IQD IRR ISK JMD JOD JPY KES KGS KHR KMF KPW KRW KWD KYD KZT LAK LBP LKR LRD LSL LTL LVL LYD MAD MDL MKD MMK MNT MOP MUR MVR MWK MXN MXV MYR MZN NAD NGN NIO NOK NPR NZD OMR PAB PEN PGK PHP PKR PLN PYG QAR RON RSD RUB RWF SAR SBD SCR SDG SEK SGD SHP SLL SOS SRD STD SYP SZL THB TJS TMT TND TOP TRY TTD TWD TZS UAH UGX USD UZS VEF VND VUV WST XAF XCD XOF XPF YER ZAR ZMK ZWL

Balance Sign

String Available since 1.0.0

BalanceSign

An enum representing the balance sign

Possible values

POSITIVE_SIGN('C') NEGATIVE_SIGN('D')

Device

object Available since 5.3.1

Device

An object to store the information about the payment terminal in use.

Methods

Constructor

Device( String name , String address , String port , ConnectionMethod connectionMethod , String sharedSecret , int timeout );

Parameter Notes
name *
String
A name to identify the device
address *
String
The address of the device you wish to connect to. E.g.: "08:00:69:02:01:FC" for bluetooth, "9822032398-PAXA920" for CLOUD (composition of serial number and model of the target device) or just an identifier if your application is running directly on a PAX or Telpo device (ConnectionMethod.ANDROID_PAYMENT).
port *
String
The port to connect to (optional).
connectionMethod *
ConnectionMethod
Type of connection with the payment terminal. E.g: Bluetooth, Cloud, Serial, etc...
sharedSecret
String
Replaces the default shared secret proviced in the initialization step.
timeout
int
The number of miliseconds until a connection is considered timed out. If not set, the default timeout is 15 seconds.

Properties

Property Description
Id
String
A unique identifier for the payment terminal.

Code example

//Create and init a new Datecs Device
Device dev = new Device("CardReader7", "08:00:69:02:01:FC", "1", ConnectionMethod.BLUETOOTH);

//Create and init a new PAX/Telpo Device for a CLOUD connection
Device dev = new Device("CloudDevice", "9822032398-PAXA920", "", ConnectionMethod.CLOUD);
// The address is the composition of the serial number and model ot the target device.
//Example for a PAX A920 device: serial_number - model  -> 9822032398-PAXA920

//Create and init a new PAX/Telpo Device for a ANDROID_PAYMENT connection
Device dev = new Device("LocalPaxOrTelpo", "LocalHost", "", ConnectionMethod.ANDROID_PAYMENT);

Device capabilities

object Available since 5.0.0

DeviceCapabilities

An object holding the capabilities of the payment terminal.

Properties

Property Description
printer
Boolean
True if the terminal has printer, false otherwise
cloudApi
Boolean
True if the terminal is cloud-enabled, false otherwise

Balance

object Available since 1.0.0

Balance

Balance available on the card

Properties

Property Description
amount
Integer
The amount balance
currency
Curreny
The balance currency
sign
BalanceSign
Positive (C) or negative (D) balance. You can retrieve the balance sign using the methods isPositive() or isNegative()

Code example

Balance balance = Balance.Companion.factory(
    "1000", 
    Currency.EUR.getAlpha(), 
    BalanceSign.POSITIVE_SIGN.name()
  )

Device Parameter

enum Available since 3.0.0

DeviceParameter

An enum describing all the admin commands to send to a payment terminal.

Possible values

BluetoothName BluetoothPass SystemTimeout ScreenTimeout SignatureTimeout

Device Status

object Available since 3.2.1

DeviceStatus

A class which holds the status of the payment terminal.

Properties

Property Description
SerialNumber
String
Gets the serial number of the payment terminal
BatteryStatus
String
Gets the battery status of the payment terminal (in percentages)
BatterymV
String
Gets the battery voltage of the payment terminal
BatteryCharging
String
Gets the battery charging status of the payment terminal
ExternalPower
String
Gets the status of the payment terminal external power
ApplicationName
String
Gets the application name of the payment terminal
ApplicationVersion
String
Gets the application version of the payment terminal

Financial Status

enum Available since 4.0.1

FinancialStatus

An enum representing different statuses of a finalized transaction

Possible values

UNDEFINED AUTHORISED DECLINED PROCESSED FAILED CANCELLED PARTIAL_APPROVAL

Hapi Manager

object Available since 3.2.5

HapiManager

A static class containing information about the current status of the SDK

Properties

Property Description
DefaultSharedSecret
String
Gets the default shared secret in use.
LogLevel
LogLevel
Gets the current log level of the SDK and payment terminal.
inTransaction
boolean
Checks whether the SDK is in the middle of a transaction. True if the SDK is in a transaction, false otherwise.
SdkVersion
String
Gets the current SDK version.
isTransactionResultPending
boolean
In the case of a communication failure between the payment terminal and the SDK a TransactionResult might have not been delivered. This function checks if there is a pending TransactionResult. This field is only updated when connecting to a payment terminal. If this function returns true the TransactionResult (which includes the receipt) can be fetched.getPendingTransactionResult();. This function serves the same functionality as the event pendingTransactionResult(Device device), so every time this event is invoked, HapiManager.IsTransactionResultPending() is true until the result is fetched.
Settings.AutomaticReconnection
boolean
When this property is set to true, the SDK will automatically try to reconnect to the payment terminal after a disconnection. The SDK internally maintains a reconnection thread which keeps on trying to connect until it succeeds. The delay between reconnections is exponentially increased on every new attempt. The default value for this property is true

Code example

//Check if the SDK is in transaction
boolean inTransaction = HapiManager.inTransaction(someConnectedDevice);
//Check the current logLevel
LogLevel level = HapiManager.getLogLevel();

//Disable automatic reconnection feature
HapiManager.Settings.AutomaticReconnection = false;

Handpoint API (Hapi) factory

object Available since 5.3.1

HapiFactory

A factory to provide a unified entrypoint and simplify the instantiation of the Hapi object.

Methods

Static factory

getAsyncInterface( Events.Required requiredListener , Context context , HandpointCredentials handpointCredentials );

Parameter Notes
requiredListener *
Events.Required
A listener object to report the required events.
context *
Context
The Android context.
handpointCredentials *
HandpointCredentials
An object containing the actor's shared secret key or shared secret key AND Api Key for CLOUD connections.

Code example

//InitApi for Datecs devices or PAX/Telpo ConnectionMethod.ANDROID_PAYMENT
public void InitApi()
{
	String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
	api = HapiFactory.getAsyncInterface(this, new HandpointCredentials(sharedSecret));
	//The api is now initialized. Yay! we've even set a default shared secret
}

//InitApi for Cloud devices
public void InitApi()
{
	String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
	String apikey = "This-Is-The-Merchant-Api-Key";
	api = HapiFactory.getAsyncInterface(this, new HandpointCredentials(sharedSecret, apikey));
	//The api is now initialized. Yay! we've even set a default shared secret and the merchant Api Key!
}

Handpoint Credentials

object Available since 5.3.1

HandpointCredentials

A class containing the credentials used to communicate with the payment terminal, the shared secret (always required) and Cloud API Key (ony required when using CLOUD connection method).

Properties

Property Description
SharedSecret
String
String the value of the Shared secret.
CloudApiKey
String
String the value of the merchant Cloud API Key, only required when using CLOUD connection method

Code example

{
	String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
	HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret);
	//We've even set a default shared secret!
}
	
{
	String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
	string apikey = "This-Is-The-Merchant-Api-Key";
	HandpointCredentials handpointCredentials = new HandpointCredentials(sharedSecret, apikey);
	//We've even set a default shared secret and the merchant Api Key!
}

Log Level

enum Available since 3.0.0

LogLevel

An enum describing the different levels of logging available.

Possible values

None Info Full Debug

Optional Transaction Parameters

object Available since 3.0.0

OptionalParameters

A class containing optional transaction parameters supported by the payment terminal.

Properties

Property Description
Budget
String
Budget is only available for sale transactions.
A String representing the key for a budget number.

A budget number can be used to split up an amout over a period of months. The value has to be a String of 2 digits representing the number of months to split the transaction to.

CustomerReference
String
CustomerReference is available for all transactions.
A String representing the key for a customer reference.

A customer reference can be used for an internal marking system. The value is sent as a String of a maximum 25 characters and received back when the transaction has been processed.

Payment Scenario

enum Available since 5.11.0

PaymentScenario

An enum representing different types of scenario.

Possible values

UNKNOWN MAGSTRIPE MAGSTRIPECONTACTLESS CHIP CHIPCONTACTLESS FALLBACK CONTACTLESS CHIP_RECEIPT SWIPED FALLBACK_SWIPE

Print Error

enum Available since 5.8.0

PrintError

An enum representing different errors that can come from print action.

Possible values

Unexpected InvalidArgument CantConnectToPrinter NotSupported NoPermission PrinterDisabled NotWhitelisted Busy OutOfPaper DataPacketInvalid PrinterHasProblems PrinterOverheating PrintingUnfinished FontNotPresent FontFormatError TooLong BatteryTooLow PaperCutterError PaperCutterJam CoverOpen UnsupportedEncoding

Status

enum Available since 5.3.1

status

An enum containing information about the status of a transaction.

Possible values

Success InvalidData ProcessingError CommandNotAllowed NotInitialised ConnectTimeout ConnectError SendingError ReceivingError NoDataAvailable TransactionNotAllowed UnsupportedCurrency NoHostAvailable CardReaderError CardReadingFailed InvalidCard InputTimeout UserCancelled InvalidSignature WaitingForCard CardInserted ApplicationSelection ApplicationConfirmation AmountValidation PinInput ManualCardInput WaitingForCardRemoval TipInput SharedSecretInvalid SharedSecretAuth WaitingSignature WaitingHostConnect WaitingHostSend WaitingHostReceive WaitingHostDisconnect PinInputCompleted PosCancelled RequestInvalid CardCancelled CardBlocked RequestAuthTimeout RequestPaymentTimeout ResponseAuthTimeout ResponsePaymentTimeout IccCardSwiped RemoveCard ScannerIsNotSupported ScannerEvent BatteryTooLow AccountTypeSelection BtIsNotSupported PaymentCodeSelection PartialApproval AmountDueValidation InvalidUrl WaitingCustomerReceipt PrintingMerchantReceipt PrintingCustomerReceipt UpdateStarted UpdateFinished UpdateFailed UpdateProgress WaitingHostPostSend WaitingHostPostReceive Rebooting PrinterOutOfPaper ErrorConnectingToPrinter CardTapped ReceiptPrintSuccess InvalidPinLength OfflinePinAttempt OfflinePinLastAttempt ProcessingSignature InitialisationComplete Undefined

Supported Locales

enum Available since 5.0.0

SupportedLocales

An enum of the SDK supported languages.

Possible values

en_CA en_UK en_US es_ES hr_HR is_IS

Settings

object Available since 5.0.0

Settings

An Object holding the SDK initialization settings

Properties

Property Description
automaticReconnection
Boolean
When this property is set to true, the SDK will automatically try to reconnect to the terminal after disconnection. The SDK maintains internally a reconnection thread which keeps on trying to connect until it succeeds. The delay between reconnections is exponentially increased on every new attempt. The default value for this property is false
autoRecoverTransactionResult
Boolean
. The default value for this property is false
sendToDeviceMaxAttempts
Integer
Number of retry attemps when there is an error communicating with the card reader. The default value for this property is 3
timeBetweenAttempts
Integer
Time in milliseconds between attempts. The default value for this property is 5000
showSDKUIComponents
Boolean
. The default value for this property is false
getReceiptsAsURLs
Boolean
. The default value for this property is false

Status Info

object Available since 3.5.5

statusInfo

A class containing information about the status of the transaction.

Properties

Property Description
cancelAllowed
bool
A boolean letting the integrator know if the terminal will accept a stop transaction request.
status
Status
A Status enum representing the status of the transaction.
message
String
A String containing the status message of the transaction.
deviceStatus
DeviceStatus
A DeviceStatus object containing information about the payment terminal.

Signature Request

object Available since 3.2.1

SignatureRequest

A class containing information about a signature verification.

Properties

Property Description
Timeout
int
int the value of the timeout in seconds.
MerchantReceipt
String
String the merchant receipt as html.

Tender Type

enum Available since 5.11.0

TenderType

An enum representing different tender types.

Possible values

NOT_SET CREDIT DEBIT

Transaction Type

enum Available since 5.9.0

TransactionType

An enum representing different types of transactions.

Possible values

UNDEFINED SALE VOID_SALE REFUND VOID_REFUND REVERSAL CANCEL_SALE CANCEL_REFUND TOKENIZE_CARD SALE_AND_TOKENIZE_CARD CARD_PAN

Type of result

enum Available since 5.0.0

TypeOfResult

An enum representing different types of device reports.

Possible values

STATUS REPORT BLUETOOTHNAME EMVCONFIGURATION

Verification Method

enum Available since 3.0.0

VerificationMethod

An enum representing different verification methods used in the transaction.

Possible values

UNDEFINED SIGNATURE PIN PIN_SIGNATURE FAILED NOT_REQUIRED