package com.payex.payexpos;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import com.ingenico.pclservice.PclService;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Vector;
// This class does the actual communication with
// the iSMP Companion
public class Backend {
// See Mynt ECR Interface for _CMD and _TAG
// values
final int START_TRANSACTION_CMD = 222;
final byte PURCHASE_TAG = 0;
final byte REVERSAL_TAG = 2;
final byte CASHBACK_TAG = 3;
final byte REFUND_TAG = 4;
private Context context = null;
private PclServiceConnection pclServiceConnection = null;
// Initialize connection
public Backend(Context ctx) {
context = ctx;
pclServiceConnection = new PclServiceConnection();
Intent intent = new Intent(ctx, PclService.class);
ctx.bindService(intent, pclServiceConnection, Context.BIND_AUTO_CREATE);
}
// De-initialize connection, important to call this
// when done with the class
public void deInit() {
context.unbindService(pclServiceConnection);
context = null;
}
// Constructs a base BER-TLV string, containing the first
// required field, purchase type.
// This function should always be called for transaction
// commands, and then you just add more BER-TLV fields on top
// of it.
private Vector<Byte> constructBaseTransactionWithTag(byte tag) {
Vector<Byte> storage = new Vector<Byte>();
storage.add((byte)-97); // Purchase type tag
storage.add((byte)-103);
storage.add((byte)1);
storage.add((byte)1); // 1 in length
storage.add(tag);
return storage;
}
// Append a big endian int to a byte-vector
private void addNumberAsBigEndian(int num, Vector<Byte> into) {
ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.order(ByteOrder.BIG_ENDIAN);
buffer.putInt(num);
byte[] intInBytes = buffer.array();
for (byte bite : intInBytes) {
into.add(bite);
}
}
// Helper function to add a byte-array to a byte-vector
private void addBytesToTransaction(byte[] bytes, Vector<Byte> trx) {
for (byte bite : bytes) {
trx.add(bite);
}
}
// Adds an BER-TLV field representing the amount of the transaction
private void addAmountToTransaction(int amount, Vector<Byte> trx) {
final byte[] amountTag = {
-127, 4 // Amount tag + 4 (int32) in length
};
addBytesToTransaction(amountTag, trx);
addNumberAsBigEndian(amount, trx);
}
// Create ECR Interface header, append BER-TLV encoded message
// return byte-array ready to send to terminal
private byte[] finalizeTransaction(int command, Vector<Byte> data) {
Vector<Byte> trx = new Vector<Byte>();
// Add header, as per Mynt ECR Interface
addNumberAsBigEndian(0, trx);
addNumberAsBigEndian(0, trx);
addNumberAsBigEndian(command, trx);
addNumberAsBigEndian(data.size(), trx);
// Append ata
trx.addAll(data);
byte[] finalized = new byte[trx.size()];
for (int i = 0; i < trx.size(); i++) {
finalized[i] = trx.get(i);
}
return finalized;
}
// Send BER-TLV encoded command to the device
// Work is performed in a background thread
private void send(byte[] message, TransactionResult res) {
TerminalCommunicationData data = new TerminalCommunicationData(
message,
pclServiceConnection.getPclService(),
res
);
new DoTerminalCommunicationTask().execute(data);
}
// Build and send a command to the iSMP Companion
// that starts a purchase transaction
public void purchase(int amount, TransactionResult res) {
Vector<Byte> trx = constructBaseTransactionWithTag(PURCHASE_TAG);
addAmountToTransaction(amount, trx);
byte[] command = finalizeTransaction(START_TRANSACTION_CMD, trx);
send(command, res);
}
// Build and send a command to the iSMP Companion
// that starts a cashback transaction
public void cashback(int totalAmount, int cashbackAmount, TransactionResult res) {
final byte[] trxCashback = {
-97, 4, // Cashback tag
4 // Length of (int32) cashback amount
};
Vector<Byte> trx = constructBaseTransactionWithTag(CASHBACK_TAG);
addAmountToTransaction(totalAmount, trx);
addBytesToTransaction(trxCashback, trx);
addAmountToTransaction(cashbackAmount, trx);
byte[] command = finalizeTransaction(START_TRANSACTION_CMD, trx);
send(command, res);
}
// Build and send a command to the iSMP Companion
// that starts a reversal transaction
public void reversal(TransactionResult res) {
Vector<Byte> trx = constructBaseTransactionWithTag(REVERSAL_TAG);
byte[] command = finalizeTransaction(START_TRANSACTION_CMD, trx);
send(command, res);
}
// Build and send a command to the iSMP Companion
// that starts a refund transaction
public void refund(int amount, TransactionResult res) {
Vector<Byte> trx = constructBaseTransactionWithTag(REFUND_TAG);
addAmountToTransaction(amount, trx);
byte[] command = finalizeTransaction(START_TRANSACTION_CMD, trx);
send(command, res);
}
// Container for data needed in a TerminalCommunication
private class TerminalCommunicationData {
public byte[] messageToSend;
public PclService pclService;
public TransactionResult transactionResult;
public TerminalCommunicationData(byte[] mts, PclService service, TransactionResult res) {
messageToSend = mts;
pclService = service;
transactionResult = res;
}
}
// Sends command, and receives response in background thread
// Result is passed on to given TransactionResult class
// You might want to read on how AsyncTask works if you don't already
// know (Android API)
private class DoTerminalCommunicationTask
extends AsyncTask<TerminalCommunicationData, Void, byte[]>
{
TerminalCommunicationData data;
@Override
protected byte[] doInBackground(TerminalCommunicationData... dataPackets) {
data = dataPackets[0];
byte[] messageToSend = data.messageToSend;
int[] bytesSent = new int[1];
data.pclService.sendMessage(messageToSend, bytesSent);
byte[] messageReceived = new byte[1024];
int[] bytesReceived = new int[1];
// Keep receiving until we actually have received
// something. There are probably better ways, but it works...
while (bytesReceived[0] == 0) {
data.pclService.receiveMessage(messageReceived, bytesReceived);
}
data.pclService.flushMessages();
return messageReceived;
}
@Override
protected void onPostExecute(byte[] response) {
data.transactionResult.Result(response);
}
}
}