WisePad 2

How to use the WisePad2 mobile payment device with the Decryptx Payload Parser.

The Decryptx Payload Parser supports the BBPOS WisePad 2 mobile point of sale (POS) device. The device supports magnetic stripe, keyed and EMV (contact and contactless) input, and has Bluetooth, USB and wireless interfaces which allow it to operate with mobile devices such as phones or tablets. It also has a 2G/3G modem that allows it to communicate directly over the Internet.

BBPOS supplies software development kits (SDK) for a wide variety of operating systems and development languages on their developer portal. They make it easy for the POS application or mobile device to obtain data for the payment terminal. In this guide, we include sample code that use WisePad's Android SDK to extract P2PE secured payloads. The vendor provides similar SDKs in Objective-C and a number of other languages. While the programming languages are different, the commands and the command flow are the same.

Typically when capturing a payload, text should be displayed on the device instructing the user to insert their card or swipe. For brevity, we have omitted text prompts from the samples below. The BBPOS WisePad 2 documentation has a section outlining the appropriate payment data capture flow and text to display at each step during that flow.

WisePad 2 Payloads

To capture a magnetic stripe payload we need to send the checkCard command with the CheckCardMode set to Swipe:

Hashtable<String, Object> data = new Hashtable<String, Object>();
data.put("checkCardMode",    CheckCardMode.SWIPE);
data.put("checkCardTimeout", new SimpleDateFormat("yyMMddHHmmss").format(Calendar.getInstance().getTime()) );
bbDeviceController.checkCard(data);

When the WisePad 2 receives this command and it enables Magnetic Stripe Reader (MSR) and sends a response to APP/SDK, the SDK then calls to the onWaitingForCard callback function. From this point, the user can swipe their card in the MSR, however, you may want to display a message to the user prompting them to swipe their card.

new BBDeviceControllerListener(){

    @Override
    public void onWaitingForCard(CheckCardMode checkCardMode) {
        if ( checkCardMode == SWIPE ){
            statusEditText.setText( "Please swipe card." );
        }
    }
};

When the user swipes their card, the data is passed to the BBDeviceControllerListener's onReturnCheckCardResult callback with the checkCardResult set to MSR. From the decodedData hash table, you can extract your parameters for the Decryptx Parser API call. Note that the WisePad 2 outputs the Track 1 and Track 2 payloads as discrete values; that means that you need to make two separate API calls to the Parser if you want to decrypt both values. Refer to the BBPOS documentation for a full description of the data elements included in the decodeData hash table.

@Override
public void onReturnCheckCardResult(CheckCardResult checkCardResult, Hashtable<String, String> decodeData) {
    
    if( checkCardResult == CheckCardResult.MSR ) {
        String ksn       = decodeData.get("ksn");
        
        String encTrack1 = decodeData.get("encTrack1");
        String encTrack2 = decodeData.get("encTrack2");
        
        String serialNumber = decodeData.get("serialNumber");
        
    }
}

Sample Track 1 payload:

5BD4EC2E6CFEA6248C2DE7B2262D3581D937D28041EE742DF2E3CF43D504D9858AE72AC5FD5D10D5F4B28BAEA26040C11F60186E89C861446369E40290A43654239203E43FE5923AF9941E7A1270CD5D

Sample Track 2 payload:

5EAC71D98947BD5AB362169B0394F122A22B6F6AF0401CF5F870AFCED8390172B526C6296B9DA7C1

Sample KSN:

02400810160051000027

Sample serial number:

WPC204633000051

Sample cURL request with Track 1 payload and KSN:

curl 'https://cert-parser.decryptx.com/api/decrypt/parser' \
    -X POST \
    --header 'Content-Type: application/json' \
    --header 'Accept: application/json' \
    -d '{
        	"partnerId"     : "?????????",
        	"partnerKey"    : "ef1ad938150fb15a1384b883a104ce70",
        	"reference"     : "723f57e1-e9c8-48cb-81d9-547ad2b76435",
        	"ksn"           : "02400810160051000027",
        	"deviceType"    : "wisepad2",
        	"deviceSerial"  : "WPC204633000051",
        	"devicePayload" : "5BD4EC2E6CFEA6248C2DE7B2262D3581D937D28041EE742DF2E3CF43D504D9858AE72AC5FD5D10D5F4B28BAEA26040C11F60186E89C861446369E40290A43654239203E43FE5923AF9941E7A1270CD5D"
        }'

Response body from the cURL request:

{
    "success"  : true,
    "messageId": "1201709151448251211602128",
    "meta" : {
        "device" : "bbpos",
        "serial" : "WPC204633000051",
        "mode"   : "swiped"
    },
    "track1" : {
        "decrypted" : "2542353431353234343434343434343434345e544553542f424c554546494e5e323231323130313132333435363738393f3f",
        "encoding"  : "hex",
        "length"    : 50,
        "ascii"     : "%B5415244444444444^TEST/BLUEFIN^2212101123456789??",
        "masked"    : "%*541524******4444^TEST/BLUEFIN^2212************?*"
    },
    "extracted" : {
        "PAN"       : "5415244444444444",
        "EXPY"      : "1222",
        "Surname"   : "TEST",
        "FirstName" : "BLUEFIN",
        "ServiceCode"   : "101",
        "Discretionary" : "123456789"
    }
}

Sample cURL request with Track 2 payload and KSN:

curl 'https://cert-parser.decryptx.com/api/decrypt/parser' \
    -X POST \
    --header 'Content-Type: application/json' \
    --header 'Accept: application/json' \
    -d '{
        	"partnerId"     : "WATERFORD",
        	"partnerKey"    : "ef1ad938150fb15a1384b883a104ce70",
        	"reference"     : "723f57e1-e9c8-48cb-81d9-547ad2b76435",
        	"ksn"           : "02400810160051000027",
        	"deviceSerial"  : "WPC204633000051",
        	"deviceType"    : "wisepad2",
        	"devicePayload" : "5EAC71D98947BD5AB362169B0394F122A22B6F6AF0401CF5F870AFCED8390172B526C6296B9DA7C1"
        }'

Response body from the cURL request (the decrypted data contains track2equivalent data, the Parser automatically includes standard Track 2 data when it encounters track2equivalent data in the decrypted data):

{
    "success"   : true,
    "messageId" : "1201709151450471223747721",
    "meta": {
        "device" : "bbpos",
        "serial" : "WPC204633000051",
        "mode"   : "swiped"
    },
    "track2equivalent" : {
        "decrypted" : "b5415244444444444d2212101123456789f800000000000000000000000000000000000000000000",
        "encoding"  : "ascii",
        "length"    : 36,
        "ascii"     : "b5415244444444444d2212101123456789f8",
        "masked"    : "b541524******4444d2212************f*"
    },
    "track2": {
        "decrypted" : ";5415244444444444=2212101123456789?8",
        "encoding"  : "ascii",
        "length"    : 36,
        "ascii"     : ";5415244444444444=2212101123456789?8",
        "masked"    : ";541524******4444=2212************?*"
    },
    "extracted": {
        "PAN"  : "5415244444444444",
        "EXPY" : "1222",
        "ServiceCode"   : "101",
        "Discretionary" : "123456789"
    }
}

Keyed Payloads

The device's serial number is a required parameter when calling the decrypt endpoint. While the value is output by the capture EMV and swiped payloads, it is not output as part of a keyed transaction. A version of the serial number is printed on the back of the device. It can also be identified by sending a getDeviceInfo command to the device. The Android looks like this:

final BBDeviceController bbDeviceController = BBDeviceController.getInstance(getApplicationContext(), listener );

bbDeviceController.getDeviceInfo();

The listener variable that is passed into the getInstance method is an instance of the BBDeviceControllerListener class. This class has many callback methods that are executed when a command completes. In this case, the onReturnDeviceInfo method is called with a dictionary object containing information relating to the device.

new BBDeviceControllerListener(){

    @Override
    public void onReturnDeviceInfo(Hashtable<String, String> deviceInfoData) {
        final String serialNumber = deviceInfoData.get( "serialNumber" );
    }

};

The WisePad 2 is equipped with a secure keypad that is capable of encrypting PAN data as it is captured. To instruct the WisePad 2 to capture a Keyed payload send it the checkCard command with the CheckCardMode set to Manual PAN Entry.

Hashtable<String, Object> data = new Hashtable<String, Object>();
data.put("checkCardMode",    CheckCardMode.MANUAL_PAN_ENTRY);
data.put("checkCardTimeout", new SimpleDateFormat("yyMMddHHmmss").format(Calendar.getInstance().getTime()) );
bbDeviceController.checkCard(data);

When the WisePad 2 receives this command, it prompts the user to enter a card number followed by the expiry data. After the user enters their card number, the data is passed to the onReturnCheckCardResult callback with the checkCardResult set to MANUAL_PAN_ENTRY. From the decodedData hash table, you can extract your parameters for the Decryptx Parser API call. Refer to the BBPOS documentation for a full description of the data elements included in the decodeData hash table.

@Override
public void onReturnCheckCardResult(CheckCardResult checkCardResult, Hashtable<String, String> decodeData) {
 
    if( checkCardResult == CheckCardResult.MANUAL_PAN_ENTRY ) {
    
         String ksn       = decodeData.get("ksn");
         String encPAN    = decodeData.get("encPAN");
         
         //The decodeData hash table also have the following
         //non-P2PE data elements. posEntryMode will be 06 
         //for manual entry
         String posEntryMode = decodeData.get("posEntryMode");
         String expiryDate   = decodeData.get("expiryDate");
         String maskedPAN    = decodeData.get("maskedPAN");
     
    }
}

Sample encrypted manual PAN entered payload:

B1F1A716216EA218A04F21D11E52755D

Sample KSN:

02400810160146000024

Sample cURL request with keyed payload and KSN:

curl 'https://cert-parser.decryptx.com/api/decrypt/parser' \
     -X POST \
     --header 'Content-Type: application/json' \
     --header 'Accept: application/json' \
     -d '{
        	"partnerId"     : "?????????",
         	"partnerKey"    : "ef1ad938150fb15a1384b883a104ce70",
         	"reference"     : "723f57e1-e9c8-48cb-81d9-547ad2b76435",
         	"ksn"           : "02400810160146000024",
            "deviceType"    : "wisepad2",
         	"deviceSerial"  : "WPC204633000051",
         	"devicePayload" : "B1F1A716216EA218A04F21D11E52755D"
         }'

Response body from the cURL request:

{
    "success"   : true,
    "messageId" : "1201709180947581213150291",
    "meta": {
        "device" : "bbpos",
        "serial" : "WPC204633000051",
        "mode"   : "keyed"
    },
    "keyed": {
        "decrypted" : "47617390010101190808080808080808",
        "encoding"  : "ascii",
        "length"    : 32,
        "ascii"     : "4761739001010119",
        "masked"    : "476173******0119"
    },
    "extracted": {
        "PAN": "4761739001010119"
    }
}

EMV Payloads

The Wisepad 2 is capable of capturing both contact and contactless EMV payloads. To instruct the WisePad 2 to capture an EMV payload, send the checkCard command with the CheckCardMode set to INSERT_OR_TAP. There are also standalone CheckCardModes TAP or INSERT that can be used.

Hashtable<String, Object> data = new Hashtable<String, Object>();
data.put("checkCardMode",    CheckCardMode.INSERT_OR_TAP);
data.put("checkCardTimeout", new SimpleDateFormat("yyMMddHHmmss").format(Calendar.getInstance().getTime()) );
bbDeviceController.checkCard(data);

When the device receives this command, it solicits the transaction amount. This triggers the BBDeviceControllerListener's onRequestSetAmount callback. Typically, this information is captured on a dialog once you have the amount data you need to call the bbDeviceController's setAmount method.

final String amount         = "12.00";
final String cashbackAmount = "0";
final String currencyCode   = "840";

final TransactionType transactionType = TransactionType.GOODS

final CurrencyCharacter[] currencyCharacters = new CurrencyCharacter[] { CurrencyCharacter.DOLLAR };

bbDeviceController.setAmount( amount, cashbackAmount, currencyCode, transactionType, currencyCharacters )

Once the setAmount command is sent, the device can capture an EMV payload. The BBDeviceControllerListener's onWaitingForCard callback is called at this point. The application can prompt the user to insert or tap their card.

Contact vs Contactless Flow

At this point, the transaction flow for contactless and contact EMV transactions diverge. In the contact EMV transaction flow, the user is prompted for pin entry and depending on the EMV card, they may be asked to select application. A contactless transaction does not require these steps.

Select Application (Contact Only)

An EMV card may support multiple payment applications. For contact transactions, the Wisepad 2 will read the list of applications supported on the EMV card and return them to the application by calling the BBDeviceControllerListener's onRequestSelectApplication callback. The user must select the desired application.

@Override
public void onRequestSelectApplication(ArrayList<String> appList) {
    //a dialog should be created that lists the available
    //applications. The user can then choose the one that they
    //want. The application is choosen by its position. 
    bbDeviceController.selectApplication( 0 );
}

Next, the device will read the application data from the EMV card and will apply the card verification method. Some EMV cards will require a PIN, whereas others will require a signature. The Wisepad 2 is capable of capturing the PIN on the device, however the onRequestPinEntry callback is still executed.

Once the PIN is entered, the Wisepad 2 and the EMV cards negotiate terminal risk and action analysis; it also performs card risk management. The transaction can fail at this point; if it does, the onReturnTransactionResult callback is executed.

Online Processing

At this point, the EMV tags must be forwarded to a Payment Gateway for processing. These EMV tags are located in a TLV string that is encrypted and embedded within a parent TLV string. The encrypted data must be extracted from the TLV and sent to a secured backend server. Once it is on the backend server, it is decrypted with an API call to the Parser endpoint. The decrypted value string is sent to the Payment Gateway. The result from the Payment Gateway must then be passed back to the mPOS application and fed into the SDK's EMV kernel.

This process begins in the BBDeviceControllerListener's onRequestOnlineProcess callback method:

@override
public void onRequestOnlineProcess(String tlv) { 
    //Parse the TLV string with the built in parser.
     Hashtable<String, String> decodeData = BBDeviceController.decodeTlv(tlv);

    //Extract the encrypted device serial number, EMV tags and the KSN.
    String onlineMessageKsn = decodeData.get( "onlineMessageKsn" );
    String encOnlineMessage = decodeData.get( "encOnlineMessage" );
    String serialNumber     = decodeData.get( "serialNumber"     );

    //Alternatively they can be obtained with by their tags.
    //String onlineMessageKsn = decodeData.get( "C0" );
    //String encOnlineMessage = decodeData.get( "C2" );
    //String serialNumber     = decodeData.get( "DF826E" );

    //Forward the EMV tags and KSN to a backend server for decryption and Gateway processing.

    final String gatewayResult = sendToBackendServer( serialNumber, encOnlineMessage, onlineMessageKsn );

    //Feed processing result to EMV Kernel e.g. an approval "8A023030".
    bbDeviceController.sendOnlineProcessResult( gatewayResult );
}

The BACKEND SERVER will receive the following data:

serialNumber = 575043323034363333303030313436
onlineMessageKsn = 02410810160146000069
encOnlineMessage = B30D467564DA09D5B7AF7A6D3767F8258DAB47AD85B4646EC2046D2809B266A65C8DCA493A48070AD0DAA86E1EE3BF1A83C7BCD4AEC2D393173F5ECB217D664301DFA5E3A3994DAB48F68AED02F1C76D98E35E3526EC1F839E5159AEFEFC8C3500C5B351C52CF8D37DFA819CA29350038FCE8A6070D0FE7738D15C1F07304427E1CD90773056E24C5C9C89774726B9BF532E0B1CB57945F9D65CF6558F243CBA31B8F708804873798D07F95E8276103341EA91930CAE179CD3E19FCE79B42548DB229562ED8322ACD089BB22AFB3D1E450A0DF122144792FF8726E9F6C3C13FB8D3551E7E2FE37B6B713469D7E7E955962D404EE505682B3F442EDD73CA6CFF9F367EEEDF44474E540832EAF4D56C880BE55E289E86A66B926204C4B85424E10834FF0AD7DEBF0F19EC6CDD10E11CB2E1C5970C4BAC9C6E099D5ACE3354DA587

  1. First, convert the serial number from HEX to ASCII:
serialNumber = hexToAscii( '575043323034363333303030313436' ); //WPC204633000146
  1. Then, decrypt the data:
curl 'https://cert-parser.decryptx.com/api/decrypt/parser' \
     -X POST \
     --header 'Content-Type: application/json' \
     --header 'Accept: application/json' \
     -d '{
         	"partnerId"     : "WATERFORD",
         	"partnerKey"    : "ef1ad938150fb15a1384b883a104ce70",
         	"reference"     : "723f57e1-e9c8-48cb-81d9-547ad2b76435",
         	"ksn"           : "02410810160146000069",
            "deviceType"    : "wisepad2",
         	"deviceSerial"  : "WPC204633000146",
         	"devicePayload" : "B30D467564DA09D5B7AF7A6D3767F8258DAB47AD85B4646EC2046D2809B266A65C8DCA493A48070AD0DAA86E1EE3BF1A83C7BCD4AEC2D393173F5ECB217D664301DFA5E3A3994DAB48F68AED02F1C76D98E35E3526EC1F839E5159AEFEFC8C3500C5B351C52CF8D37DFA819CA29350038FCE8A6070D0FE7738D15C1F07304427E1CD90773056E24C5C9C89774726B9BF532E0B1CB57945F9D65CF6558F243CBA31B8F708804873798D07F95E8276103341EA91930CAE179CD3E19FCE79B42548DB229562ED8322ACD089BB22AFB3D1E450A0DF122144792FF8726E9F6C3C13FB8D3551E7E2FE37B6B713469D7E7E955962D404EE505682B3F442EDD73CA6CFF9F367EEEDF44474E540832EAF4D56C880BE55E289E86A66B926204C4B85424E10834FF0AD7DEBF0F19EC6CDD10E11CB2E1C5970C4BAC9C6E099D5ACE3354DA587"
         }'

Response body from the cURL request:

{
    "success"   : true,
    "messageId" : "1201804091506121001177801",
    "reference" : "723f57e1-e9c8-48cb-81d9-547ad2b76435",
    "meta": {
        "device" : "bbpos",
        "serial" : "WPC204633000146",
        "mode"   : "emv"
    },
    "tlv": {
        "decrypted": "4f07a0000000031010500b564953412043524544495457134761739001010119d22122011143804400000f5a084761739001010119820220008407a0000000031010950500000000009a031801059b0200009c01005f24032212315f2a0208405f3401019f02060000000100009f03060000000000009f0607a00000000310109f0902008c9f100706010a03a000009f160f3030393837363534333231323334359f1a0208409f1c0831313232333334349f1e0831323334353637389f21031741179f2608d50d94cf294b20309f2701809f330360f0c89f34033f00009f3501219f360200329f370406606e039f3901079f4005f000a0b0019f41030001059f4e0d54657374204d65726368616e749f5301ff9f6604a68040009f6e0420700000df170138df826e0f575043323034363333303030313436"
    },
    "track2equivalent": {
        "decrypted" : "4761739001010119D22122011143804400000F",
        "encoding"  : "ascii",
        "length"    : 38,
        "ascii"     : "4761739001010119D22122011143804400000F",
        "masked"    : "476173******0119D2212****************F"
    },
    "track2": {
        "decrypted" : ";4761739001010119=22122011143804400000?",
        "encoding"  : "ascii",
        "length"    : 39,
        "ascii"     : ";4761739001010119=22122011143804400000?",
        "masked"    : ";476173******0119=2212****************?"
    },
    "extracted": {
        "PAN"  : "4761739001010119",
        "EXPY" : "1222",
        "ServiceCode"  : "201",
        "Discretionary": "1143804400000"
    }
}
  1. Next, the decrypted TLV string is sent to the Payment Gateway for processing.
  2. The Payment Gateway result is sent back to the mPOS, where it is fed into the EMV kernel (via the sendOnlineProcessResult method).

Completion

At this point, the device and the EMV card complete the transaction and process any issuer scripts that are included. Once the transaction is complete, an encrypted TLV string is output to the BBDeviceControllerListener's onReturnBatchData method. This encrypted EMV TLV data must be sent to and stored in a backend server as it is required for later settlement. The data can be decrypted with the Decryptx service in a similar fashion to the onlineMessage data.

Extracting the batch message from the TLV:

@Override
public void onReturnBatchData(String tlv) {
    //Parse the TLV string with the built in parser.
     Hashtable<String, String> decodeData = BBDeviceController.decodeTlv(tlv);

    //Extract the encrypted EMV tags and KSN.
    String batchKsn        = decodeData.get( "batchKsn" );
    String encBatchMessage = decodeData.get( "encBatchMessage" );
    String serialNumber    = decodeData.get( "serialNumber"     );

    //Alternatively they can be obtained with by their tags.
    //String batchKsn        = decodeData.get( "C3" );
    //String encBatchMessage = decodeData.get( "C5" );
    //String serialNumber    = decodeData.get( "DF826E" );

    //Forward the batch message and KSN to a backend server.
}