Friday, October 10, 2014

The Absolute Simplest HCE "Pay" Application Enablement


Most of the code below relates to GUI messaging,  but this Android app is full HCE for a Cloud Based Payments card.  It is a total of 201 lines of code including comments, and line spaces. It is a single class application for simplicity.
The goal is to show that the libraries for managing CBP on the mobile app are lightweight for developers to use.
The libs are designed to be placed into ANY application that may want to enable CBP.  The one below is about the simplest integration.
The full application is called SimplestTapp and can be downloaded and tested here:
http://wiki.simplytapp.com/home/self-driven-hce-pilot/make-a-payment-card/simplytapp-s-vcbp-changelog/software-dev-kits/mobile-sdk

it also may be good to view this help resource for creating and managing the card perso and lifecycle in the cloud:
http://wiki.simplytapp.com/home/using-admin-api-console

The core of the card download and enablement is really contained in this section.  This section instantiates a VirtualCard that will be loaded from the remote server.  In order to instantiate the CBP card, you will see that there are some required credential information because a request will be made to the CBP server to retrieve the card that will be used.
The data in the snippet is sample data only.

The AsyncTask is used because the load() method makes network calls and can't be run on the UI thread.

        // Create a card with data from AdminApi console
        // See wiki for help:
        // http://wiki.simplytapp.com/home/using-admin-api-console
        //
        // Values are found within your AdminApi Console. Click Get APP
        // Credentials.
        //
        // card agent code hash is found inside the Issuer Entity portion of the
        // web portal
        //
        final VirtualCard card = VirtualCardBuilder
                .cardId("5901")
                .mobileAppConsumerKey(
                        "oJmqoey7I97FBOZQDXoH0A9RbM7BciXbD8CHZDMU")
                .mobileAppConsumerSecret(
                        "ugTD5dGjBUEC1pyzxjMNtw6k9TUocGtY4plta9he")
                .walletAccessToken("O15F4jG3PcwC44NpkfEqv1ZD7tjNo5NhGpHHFEax")
                .walletTokenSecret("Ur504iPQn2lxgsS5fVBycf2zK9uuSglppvn2mBa3")
                .cardAgentCodeHash("aa4185c2364048447a38aa02499a9897")
                .context(this).build();

        // now load the card...
        // this requires network connectivity and is blocking
        // so we must use AsyncTask to avoid running this in UI thread
        //
        new AsyncTask() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    myActivity.postUi("=================================="
                            + "\n" + "=====Loading Card From Server====="
                            + "\n" + "=================================="
                            + "\n");
                    card.load();
                } catch (IOException ex) {
                    Log.e(TAG, "Failed to load", ex);
                }
                return (Void) null;
            }
        }.execute();

 Once the VirtualCard successfully loads, it has a mind of it's own and will perform all activities related to making a successful payment when requested to do so.  For this, the feedback mechanism from the card to the mobile application is a messaging interface and should be defined in a block similar to the one below.  You will notice that the block below is responsible for handling feedback from the card and also notifying the card to activate after it was successfully created:

    // define the message default card message handler
    // and set it in the lib
    static {
        VirtualCardMessaging virtualCardMessenger = new VirtualCardMessaging() {
            @Override
            public void virtualCardMessage(VirtualCardMessaging.Message message) {
                switch (message.getCode()) {
                case VirtualCardMessaging.CARD_CREATION_COMPLETED:
                    try {
                        // when card is created, then activate the card agent,
                        // then connect it to the ApduService,
                        // then ask the user if they want this app the default
                        // app
                        // for servicing the reader
                        message.getVirtualCard().activate();
                        ApduService.setVirtualCard(message.getVirtualCard());
                        myActivity.postUi("========================"
                                + "\n"
                                + "=====Card Created======="
                                + "\n"
                                + "========================"
                                + "\n"
                                + "==Card Id: "
                                + message.getVirtualCard().getVirtualCardId()
                                + "\n"
                                + "==Num: "
                                + message.getVirtualCard()
                                        .getVirtualCardNumber()
                                + "\n"
                                + "==Card Exp: "
                                + message.getVirtualCard()
                                        .getVirtualCardExpDate() + "\n"
                                + "==Card Type: "
                                + message.getVirtualCard().getVirtualCardType()
                                + "\n" + "==Card Logo: "
                                + message.getVirtualCard().getVirtualCardLogo()
                                + "\n");
                        myActivity.postUi("========================" + "\n"
                                + "=====Card Activating====" + "\n"
                                + "========================" + "\n");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    break;
                case VirtualCardMessaging.CARD_ACTIVATE_COMPLETED:
                    myActivity.postUi("========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "=====Card Activated=====" + "\n"
                            + "=====Ready To Tap=======" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n");
                    break;
                case VirtualCardMessaging.TRANSACTION_ENDED:
                    myActivity.postUi("========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "===Ready To Tap Again====" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n");
                    break;
                default:
                    break;
                }

                // log the message
                Log.d(TAG,
                        message.getMessage() + " cardId="
                                + message.getVirtualCardId() + " code="
                                + message.getCode());
                myActivity.postUi(message.getMessage() + " cardId="
                        + message.getVirtualCardId() + " code="
                        + message.getCode() + "\n");
            }

        };

        // set the default messenger
        VirtualCard.setDefaultVirtualCardMessaging(virtualCardMessenger);
    }


You will notice above that the messaging loop is logging all messages from the card and printing them to the screen of the app and the debugger.  This can give you an idea of the events the card notifies the app.  As far as API requests to the card from the app, the VirtualCard library contains many methods.  Here is the JavaDoc:
http://simplytapp.github.io/android/virtualcard/doc/

Full activity class code for this example:

package com.simplytapp.example.simplesttapp;

import java.io.IOException;

import com.simplytapp.virtualcard.ApduService;
import com.simplytapp.virtualcard.VirtualCard;
import com.simplytapp.virtualcard.VirtualCardBuilder;
import com.simplytapp.virtualcard.VirtualCardMessaging;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.cardemulation.CardEmulation;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends Activity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private static MainActivity myActivity = null;
    private TextView tv = null;

    // this method posts strings to the textview box in the UI thread
    private void postUi(final String t) {
        new Runnable() {
            public void run() {
                runOnUiThread(new Runnable() {
                    public void run() {
                        tv.append(t);
                        if (tv.getLayout() != null) {
                            // auto-scroll vertically
                            final int scrollAmount = tv.getLayout().getLineTop(
                                    tv.getLineCount())
                                    - tv.getHeight();
                            // if there is no need to scroll, scrollAmount will
                            // be <=0
                            if (scrollAmount > 0)
                                tv.scrollTo(0, scrollAmount);
                            else
                                tv.scrollTo(0, 0);
                        }
                    }
                });
            }
        }.run();
    }

    // define the message default card message handler
    // and set it in the lib
    static {
        VirtualCardMessaging virtualCardMessenger = new VirtualCardMessaging() {
            @Override
            public void virtualCardMessage(VirtualCardMessaging.Message message) {
                switch (message.getCode()) {
                case VirtualCardMessaging.CARD_CREATION_COMPLETED:
                    try {
                        // when card is created, then activate the card agent,
                        // then connect it to the ApduService,
                        // then ask the user if they want this app the default
                        // app
                        // for servicing the reader
                        message.getVirtualCard().activate();
                        ApduService.setVirtualCard(message.getVirtualCard());
                        myActivity.postUi("========================"
                                + "\n"
                                + "=====Card Created======="
                                + "\n"
                                + "========================"
                                + "\n"
                                + "==Card Id: "
                                + message.getVirtualCard().getVirtualCardId()
                                + "\n"
                                + "==Num: "
                                + message.getVirtualCard()
                                        .getVirtualCardNumber()
                                + "\n"
                                + "==Card Exp: "
                                + message.getVirtualCard()
                                        .getVirtualCardExpDate() + "\n"
                                + "==Card Type: "
                                + message.getVirtualCard().getVirtualCardType()
                                + "\n" + "==Card Logo: "
                                + message.getVirtualCard().getVirtualCardLogo()
                                + "\n");
                        myActivity.postUi("========================" + "\n"
                                + "=====Card Activating====" + "\n"
                                + "========================" + "\n");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    break;
                case VirtualCardMessaging.CARD_ACTIVATE_COMPLETED:
                    myActivity.postUi("========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "=====Card Activated=====" + "\n"
                            + "=====Ready To Tap=======" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n");
                    break;
                case VirtualCardMessaging.TRANSACTION_ENDED:
                    myActivity.postUi("========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "===Ready To Tap Again====" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n"
                            + "========================" + "\n");
                    break;
                default:
                    break;
                }

                // log the message
                Log.d(TAG,
                        message.getMessage() + " cardId="
                                + message.getVirtualCardId() + " code="
                                + message.getCode());
                myActivity.postUi(message.getMessage() + " cardId="
                        + message.getVirtualCardId() + " code="
                        + message.getCode() + "\n");
            }

        };

        // set the default messenger
        VirtualCard.setDefaultVirtualCardMessaging(virtualCardMessenger);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myActivity = this;
        tv = (TextView) findViewById(R.id.textView1);
        tv.setMovementMethod(new ScrollingMovementMethod());

        // try to set this app as the default HCE application app
        CardEmulation cardEmulationManager = CardEmulation
                .getInstance(NfcAdapter.getDefaultAdapter(this));
        ComponentName paymentServiceComponent = new ComponentName(
                getApplicationContext(), ApduService.class.getCanonicalName());

        if (!cardEmulationManager.isDefaultServiceForCategory(
                paymentServiceComponent, CardEmulation.CATEGORY_PAYMENT)) {
            Intent intent = new Intent(CardEmulation.ACTION_CHANGE_DEFAULT);
            intent.putExtra(CardEmulation.EXTRA_CATEGORY,
                    CardEmulation.CATEGORY_PAYMENT);
            intent.putExtra(CardEmulation.EXTRA_SERVICE_COMPONENT,
                    paymentServiceComponent);
            startActivityForResult(intent, 0);
        }

        // Create a card with data from AdminApi console
        // See wiki for help:
        // http://wiki.simplytapp.com/home/using-admin-api-console
        //
        // Values are found within your AdminApi Console. Click Get APP
        // Credentials.
        //
        // card agent code hash is found inside the Issuer Entity portion of the
        // web portal
        //
        final VirtualCard card = VirtualCardBuilder
                .cardId("5901")
                .mobileAppConsumerKey(
                        "oJmqoey7I97FBOZQDXoH0A9RbM7BciXbD8CHZDMU")
                .mobileAppConsumerSecret(
                        "ugTD5dGjBUEC1pyzxjMNtw6k9TUocGtY4plta9he")
                .walletAccessToken("O15F4jG3PcwC44NpkfEqv1ZD7tjNo5NhGpHHFEax")
                .walletTokenSecret("Ur504iPQn2lxgsS5fVBycf2zK9uuSglppvn2mBa3")
                .cardAgentCodeHash("aa4185c2364048447a38aa02499a9897")
                .context(this).build();

        // now load the card...
        // this requires network connectivity and is blocking
        // so we must use AsyncTask to avoid running this in UI thread
        //
        new AsyncTask() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    myActivity.postUi("=================================="
                            + "\n" + "=====Loading Card From Server====="
                            + "\n" + "=================================="
                            + "\n");
                    card.load();
                } catch (IOException ex) {
                    Log.e(TAG, "Failed to load", ex);
                }
                return (Void) null;
            }
        }.execute();

    }

}

17 comments:

  1. Hi Doug, we are trying to implement HCE using SimplyTapp and getting BAD_CREDENTIAL error while trying to load card. Below is my code snippet, your help would be appreciable. Thanks

    final VirtualCard card = VirtualCardBuilder
    .cardId("446")
    .mobileAppConsumerKey(
    "USDzf9qamTyTYfqYD1el2Oq2wlcEuHF2SfeENYLN")
    .mobileAppConsumerSecret(
    "Iqu7YLaNDPSacMIfQXUE66MmFQg1WogS6RwccLkv")
    .walletAccessToken("GEwD0huz2NZ132rFrF9sSSqnsxGx2LAWCcrViFRt")
    .walletTokenSecret("TUywzsxHGTDkk0z6qw3M4uOhbBDrUzSokOrVJErK")
    .cardAgentCodeHash("a12d08522c8a0026f85e5bb1f1a108f6")
    .context(this).build();

    ReplyDelete
  2. looking at the DB, it looks like the access token and secret you have are for your issuer entity specific card Access Token and secret. also it looks like your cardId is wrong. looking at the db, i think the call should be:

    final VirtualCard card = VirtualCardBuilder
    .cardId("6069")
    .mobileAppConsumerKey(
    "USDzf9qamTyTYfqYD1el2Oq2wlcEuHF2SfeENYLN")
    .mobileAppConsumerSecret(
    "Iqu7YLaNDPSacMIfQXUE66MmFQg1WogS6RwccLkv")
    .walletAccessToken("hq11CShDLvukQUZwmhoQ17eJzBoXdLCPWLRrAlEM")
    .walletTokenSecret("7vYxPf5r84BbCkEOOmFjwNb4rQqZwQLhk0pdMCVu")
    .cardAgentCodeHash("a12d08522c8a0026f85e5bb1f1a108f6")
    .context(this).build();

    ReplyDelete
    Replies
    1. Thanks for your reply Doug, its working fine now :) .

      Delete
  3. Hi Doug, now am able to load the card in my Android app using the steps you have given in this link. I also want to develop an app which acts as a NFC terminal and communicates with the VirtualCard created above and completes the transaction successfully. Can you please provide me any link which I can go through to achieve this?

    Thanks,
    Pradeep.

    ReplyDelete
  4. https://github.com/SimplyTapp/SoftTerminal

    ReplyDelete
  5. Hi Doug, Thanks for the link. The source code seems to be java lib, any sample Android app which uses this java lib to interact with the virtual card over NFC?

    Also I have written an Android app as a terminal to interact with my card over NFC. The terminal app is able to read "PPSE if I send Ppse Aid" and "Card Applet if I send Card Aid" from my Virtual Card app running on another phone. But to complete the transaction I should be able to read "processing options" and "record data" as well which am not able to. Please guide me how I can read the "processing options" and "record data" of the virtual card to complete the transaction. Below are the Aids am sending over NFC to read the Ppse and card applet value from my terminal.

    private static final byte[] AID_ANDROID = { (byte)0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10 };
    private static final byte[] PPSE_ANDROID = { (byte)0x32, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31 };


    Thanks.

    ReplyDelete
  6. Hi Doug,
    I am able to read the VirtualCard data(Ppse, Card Aid, Processing options and Record data) from my terminal app running on another phone over NFC. But I have a requirement where I need to read all the above data(Ppse, Card Aid, Processing options and Record data) from VirtualCard object in the same app. Can you guide me how I can achieve this?

    Thanks,
    Pradeep.

    ReplyDelete
  7. Hi Doug, in the example above inside virtualCardMessage() function am getting ApprovalData as null when I try accessing it by message.getApprovalData() . Can you help me how I can get ApprovalData of a VirtualCard object.

    Thanks,
    Pradeep.

    ReplyDelete
  8. using ApprovalData:

    2) using callback from agent to mobile app:

    agent calls:

    postMessage(String msg, boolean getApproval, ApprovalData approvalData)

    msg can be any string

    getApproval should be true

    ApprovalData should be:

    ApprovalData myData = new ApprovalData(new ApprovalData.StringData(-1,-1,0))

    that passes an object that is requesting a String from the mobile app that can be any length and any characters



    this message actually goes to the mobile application is a message through the VirtualCardInterface defined at the creation of new VirtualCard(...)

    you can filter for message: VirtualCardInterface.POST_MESSAGE

    if(message.getCode()==VirtualCardInterface.POST_MESSAGE)

    {

    //a post message from the agent is here

    ApprovalData approvalData = message.getApprovalData();

    ApprovalData.StringData sd = (ApprovalData.StringData)approvalData.getApprovalData();

    sd.setAnswer("my answer");

    //then send this back to the agent

    virtualCard.messageApproval(true, approvalData);

    }



    this "virtualCard.messageApproval(...)" will trigger messageApproval(boolean approved,ApprovalData approvalData) agent method. so the agent code should have this:

    @Override

    public void messageApproval(boolean approved,ApprovalData approvalData){

    //check my answer from the app:

    ApprovalData.StringData sd = (ApprovalData.StringData)approvalData.getApprovalData();

    String answer = sd.getAnswer();

    //now answer contains string data from the application.

    }

    ReplyDelete
  9. Using SoftPCD interface to read an interac flash card:

    SoftPcd softPcd = new SoftPcd((short)5000);
    try {
    virtualCard.transactWithSoftPcd(softPcd);
    } catch (IOException e) {
    e.printStackTrace();
    }
    try {
    softPcd.connect();
    byte[] apdu = softPcd.transceiveWithCard(new byte[]{0x00,(byte)0xA4,0x04,0x00,0x05,(byte)0x32,0x50,0x41,0x59,(byte)0x2E});
    apdu = softPcd.transceiveWithCard(new byte[]{0x00,(byte)0xA4,0x04,0x00,0x07,(byte)0xA0,0x00,0x00,0x02,0x77,0x10,0x10,0x00});
    apdu = softPcd.transceiveWithCard(new byte[]{(byte)0x80,(byte)0xA8,0x00,0x00,0x15,(byte)0x83,0x13,(byte)0xD0,(byte)0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x01,0x01,0x24,0x01,0x24,0x02,0x66,0x33,(byte)0x82,0x01,0x00});
    apdu = softPcd.transceiveWithCard(new byte[]{0x00,(byte)0xB2,0x01,0x0C,0x00});
    apdu = softPcd.transceiveWithCard(new byte[]{0x00,(byte)0xB2,0x01,0x14,0x00});
    apdu = softPcd.transceiveWithCard(new byte[]{0x00,(byte)0xB2,0x02,0x14,0x00});
    apdu = softPcd.transceiveWithCard(new byte[]{0x00,(byte)0xB2,0x03,0x14,0x00});
    apdu = softPcd.transceiveWithCard(new byte[]{0x00,(byte)0xB2,0x04,0x14,0x00});
    apdu = softPcd.transceiveWithCard(new byte[]{0x00,(byte)0xB2,0x01,0x1C,0x00});
    apdu = softPcd.transceiveWithCard(new byte[]{(byte)0x80,(byte)0xAE,(byte)0x80,0x00,0x2A,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x24,
    0x00,(byte)0x80,0x00,(byte)0x80,0x00,0x01,0x24,0x13,0x06,0x27,0x00,0x02,0x66,0x33,(byte)0x82,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x00});
    softPcd.disconnect();
    } catch (IOException e) {
    e.printStackTrace();
    }



    ReplyDelete
  10. Hi Doug, thanks for your response. As specified above I am using SoftPcd to read card data, unfortunately 2 crashes are getting reported in the app
    1 . One crash is seen after loading the card, I had never come across this crash before. Below is the stacktrace which points to the cardagent :

    W/dalvikvm( 9324): threadid=17: thread exiting with uncaught exception (group=0x41efec08)
    D/MainActivity( 9324): Account is Disabled cardId=6164 code=24
    D/MainActivity( 9324): Account is Disabled cardId=6164 code=24
    E/AndroidRuntime( 9324): FATAL EXCEPTION: Thread-2714
    E/AndroidRuntime( 9324): Process: com.example.sampletapp, PID: 9324
    E/AndroidRuntime( 9324): java.lang.NullPointerException
    E/AndroidRuntime( 9324): at com.simplytapp.cardagent.c.run(SourceFile:1794)
    E/AndroidRuntime( 9324): at java.lang.Thread.run(Thread.java:841)
    W/ActivityManager( 3042): Force finishing activity com.example.sampletapp/.MainActivity
    I/CardAgent( 9324): activated, tGetAccountParams is still accessing remote card applet, waiting...
    I/ServiceKeeper( 3042): In getseinfo pid = 3042 uid = 1000 seinfo= system
    D/CrashAnrDetector( 3042): processName: com.example.sampletapp
    D/CrashAnrDetector( 3042): broadcastEvent : com.example.sampletapp data_app_crash
    W/ApplicationPackageManager( 3042): getCSCPackageItemText()
    V/SmartFaceService - 3rd party pause( 3042): onReceive [android.intent.action.ACTIVITY_STATE/com.example.sampletapp/pause]
    D/SSRMv2:CustomFrequencyManagerService( 3042): acquireDVFSLockLocked : type : DVFS_MIN_LIMIT frequency : 1200000 uid : 1000 pid : 3042 pkgName : ACTIVITY_RESUME_BOOSTER@4
    W/ActivityManager( 3042): mDVFSHelper.acquire()

    2 . Another crash is found for the code
    byte[] apdu = softPcd.transceiveWithCard(new byte[]{0x00,(byte)0xA4,0x04,0x00,0x05,(byte)0x32,0x50,0x41,0x59,(byte)0x2E});

    and below is the stacktrace.

    I/CardAgent( 9324): activated, tGetAccountParams is still accessing remote card applet, waiting...
    I/System.out( 9845): Thread-2815(HTTPLog):SmartBonding Enabling is false, log to file is false, DBG is false
    W/System.err( 9324): java.io.IOException: PCD_TIMEOUT
    W/System.err( 9324): at com.simplytapp.virtualcard.SoftPcd.transceiveWithCard(SourceFile:94)
    W/System.err( 9324): at com.example.sampletapp.MainActivity.readCardData(MainActivity.java:149)
    W/System.err( 9324): at com.example.sampletapp.MainActivity.access$400(MainActivity.java:23)
    W/ActivityManager( 3042): destPackageName = null
    W/System.err( 9324): at com.example.sampletapp.MainActivity$3.doInBackground(MainActivity.java:227)
    W/System.err( 9324): at com.example.sampletapp.MainActivity$3.doInBackground(MainActivity.java:218)
    W/System.err( 9324): at android.os.AsyncTask$2.call(AsyncTask.java:288)
    W/System.err( 9324): at java.util.concurrent.FutureTask.run(FutureTask.java:237)
    W/System.err( 9324): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
    W/System.err( 9324): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
    W/System.err( 9324): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
    W/System.err( 9324): at java.lang.Thread.run(Thread.java:841)


    Hope I am not disturbing you much, your help would be greatly appreciated.

    Thanks,
    Pradeep.

    ReplyDelete
  11. both of these appear to happening in the cardAgent. the latter is a timeout that is quite normal if the card Agent does not respond. the former looks like a card agent code side bug.

    ReplyDelete
  12. Hi Doug, thanks for your quick response. Can PCD_TIMEOUT issue be fixed by increasing timeout while creating SoftPcd instance ? I never faced first issue before, these 2 issues have become a blocker for our development, your suggestions would help me in a great way.

    Thank,
    Pradeep.

    ReplyDelete
  13. Hi Doug, PCD_TIMEOUT issue is fixed but first issue still persists. softPcd.transceiveWithCard() API is returning same value for all the inputs that I pass. If I convert byte array returned to Hex string its 6F00 in all the cases, any pointers on this?

    Thanks,
    Pradeep.

    ReplyDelete
  14. Hi Doug
    The link below describes an interesting and simple service issuers could offer to their existing cardholders.

    https://www.dropbox.com/s/k933b6g317aw4z1/Virtual%20Temporary%20Supplementary%20Card%20Concept%20Description.pdf?dl=0

    Take a look and let me know your feedback

    Milos

    ReplyDelete
  15. Hi Milos

    I would be interested in that document.

    Unfortunately the link you provided does not work.

    Regards,
    Diego

    ReplyDelete