You are currently viewing the documentation for version

Introduction

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

Awesomely simple
Created for humans, coders, geeks, no need of dark and complex knowledge of the payment industry.
Superly secure
We take care of the PCI side so you don't have to spend months becoming compliant.
The Handpoint card reader encrypts all sensitive cardholder data so your app does not have to deal with it.
Working with the SDK
The Handpoint Android SDK is compatible with Android version 4.0.3 (API level 15) and up.
Latest version is compiled with java 1.7.0_25.

1. Download the SDK.
2. Import the SDK to your project.
3. Follow the getting started guide, to get you up and running or dive into the documentation.

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

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.

version 4.4.0

Get the SDK

API overview

How to implement a Sale Transaction

The below flow chart shows the interaction between the SDK, the payment terminal and your application. The orange arrows represent methods (requests) that need to be invoked to communicate with the Handpoint SDK's. The dark arrows represent events that need to be integrated in your code in order to retrieve information from the SDK´s and the card reader.


How to implement a Sale Transaction with Recovery feature

At some point, the connection between the SDK and the card reader can become unstable. For example, the Bluetooth connection can be cut in the middle of a sale transaction if the smartphone runs out of battery. If this happens, you need to have implemented the “transaction recovery feature” in order to get the receipts from the previous transaction and knowing if it was successful despite the connection problem.



Supported functionality

  • Discovery of remote BT devices.
  • Connect to remote BT device.
  • Physical connection to HiPro external accessory.
  • Automatic or manual reconnection to the card reader.
  • Executing financial transaction.
  • Reporting status of transactions.
  • Control and access to device logs.
  • Barcode scanner with HiPro card readers.
  • Limited card reader simulation.

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


Getting started

Getting started with the simulator

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. We undergo an audit on a yearly basis to be able to maintain the license to handle, process, store and transmit card data.

Connecting to the simulator

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


hapi.useDevice(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.useDevice(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).defaultSharedSecret(sharedSecret);
            // The api is now initialized. Yay! we've even set a default 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).defaultSharedSecret(sharedSecret);
            // The api is now initialized. Yay! we've even set a default 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.useDevice(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).defaultSharedSecret(sharedSecret);
            // The api is now initialized. Yay! we've even set a default 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.useDevice(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!

Getting started with a card reader

Introduction

This tutorial is guiding you through all the steps to create a basic payment application for Android devices using a development card reader.

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. We undergo an audit on a yearly basis to be able to maintain the license to handle, process, store and transmit card data.

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";
            this.api = HapiFactory.getAsyncInterface(this, context).defaultSharedSecret(sharedSecret);
            // The api is now initialized. Yay! we've even set a default 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.
        }
    }
                        
  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";
            this.api = HapiFactory.getAsyncInterface(this, context).defaultSharedSecret(sharedSecret);
            // The api is now initialized. Yay! we've even set a default 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)

    
    public void discoverDevices(){
        this.api.listDevices(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.useDevice(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.useDevice(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";
            this.api = HapiFactory.getAsyncInterface(this, context).defaultSharedSecret(sharedSecret);
            // The api is now initialized. Yay! we've even set a default 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.listDevices(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.useDevice(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.useDevice(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";
            this.api = HapiFactory.getAsyncInterface(this, context).defaultSharedSecret(sharedSecret);
            // The api is now initialized. Yay! we've even set a default 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.listDevices(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.useDevice(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.useDevice(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

Transactions

Sale

method Available since 3.1.1

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 while during transaction with different statuses from card reader
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

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

Reversal

method Available since 3.1.1

saleReversal

A saleReversal initiates a reversal operation for a previous sale operation to the card reader. This operation reverts (if possible) a specific sale 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 the same day as the transaction was made.

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
As received from the card reader (EFTTransactionID)
map
Map
A map including extra optional transaction parameters.


Events invoked

currentTransactionStatus
Invoked while during transaction with different statuses from card reader
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

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

Refund

method Available since 3.1.1

refund

A refund initiates a refund operation to the card reader. This operation moves funds from your account to the cardholders 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.

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 while during transaction with different statuses from card reader
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

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

Refund reversal

method Available since 3.1.1

refundReversal

A RefundReversal initiates a reversal operation for a previous refund operation to the card reader. 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 the same day as the transaction was made.

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
As received from the card reader (EFTTransactionID)
map
Map
A map including extra optional transaction parameters.


Events invoked

currentTransactionStatus
Invoked while during transaction with different statuses from card reader
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

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

Cancel transaction

method Available since 3.1.1

cancelRequest

This method attempts to cancel the current operation on device. Note that operations cannot be cancelled at certain points in the transaction process

Events invoked

currentTransactionStatus
Invoked while during transaction with different statuses from card reader
endOfTransaction
Invoked when the card reader finishes processing the transaction

Returns

Boolean
true if the operation was successfully sent to device

Code example

//Attempts to cancel an operation
api.cancelRequest();

Signature result

method Available since 3.1.1

signatureResult

A signatureRequest event is invoked during transaction when signature verification is needed (f.ex when payment is done with a magstripe card). The merchant is required to ask the cardholder for signature and approve (or disapprove) the signature. signatureResult tells the card reader if the signature was approved by passing true in the method. To disapprove then false is passed.

Parameters

Parameter Notes
accepted *
Boolean
pass true if merchant accepts customer signature


Events invoked

currentTransactionStatus
Invoked while during transaction with different statuses from card reader
endOfTransaction
Invoked when the card reader finishes processing the transaction

Returns

Boolean
true if the operation was successfully sent to device

Code example

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

Device management

Connect

method Available since 3.0.0

useDevice

Configures the device as the preferred device and tries to connect to it. 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 to the system which device you want to use for the operations.


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);

api.useDevice(device);

Disconnect

method Available since 3.1.1

disconnect

Disconnect will stop the active connection (or reconnection process). Please note that the method ignores the current state of the card reader and just cuts the connection. That might result in comm error on device if done mid transaction or right after.

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();

Set merchant key

method Available since 3.1.1

setSharedSecret

Validates the app for this session, thus enabling financial transactions

Parameters

Parameter Notes
sharedSecret *
String
The shared secret is a key provided by Handpoint when you get your account that enables you to perform live operations with the card reader. However, if you're developing with a starter kit, the test shared secret is specified in the example


Events invoked

None
No events invoked.

Returns

Boolean
true if the operation was successfully sent to device

Code example

//Sets the shared secret using the test key
api.setSharedSecret("0102030405060708091011121314151617181920212223242526272829303132");

Set parameter

method Available since 3.1.1

setParameter

Changes values of certain parameters on the card reader.

Parameters

Parameter Notes
param *
DeviceParameter
The name of the parameter to change
value *
String
New value of the parameter


Events invoked

None
No events are invoked.

Returns

Boolean
true if the operation was successfully sent to device

Code example

//Changes the bluetooth name of card reader
api.setParameter(DeviceParameter.BluetoothName, "OrangeCardReader");

Set logging level

method Available since 3.2.1

setLogLevel

Sets the log level of the hapi for both the device and the API.

Parameters

Parameter Notes
level *
LogLevel
The desired log level. Can be LogLevel.None, LogLevel.Info, LogLevel.Full, LogLevel.Debug


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);

Request device logs

method Available since 3.1.1

getDeviceLogs

Fetches the logs from the device and reports them to the deviceLogsReady event.

Events invoked

deviceLogsReady
Invoked when hapi has finished downloading logs from the card reader.

Returns

Boolean
true if the operation was successfully sent to device

Code example

//Downloads logs from device
api.getDeviceLogs();

Update device

method Available since 3.1.1

update

The update operation checks for update to the card reader and initiates an update if needed.

Events invoked

None
Information about this process should be available at the device's screen.

Returns

Boolean
true if the operation was successfully sent to device

Code example

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

List Devices (search)

method Available since 3.0.0

listDevices

Starts the search for devices to connect to with the specified ConnectionMethod

Parameters

Parameter Notes
method *
ConnectionMethod
The means of connection you intend to use to talk to the device. (Bluetooth, Serial, USB, etc...)


Events invoked

deviceDiscoveryFinished
Invoked after the search is finished returning a list of the devices finished.

Code example

//Search for Bluetooth devices
api.listDevices(ConnectionMethod.BLUETOOTH);

Events subscribers

Add Required Event Handler

method Available since 3.1.1

addRequiredEventHandler

Adds a listener so necessary information can be retrieved. Such as signature verification, receipts and list of devices. This listener fetches information from the following events : deviceDiscoveryFinished, signatureRequired and endOfTransaction. The corresponding removal method, removeRequiredEventHandler, must be called before the end of your applications life-time to prevent memory leakage.

Parameters

Parameter Notes
listener *
Required
An implementation of the Events.Required interface


Returns

Boolean
true if the new listener was added successfully

Code example

//Register a listener for required events
this.api.addRequiredEventHandler(this);
//In this context the keyword "this" is an implementation of:
interface Events.Required {

    void signatureRequired(SignatureRequest request, Device device);

    void endOfTransaction(TransactionResult result, Device device);

    void deviceDiscoveryFinished(List devices);
}

Add Status Notification Event Handler

method Available since 3.1.1

addStatusNotificationEventHandler

Adds a listener so transaction information can be retrieved. This listener fetches information from the following events : connectionStatusChanged, currentTransactionStatus. The corresponding removal method, removeStatusNotificationEventHandler, must be called before the end of your applications life-time to prevent memory leakage.

Parameters

Parameter Notes
listener *
Status
An implementation of the Events.Status interface


Returns

Boolean
true if the new listener was added successfully

Code example

//Register a listener for transaction information events
this.api.addStatusNotificationEventHandler(this);
//In this context the keyword "this" is an implementation of:
interface Events.Status {

    void connectionStatusChanged(ConnectionStatus status, Device device);

    void currentTransactionStatus(StatusInfo info, Device device);
}

Add Log Event Handler

method Available since 3.1.1

addLogEventHandler

Adds a listener so log information can be retrieved. This listener fetches information from the following events : onMessageLogged, deviceLogsReady. The corresponding removal method, removeLogEventHandler, must be called before the end of your applications life-time to prevent memory leakage.

Parameters

Parameter Notes
listener *
Log
An implementation of the Events.Log interface


Returns

Boolean
true if the new listener was added successfully

Code example

//Register a listener for log information events
this.api.addLogEventHandler(this);
//In this context the keyword "this" is an implementation of:
interface Events.Log {

    void deviceLogsReady(String logs, Device device);

    void onMessageLogged(LogLevel level, String message);
}

Remove Required Event Handler

method Available since 3.1.1

removeRequiredEventHandler

Removes a listener so information is not passed on to that listener any more.

Parameters

Parameter Notes
listener *
Required
The listener to be removed


Returns

Boolean
true if the listener was removed successfully

Code example

//Remove a listener for required events
this.api.removeRequiredEventHandler(this);

Remove Status Notification Event Handler

method Available since 3.1.1

removeStatusNotificationEventHandler

Removes a listener so information is not passed on to that listener any more.

Parameters

Parameter Notes
listener *
Status
The listener to be removed


Returns

Boolean
true if the listener was removed successfully

Code example

//Remove a listener for status notification events
this.api.removeStatusNotificationEventHandler(this);

Remove Log Event Handler

method Available since 3.1.1

removeLogEventHandler

Removes a listener so information is not passed on to that listener any more.

Parameters

Parameter Notes
listener *
Log
The listener to be removed


Returns

Boolean
true if the listener was removed successfully

Code example

//Remove a listener for log events
this.api.removeLogEventHandler(this);

Events

Device discovery finished

method Available since 3.0.0

deviceDiscoveryFinished

deviceDiscoveryFinished event gets called when a device discovery has finished and returns a list of devices.

Parameters

Parameter Notes
device *
Device
The device that is invoking the event


Subscribers Needed

addRequiredEventHandler
This listener has to be implemented (preferably during initialisation) in order to retrieve the devices information.

Code example

//Receiving a list of connectable devices
List myListOfDevices = new List();
@Override
public void DeviceDiscoveryFinished(List devices)
{
	foreach(Device device in devices){		myListOfDevices.Add(device);
	}
}

Signature required

method Available since 3.0.0

signatureRequired

signatureRequired event gets called when a card requires a signature instead of PIN entry and has two parameters, request and device.

Parameters

Parameter Notes
request *
SignatureRequest
Holds the signature request
device *
Device
The device that is invoking the event


Subscribers Needed

addRequiredEventHandler
This listener has to be implemented (preferably during initialisation) in order to retrieve signature information.

Code example

@Override
public void signatureRequired(SignatureRequest signatureRequest, Device device) {
	signatureRequest.getMerchantReceipt();
	api.signatureResult(true);
}

End of transaction

method Available since 3.0.0

endOfTransaction

endOfTransaction event gets called at the end of each transaction and has two parameters, result and device.

Parameters

Parameter Notes
result *
TransactionResult
Holds the results for the transaction
device *
Device
The device that is invoking the event


Subscribers Needed

addRequiredEventHandler
This listener has to be implemented (preferably during initialisation) in order to retrieve transaction information.

Code example

@Override
public void endOfTransaction(TransactionResult transactionResult, Device device) {
	Log.d(App, transactionResult.getCustomerReceipt());
}

Connection status changed

method Available since 3.0.0

connectionStatusChanged

connectionStatusChanged event gets called when the state of a card reader connection changes.

Parameters

Parameter Notes
status *
ConnectionStatus
An enum containing the status code for the connection
device *
Device
The device that is invoking the event


Subscribers Needed

addStatusNotificationEventHandler
This listener has to be implemented (preferably during initialisation) in order to retrieve the different connection statuses (e.g : CONNECTED, DISCONNECTED...).

Code example

@Override
public void connectionStatusChanged(ConnectionStatus connectionStatus, Device device) {
	Log.d(App, connectionStatus.name());
}

Current transaction status

method Available since 3.1.1

currentTransactionStatus

currentTransactionStatus event gets called when the state of an ongoing transaction changes.

Parameters

Parameter Notes
statusInfo *
StatusInfo
An object containing information about the current transaction
device *
Device
The device that is invoking the event


Subscribers Needed

addStatusNotificationEventHandler
This listener has to be implemented (preferably during initialisation) in order to retrieve the different states from the card reader (e.g : Waiting for card, Waiting for PIN entry...).

Code example

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

Message logged

method Available since 3.0.0

onMessageLogged

onMessageLogged event gets called for all log messages that are being logged. This is only intended for debugging.

Parameters

Parameter Notes
logLevel *
LogLevel
An enum containing the log level
message *
String
A String containing the current log message


Subscribers Needed

addLogEventHandler
This listener has to be implemented (preferably during initialisation) in order to retrieve the different log messages.

Code example

@Override
public void onMessageLogged(LogLevel logLevel, String message){
	Log.d("API log", message);
}

Logs ready

method Available since 3.0.0

deviceLogsReady

DeviceLogsReady event gets called when the card reader logs requested by a call to getDeviceLogs() are ready. This Event is really useful if there has been a communication error between the card reader and the API (e.g : Bluetooth communication lost). After reconnecting, you can then fetch the card reader logs to the API.

Parameters

Parameter Notes
logs *
String
String containing the current log
device *
Device
The device that is invoking the event


Subscribers Needed

addLogEventHandler
This listener has to be implemented (preferably during initialisation) in order to retrieve the card reader logs.

Code example

@Override
public void deviceLogsReady(String logs, Device device){
	Log.d("Device logs:", logs);
}

Objects

Transaction Type

enum Available since 3.0.0

TransactionType

An enum representing different types of transactions.

Possible values

UNDEFINED SALE VOID_SALE REFUND VOID_REFUND CANCEL_SALE CANCEL_REFUND

Transaction Result

object Available since 3.2.1

TransactionResult

A class holding information about a transaction result.

Properties

Property Description
StatusMessage
String
Gets the statusMessage from the TransactionResult
Type
TransactionType
Gets the transaction type from the TransactionResult
FinStatus
FinancialStatus
Gets the financial status from the TransactionResult
RequestedAmount
BigInteger
Gets the requested amount from the TransactionResult
GratuityAmount
BigInteger
Gets the gratuity amount from the TransactionResult
GratuityPercentage
double
Gets the gratuity percentage from the TransactionResult
TotalAmount
BigInteger
Gets the total amount from the TransactionResult
Currency
Currency
Gets the currency from the TransactionResult
TransactionID
String
Gets the device transaction id from the TransactionResult
eFTTransactionID
String
Gets the device transaction id from the TransactionResult
OriginalEFTTransactionID
String
Gets the original EFT transaction id from the TransactionResult
eFTTimestamp
Date
Gets the time stamp from the TransactionResult
AuthorisationCode
String
Gets the authorisation code from the TransactionResult
VerificationMethod
VerificationMethod
Gets the verification method from the TransactionResult
CardEntryType
CardEntryType
Gets the card entry type from the TransactionResult
CardSchemeName
String
Gets the card scheme name from the TransactionResult
ErrorMessage
String
Gets the error message from the TransactionResult
CustomerReference
String
Gets the customer reference from the TransactionResult
BudgetNumber
String
Gets the budget number from the TransactionResult
MerchantReceipt
String
Gets the merchant receipt from the TransactionResult
CustomerReceipt
String
Gets the customer receipt from the TransactionResult
DeviceStatus
DeviceStatus
Gets the device status from the TransactionResult
RecoveredTransaction
boolean
Is true if the transaction result is from a previous transaction which failed to get sent from the card reader, false otherwise.

Handpoint API (Hapi) factory

object Available since 3.0.0

HapiFactory

A factory to provide a unified entrypoint and to simplify the way to instantiate the Hapi object.

Methods

Static factory

getAsyncInterface( Events.Required requiredListener , Context context );

Parameter Notes
requiredListener *
Events.Required
A listener object to report the required events.
context *
Context
The Android context in order to handle bluetooth.

Code example

//An Android Context is required to be able to handle bluetooth
public void initApi(Context context) {
	String sharedSecret = "0102030405060708091011121314151617181920212223242526272829303132";
	this.api = HapiFactory.getAsyncInterface(this, context).defaultSharedSecret(sharedSecret);
}

Connection Method

enum Available since 3.2.1

ConnectionMethod

An enum representing different types of connection methods.

Currently BLUETOOTH and SIMULATOR are supported for android devices.

Possible values

USB SERIAL BLUETOOTH HTTPS WIFI ETHERNET SIMULATOR

Code example

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

Device

object Available since 3.0.0

Device

An object to store the information about the device we're working with.

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 or "192.168.1.105" for ethernet.
port *
String
The port to connect to.
connectionMethod *
ConnectionMethod
Enumerated type to specify the type of connection with the device. E.g: Bluetooth, Serial, etc...
sharedSecret
String
This is used if you want this specific device to use the specified sharedSecret instead of the default one proviced in the initialization.
timeout
int
The amount of miliseconds to consider the connection has timed out. If not set, the default timeout is 15 seconds.

Properties

Property Description
Id
String
An unique identifier of the device.

Code example

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

Connection Status

enum Available since 3.2.1

ConnectionStatus

A list of statuses given to a connection

Possible values

Connected Connecting Disconnected Disconnecting Initializing NotConfigured

Signature Request

object Available since 3.2.1

SignatureRequest

A class containing information about a signature request/signature verification.

Properties

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

Currency

enum Available since 3.0.0

Currency

An enum of most currencies in the world.

Contains the ISO name, ISO number and the name of the currency. Additionally contains 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

Device Parameter

enum Available since 3.0.0

DeviceParameter

An enum describing all the available commands to send to a device.

When used a legal value is expected with the command

Possible values

BluetoothName BluetoothPass SystemTimeout ScreenTimeout SignatureTimeout

Device Status

object Available since 3.2.1

DeviceStatus

A class that holds the device status.

Properties

Property Description
SerialNumber
String
Gets the serial number of the device
BatteryStatus
String
Gets the battery status in percentages of a device
BatterymV
String
Gets the battery milli volts of a device
BatteryCharging
String
Gets the battery charging status of a device
ExternalPower
String
Gets the status of an external power of a device
ApplicationName
String
Gets the application name used on a device
ApplicationVersion
String
Gets the application version number used on a device

Financial Status

enum Available since 3.0.0

FinancialStatus

An enum representing different statuses of a finalized transaction

Possible values

UNDEFINED AUTHORISED DECLINED PROCESSED FAILED CANCELLED

Status Info

enum Available since 3.0.0

StatusInfo

A class containing information about the status of the transaction.

Possible values

Undefined 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 InitialisationComplete

Log Level

enum Available since 3.0.0

LogLevel

An enum describing the different levels of logging used in the hapi and used in the device.

Possible values

None Info Full Debug

Optional Transaction Parameters

object Available since 3.0.0

OptionalParameters

A class containing optional transaction parameters now supported by the device.

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.

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

Status

enum Available since 3.1.1

status

An enum containing information about the status of the transaction.

Possible values

UserCancelled WaitingForCard CardInserted ApplicationSelection ApplicationConfirmation AmountValidation PinInput ManualCardInput WaitingForCardRemoval TipInput AuthenticatingPos WaitingForSignature ConnectingToHost SendingToHost ReceivingFromHost DisconnectingFromHost PinInputComplete Undefined

Card Entry Type

enum Available since 3.0.0

CardEntryType

An enum representing different card entry types.

Possible values

UNDEFINED MSR ICC CNP