Miura

How to use Miura terminals with the Decryptx Payload Parser.

The Decryptx Payload Parser supports two Miura terminal models:

  1. M010
  2. Shuttle

Both devices support magnetic stripe, keyed and EMV (chip&pin) input. The M010 also has a contactess reader. Both the M010 and Shuttle have Bluetooth, USB and TCP/IP interfaces; they are designed to operate as countertop and mobile devices. Mobile devices will typically use the Bluetooth interface, whereas point of sale (POS) applications will use the serial USB or TCP/IP over wireless interfaces. While the pin entry devices (PED) support multiple interfaces, the commands sent and responses received from them are the same no matter which interface is used.

The terminal vendor supplies software development kits (SDK) for a wide variety of operating systems and development languages on their developer's 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 uses Miura's Android SDK to extract a P2PE secured payload from an RBA terminal. Miura provides similar SDKs in Python, Objective-C, and a number of other languages. While the programming languages are different, the software uses a similar pattern when extracting the encrypted data from the terminal.

Raw Commands and SDKs

As mentioned above, the Miura PEDs are designed to work with Bluetooth and USB. These interfaces consume command strings and produce response strings in an asynchronous fashion. As with most devices that utilize a Bluetooth interface, keeping these command strings compact is a high priority; they use a single byte to represent a command and as little bytes as possible. For example the following command 010004D0020000D4 has a prologue of 010004 and an LRC at end of D4; the actual command of D0020000 requests the device information. Please refer to the Miura MPI API documentation for the full description of these commands. Thankfully, Miura has a number of SDKs that abstract away these command strings. The following example retrieves the same information (note the listener class that waits for the async response):

MiuraManager.getInstance().getSoftwareInfo(new ApiGetSoftwareInfoListener() {
   
   @Override
   public void onSuccess(SoftwareInfo softwareInfo) {
       //information can be accessed here. 
   }

   @Override
   public void onError() {
       System.out.println( "Error occurred" );
   }
});

Identifying the serial number

A version of the serial number is printed on the back of the device. The printed value typically has a dash, e.g., 010-589951, however the API expects that the serial value drop the dash and leading zero to, e.g., 10589951. The serial number can also be identified by sending a GET DEVICE INFO command to the PED. The raw request is 010004D0020000D4. The following sample code uses the Miura Android SDK to extract the serial number from the M010. Miura provides similar SDKs in Python, Objective-C, and several other languages. While the programming languages are different, the software uses a similar pattern when extracting the encrypted data from the terminal.

MiuraManager.getInstance().getSoftwareInfo(new ApiGetSoftwareInfoListener() {
       
       @Override
       public void onSuccess(SoftwareInfo softwareInfo) {
           System.out.println( "Serial Number" + softwareInfo.getSerialNumber() );
       }
    
    });

Once you have the serial numbers, extract the payload and KSN. By default, all the readers (magnetic stripe, chip, and contactless) are disabled on the M010.

Swiped Payloads

To capture a magnetic stripe payload, we need to send the CARD STATUS command to the device. The card status command queries the EMV slot to see if a card is inserted. It also enables or disables unsolicited card swipes. In the following Android code connectionDelegate is attached, which listens for card status changes, and the CARD STATUS is set to true.

MiuraManager.getInstance().setConnectionDelegate(connectionDelegate);
    MiuraManager.getInstance().cardStatus(true, null);

The following is a sample class that listens for a card swipes. When a card is swiped or inserted into the EMV slot, a CardData object is passed into the onCardStatusChange method. In the example, we check that the card data object has Track 2 data. If Track 2 data is present, we know it is a magnetic stripe payload and it will contain SRED and KSN data.

private MPIConnectionDelegate connectionDelegate = new MPIConnectionDelegate() {
        
        public void onCardStatusChange( CardData cardData ) {
            if ( cardData.getTrack2Data() != null ) {
                final String payload = cardData.getSredData();
                final String ksn     = cardData.getSredKSN();
                
            }
        }
    }

The following is a sample P2PE encrypted magnetic strip payload and associated KSN:

Payload

0e64f89987343361346fcb63419cadb9733be3d555c4024294d1ec7c9fc6997010313c15212628348b22f0cec67ab5d5c7ee63c01dd28644aad4ba94223dc224089a142d5db1a3adf7803b3396269154a5584b8c61bbffa7f382ff713e28d3cd

KSN

fffff001030000200001

Sample cURL request with magnetic swipe 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"           : "fffff001030000200001",
        	"deviceType"    : "miura",
        	"deviceSerial"  : "10589951",
        	"devicePayload" : "0e64f89987343361346fcb63419cadb9733be3d555c4024294d1ec7c9fc6997010313c15212628348b22f0cec67ab5d5c7ee63c01dd28644aad4ba94223dc224089a142d5db1a3adf7803b3396269154a5584b8c61bbffa7f382ff713e28d3cd"
        }'

Response body from the cURL request:

{
  "success"   : true,
  "messageId" : "1201706151533581022507812",
  "reference" : "723f57e1-e9c8-48cb-81d9-547ad2b76435",
  "meta" : {
    "device" : "MIURA",
    "serial" : "10589951",
    "mode"   : "swiped"
  },
  "track1" : {
    "decrypted" : "2542343132343933393939393939393939305e544553542f424c554546494e5e323231323130313132333435363738393f5f",
    "encoding"  : "hex",
    "length"    : 50,
    "ascii"     : "%B4124939999999990^TEST/BLUEFIN^2212101123456789?_",
    "masked"    : "%*412493******9990^TEST/BLUEFIN^2212*********?*"
  },
  "track2" : {
    "decrypted" : "3b343132343933393939393939393939303d323231323130313132333435363738393f3b",
    "encoding"  : "hex",
    "length"    : 36,
    "ascii"     : ";4124939999999990=2212101123456789?;",
    "masked"    : ";412493******9990=2212************?*"
  },
  "extracted" : {
    "PAN"       : "4124939999999990",
    "EXPY"      : "1222",
    "Surname"   : "TEST",
    "FirstName" : "BLUEFIN",
    "ServiceCode": "101",
    "Discretionary": "123456789"
  }
}

The response from a swiped payload will contain Track 1 and Track 2 data. The extracted object will contain PAN, EXPY, Surname and FirstName data.

EMV (Chip & Pin) Payloads

To capture an EMV payload, start by sending the same CARD STATUS command to the PED. When the CardData object is passed to the onCardStatusChange method of the MPIConnectionDelegate, it will have a number of properties that will inform you if the card is EMV enabled cardData.getCardStatus().isEMVCompatible() and in the chip reader slot cardData.getCardStatus().isCardPresent(). If both of these are true then, initiate an EMV transaction with the START TRANSACTION command.

The following sample code initiates an EMV transaction; specified in the command call is the transaction type, the transaction amount and the currency code. Attached to the command is an ApiStartTransactionListener with code that is executed once the application receives data from the contactless transaction. Included in this data are the encrypted payload and the KSN. Also included in the data is issuer/acquirer data which can be checked before a CONTINUE TRANSACTION command is sent.

MiuraManager.getInstance().setConnectionDelegate(connectionDelegate);
MiuraManager.getInstance().cardStatus(true, null); 


private MPIConnectionDelegate connectionDelegate = new MPIConnectionDelegate() {

    @Override
    public void connected() {}
    @Override
    public void disconnected() {}
    @Override
    public void connectionState(boolean flg) {}
    @Override
    public void onKeyPressed(int keyCode) {}
    @Override
    public void onCardStatusChange( CardData cardData ) {
    
        if (cardData.getCardStatus().isCardPresent() && cardData.getCardStatus().isEMVCompatible()) {
        
            MiuraManager.getInstance().startTransaction(TransactionType.Purchase, transactionInfo.getAmount(), MiuraApplication.currencyCode.getValue(), new ApiStartTransactionListener() {
            
                @Override
                public void onSuccess(byte[] result) {
        
                    List<TLVObject> tlvObjects = TLVParser.decode(result);
        
                    final TLVObject payloadObj = CommandUtil.firstMatch( tlvObjects, Description.SRED_Data );
                    final TLVObject ksnObj     = CommandUtil.firstMatch( tlvObjects, Description.SRED_KSN  );
                    
                    final String payload = payloadObj.getData();
                    final String ksn     = ksnObj.getData();
                    
                    //the tlvObject contains other issuer data. That data should be presented
                    //to the user before the continueTransaction command is executed. The response 
                    //to the continue command will also contain the payload and ksn data.
                }
        
                @Override
                public void onError(TransactionResponse response) {
                }
            });
        }
    }
    @Override
    public void onDeviceStatusChange(DeviceStatus deviceStatus, String statusText) {}
    @Override
    public void onBatteryStatusChange(BatteryData batteryData) {}
    @Override
    public void onBarcodeScan(String scanned) {}
    @Override
    public void onPrintSledStatus(M012Printer m012PrinterStatus) {}
}

Sample EMV Payload:

cdeb04ca81940babb24acf4ffa9d6b77fb52c6bc0890edbcfc518c7b724d1be1

Sample KSN:

fffff00103000020001b

Sample cURL request with EMV 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"           : "fffff00103000020001b",
        	"deviceType"    : "miura",
        	"deviceSerial"  : "10589951",
        	"devicePayload" : "cdeb04ca81940babb24acf4ffa9d6b77fb52c6bc0890edbcfc518c7b724d1be1"
        }'

Response body from the cURL request:

{
  "success"   : true,
  "messageId" : "1201706160927451032904876",
  "reference" : "723f57e1-e9c8-48cb-81d9-547ad2b76435",
  "meta": {
    "device" : "MIURA",
    "serial" : "10589951",
    "mode"   : "emv"
  },
  "track2equivalent": {
    "decrypted" : "4761739001010119d22122011143804400000",
    "encoding"  : "ascii",
    "length"    : 37,
    "ascii"     : "4761739001010119d22122011143804400000",
    "masked"    : "476173******0119d2212****************"
  },
  "track2": {
    "decrypted" : ";4761739001010119=22122011143804400000?",
    "encoding"  : "ascii",
    "length"    : 39,
    "ascii"     : ";4761739001010119=22122011143804400000?",
    "masked"    : ";476173******0119=2212****************?"
  },
  "extracted": {
    "PAN"  : "4761739001010119",
    "EXPY" : "1222",
    "ServiceCode"   : "201",
    "Discretionary" : "1143804400000"
  }
}

The response from an EMV payload will contain a track2equivalent object. This object will contain EMV TAG 57 data. We also create a Track 2 object based on the contents of the track2equivalent object. The Track 2 data follows the standard Track 2 swipe format. The extracted object will contain PAN and EXPY data.

Contactless Payloads

As the form factor of the M010 PED is quite compact, it could accidentally read a contactless payload when a user attempts to swipe their card; therefore, the PED's contactless reader is not enabled by default. A START CONTACTLESS TRANSACTION command must be sent to the PED.

The following sample code initiates a contactless transaction; specified in the command call is the transaction type, the transaction amount and the currency code. Attached to the command is an ApiStartTransactionListener with code that is executed once the application receives data from the contactless transaction. Included in this data are the encrypted payload and the KSN.

MiuraManager.getInstance().startContactlessTransaction(TransactionType.Purchase, transactionInfo.getAmount(),
    MiuraApplication.currencyCode.getValue(), new ApiStartTransactionListener() {

    @Override
    public void onSuccess(byte[] result) {
    
        List<TLVObject> tlvObjects = TLVParser.decode(result);
        final TLVObject payloadObj = CommandUtil.firstMatch( tlvObjects, Description.SRED_Data );
        final TLVObject ksnObj     = CommandUtil.firstMatch( tlvObjects, Description.SRED_KSN  );
        
        
        final String payload = payloadObj.getData();
        final String ksn     = ksnObj.getData();
            
        //the tlvObject contains other issuer data. That data should be presented
        //to the user before the continueContactlessTransaction command is executed. 
        //The response to the continue command will also contain the payload and ksn data.
    }

    @Override
    public void onError(TransactionResponse response) {
    }
});

Sample contactless payload:

c31b8d370f9edc2d6bc2af63d49f9b37372a481653d1df25

Sample KSN:

fffff00103000020004d

Sample cURL request with contactless 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"           : "fffff00103000020004d",
        	"deviceType"    : "miura",
        	"deviceSerial"  : "10589951",
        	"devicePayload" : "c31b8d370f9edc2d6bc2af63d49f9b37372a481653d1df25"
        }'

Response body from the cURL request:

{
  "success"   : true,
  "messageId" : "1201706160934521032652372",
  "reference" : "723f57e1-e9c8-48cb-81d9-547ad2b76435",
  "meta" : {
    "device" : "MIURA",
    "serial" : "10589951",
    "mode"   : "emv"
  },
  "track2equivalent": {
    "decrypted" : "4761739001010119d22122011143804400000",
    "encoding"  : "ascii",
    "length"    : 37,
    "ascii"     : "4761739001010119d22122011143804400000",
    "masked"    : "476173******0119d2212****************"
  },
  "track2": {
    "decrypted" : ";4761739001010119=22122011143804400000?",
    "encoding"  : "ascii",
    "length"    : 39,
    "ascii"     : ";4761739001010119=22122011143804400000?",
    "masked"    : ";476173******0119=2212****************?"
  },
  "extracted": {
    "PAN"  : "4761739001010119",
    "EXPY" : "1222",
    "ServiceCode"   : "201",
    "Discretionary" : "1143804400000"
  }
}

The response from an contactless payload is the same as an EMV payload. The track2equivalent object will contain EMV TAG 57 data. We also create a Track 2 object based on the contents of the track2equivalent object. The Track 2 data follows the standard Track 2 swipe format. The extracted object will contain PAN and EXPY data.

Keyed Payloads

To capture keyed card data the command GET SECURE PAN must be sent to the device. A prompt is displayed on the device to enter card data. Once you press the enter key the encrypted payload and KSN data are returned. The encrypted payload contains the PAN data only; expiry date and/or CVV must be retrieved separately via GET TEXT command.

The MiuraManager class doesn not have a method to initiate a keyed payload capture; therefore, you must use the low level MPICommandCreator class. The command string to request the PAN is as follows: 01000FD25A00010AE008DFA2060400000001 where 01000F is the prologue, D25A is the command request a secure PAN, 0001 configures the backlight (0001 is on; 0000 is off) and 0AE008DFA2060400000001 specifies the index of the prompt text. MPICommandCreator looks after the prologue, you specify the command type, the config options and the data field.

BaseBluetooth.getInstance().setDelegate(connectionDelegate, commandDelegate);
final byte[] dataField = BinaryUtil.parseHexBinary( "E008DFA2060400000001" );
final byte[] command   = MPICommandCreator.createCommand(InterfaceType.MPI, MPICommandCreator.CommandType.Get_Secure_PAN, (byte) 0, (byte) 1, dataField);
BluetoothService.getInstance().write(command);
return command;

In the example code above we set a command delegate on the BaseBluetooth object that listens for the message containing the decrypted PAN data. The following sample MPICommandDelegate extracts the encrypted payload and KSN from the message.

final MPICommandDelegate commandDelegate = new MPICommandDelegate() {
    @Override
    public void receivedSolicitedMessage( ResponseMessage responseMessage ) {
        if( responseMessage.isSuccess() && responseMessage.getBody().length > 1) {

            final List<TLVObject> tlvObjects = TLVParser.decode(responseMessage.getBody());

            final TLVObject payloadObj = CommandUtil.firstMatch( tlvObjects, Description.SRED_Data );
            final TLVObject ksnObj     = CommandUtil.firstMatch( tlvObjects, Description.SRED_KSN );
            
            
            final String payload = payloadObj.getData();
            final String ksn     = ksnObj.getData();
            
            //send P2PE data to backend server to process.
        } 
    }
});

Sample keyed payload:

bec06a99af283f8541875de76d05ed8676aef285646ee758

Sample KSN:

fffff00103000020004f

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"     : "WATERFORD",
        	"partnerKey"    : "ef1ad938150fb15a1384b883a104ce70",
        	"reference"     : "723f57e1-e9c8-48cb-81d9-547ad2b76435",
        	"ksn"           : "fffff00103000020004f",
        	"deviceType"    : "miura",
        	"deviceSerial"  : "10589951",
        	"devicePayload" : "bec06a99af283f8541875de76d05ed8676aef285646ee758"
        }'

Response body from the cURL request:

{
    "success"   : true,
    "messageId" : "1201706160843391032761684",
    "reference" : "723f57e1-e9c8-48cb-81d9-547ad2b76435",
    "meta" : {
        "device" : "MIURA",
        "serial" : "10589951",
        "mode"   : "keyed"
    },
    "keyed" : {
        "decrypted" : "103630313130313333333333333333333100000000",
        "encoding"  : "hex",
        "length"    : 16,
        "ascii"     : "6011013333333331",
        "masked"    : "601101******3331"
    },
    "extracted" : {
        "PAN" : "6011013333333331"
    }
}

The response from a keyed payload will contain a keyed object. This keyed ASCII value will simply be the PAN as that is all that is included in the encrypted payload. The extracted object will only contain the PAN.