Skip to main content

Comcarde Android Client SDK

Introduction#

The Comcarde Android Client SDK helps you gather payment data from your customer's Android device, for use in payments on your server.

overview diagram

  1. Comcarde Android Client SDK helps you gather payment data from your customer's device. The SDK will handle any necessary communication with the Comcarde REST API (using your Client API Key).
  2. Your Android client passes the payment information to your server.
  3. Your server creates payments via the Comcarde REST API (using your Server API Key).

Client SDKs can help you gather information required for payments, but only a server with a Server API Key can create payment transactions. This ensures that it is not possible to create unauthorized payment transactions using keys visible to client devices.

Requirements#

  • Android minimum SDK: Android API 22

Installation#

  1. In your root-level (project-level) Gradle file (build.gradle), add Comcarde's Maven repository. You can obtain credentials by emailing support@comcarde.com.

    Additionally you should add the Braintree Maven repository for Three-D-Secure support.

    allprojects {   // ...   repositories {
         // this links the Comcarde SDK to your project     maven {         url "https://comcarde.mycloudrepo.io/repositories/android-bridge-sdk"         credentials {            username = <YOUR_COMCARDE_MAVEN_REPOSITORY_USERNAME>            password = <YOUR_COMCARDE_MAVEN_REPOSITORY_PASSWORD>         }     }
         // this links the Braintree Three-D-Secure SDK to your project     maven {         url "https://cardinalcommerce.bintray.com/android"         credentials {             username 'braintree-team-sdk@cardinalcommerce'             password '220cc9476025679c4e5c843666c27d97cfb0f951'         }     }
         // ...   }}
  1. In your module (app-level) Gradle file (usually app/build.gradle), add the Comcarde Android Client SDK dependency.

    dependencies {     implementation "com.comcarde:comcardesdk:2.0.0"}
  2. The SDK requires data binding to be enabled, to enable data binding ensure that the following is defined in your app's build.gradle:

android {  ...
  buildFeatures {    dataBinding  true  }    ...}

SDK Initialization#

The SDK needs to be initialized with an environment, and a Client API Key before it can be used. Use the ComcardeConfigBuilder to obtain the configuration you require.

val apiKey:String = [SANDBOX_CLIENT_API_KEY]
val sdkConfig:ComcardeConfig = ComcardeConfigBuilder().run {    setEnvironment("https://sandbox.comcarde.com")    setClientApiKey(apiKey)    setCvvRequired(false) // optional, true by default    .build()}
ComcardeSDK.init(application, sdkConfig)

System Flow#

Fully outsource all your cardholder data functions to Comcarde by using the Android Client SDK as part of a Tokenized Payment Flow.

Features:

  • Enables you to qualify for the simplest SAQ-A PCI Compliance Form level. The SDK will output a token that can be used by your server to generate a payment.
  • Support for digital wallet payments using Google Pay, and PayPal.
  • Your customers can opt to save cards for later use.

The Android system flow describes the frontend ( UI ) and backend flows alongside system events and expected closures to your application.

Drop-In UI#

Comcarde Drop-In UI are a collection of visual user interface ( UI ) views invoked by SDK API calls and are used in the following scenarios:

  • Adding a credit/debit card
  • Removing a credit/debit card (optional)
  • Initiating payment flows (The UI to allow for for choosing payment by Card, Google Pay or PayPal)
  • CVV validation
  • 3D Secure additional actions

For the most part your Activity/Fragment handles IO events for your app to interpret as required. When a user successfully completes the Drop-In UI your client code obtains the relevant information in relation to the user input and actions.

Adding Cards and Tokenization#

Tokenization events are received by your Activity or Fragment via the SDK. The onActivityResult method of your class will be responsible for dealing with the received data. It is recommended to set up a companion object with unique identifiers in order to read the type of result.

companion object {    private const val TOKEN_REQUEST_CODE = 0    private const val THREE_D_SECURE_REQUEST_CODE = 1}

Wheter you are tokenizing a Card, Google Pay or PayPal payment instrument the onActivityResult method will be triggered with a resulting data object which can be parsed according to the requestCode value.

// A unique identifer for your customerval customerId = "example-customer-id"
// Create the order details required for the Tokenization journeyval orderDetails = ComcardeOrderDetails(                        amount = 100, // note this is in pence                        currencyCode = "GBP",                        customerEmail = "customer@email.com",                        customerOrderCode = "The order code",                        customerFirstName = "Alex",                        orderDescription = "The order description"                    )
// (Optional) Create the visual configuration used for Tokenizationval cardFormOptions = ComcardeCardVisualOptions().apply {    cardHolderNameRequired = CardFormFieldStatus.OPTIONAL    maskCardNumber = true    maskCvv = true    postalCodeRequired = false    // etc.}
// Any previously saved cards for your customer.// This data should be sourced by your server against your customer's identifier.// The data returned from the server may differ from this example, and only last 4 pan// digits will be visibleval previouslyTokenizedCards:Set<ObfuscatedCard> =                              setOf(ObfuscatedCard(cardToken = "my_previously_tokenized_card_token",                                                    pan = "5454545454545454",                                                    expiryDate = "202012",                                                    cardType = CardType.MASTERCARD),                                    ObfuscatedCard(cardToken = "my_previously_tokenized_card_token",                                                    pan = "4444333322221111",                                                    expiryDate = "202012",                                                    cardType = CardType.VISA))
// Create the input data used to start the Tokenization processval comcardeInputData = ComcardeInputData(orderDetails,                                          comcardeVisualConfig,                                          customerId,                                          previouslyTokenizedCards)
// Start Tokenization flow, bringing up SDK UIComcardeSDK.getComcardeTokenization().startTokenization(<activity> or <fragment>,                                                        TOKEN_REQUEST_CODE,                                                        comcardeInputData)

Starting a Tokenization flow requires a ComcardeInputData object. The ComcardeInputData object consists of the following fields:

  • ComcardeOrderDetails - Order details needed to facilitate the creation of a Token, such as amount and currency code. Set the amount to zero if you require to just tokenize a card for later use.
  • ComcardeCardVisualOptions - Options determining visual aspects of the Card Tokenization flow, such as what fields to show and in what state.
  • CustomerId - A unique identifer for your customer to associate saved cards to them. You can pass null if you want to track the associations yourself.
  • Set\<ObfuscatedCard> - Any previously saved cards for your customers. This data should be sourced by your server.

Note: If you simply require to add a card against a customerId without the need to continue to make a payment then pass through a zero value for orderDetails.amount, this will then open up the Add Card Drop-In UI directly for user completion

Payment Chooser & Add Card Drop-In UI#

The Payment Chooser Drop-In UI
The Add Card Drop-In UI

Before displaying this screen, Comcarde Android SDK will check if Google Pay and Paypal is available, and choose the preffered PSP for each of digital wallets to be used for this payment.

Note: If these checks fail or there is no PSP available for the digital wallet type, then that payment method will not be displayed and won't be available for the customer

Storing and Retrieving Cards#

Once the SDK has successfully tokenized a card it is up to your application's onActivityResult method to decide what to do with the result. If a customerId was passed through with the ComcardeInputData object then this tokenized card will have been registered against that in Comcarde's backend system.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {        TOKEN_REQUEST_CODE ->            when (resultCode) {                ComcardeTokenization.TOKENIZATION_RESULT_ERROR -> ComcardeSDK.getComcardeTokenization()                    .getReturnedError(data)                    .also { ... // Deal with error here                    }                Activity.RESULT_OK -> ComcardeSDK.getComcardeTokenization()                    .getTokenFromResultData(data)                    ?.also {                        Toast.makeText(                            requireContext(),                            "UntokenizedCard has been tokenized: $it",                            Toast.LENGTH_LONG                        ).show()                    }                    ?: Toast.makeText(                        requireContext(),                        "UntokenizedCard has NOT been tokenized",                        Toast.LENGTH_LONG                    ).show()            }}

To retrieve cards, for security reasons, it is up to your server deliver the result against a customer profile into the Android SDK. This result will be a set of cards ( as Set<ObfuscatedCard>) which you use with ComcardeInputData when starting the SDK tokenization request.

Removing Cards#

Removing a card will send the tokenized card details to the system for card deletion and then return the result in the form of ObfuscatedCardDeletion object or a Throwable to the application developer.

Note, this example uses Reactive X

import com.comcarde.comcardesdk.setup.ComcardeSDKimport com.comcarde.comcardesdk.external.ComcardeDirectimport androidx.lifecycle.MutableLiveDataimport io.reactivex.android.schedulers.AndroidSchedulersimport io.reactivex.disposables.CompositeDisposableimport io.reactivex.schedulers.Schedulers
...
var compositeDisposable:CompositeDisposable = CompositeDisposable()var comcardeDirect:ComcardeDirect = ComcardeSDK.getComcardeDirect()var error = MutableLiveData<Throwable>()
comcardeDirect.deleteCardForTokenObservable(config.clientApiKey, tokenizedCard.cardToken)    .subscribeOn(Schedulers.io())    .ignoreElement()    .doOnTerminate { ... }    .observeOn(AndroidSchedulers.mainThread())    .subscribe({ ... }, error::postValue)    .let { compositeDisposable.add(it) }

Payment requests#

The Comcarde Android SDK presents a Drop-In UI selector for the user to choose the sort of payment they want to make as follows:

  1. Credit / Debit Card (allowing for using a previously tokenized card or adding a new card)
  2. PayPal
  3. Google Pay
The payment chooser Drop-In UI

Once the user has made their choice from the Drop-In UI selector the payment instrument (Card, PayPal or Google Pay) is invoked with the payment request.

The payment journey for each type of payment instrument involves the following flow:

  • Invoke ( where your application defines the customer order as a ComcardeInputData object and then proceeds to Start )
  • Start ( where your application starts the tokenization process via the Comcarde Android SDK )
  • Parse ( where the Comcarde Android SDK parses any response and returns a payment instrument nonce to your application )
  • Send ( where your application requires to utilize your server to handle the nonce and process the payment )
  • Complete ( where you should inform the user of the result )

Invoking Card Payments#

After a card has been chosen from the Payment Chooser Drop-In UI then your activity should use the SDK to convert the data into a TokenizedPaymentInstrument. The TokenizedPaymentInstrument object contains information about the tokenization result, including whether the user is initiating a payment or not (as opposed to just adding a card for later use).

On completion a token will be returned to your Activity as follows:

import com.comcarde.comcardesdk.api.models.TokenizedInstrumentType
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {        TOKEN_REQUEST_CODE ->            ComcardeSDK.getComcardeTokenization().getTokenFromResultData(data)                ?.also {                    val paymentInstrument:TokenizedPaymentInstrument = it                    if (paymentInstrument.tokenType == TokenizedInstrumentType.TOKENIZED) {                        if (paymentInstrument.userHasInitiatedPaymentJourney) {                            // use the paymentInstrument.token with the tokenized payment                            // instrument via your server API                        }                    }                }    }}

This is an asynchronous service and the above example uses Reactive X to obtain the payment instrument object.

It is then up to your server to use this payment instrument and the token value within for the payment.

Note: In the event of a multiUseToken being present in your TokenizedPaymentInstrument - this should not be used for your server-side payment processing. The multiUseToken object is reserved to represent a saved card entity.

CVV Validation#

If CVV is required ( true by default ) then the Comcarde Android SDK will present a Drop-In UI for user input prior to sending the payment request.

The inputted CVV is not stored in the SDK at any stage - it is securely transmitted alongside a previously tokenized card to the Comcarde Token Gateway and returns a valid payment token, after this it is discarded.

If the CVV value entered by user is wrong, the SDK will return PaymentResponse object with error code 4210. The integrator then can choose how to handle this error.

3D Secure Payments#

The 3D Secure Payment Flow requires to be invoked in the event of a payment request returning a 3D secure action code. The com.comcarde.comcardesdk.api.models.PaymentResponse object will contain a code and in the event of a 2001 code then a 3D Secure action will be required.

Handling 3D Secure Additional Actions#

3D Secure Overview Diagram

Comcarde Android Client SDK automates handling of 3D Secure additional action required responses to Payment Requests made by your server.

Your app should query the ComcardeSDK 3D Secure service via ComcardeSDK.getComcarde3DS() to attempt to fetch a ThreeDSecureNonce instance for usage as a payment instrument.

To handle 3D Secure responses standard Activity method onActivityResult with a corresponding requestCode should be implemented.

Note: within the ThreeDSecureNonce instance you will have values for 'liabilityShifted' and 'liabilityShiftPossible' which your payment service can utilize accordingly.

// Start 3DS flow, bringing up SDK UIComcardeSDK.getComcarde3DS()            .startThreeDSecure(activity, THREE_D_SECURE_REQUEST_CODE, paymentResponse)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
        THREE_D_SECURE_REQUEST_CODE -> {                when (resultCode) {                    Comcarde3DS.THREE_DS_ERROR_GENERAL -> ComcardeSDK.getComcarde3DS()                        .getReturnedError(data)                        .let {                            // 3D Secure error                        }                    Comcarde3DS.THREE_DS_CANCELLED -> // 3D Secure cancelled                    Activity.RESULT_OK -> ComcardeSDK.getComcarde3DS()                        .getReturnedThreeDSecureNonce(data)                        ?.let { payWithThreeDSecureNonce(it)}                        ?: Toast.makeText(requireContext(), "3DS Nonce missing!", Toast.LENGTH_LONG)                            .show()                }            }        }    }}
fun payWithThreeDSecureNonce(threeDSecurePaymentNonce: ThreeDSecureNonce?) {    if(threeDSecurePaymentNonce.liabilityShifted == null            || threeDSecurePaymentNonce.liabilityShifted) {
         /*          * 3D Secure worked and authentication succeeded.          *          * This will also be true if the issuing bank does not support 3D Secure,          * but the payment method does. In both cases, the liability for fraud          * has been shifted to the bank. You can now finalize the payment using          * threeDSecurePaymentNonce.nonce on your server.          */
      } else {
         /*          * 3D Secure authentication failed. It is still possible to finalize the          * payment using the threeDSecurePaymentNonce.nonce via your server, but          * you will need to set 3D secure required flag to false.          */
         if(threeDSecurePaymentNonce.liabilityShiftPossible) {            // the payment instrument was eligible for 3D Secure         } else {            // the payment instrument was not eligible for 3D secure.         }
      }}

Note: Context is required to initiate the 3D Secure Additional Actions flow, as it needs to show visual screens and elements to the user

Finalising 3DS payments#

When attempting to make a payment with the threeDSecurePaymentNonce.nonce via your server, two additional things needs to be done.

  1. Payment response may come back with 2001 code again, which means the additional 3DS information is required by PSP. In that case you just need to restart the challenge process by calling ComcardeSDK.getComcarde3DS().startThreeDSecure(activity, THREE_D_SECURE_REQUEST_CODE, paymentResponse) again.

  2. In any other scenario(successful payment, or error), before handling the response, ComcardeSDK.getComcarde3DS().finishThreeDSecure() function needs to be called. This signals the SDK that 3DS payment has finished and it can reset any values used back to their defaults.

Here is an example of how it could look if Live Data would be used for getting Payment response from paying via server using 3DS nonce.

viewModel.observeThreeDSecurePaymentResponse().observe(viewLifecycleOwner, Observer {            /// Note: 2001 = further action required,            /// If this is the case we need to revisit the 3D Secure flow            if (it.code.toInt() == 2001) {                ComcardeSDK.getComcarde3DS().startThreeDSecure(this, THREE_D_SECURE_REQUEST_CODE, it)            } else {                ComcardeSDK.getComcarde3DS().finishThreeDSecure()                handlePaymentResult(it)            }
        })

Invoking PayPal Payments#

After the PayPal UI flow a token will be returned to your Activity as follows:

import com.comcarde.comcardesdk.api.models.TokenizedInstrumentType
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {        TOKEN_REQUEST_CODE ->            ComcardeSDK.getComcardeTokenization().getTokenFromResultData(data)                ?.also {                    val paymentInstrument:TokenizedPaymentInstrument = it                    if (paymentInstrument.tokenType == TokenizedInstrumentType.PAYPAL) {                        // use the token with the PayPal payment instrument via your server API                    }                }    }}

Invoking Google Pay Payments#

Google Pay can be selected from the Payment Chooser Drop-In UI provided that your client configuration contains all the Google Pay credentials and that the SDK has confirmed that this type of payment method is currently available.

After the Google Pay UI flow a token will be returned to your Activity as follows:

import com.comcarde.comcardesdk.api.models.TokenizedInstrumentType
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {        TOKEN_REQUEST_CODE ->            ComcardeSDK.getComcardeTokenization().getTokenFromResultData(data)                ?.also {                    val paymentInstrument:TokenizedPaymentInstrument = it                    if (paymentInstrument.tokenType == TokenizedInstrumentType.GOOGLE_PAY) {                        // use the token with the Google Pay payment instrument via your server API                    }                }    }}

UI/UX Customization#

Theming has been done through the use of Styles in the SDK. This means that any existing styling can be overridden by any integrating party to achieve the desired visual styling. Not everything about the UI can be changed. For example, the order in which UI elements appear cannot be altered, but most aspects of the UI elements themselves can be customized by overriding their styles.

Styling has been divided into the following categories: Colors, Dimensions and Element Styles. All declared styles can be found in the SDK styles.xml, which is automatically merged with the styles.xml file of any integrating party during compile time. See Android Themes for more.

Colors#

Your own colors.xml can freely overwrite default values as required. The full list of colors which are used in the Drop-In UI is as follows:

...<color name="comcarde_colorPrimaryDark">#333333</color><!-- The accent color for highlighted items and the loader --><color name="comcarde_colorAccent">#333333</color><color name="comcarde_white">#FFFFFF</color><color name="comcarde_black">#000000</color><color name="comcarde_backgroundBlockerColor">#8a000000</color><color name="comcarde_ripple_color">#9F9EA1</color><color name="comcarde_base_background_color">@color/comcarde_white</color><color name="comcarde_toolbar_tint">@color/comcarde_black</color><color name="comcarde_toolbar_background">@color/comcarde_white</color><color name="comcarde_item_divider_color">@color/comcarde_black</color><!-- Button colors --><color name="comcarde_buttonBackgroundColor">#009BDE</color><color name="comcarde_buttonTextColor">@color/comcarde_white</color>...

Dark mode#

Comcarde SDK UI elements supports Android Dark Theme. You can change colors for it in the same way, by overriding the values in your values-night/colors.xml file

The Payment Chooser Drop-In UI
The Payment Chooser Drop-In UI in Dark Mode
Add Card Drop-In UI
Add Card Drop-In UI in Dark Mode

Dimensions#

For example, paddings and margins have been given a range of dimensions in the SDK, as shown by the declaration below:

...<item name="comcarde_margin_padding_tiny">@dimen/comcarde_sdk_margin_padding_tiny</item><item name="comcarde_margin_padding_small">@dimen/comcarde_sdk_margin_padding_small</item><item name="comcarde_margin_padding_medium">@dimen/comcarde_sdk_margin_padding_medium</item><item name="comcarde_margin_padding_large">@dimen/comcarde_sdk_margin_padding_large</item><item name="comcarde_margin_padding_xlarge">@dimen/comcarde_sdk_margin_padding_xlarge</item><item name="comcarde_margin_padding_xxlarge">@dimen/comcarde_sdk_margin_padding_xxlarge</item>...

The dimensions specified in the declaration above are used throughout the SDK UI.

However, if an integrating party wishes to override these dimensions throughout their implementation of the SDK, all that is required is to create overriding declarations in their own project's styles.xml, as shown below:

...<item name="comcarde_margin_padding_tiny">1dp</item><item name="comcarde_margin_padding_small">2dp</item><item name="comcarde_margin_padding_medium">4dp</item><item name="comcarde_margin_padding_large">8dp</item><item name="comcarde_margin_padding_xlarge">16dp</item><item name="comcarde_margin_padding_xxlarge">32dp</item>...

These new dimensions declared in the integrating parties styles.xml will replace the SDK declared dimensions wherever they are used throughout the SDK UI.

Screen Orientation#

Currently Comcarde SDK supports portrait only screen orientation. Thought some 3DS challenge journeys user might be able to change the orientation to landscape.

Element Styles#

Just like color, dimension and animation styles, the SDK contains declared styles for UI Elements. These can be UI elements such as Buttons, Switches, Cards, etc.

For example, styling the header toolbar for the Drop-In Add Card view to be white text on a grey background would use the following:

<style name="ComcardeSDKTheme.Toolbar" parent="@style/ThemeOverlay.AppCompat.Dark.ActionBar">    <item name="titleTextColor">#FFFFFF</item>    <item name="android:background">#999999</item></style>

To set the title of the Toolbar in the Drop-In Add Card view, the following string values can be overriden in your own strings.xml file:

...<!-- The title used when just entering the card number --><string name="comcarde_sdk_add_card_numbers_title">Card Details</string><!-- The title used when entering the other card details --><string name="comcarde_sdk_add_card_details_title">Card Details</string>...

API Reference Documentation#

API Reference Documentation