# Direct card API
# Overview
Poynt Direct card API allows reading non payment cards using the card reader on poynt terminals. This tutorial will explain the steps required to integrate.
# Prerequisites
This tutorial assumes you have access to an unlocked Poynt terminal(developer mode) and Android studio.
Include Poynt sdk in your android project
implementation 'co.poynt.api:android-api-model:1.2.140'
implementation 'co.poynt.android.sdk:poynt-sdk:1.2.42'
TIP
For latest sdk and api versions refer to SDK Versions
Cards supported
The interface supports cards with protocols listed below
- SLE 442
- SLE 428
- EMV L1 Compliant Cards
- ISO 7816-3
# Integrating with the SDK
# Interface
The API consist of 3 main operations: connectToCard(), exchangeAPDU() and disconnectFromCard().
DANGER
Make sure to call disconnectFromCard after you are done reading the card info to put the card reader back in payment card mode. Not doing so can result in transaction failures.
interface IPoyntCardReaderService{
/**
* Initiates the card reader to connect over the specific interface (CT or CL or Both). If a
* card is found on any of the specified interfaces, the reader establishes an L1 connection
* to the card. If a card is not found for the specified duration of time, the command returns
* an apprepriate error code.
*
* @param card read connection options
* @param callback {@link IPoyntConnectToCardListener}
*/
void connectToCard(in ConnectionOptions connectionOptions, IPoyntConnectToCardListener callback);
/**
* Disconnects from the card (CT or CL or both) and powers off corresponding interface.
*
* @param card read connection options
* @param callback {@link IPoyntDisconnectFromCardListener}
*/
void disconnectFromCard(in ConnectionOptions connectionOptions, IPoyntDisconnectFromCardListener callback);
/**
* Checks whether a card is present in the card reader slot or not. This command does not check
* whether a connection to the card has been established or not. It only checks if a card is
* physically inserted in the slot such that the card inserted switch is asserted. It does not
* apply to contactless interface.
*
* @param card read connection options
* @param callback {@link IPoyntCardInsertListener}
*/
void checkIfCardInserted(in ConnectionOptions connectionOptions, IPoyntCardInsertListener callback);
/**
* Send an APDU command to the card and receive the response APDU.
*
* @param apduData containing C-APDU
* @param callback {@link IPoyntAPDUListener}
*/
void exchangeAPDU(in APDUData apduData, IPoyntExchangeAPDUListener callback);
/**
* Abort any pending connect or wait for card removal operations.
*
*/
void abort();
/**
* Send a list of APDU commands to the card and receive the response APDU.
*
* @param apduData containing list of C-APDU
* @param callback {@link IPoyntAPDUListListener}
*/
void exchangeAPDUList(in List<APDUData> apduData, IPoyntExchangeAPDUListListener callback);
}
# Binding to service
..
..
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
cardReaderService = IPoyntCardReaderService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
// service is disconnected
}
};
protected void onResume() {
super.onResume();
// Bind to service
bindService(Intents.getComponentIntent(Intents.COMPONENT_POYNT_CARD_READER_SERVICE),
serviceConnection, BIND_AUTO_CREATE);
}
protected void onPause() {
super.onPause();
// Make sure you unbind from service to prevent memory leaks
unbindService(serviceConnection);
}
..
..
# Check card presence
Card reader service API allows checking for card presence, the API returns the result as a callback.
ConnectionOptions connectionOptions = new ConnectionOptions();
// select the interface to check, can be one of EMV, GSM, SLE
connectionOptions.setContactInterface(ConnectionOptions.ContactInterfaceType.GSM);
connectionOptions.setTimeout(60);
try {
cardReaderService.checkIfCardInserted(connectionOptions,
new IPoyntCardInsertListener.Stub() {
@Override
public void onCardFound() throws RemoteException {
// code to handle card found case
}
@Override
public void onCardNotFound() throws RemoteException {
// code to handle card not found
}
@Override
public void onError(PoyntError poyntError) throws RemoteException {
// error occured while checking for card presence, check the error message
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
# Connect to card
Once the card is inserted, connect to the card to verify the connection.
ConnectionOptions connectionOptions = new ConnectionOptions();
connectionOptions.setContactInterface(ConnectionOptions.ContactInterfaceType.GSM);
connectionOptions.setTimeout(60);
try {
cardReaderService.connectToCard(connectionOptions,
new IPoyntConnectToCardListener.Stub() {
@Override
public void onSuccess(ConnectionResult connectionResult) throws RemoteException {
// code to handle card connected case
}
@Override
public void onError(PoyntError poyntError) throws RemoteException {
// code to hande error case
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
# Read card data
The card reader is ready for APDU commands once the card is connected, the APDU commands can be sent one at a time or as a list which will be executed in order.
# APDU format
APDU is of the following format
|CASE CLS INS P1 P2 *Lc *Data Le | (* optional)
Sample APDU Commands
// Select MasterFile
APDU: 04 A0 A4 00 00 02 3F00 00
// Get Response of MasterFile
APDU: 02 A0 C0 00 00 00
// Select GSM Directory
APDU: 04 A0 A4 00 00 02 7F20 00
// Select IMSI
APDU: 04 A0 A4 00 00 02 6F07 00
// Get Response of Select IMSI
APDU: 02 A0 C0 00 00 00
// Read Binary IMSI
APDU: 02 A0 B0 00 00 09
# Send single APDU command
APDUData apduData = new APDUData();
apduData.setContactInterface(APDUData.ContactInterfaceType.GSM);
apduData.setTimeout(60);
apduData.setCommandAPDU("<YOUR APDU COMMAND>");
try {
cardReaderService.exchangeAPDU(apduData,
new IPoyntExchangeAPDUListener.Stub() {
@Override
public void onSuccess(String rAPDU) throws RemoteException {
// code to handle APDU response
}
@Override
public void onError(PoyntError poyntError) throws RemoteException {
// code to handle APDU command error
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
# Send multiple APDU commands at once
// first command
APDUData apduData = new APDUData();
apduData.setContactInterface(APDUData.ContactInterfaceType.EMV);
apduData.setTimeout(60);
apduData.setCommandAPDU("<APDU_1>");
// second command
APDUData apduData2 = new APDUData();
apduData2.setContactInterface(APDUData.ContactInterfaceType.EMV);
apduData2.setTimeout(60);
apduData2.setCommandAPDU("<APDU_2>");
ArrayList<APDUData> apdus = new ArrayList<>();
apdus.add(apduData);
apdus.add(apduData2);
try {
cardReaderService.exchangeAPDUList(apdus,
new IPoyntExchangeAPDUListListener.Stub() {
@Override
public void onResult(List<String> list, PoyntError poyntError) throws RemoteException {
if (poyntError != null) {
// error occurred
} else {
// APDU response is in the same order as the commands sent
}
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
# Disconnect from card
Disconnect from the card and prompt the user to remove the card to make sure the card reader is ready for next transaction.
ConnectionOptions connectionOptions = new ConnectionOptions();
connectionOptions.setContactInterface(ConnectionOptions.ContactInterfaceType.GSM);
connectionOptions.setTimeout(60);
try {
cardReaderService.disconnectFromCard(connectionOptions,
new IPoyntDisconnectFromCardListener.Stub() {
@Override
public void onDisconnect() throws RemoteException {
// code to handle card disconnected
}
@Override
public void onError(PoyntError poyntError) throws RemoteException {
// handle error
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
# Frequently Asked Questions
# Can we use this interface to read payment cards?
No. Payment cards are encrypted by the secure module in the card reader, no application has access to the card data in clear text.
# Is there a code sample ready to use?
Yes, refer to this code sample (opens new window)