Existing customers: Get your sandbox and production credentials for Heartland/Portico integration.
Our Android SDK lets you connect to a C2X or Moby 5500 device and process credit card payments.
Installing the SDK into your solution is usually done using Maven.
To install via Maven, add the below dependency to the POM file:
implementation 'com.globalpayments:android-device-lib:2.0.5'The SDK manages the communication with the device and the gateway. Neither the SDK nor the device maintains a copy of the merchant account credentials used. These need to be set by your application at runtime. Below, you'll find the code snippets to use for SDK imports and configuring the SDK with credentials.
import java.math.BigDecimal;
import com.globalpayments.library.entities.Card;
import com.globalpayments.library.terminals.ConnectionConfig;
import com.globalpayments.library.terminals.DeviceListener;
import com.globalpayments.library.terminals.entities.TerminalResponse;
import com.globalpayments.library.terminals.TransactionListener;
import com.globalpayments.library.terminals.transactions.BaseBuilder;
import com.globalpayments.library.terminals.c2x.C2XDevice;
import com.globalpayments.library.terminals.moby.MobyDevice;
import com.globalpayments.library.terminals.transactions.CreditAuthBuilder;
import com.globalpayments.library.terminals.entities.CardholderInteractionRequest;
import com.globalpayments.library.terminals.entities.CardholderInteractionResult;
import com.globalpayments.library.terminals.enums.Environment;
import com.globalpayments.library.terminals.enums.TransactionStatus;ConnectionConfig connectionConfig = new ConnectionConfig();
connectionConfig.setUsername("xxxxxxxxxxxx");
connectionConfig.setPassword("xxxxxxxx");
connectionConfig.setSiteId("xxxxx");
connectionConfig.setDeviceId("xxxxxxx");
connectionConfig.setLicenseId("xxxxx");
connectionConfig.setConnectionMode(ConnectionMode.BLUETOOTH);
connectionConfig.setEnvironment(Environment.TEST);
connectionConfig.setSafEnabled(safEnabled);
connectionConfig.setSafExpirationInDays(5);
connectionConfig.setSurchargeEnabled(surchargeEnabled);C2XDevice device = new C2XDevice(
this.getApplicationContext(), // when called from an activity
connectionConfig
);MobyDevice device = new MobyDevice(
this.getApplicationContext(), // when called from an activity
connectionConfig
);Before creating a Bluetooth connection with the device, your application requires some additional code in the form of a device listener or delegate object. Here’s a brief description of each of the available methods and how they can assist in your application development:
onBluetoothDeviceFound – As the Bluetooth scanning process continues, this method will be called by the SDK as soon as a device is found, allowing your application to immediately update its UI to present this information to the user.onBluetoothDeviceList – Once the Bluetooth scanning process completes, this method will be called by the SDK, signaling that no additional devices are currently available. The user should select one of the available devices or check their settings before initiating another Bluetooth scan.onConnected – This method will be called by the SDK once a successful Bluetooth connection has been established with the selected device.onDisconnected – This method will be called by the SDK when the Bluetooth connection has been lost.onError – This method will be called by the SDK if any errors occur during the Bluetooth connection process.onTerminalInfoReceived – This method may be called by the SDK if any information about the device is available.Your application will need to initiate the Bluetooth scanning process by calling the initialize method on the device object. Any progress along the way will be indicated by the SDK calling the appropriate method on your application’s device listener or delegate object.
Once the user selects the correct Bluetooth device, your application will need to update the SDK by calling the connect method on the device object, and this method will require an object obtained by either the onBluetoothDeviceFound method or the onBluetoothDeviceList method.
device.setDeviceListener(new DeviceListener() {
@Override
public void onBluetoothDeviceFound(BluetoothDevice bluetoothDevice) {
// receive available bluetooth devices as they are found
}
@Override
public void onBluetoothDeviceList(HashSet<BluetoothDevice> deviceList) {
// receive list of available bluetooth devices
// present to user for selection/confirmation
if (deviceList == null || deviceList.isEmpty()) {
return;
}
// connect to the selected device
device.connect(deviceList.iterator().next());
}
@Override
public void onConnected(TerminalInfo terminalInfo) {
// device is connected
// allow processing
}
@Override
public void onDisconnected() {
// device has disconnected
}
@Override
public void onError(Error Error, ErrorType errorType) {
// handle errors
}
@Override
public void onTerminalInfoReceived(TerminalInfo terminalInfo) {
// handle received device information
}
});
// initiate a Bluetooth scan
device.initialize();If you want to use a USB connection instead of Bluetooth, you can use Moby 5500.
Just like with Bluetooth, you'll need to set up the same DeviceListener shown above in the Bluetooth Pairing section above. Your application will initiate the USB connection process by calling the initialize method on the device object. The onConnected method will be called when the connection is established.
Before starting a transaction request, your application requires some additional code in the form of a transaction listener or delegate object.
Here’s a brief description of each of the available methods and how they can assist in your application development:
onStatusUpdate – As the EMV process occurs, this method will be called by the SDK when the device may need to prompt the user with information about the current status. Any status update received by this method should be displayed to the user.onCardholderInteractionRequested – As the EMV process occurs, this method will be called by the SDK when the card holder needs to take action. This is currently limited to EMV application selection and confirmation of the final authorization amount. Note: Version 1.4.0 and above changes this function to return a boolean instead of void. Return true if you handled the interaction and false if you did not.onTransactionCompleted – Once the authorization request has been submitted to the gateway, this method will be called by the SDK, passing the authorization response to your application.onError – This method will be called by the SDK if any errors occur during the transaction process.device.setTransactionListener(new TransactionListener() {
@Override
public void onStatusUpdate(TransactionStatus transactionStatus) {
// inform user of status updates
}
@Override
public boolean onCardholderInteractionRequested(
CardholderInteractionRequest cardholderInteractionRequest) {
// prompt user for action
CardholderInteractionResult result;
switch (cardholderInteractionRequest.getCardholderInteractionType()) {
case EMV_APPLICATION_SELECTION:
String[] applications = cardholderInteractionRequest.getSupportedApplications();
// prompt user to select desired application
// send result
result = new CardholderInteractionResult(
cardholderInteractionRequest.getCardholderInteractionType()
);
result.setSelectedAidIndex(0);
device.sendCardholderInteractionResult(result);
return true;
case FINAL_AMOUNT_CONFIRMATION:
// prompt user to confirm final amount
result = new CardholderInteractionResult(
cardholderInteractionRequest.getCardholderInteractionType()
);
result.setFinalAmountConfirmed(true);
device.sendCardholderInteractionResult(result);
return true;
case SURCHARGE_REQUESTED:
// prompt user to approve/decline the surcharge amount
result = new CardholderInteractionResult(
CardholderInteractionType.CARDHOLDER_SURCHARGE_CONFIRMATION);
result.setFinalAmountConfirmed(isApproved);
device.sendCardholderInteractionResult(result);
return true;
default:
break;
}
return false;
}
@Override
public void onTransactionComplete(TerminalResponse response) {
// handle response
}
@Override
public void onError(Error error, ErrorType errorType) {
// handle errors
}
});Our SDK comes with two builders to help you get set up to start processing transactions quickly, one for transactions initiated using manual entry and another for transactions using tap, insert, or swipe methods.
Your application will need to initiate the transaction request process by creating the correct request builder, preparing any required data or information, and executing the builder. Any progress along the way will be indicated by the SDK calling the appropriate method on your application’s transaction listener or delegate object. Example code for these methods is provided below.
// Initiate Transaction – CreditAuth – Manual Entry (example code)
Card card = new Card();
card.setNumber("424242******4242");
card.setExpMonth(12);
card.setExpYear(2025);
card.setCvv("123");
CreditAuthBuilder builder = new CreditAuthBuilder(device);
builder.setAmount(new BigDecimal("10.00"));
builder.setCreditCard(card);
builder.execute();// Initiate Transaction – CreditAuth – Tap/Insert/Swipe (example code)
CreditAuthBuilder builder = new CreditAuthBuilder(device);
builder.setAmount(new BigDecimal("10.00"));
builder.execute();// Initiate Transaction – CreditAddToBatch (example code)
CreditCaptureBuilder builder = new CreditCaptureBuilder(device);
builder.setTransactionId(transactionId);
builder.execute(); // Initiate Transaction – CreditSale – Manual Entry (example code)
Card card = new Card();
card.setNumber("424242******4242");
card.setExpMonth(12);
card.setExpYear(2025);
card.setCvv("123");
CreditSaleBuilder builder = new CreditSaleBuilder(device);
builder.setAmount(new BigDecimal("10.00"));
builder.setCreditCard(card);
builder.execute();// Initiate Transaction – CreditSale – Dip/Swipe (example code)
CreditSaleBuilder builder = new CreditSaleBuilder(device);
builder.setAmount(new BigDecimal("10.00"));
builder.execute();// Initiate Transaction – Tip Adjust (example code)
CreditAdjustBuilder builder = new CreditAdjustBuilder(device);
builder.setTransactionId(transactionId);
builder.setAmount(new BigDecimal("12.00"));
builder.setGratuity(new BigDecimal("2.00"));
builder.execute();// Initiate Transaction – CreditReturn by Transaction ID (example code)
CreditReturnBuilder builder = new CreditReturnBuilder(device);
builder.setTransactionId(transactionId);
builder.setAmount(new BigDecimal("10.00"));
builder.execute();// Initiate Transaction – CreditVoid (example code)
CreditVoidBuilder builder = new CreditVoidBuilder(device);
builder.setTransactionId(transactionId);
builder.execute();This function will have no effect if there is no transaction active.
// Cancel a transaction (example code)
device.cancelTransaction();You can get the SVA PAN data from a gift card when using a Moby 5500. Use the function doSvaStartCard() to start the card reading process and then receive the data on the callback to onTransactionComplete().
To use your own Client Transaction ID, set the value that you want by using the setReferenceNumber method of the builder, as shown below. If you do not set your own reference number, a transaction ID will be generated automatically.
//Set the reference number
builder.setReferenceNumber(yourClientTxnId);Once the transaction is complete, you will see the same value returned inside the TerminalResponse object parameter of onTransactionComplete:
// Get the transaction ID
`transaction.getTransactionId();`Auto-substantiation is commonly used in the healthcare industry to support FSA and HRA debit card transactions. It ensures qualified healthcare expenses are approved at the point of sale. Consumers appreciate merchants that support auto-substantiation at the POS because it means they don't have to file a separate claim for benefits.
To take advantage of auto-substantiation, the merchant must use an Inventory Information Approval System (IIAS) to identify the qualified healthcare products being purchased by the cardholder at the POS. The IIAS must be able to:
The SDK will use these values to tag the transaction appropriately for healthcare processing.
Set the subtotal values per healthcare category (dental, prescription, clinic, and vision) before executing a transaction.
These amounts must not add up to more than the main transaction amount or an exception will be thrown.
//Set the healthcare values
creditSaleBuilder.setClinicSubTotal(new BigDecimal(5));
creditSaleBuilder.setDentalSubTotal(new BigDecimal(5));
creditSaleBuilder.setPrescriptionSubTotal(new BigDecimal(5));
creditSaleBuilder.setVisionSubTotal(new BigDecimal(5));The SDK supports OTA updates for the C2X/C3X and Moby 5500 firmware and configuration updates for C2X/C3X. These updates improve device functionality without requiring manual installation.
You'll need to establish a Bluetooth connection with the device to check for and install updates.
The Moby 5500 does not currently support OTA configuration updates.
The first step is for your application to request the available versions for either firmware or config. You will also need to set the proper listener so the SDK can notify for success or failure.
//Set the listener for the available terminal versions
device.setAvailableTerminalVersionsListener(new AvailableTerminalVersionsListener() {
@Override
public void onAvailableTerminalVersionsReceived(TerminalUpdateType type, List<String>
versions) {
//choose a version to install
}
@Override
public void onTerminalVersionInfoError(Error error) {
//handle error
}
});
//Request the available terminal firmware versions
device.getAvailableTerminalVersions(TerminalUpdateType.FIRMWARE);
//Request the available terminal config versions
device.getAvailableTerminalVersions(TerminalUpdateType.CONFIG);For Moby 5500, the List of versions returned in the onAvailableTerminalVersionsReceived callback will only return a single String stating “Update available” as it does not provide the actual version number.
After getting the available terminal version for the selected TerminalUpdateType, you can initiate the download and install to the device. Make sure that the SDK is able to complete the process without interruption. A separate listener is used for the installation and will give progress updates.
//Set the listener for terminal updates
c2XDevice.setUpdateTerminalListener(new UpdateTerminalListener() {
@Override
public void onProgress(@Nullable Double completionPercentage, @Nullable String
progressMessage) {
//Progress updates
}
@Override
public void onTerminalUpdateSuccess() {
//Update completed successfully
}
@Override
public void onTerminalUpdateError(Error error) {
//Handle error
}
});Request the terminal update by using one of the versions returned inside the available terminal updates listener method, onAvailableTerminalVersionsReceived.
The Moby 5500 callback for available versions does not give an actual version and so the call to updateTerminal will ignore the 2nd parameter for version. This means that you can pass in a null value or any string value you want:
//Request the terminal update
device.updateTerminal(TerminalUpdateType.FIRMWARE, versions.get(i));After a successful update, the device will reboot. Watch for blinking lights as it completes the final steps. Once it’s finished, reconnect the device to resume normal operations.
The SDK requires the following Android permissions:
INTERNET permission is required to perform the transaction network requests and bluetooth-related permissions necessary for the SDK to connect to the terminals via bluetooth.ACCESS_FINE_LOCATION (one of the bluetooth permissions) is required because on Android 11 and lower, a bluetooth scan could potentially be used to gather information about the location of the user.You will need to request these permissions in your application depending on what functionality you want to use.
The SDK includes the helper class PermissionHelper to make it easy to request the necessary permissions. Refer to the example app included in the SDK package for complete instructions on how to use this.
SAF allows transactions to be stored when connection issues prevent processing. Stored transactions can then be manually processed when the connection is stable again. When SAF is enabled, any supported transaction that fails to process due to server errors or client-side connection issues will be stored on the Android device in a secure database.
When confident that the server or connection issues are resolved, the user can upload all the pending transactions that are stored on the device. During the upload process, each stored transaction will be processed one at a time, from oldest to newest.
To enable SAF, simply add a few lines of code to your ConnectionConfig setup. Use setSafEnabled to enable the feature and use setSafExpirationInDays to set the number of days that a stored transaction can be saved before it is automatically deleted. As with other ConnectionConfig settings, these must be set before connecting to the device:
connectionConfig.setSafEnabled(true);
connectionConfig.setSafExpirationInDays(5); To receive callbacks related to SAF, you’ll also need to use setSafListener on the device object:
device.setSafListener(new SafListener() {
@Override
public void onProcessingComplete(List<TransactionResponse> responses) {
//callback for when all previously stored transactions have been attempted
}
@Override
public void onAllSafTransactionsRetrieved(List<SafTransaction> obfuscatedSafTransactions){
//callback when the requested transactions have been retrieved
}
@Override
public void onError(Error error) {
//callback for errors
}
@Override
public void onTransactionStored(String id, int totalCount, BigDecimal totalAmount) {
//callback when a transaction has been stored in the database
}
@Override
public void onStoredTransactionComplete(String id, TransactionResponse transactionResponse) {
//callback when a single transaction processing has been attempted
}
});When you're ready to upload stored transactions, simply call uploadSAF to initiate the upload process shown using the code shown below. When the process is complete, the onProcessingComplete callback will be called with a full list of results.
device.uploadSAF();When a transaction has been completed with approved or declined, the transaction should be acknowledged so it can be deleted from the database. If the transaction is not acknowledged, it will stay in the database until it is expired.
To set up transaction acknowledgements, use the following method inside the callback for onStoredTransactionComplete (refer to the example app included in the SDK package for complete setup instructions).
device.acknowledgeSAFTransaction(id);During testing or whenever your connection is unsteady, you can force SAF for a period of time. This option can be toggled at any time once connected to the device and will automatically store supported transactions, whether or not you have a working internet connection. Here's how to use it:
device.setForcedSafEnabled(true);Special steps are required when dealing with follow-up transactions like refund or void of stored and unprocessed transactions. When a transaction is stored, it provides a SAF ID for it. This SAF ID is used in place of a transaction ID when creating a follow-up transaction. For example, if a sale transaction is stored and you would like to void it, you would use the SAF ID provided for the transaction ID in the void request.
When the SAF transactions are uploaded or forwarded, each stored transaction will be processed one at a time, from oldest to newest, and update the void request with the actual transaction ID after the sale is processed.
Surcharge functionality allows merchants to apply a percentage fee on credit card transactions.
With surcharge enabled, a 3% charge (default value) will be added to the total for sale or auth transactions that use a credit card. No charge is added if the card type is unable to be detected. To enable surcharge, simply set the option in the ConnectionConfig before connecting to the terminal:
connectionConfig.setSurchargeEnabled(surchargeEnabled);Surcharge requires that the cardholder is given the opportunity to approve or decline the charge. So, if you want to enable surcharge, you'll need to add the new cardholder interaction type of SURCHARGE_REQUESTED as shown below. Note that no fee is applied if the card type cannot be determined. This interaction request will occur only when surcharge is enabled and the card is determined to be a credit card:
case SURCHARGE_REQUESTED:
// prompt user to approve/decline the surcharge amount
return true; //Only return true if you actually showed a promptBy default, the surcharge is calculated on the total amount including tax. Depending on the requirements of the state that you're operating in, you may be required to calculate surcharges without including tax. You can set it up to calculate the surcharge on the pre-tax total using the ConnectionConfig option below:
connectionConfig.setSurchargePreTax(surchargePreTaxEnabled);Note that to use this option, you must also provide the tax amount. The tax amount value is treated the same as a gratuity and is considered a part of the total. The SDK will take the provided total, subtract tax amount, and then calculate the surcharge. The CreditSaleBuilder and CreditAuthBuilder classes have been updated to include this new tax amount variable:
creditSaleBuilder.setTaxAmount(new BigDecimal(taxAmount));By default, the surcharge is calculated at 3%, but you can set a custom percentage between 2% and 3%. You can use this ConnectionConfig option to set a custom percentage for the surcharge:
connectionConfig.setSurchargePercent(customSurchargePercent);If you run into trouble with your application, check out the list below for some basic troubleshooting steps. Chances are pretty good that you'll find your solution. We're always here to help, though. So if you can't find the solution, use the Developer Support link at the bottom of the page to contact us.