Android SDK

Overview

Cardknox Android SDK is a mobile SDK targeted towards Android developers, allowing developers to process transactions with the Cardknox Transactions API.

Due to the necessity of the API key in this integration method, we strongly recommend reserving these features for integrations to be used solely on merchant-owned devices.

Getting started

To start, download the SDK framework file:

Integrate the SDK file into your Android Studio project by referring to the technical documentation.

Choose your integration path

The SDK offers developers a couple of ways to process transactions:

  • In scope function

  • Out of scope function

  • Custom UI set of functions

Out of scope

Use the out of scope function when the user needs to provide their credit card information. This function displays the SDK user interface, effectively giving the control over to the SDK to acquire the sensitive credit card data from the user. The user provides the sensitive information either via a form or via a credit card device, and then the SDK processes the transaction with the gateway.

In scope

Use the in scope function when there is no need for the SDK to interact with the user through a user interface. The developer should either pass in a card number + an expiration date, or provide a tokenized card data via the xToken parameter to this function to quickly process the transaction and retrieve back the results.

Custom UI

Custom-UI integration consists of a set of functions to control the card reader device via the SDK. Currently supported card reader device is a Bluetooth VP3300 card reader. This integration path is useful when the developer has an existing UI and wishes to use a card reader device to obtain users' card sensitive information and then process the transaction with the gateway. The SDK offers a set of functions to control the card reader device. The SDK takes care of processing with the gateway and notifying the Developer’s application with the processing results.

The SDK offers the following functions:

  • “start scanning for devices”

  • “stop scanning for devices”

  • “connect to device”

  • “disconnect from device”

  • “start transaction”

  • “stop transaction”

Transaction workflows

Download our sample applications:

Basic parameters

Basic parameter functions

Prior to any processing, the Cardknox SDK needs to be configured with user’s metadata and the account key. These functions can be called anywhere in the application any number of times to change the metadata and/or current account key.

Transaction required parameters

Each integration path has a “process” function that accepts a “transaction parameters” object. Developers specify required values for transaction processing through that object. The same object can be used to specify optional; parameters to associate with a transaction, such as invoice numbers, billing address, etc.

Transaction optional parameters

Optional transaction parameters further complement the transaction processing. All the parameters are sent to the Gateway during processing.

Retrieving results with callbacks

The SDK can notify the application about various events during processing, such as about different card reader events during out of scope processing, or perhaps about a completed bluetooth device scan during custom UI processing.

Developers opt in to receive callbacks by registering BroadcastReceivers with IntentFilters, using a predefined value from the SDK for the “action” parameter.

The SDK uses the same “action” value to report results & various information back to subscribers.

Available callback types and integrations where they are applicable in are as follows:

Callback subscriptions & result handling

Based on your integration path choice, choose an available callback type for that integration path and register a BroadcastReceiver to receive appropriate information back from the SDK.

Transaction result callback subscription

This callback delivers a “transaction processed response” object to the subscriber.

Subscription can be made in the appropriate Fragment lifecycle method:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

import cardknox.payments.sdk.CardknoxSDK;
import cardknox.payments.sdk.PaymentTransactionResponse;

public class TransactionResultExampleFragment extends Fragment {

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        RegisterIntentFilters();
    }

    @Override
    public void onDetach() {
        super.onDetach();
        UnregisterIntentFilters();
    }

    private void RegisterIntentFilters()
    {
        Context c = getContext();

        if(c != null)
        {
            c.registerReceiver(Receiver_TransactionProcessing, new IntentFilter(CardknoxSDK.TRANSACTION_CALLBACK_INTENTFILTER_ACTION()));
        }
    }

    private void UnregisterIntentFilters()
    {
        Context c = this.getContext();

        if(c != null)
        {
            c.unregisterReceiver(Receiver_TransactionProcessing);
        }
    }

    private final BroadcastReceiver Receiver_TransactionProcessing = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent)
        {
            String action = intent.getAction();

            // Verify that the Cardknox SDK broadcasted the Intent
            if(action.equals(CardknoxSDK.TRANSACTION_CALLBACK_INTENTFILTER_ACTION()))
            {
                String extraKey = CardknoxSDK.TRANSACTION_CALLBACK_INTENTFILTER_EXTRA();
                java.lang.Object extra = intent.getParcelableExtra(extraKey);

                // Cast the 'extra' to the Cardknox SDK response type
                PaymentTransactionResponse response = (PaymentTransactionResponse)extra;

                if(response.GetIsSuccess())
                {
                    // Transaction successfully processed
                    String xRefNum = response.GetxRefNum();
                    // approved, declined, ...
                    String xStatus = response.GetxStatus();
                    String cvv = response.GetxCvvResult();
                    String avs = response.GetxAvsResult();
                    // ... other properties ...

                    Output("Transaction approved. Ref num: " + xRefNum);
                }
                else
                {
                    // Transaction processing resulted in an error; message can be extracted from this property:
                    String errorMessage = response.GetErrorMessage();
                    String errorCode = response.GetxErrorCode();
                    String error = response.GetxError();

                    Output("Transaction declined/errored. Ref num: " + response.GetxRefNum());
                }
            }
        }
    };

    private void Output(String text)
    {
        android.util.Log.d("ExampleTag", text);
    }
}

Card reader event callback subscription

This callback delivers information about various events happening between the application & the card reader.

For example, while out-of-scope processing the SDK can report back error events related to bluetooth device pairing, such as “bluetooth not turned on” to indicate that the mobile device wanted to use the bluetooth service to find a nearby card reader device but the service is unavailable, or an error such as “waiting for device bluetooth response” to indicate that the mobile device found an eligible bluetooth card reader device and is expecting the card reader to respond back with bluetooth data. This could mean that the bluetooth button on the card reader needs to be pressed.

After a bluetooth pairing is established, the SDK reports back events related to obtaining the card data via the card reader. For example, a “connected” event means that the mobile device & the card reader are connected and a card data transaction can start. A “transaction started” event means that the SDK initiated a card data transaction with the card reader and the physical card can be tapped onto the card reader.

Subscription can be made in the appropriate Fragment lifecycle method:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

import cardknox.payments.sdk.CardknoxCardReaderCallback;
import cardknox.payments.sdk.CardknoxCardReaderCallbackType;
import cardknox.payments.sdk.CardknoxSDK;

public class CardReaderEventsExampleFragment extends Fragment {
    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        RegisterIntentFilters();
    }

    @Override
    public void onDetach() {
        super.onDetach();
        UnregisterIntentFilters();
    }

    private void RegisterIntentFilters()
    {
        Context c = getContext();

        if(c != null)
        {
            c.registerReceiver(Receiver_CardReaderEvents, new IntentFilter(CardknoxSDK.CARDREADER_CALLBACK_INTENTFILTER_ACTION()));
        }
    }

    private void UnregisterIntentFilters()
    {
        Context c = this.getContext();

        if(c != null)
        {
            c.unregisterReceiver(Receiver_CardReaderEvents);
        }
    }

    private final BroadcastReceiver Receiver_CardReaderEvents = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent)
        {
            String action = intent.getAction();

            // Verify that the Cardknox SDK broadcasted the Intent
            if(action.equals(CardknoxSDK.CARDREADER_CALLBACK_INTENTFILTER_ACTION()))
            {
                String extraKey = CardknoxSDK.CARDREADER_CALLBACK_INTENTFILTER_EXTRA();
                CardknoxCardReaderCallback callback = intent.getParcelableExtra(extraKey);

                // Read the event code
                int code = callback.GetCode();

                // Read the event name
                String name = callback.GetName();

                Output("Card reader event: " + name);

                // Match the non-error code; for example "connected"
                if(code == CardknoxCardReaderCallbackType.CONNECTED){
                    Output("Connected!");
                }

                // Extract the message when there's an error
                if(code == CardknoxCardReaderCallbackType.ERROR){
                    String message = callback.GetMessage();
                    Output("Card reader event: " + message);
                }
            }
        }
    };

    private void Output(String text)
    {
        android.util.Log.d("ExampleTag", text);
    }
}

Card reader events

When a card reader event happens, the SDK delivers an object, of a type named similarly to “CardknoxCardReaderCallback”, back into the app.

The object encapsulates two things:

  • an event integer code

  • an event name; such as “connected”, “disconnected”, etc.

Event integer codes are enumerated in a type named similarly to "CardknoxCardReaderCallbackType".

Developers can match the received integer code value with the enumeration of interest to pinpoint a wanted event.

Scanned bluetooth device callback subscription

One of the Custom UI integration functions is a “start scanning” function. The function keeps scanning for nearby bluetooth devices until it is manually stopped with the “stop scanning” function or if it times out.

During the scanning process, for every scanned device the SDK sends a “scanned device” object that contains all the necessary metadata about the scanned device, such as the devices' display name or its internal name.

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

import cardknox.payments.sdk.CardknoxSDKCustomUI;
import cardknox.payments.sdk.CardknoxSDKCustomUIScannedDevice;

public class ScannedDeviceExampleFragment extends Fragment {
    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        RegisterIntentFilters();
    }

    @Override
    public void onDetach() {
        super.onDetach();
        UnregisterIntentFilters();
    }

    private void RegisterIntentFilters()
    {
        Context c = getContext();

        if(c != null)
        {
            c.registerReceiver(Receiver_ScannedDevice, new IntentFilter(CardknoxSDKCustomUI.SCANNED_DEVICE_INTENTFILTER_ACTION()));
        }
    }

    private void UnregisterIntentFilters()
    {
        Context c = this.getContext();

        if(c != null)
        {
            c.unregisterReceiver(Receiver_ScannedDevice);
        }
    }

    private final BroadcastReceiver Receiver_ScannedDevice = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent)
        {
            String action = intent.getAction();

            // Verify that the Cardknox SDK broadcasted the Intent
            if(action.equals(CardknoxSDKCustomUI.SCANNED_DEVICE_INTENTFILTER_ACTION()))
            {
                String extraKey = CardknoxSDKCustomUI.SCANNED_DEVICE_INTENTFILTER_EXTRA();
                java.lang.Object extra = intent.getParcelableExtra(extraKey);

                CardknoxSDKCustomUIScannedDevice device = (CardknoxSDKCustomUIScannedDevice) extra;
                // Can be used with a 'connect' method, like so:
                String address = device.GetAddress();
                CardknoxSDKCustomUI example = null;
                example.connectWithAddress(address);
                
                // Output various data about the device
                Output("Scanned device name: " + device.GetName());
                Output("Scanned device display name: " + device.GetDisplayName());
                Output("Scanned device address: " + device.GetAddress());
            }
        }
    };

    private void Output(String text)
    {
        android.util.Log.d("ExampleTag", text);
    }
}

Scan completed callback subscription

One of the Custom UI integration functions is a “start scanning” function. The function keeps scanning for nearby bluetooth devices until it is manually stopped with the “stop scanning” function or if it times out.

Once the scanning process ends, the SDK sends a list of scanned device objects to all subscribers. Any object in the retrieved list can be used as an argument to the “connect to device” method.

Subscription can be made in the appropriate Fragment lifecycle method:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

import cardknox.payments.sdk.CardknoxSDKCustomUI;
import cardknox.payments.sdk.CardknoxSDKCustomUIScanCompleted;
import cardknox.payments.sdk.CardknoxSDKCustomUIScannedDevice;

public class ScanCompletedExampleFragment extends Fragment {
    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        RegisterIntentFilters();
    }

    @Override
    public void onDetach() {
        super.onDetach();
        UnregisterIntentFilters();
    }

    private void RegisterIntentFilters()
    {
        Context c = getContext();

        if(c != null)
        {
            c.registerReceiver(Receiver_ScanCompleted, new IntentFilter(CardknoxSDKCustomUI.SCAN_COMPLETED_INTENTFILTER_ACTION()));
        }
    }

    private void UnregisterIntentFilters()
    {
        Context c = this.getContext();

        if(c != null)
        {
            c.registerReceiver(Receiver_ScanCompleted, new IntentFilter(CardknoxSDKCustomUI.SCAN_COMPLETED_INTENTFILTER_ACTION()));
        }
    }

    private final BroadcastReceiver Receiver_ScanCompleted = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent)
        {
            String action = intent.getAction();

            // Verify that the Cardknox SDK broadcasted the Intent
            if(action.equals(CardknoxSDKCustomUI.SCAN_COMPLETED_INTENTFILTER_ACTION()))
            {
                String extraKey = CardknoxSDKCustomUI.SCAN_COMPLETED_INTENTFILTER_EXTRA();
                java.lang.Object extra = intent.getParcelableExtra(extraKey);
                CardknoxSDKCustomUIScanCompleted scanCompleted = (CardknoxSDKCustomUIScanCompleted) extra;

                if(scanCompleted != null && scanCompleted.scannedDevices() != null &&
                        scanCompleted.scannedDevices().size() > 0)
                {
                    for (Object device : scanCompleted.scannedDevices()) {
                        CardknoxSDKCustomUIScannedDevice device = (CardknoxSDKCustomUIScannedDevice)d;

                        // Can be used with a 'connect' method, like so:
                        String address = device.GetAddress();
                        CardknoxSDKCustomUI example = null;
                        example.connectWithAddress(address);

                        // Output various data about the device
                        Output("Scanned device name: " + device.GetName());
                        Output("Scanned device display name: " + device.GetDisplayName());
                        Output("Scanned device address: " + device.GetAddress());
                    }
                }
            }
        }
    };

    private void Output(String text)
    {
        android.util.Log.d("ExampleTag", text);
    }
}

Out of scope integration

Out of scope processing feature allows the developer to show the Cardknox user interface for payment processing.

First, create an “out of scope” or “ui” manager kind of object. This object can create “request” objects that are capable of showing the SDK’s user interface:

CardknoxSDKUI cardknoxSDKUI = CardknoxSDKUI.create();

The developer is responsible for the CardknoxSDKUI object instance. Once the object is no longer needed, the method named similar to destroy needs to be called. A good place to maintain the object are “appear” and “disappear” callbacks.

import androidx.fragment.app.Fragment;

import cardknox.payments.sdk.CardknoxSDKUI;

public class OutOfScopeExampleFragment extends Fragment {

    private CardknoxSDKUI cardknoxSDKUI;

    @Override
    public void onResume() {
        super.onResume();
        cardknoxSDKUI = CardknoxSDKUI.create();
    }

     @Override
    public void onDetach() {
        super.onDetach();
        
        // This might not be the best place where to destroy the 'cardknox sdk ui' object.
        //
        // Why:
        // The Cardknox SDK starts a new Activity to show its' UI; effectively
        // pausing all other components. 
        // It relies on the 'cardknox sdk ui' reference not being destroyed.
        destroyCardknoxSDKUI();
    }

    /**
     * Destroy the 'cardknox sdk ui' manager object when it's no longer needed.
     * 
     * Call this when:
     * 1. your UI component (Activity, Fragment, etc.) needs to close itself.
     * 
     * Do not call this when:
     * 1. the Cardknox SDK needs to show its' UI as it relies on the 'cardknox sdk ui' reference not being destroyed
     * */
    private void destroyCardknoxSDKUI(){
        if (cardknoxSDKUI != null) {
            cardknoxSDKUI.destroy();
        }
        cardknoxSDKUI = null;
    }
}

To show the user interface, create a request object that is capable of showing a user interface:

// Create the parameters object
TransactionParameters parameters = new TransactionParameters()
{
    {
        // Required parameters
        // 1. command to use. One of the Cardknox Transaction API commands
        SetxCommand("cc:sale");
        // 2. amount
        SetxAmount(1.23);

        // Supplementary parameters
        // 1. card number - will be prefilled on the keyed form
        SetxCardNum("4444333322221111");

        // 2. expiration date - will be prefilled on the keyed form
        // Format "MMYY"
        // For example: December 2024
        SetxExp("1224");

        // Optional parameters to supplement the transaction
        // All of the available parameters will be sent to the gateway
        SetxInvoice("1234");
        SetxBillCity("New York");
        // ... other optional parameters.
    }
};

PaymentTransactionRequestUI request = cardknoxSDKUI.createRequestWithParameters(parameters);

Check if the request object is in a valid state. If it is, call the method to show the UI. Otherwise, inspect the validation errors to see what is incorrect in the request object:

PaymentTransactionRequestUI request = cardknoxSDKUI.createRequestWithParameters(parameters);

if(request.GetIsValid()){
    // Supply the Activity reference here.
    // In fragments, that might be a 'getActivity()' call.
    android.app.Activity activity = null;
    
    // Show Cardknox SDK UI
    request.process(activity);
} else {
    // Extract all validation errors that block
    // the request object from showing the SDK UI
    String[] validationErrors = request.GetValidationErrors();
}

Available user interfaces

The SDK’s user interface consists of two fullscreen parts - a manual entry screen and a card reader screen. Manual entry screen is also abbreviated as a “keyed” screen. The card reader screen is also abbreviated as a “swipe” screen.

Showing the SDK user interface via a Request object will either show one of the screens, or both. Which screen will be visible depends on the global SDK configuration state prior to showing the SDK user interface via a Request object.

Note that if the SDK is configured to allow access to both processing screens, one of them will be shown by default and both of them will have some kind of a visual way to navigate to the other one.

The following table shows available functions to control which screen will be visible & accessible:

The following mapping represents which screens will be available when the SDK shows its user interface:

The following mapping represents available Cardknox Transaction API commands on each user interface

Pre processing options

Developer using the Out Of Scope integration to process using the VP3300 card reader can specify a per-request transaction timeout value. The SDK will start a transaction with the VP3300 reader, and timeout in the specified time frame if the card is not tapped, swiped or inserted in that same time frame.

Post processing options

After the out-of-scope function finishes with transaction processing, the SDK displays a popup containing a handful of information about the transaction.

The SDK can be configured to auto close the user interface immediately after the transaction processing has completed; regardless if the transaction was approved or not.

In scope integration

In scope processing feature allows the developer to quickly process a payment and retrieve the response object.

First, create an “in scope” or “direct” manager kind of object. This object can create “request” objects that are capable of directly processing a transaction and returning back a “response” object.

CardknoxSDKDirect cardknoxSDKDirect = CardknoxSDKDirect.create();

Once the object is no longer needed, the method named similar to destroy needs to be called to clean up all the used resources. A good place to maintain the object are the appropriate Fragment lifecycle methods:

import androidx.fragment.app.Fragment;

import cardknox.payments.sdk.CardknoxSDKDirect;

public class InScopeExampleFragment extends Fragment {
    
    private CardknoxSDKDirect cardknoxSDKDirect;

    @Override
    public void onResume() {
        super.onResume();
        cardknoxSDKDirect = CardknoxSDKDirect.create();
    }

    @Override
    public void onPause() {
        super.onPause();

        if (cardknoxSDKDirect != null) {
            cardknoxSDKDirect.destroy();
        }
        cardknoxSDKDirect = null;
    }
}

To process directly, create a request object:

// Create the parameters object
TransactionParameters parameters = new TransactionParameters()
{
    {
        // Required parameters
        SetxCommand("cc:sale");
        SetxAmount(1.23);
        SetxCardNum("4444333322221111");
        // Format "MMYY"
        // For example: December 2024
        SetxExp("1224");

        // Optional parameters to supplement the transaction
        SetxInvoice("1234");
    }
};

// Create the request object
PaymentTransactionRequestDirect request = cardknoxSDKDirect.createRequestWithParameters(parameters);

Check if the request object is in a valid state. If it is, call the method to process directly. Otherwise, inspect the validation errors to see what is incorrect in the request object:

PaymentTransactionRequestDirect request = cardknoxSDKDirect.createRequestWithParameters(parameters);

if(request.GetIsValid()) {
    PaymentTransactionResponse response = request.process();

    boolean isSuccess = response.GetIsSuccess();
    String errorMessage = response.GetErrorMessage();
    String errorCode = response.GetxErrorCode();
    String refNum = response.GetxRefNum();
} 
else {
    String[] errors = request.GetValidationErrors();  
}

Available commands

Custom UI integration

Custom UI integration is similar to the “out of scope” integration in a way that the exact same methods that the “out of scope” is using under the hood for controlling the card reader, are exposed via the SDK for the Developer to use.

The Developer provides the user interface and orchestrates the entire flow for obtaining the card data via the card reader by calling appropriate Custom UI functions at specific times.

Available commands

Any of the following credit card commands are available for Custom UI:

  • cc:save

  • cc:credit

  • cc:authonly

  • cc:sale

Reference: https://docs.cardknox.com/api/transaction#credit-card

Custom UI flow

First, create a “custom ui” object to get access to all the Custom UI functions. Afterwards, subscribe to all the relevant callbacks for this integration path:

  • transaction result callback - to receive the “response” object after the SDK has processed a transaction

  • card reader event callback - to be notified about various events that take place between the application & the card reader

  • scanned bluetooth device callback - to be notified about every new scanned bluetooth device during the “scan for devices” process

  • scan completed callback - to be notified about all the scanned devices once the “scan for devices” process ends

Next step is to establish a connection between the app and the card reader device. Use one of the “connect” methods on the “custom ui” to initiate a connection; such as “connect with name” or “connect with address”.

Device name or the MAC address can be obtained with the “scan for devices” flow. Initiate the “start scanning” function call, with or without a timeout.

The scanning process stops with a call to the “stop scanning” function or when the “start scanning” function times out.

After establishing a connection with the card reader by calling one of the “connect” methods and receiving a “connected” card reader event via the BroadcastReceiver subscription, call the “start transaction” function to make the card reader ready for a card.

The SDK will report a “transaction started” event if the transaction with the card reader was successfully started, otherwise an “error” card reader event is reported back. At this point the card can be tapped, swiped or inserted into the card reader. The SDK will read the card information, process a transaction & deliver the results to the application via a callback.

If no card is tapped, swiped or inserted after the transaction started - a “timeout” card reader is reported back. The default timeout value is about 10 seconds. The developer can override this value via the “transaction parameters” object.

Versioning

Developers can call the “get version” API to obtain the SDK Semantic Versioning (SemVer source)

Logging

SDK verbose logging can be enabled or disabled with a function call:

FAQ

  1. As a Cardknox SDK user, I want to process without an internet connection. What will happen?

    • The SDK will return a PaymentTransactionResponse object with a special xErrorCode value -1

  2. As a Cardknox SDK user, I’ve encountered errors during transaction processing. What response can I expect?

    • the PaymentTransactionRequest object will encapsulate all relevant information in respective fields; for example the xErrorCode property will return a code from the Cardknox Transaction API documentation, the xErrorMessage and xError properties can be used for a descriptive error message while the xRefNum gives back a unique ref num to follow up with the customer support

Checksums

To ensure that the integrity of the downloaded SDK & sample app files & archives was not tampered with, refer to the following checksums:

Last updated