The new generation of Handpoint APIs and SDKs are engineered to make your life simpler, happier.
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.
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.
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.
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 |
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.
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))
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]
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>
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 {
}
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.
}
}
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...
}
}
public void connect() {
Device device = new Device("name", "address", "port", ConnectionMethod.SIMULATOR);
this.api.useDevice(device);
}
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
}
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>
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
}
});
}
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;
}
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();
}
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();
}
});
}
Implementing your activity lifecycle methods properly ensures your app behaves well in several ways, including that it:
@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();
}
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.
@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());
}
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();
}
});
}
Here is how MyClass and your main activity must eventually look like:
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();
}
}
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());
}
}
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!
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.
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>
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 {
}
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.
}
}
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...
}
}
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);
}
}
}
}
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);
}
public boolean pay() {
return this.api.sale(new BigInteger("1000"), Currency.GBP);
// Let´s start our first payment of 10 pounds
}
public void disconnect(){
this.api.disconnect();
//This disconnects the connection
}
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
}
}
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>
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
}
});
}
}
Implementing your activity lifecycle methods properly ensures your app behaves well in several ways, including that it:
@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();
}
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.
@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());
}
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();
}
});
}
Here is how MyClass and your main activity must eventually look like:
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();
}
}
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
}
}
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.
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.
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 device you want to use for the operations. If none is supplied the system will attempt to use a default device, if any. |
//Initiate a sale for 10.00 in Great British Pounds
api.sale(new BigInteger("1000"),Currency.GBP);
saleAndTokenizeCard
A sale that 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)
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. |
//Initiate a sale for 10.00 in Great British Pounds
api.saleAndTokenizeCard(new BigInteger("1000"),Currency.GBP);
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 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.
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. |
device
Device |
This parameter specifies to the system which device you want to use for the operations. If none is supplied the system will attempt to use a default device, if any. |
//Initiate a reversal for 10.00 in Great British Pounds
api.saleReversal(new BigInteger("1000"),Currency.GBP,"00000000-0000-0000-0000-000000000000");
refund
A refund initiates a refund operation to the card reader. This 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.
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 device you want to use for the operations. If none is supplied the system will attempt to use a default device, if any. |
//Initiate a refund for 10.00 in Great British Pounds
api.refund(new BigInteger("1000"),Currency.GBP);
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 the same day as the transaction was made.
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. |
device
Device |
This parameter specifies to the system which device you want to use for the operations. If none is supplied the system will attempt to use a default device, if any. |
//Initiate a refund reversal for 10.00 in Great British Pounds
api.refundReversal(new BigInteger("1000"),Currency.GBP,"00000000-0000-0000-0000-000000000000");
cancelRequest
This method attempts to cancel the current operation on device. Note that operations cannot be cancelled at certain points in the transaction process
//Attempts to cancel an operation
api.cancelRequest();
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.
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) |
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
}
});
signatureResult
A signatureRequired 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 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.
Parameter | Notes |
---|---|
accepted *
Boolean |
pass true if merchant accepts customer signature |
device
Device |
This parameter specifies to the system which device you want to use for the operations. If none is supplied the system will attempt to use a default device, if any. |
//Approves signature automatically in signatureRequired event
@Override
public void signatureRequired(SignatureRequest signatureRequest, Device device){
api.signatureResult(true);
}
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.
Parameter | Notes |
---|---|
device *
Device |
This parameter specifies to the system which device you want to use for the operations. |
//Connect to a device
Device device = new Device("CardReader7", "08:00:69:02:01:FC", "1", ConnectionMethod.BLUETOOTH);
api.useDevice(device);
disconnect
Disconnect will stop the active connection (and reconnection process). Please note that the method does NOT ignore the current state of the card reader. This means that if a disconnect is attempted during a transaction it will not be successful and the method will return false
. If a transaction is not in progress the disconnect will take 1-3 seconds to successfully finish and will then return true
.
Parameter | Notes |
---|---|
device
Device |
This parameter specifies to the system which device you want to use for the operations. If none is supplied the system will attempt to use a default device, if any. |
//Disconnect from current device
api.disconnect();
setSharedSecret
Validates the app for this session, thus enabling financial transactions
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 |
device
Device |
This parameter specifies to the system which device you want to use for the operations. If none is supplied the system will attempt to use a default device, if any. |
//Sets the shared secret using the test key
api.setSharedSecret("0102030405060708091011121314151617181920212223242526272829303132");
setParameter
Changes values of certain parameters on the card reader.
Parameter | Notes |
---|---|
param *
DeviceParameter |
The name of the parameter to change |
value *
String |
New value of the parameter |
device
Device |
This parameter specifies to the system which device you want to use for the operations. If none is supplied the system will attempt to use a default device, if any. |
//Changes the bluetooth name of card reader
api.setParameter(DeviceParameter.BluetoothName, "OrangeCardReader");
setLogLevel
Sets the log level (info, debug...) for both the card reader and the SDK. Note : At the end of a transaction, the card reader logs are always automatically fetched to the API.
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 you want to use for the operations. If none is supplied the system will attempt to use a default device, if any. |
//Sets the log level to info
api.setLogLevel(LogLevel.info);
getDeviceLogs
Fetches the logs from the device and reports them to the deviceLogsReady event.
Parameter | Notes |
---|---|
device
Device |
This parameter specifies to the system which device you want to use for the operations. If none is supplied the system will attempt to use a default device, if any. |
//Downloads logs from device
api.getDeviceLogs();
getPendingTransaction
In the case of a communication failure between the device and the SDK a TransactionResult might have not been delivered to the SDK. This function fetches a pending TransactionResult (which contains receipts) from the device, if any. If no TransactionResult was pending a result will be delivered containing default fields. In order to receive only valid TransactionResults this function should only be called when pendingTransactionResult event is invoked or when HapiManager.isTransactionResultPending() is true. To receive events when a TransactionResult is pending on the device please add a Events.PendingResults listener.
Parameter | Notes |
---|---|
device
Device |
This parameter specifies to the system which device you want to use for the operations. If none is supplied the system will attempt to use a default device, if any. |
//Fetches a pending TransactionResult from a device
api.getPendingTransaction();
update
The update operation checks for update to the card reader and initiates an update if needed. The update can either be a software update or a configuration update.
Parameter | Notes |
---|---|
device
Device |
This parameter specifies to the system which device you want to use for the operations. If none is supplied the system will attempt to use a default device, if any. |
//Check for card reader update
api.update();
listDevices
Starts the search for devices to connect to with the specified ConnectionMethod
Parameter | Notes |
---|---|
method *
ConnectionMethod |
The means of connection you intend to use to talk to the device. (Bluetooth, Serial, USB, etc...) |
//Search for Bluetooth devices
api.listDevices(ConnectionMethod.BLUETOOTH);
startMonitoringConnections
Starts a connection monitoring service. The service listens to events sent by the operating system about the connected hardware. If the service notices that a previously connected device suddenly disconnects on the hardware layer it attempts to reconnect to that particular device. Since this is a service it is necessary that the service is turned off before the application ends its life-time. This means that, if the service was running, stopMonitoringConnections() has to be called before the application is exited completely. Note that the service currently only works with BLUETOOTH
. In the case of BLUETOOTH
the service will attempt to reconnect to the device three times, if unsuccessful the connection is considered Disconnected
.
NOTE: you don't need to call this method explicitly since version 4.0.0.
//Starts the connection monitoring service
//app starts it's life-time
api.startMonitoringConnections();
...
//app ends its life-time
api.stopMonitoringConnections
stopMonitoringConnections
Stops a connection monitoring service. The service listens to events sent by the operating system about the connected hardware. If the service notices that a previously connected device suddenly disconnects on the hardware layer it attempts to reconnect to that particular device. Since this is a service it is necessary that the service is turned off before the application ends its life-time. This means that, if the service was running, stopMonitoringConnections() has to be called before the application is exited completely. Note that the service currently only works with BLUETOOTH
. In the case of BLUETOOTH
the service will attempt to reconnect to the device three times, if unsuccessful the connection is considered Disconnected
.
//Starts the connection monitoring service
//app starts it's life-time
api.startMonitoringConnections();
...
//app ends its life-time
api.stopMonitoringConnections
getConnectionStatus
Returns the latest stored connection status.
// Get current connection status
ConnectionStatus cs = api.getConnectionStatus();
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.
Parameter | Notes |
---|---|
listener *
Required |
An implementation of the Events.Required interface |
//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);
}
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.
Parameter | Notes |
---|---|
listener *
Status |
An implementation of the Events.Status interface |
//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);
}
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.
Parameter | Notes |
---|---|
listener *
Log |
An implementation of the Events.Log interface |
//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);
}
addPendingResultsEventHandler
Adds a listener so information can be retrieved when there is a pending TransactionResult. This listener fetches information from the following events : pendingTransactionResult, transactionResultReady. The corresponding removal method, removePendingResultsEventHandler, must be called before the end of your applications life-time to prevent memory leakage.
Parameter | Notes |
---|---|
listener *
PendingResults |
An implementation of the Events.PendingResult interface |
//Register a listener for log information events
this.api.addPendingResultsEventHandler(this);
//In this context the keyword "this" is an implementation of:
interface Events.PendingResults {
void pendingTransactionResult(Device device);
void transactionResultReady(TransactionResult transactionResult, Device device);
}
removeRequiredEventHandler
Removes a listener so information is not passed on to that listener any more.
Parameter | Notes |
---|---|
listener *
Required |
The listener to be removed |
//Remove a listener for required events
this.api.removeRequiredEventHandler(this);
removeStatusNotificationEventHandler
Removes a listener so information is not passed on to that listener any more.
Parameter | Notes |
---|---|
listener *
Status |
The listener to be removed |
//Remove a listener for status notification events
this.api.removeStatusNotificationEventHandler(this);
removeLogEventHandler
Removes a listener so information is not passed on to that listener any more.
Parameter | Notes |
---|---|
listener *
Log |
The listener to be removed |
//Remove a listener for log events
this.api.removeLogEventHandler(this);
removePendingResultsEventHandler
Removes a listener so information is not passed on to that listener any more.
Parameter | Notes |
---|---|
listener *
PendingResults |
The listener to be removed |
//Remove a listener for pending results events
this.api.removePendingResultsEventHandler(this);
deviceDiscoveryFinished
deviceDiscoveryFinished event gets called when a device discovery has finished and returns a list of devices.
Parameter | Notes |
---|---|
device *
Device |
The device that is invoking the event |
//Receiving a list of connectable devices
List myListOfDevices = new List();
@Override
public void DeviceDiscoveryFinished(List devices)
{
foreach(Device device in devices){ myListOfDevices.Add(device);
}
}
signatureRequired
signatureRequired event gets called when a card requires a signature instead of PIN entry and has two parameters, request and device.
Parameter | Notes |
---|---|
request *
SignatureRequest |
Holds the signature request |
device *
Device |
The device that is invoking the event |
@Override
public void signatureRequired(SignatureRequest signatureRequest, Device device) {
signatureRequest.getMerchantReceipt();
api.signatureResult(true);
}
endOfTransaction
endOfTransaction event gets called at the end of each transaction and has two parameters, result and device.
Parameter | Notes |
---|---|
result *
TransactionResult |
Holds the results for the transaction |
device *
Device |
The device that is invoking the event |
@Override
public void endOfTransaction(TransactionResult transactionResult, Device device) {
Log.d(App, transactionResult.getCustomerReceipt());
}
connectionStatusChanged
connectionStatusChanged event gets called when the state of a card reader connection changes.
Parameter | Notes |
---|---|
status *
ConnectionStatus |
An enum containing the status code for the connection |
device *
Device |
The device that is invoking the event |
@Override
public void connectionStatusChanged(ConnectionStatus connectionStatus, Device device) {
Log.d(App, connectionStatus.name());
}
currentTransactionStatus
currentTransactionStatus event gets called when the state of an ongoing transaction changes.
Parameter | Notes |
---|---|
statusInfo *
StatusInfo |
An object containing information about the current transaction |
device *
Device |
The device that is invoking the event |
@Override
public void currentTransactionStatus(StatusInfo statusInfo, Device device) {
}
onMessageLogged
onMessageLogged event gets called for all log messages that are being logged. This is only intended for debugging.
Parameter | Notes |
---|---|
logLevel *
LogLevel |
An enum containing the log level |
message *
String |
A String containing the current log message |
@Override
public void onMessageLogged(LogLevel logLevel, String message){
Log.d("API log", message);
}
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.
Parameter | Notes |
---|---|
logs *
String |
String containing the current log |
device *
Device |
The device that is invoking the event |
@Override
public void deviceLogsReady(String logs, Device device){
Log.d("Device logs:", logs);
}
pendingTransactionResult
In the case of a communication failure between the device and the API a TransactionResult might have not been delivered to the API. This event is invoked when the device has a pending TransactionResult. This event might be invoked when reconnecting to a device after a communication failure during a transaction.
Parameter | Notes |
---|---|
device *
Device |
The device that is invoking the event |
@Override
public void pendingTransactionResult(Device device){
//Here you might want to call api.getPendingTransaction(); to receive the TransactionResult
}
transactionResultReady
In the case of a communication failure between the device and the API a TransactionResult might have not been delivered to the API. This event will be invoked after using hapi.getPendingTransaction();. When there is no pending transaction the TransactionResult will contain default/error fields and no receipts.
Parameter | Notes |
---|---|
result *
TransactionResult |
Holds the results for the transaction |
device *
Device |
The device that is invoking the event |
@Override
public void transactionResultReady(TransactionResult transactionResult, Device device){
//Here you might want to do stuff to the transactionResult
}
TransactionType
An enum representing different types of transactions.
UNDEFINED
SALE
VOID_SALE
REFUND
VOID_REFUND
CANCEL_SALE
CANCEL_REFUND
TOKENIZE_CARD
TransactionResult
An object holding information about a transaction result.
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 |
Gets the card type id from the transaction. The card type id is an identifier inside the Handpoint gateway which represents what kind of card was used. "U015" for example represents SAS Airline-Systems in our systems. |
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. |
HapiFactory
A factory to provide a unified entrypoint and to simplify the way to instantiate the Hapi object.
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. |
//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);
}
ConnectionMethod
An enum representing different types of connection methods.
Currently BLUETOOTH
and SIMULATOR
are supported for android devices.
USB
SERIAL
BLUETOOTH
HTTPS
WIFI
ETHERNET
SIMULATOR
//Currently BLUETOOTH and SIMULATOR are the only ConnectionMethod available.
public enum ConnectionMethod {
USB,
SERIAL,
BLUETOOTH,
HTTPS,
WIFI,
ETHERNET,
SIMULATOR
}
Device
An object to store the information about the device we're working with.
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. |
Property | Description |
---|---|
Id String |
An unique identifier of the device. |
//Create and init a new Device
Device dev = new Device("CardReader7", "08:00:69:02:01:FC", "1", ConnectionMethod.BLUETOOTH);
ConnectionStatus
A list of statuses given to a connection
Connected
Connecting
Disconnected
Disconnecting
Initializing
NotConfigured
SignatureRequest
A class containing information about a signature request/signature verification.
Property | Description |
---|---|
Timeout int |
int the value of the timeout in seconds. |
MerchantReceipt String |
String the merchant receipt as html. |
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.
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
DeviceParameter
An enum describing all the available commands to send to a device.
When used a legal value is expected with the command
BluetoothName
BluetoothPass
SystemTimeout
ScreenTimeout
SignatureTimeout
DeviceStatus
A class that holds the device status.
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 |
FinancialStatus
An enum representing different statuses of a finalized transaction
UNDEFINED
AUTHORISED
DECLINED
PROCESSED
FAILED
CANCELLED
PARTIAL_APPROVAL
statusInfo
A class containing information about the status of the transaction.
Property | Description |
---|---|
cancelAllowed bool |
A bool representing if the card reader will accept a cancel 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 device. |
LogLevel
An enum describing the different levels of logging used in the hapi and used in the device.
None
Info
Full
Debug
OptionalParameters
A class containing optional transaction parameters now supported by the device.
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 |
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 |
VerificationMethod
An enum representing different verification methods used in the transaction.
UNDEFINED
SIGNATURE
PIN
PIN_SIGNATURE
FAILED
NOT_REQUIRED
status
An enum containing information about the status of the transaction.
UserCancelled
WaitingForCard
CardInserted
ApplicationSelection
ApplicationConfirmation
AmountValidation
PinInput
ManualCardInput
WaitingForCardRemoval
TipInput
AuthenticatingPos
WaitingForSignature
ConnectingToHost
SendingToHost
ReceivingFromHost
DisconnectingFromHost
PinInputComplete
Undefined
CardEntryType
An enum representing different card entry types.
UNDEFINED
MSR
ICC
CNP
HapiManager
A static class containing information about the current status of the SDK
Property | Description |
---|---|
DefaultSharedSecret String |
Gets the default shared secret is use in the SDK. |
LogLevel LogLevel |
Gets the current log level of the SDK and card reader. |
inTransaction boolean |
Checks whether the SDK is in transaction or not. True if the SDK is in transaction, false otherwise. This might return a true if there is a communication error between the SDK and card reader but the transaction has been completed on the card reader. |
SdkVersion String |
Gets the current Sdk version. |
isTransactionResultPending boolean |
In the case of a communication failure between the device and the API a TransactionResult might have not been delivered to the API. This function checks if there is a pending TransactionResult. This field is only updated when connecting to a device. If this function returns true the TransactionResult (which includes the receipt) can be fetched with hapi.getPendingTransactionResult();. This function serves the same functionality as the event pendingTransactionResult(Device device), so every time that 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 Card Reader 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 |
//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;