Android SDK

Getting Started

The Ravelin Android SDK provides a means of adding customer information to Ravelin without having to handle PCI-compliant data. The library is intended to work in your apps where you have access to card details the customer has entered. Our SDK will encrypt sensitive data so it can pass through your servers to Ravelin and reduce the scope of your PCI compliance.

This is all achieved using our library which handles client-side encryption, as well as device and session tracking functionality.

Minimum Requirements

The SDK supports a minimum Android API of 15 - 4.0.3 ICE_CREAM_SANDWICH_MR1

Contents

Installing the Ravelin Android SDK

At the time of writing, only manual installation is supported.

Due to the size of the underlying encryption code, the a universal build is roughly 31MB. Using ABI Splitting (as described below) the library size can be reduced to around 7MB per architecture.

Installing Manually

The Ravelin library is provided as an AAR file imported as a standard AAR.

Into the Project level build.gradle make sure the repositories include the local libraries directory. Assuming that the AARs are stored within lib:

allprojects {
   repositories {
      jcenter()
      flatDir {
        dirs 'libs'
      }
   }
}

Then add to the app dependencies:

dependencies {
    implementation(name:'ravelin', ext:'aar')
}

Installing the Ravelin Mobile SDK (via Gradle)

Before attempting to use Gradle for Ravelin, ensure that you have a valid username and password for the private maven repository.

IMPORTANT - Do not store the repository credentials within Source Control. They can be stored in the user’s gradle.properties

// ~/.gradle/gradle,properties

ravelinRepoUsername=username
ravelinRepoPassword=password

Within the Project level build.gradle, make sure the repositories include the appropriate Ravelin maven repository:

allprojects {
   repositories {
      /* other repositories */
      maven {            
         credentials {
            username ravelinRepoUsername
            password ravelinRepoPassword
         }
         url "https://maven.ravelin.com/repositories/releases/"
      }
   }
}

Then in the Module level build.gradle add Ravelin to the app dependencies:

dependencies {
    /* other dependencies */
    implementation 'com.ravelin.sdk:android:0.1.0'
}

APK Size Reduction

Due to the native components contained within the Ravelin SDK, we highly recommend using ABI Splitting to reduce the overall compiled size of your application. The underlying native package is already split into the different architectures so this allows your APKs to only contain the relevant library.

The list of available platforms is maintained on the Android website here.

Below is an adjusted example from the Android website outlining how to perform an ABI Split. This code must be placed into the module level build.gradle, ensuring to include all architectures needed to cover the desired userbase.

android {
    // ...
    splits {

        // Configures multiple APKs based on ABI.
        abi {
        
            // Enables building multiple APKs per ABI.
            enable true
            
            // Resets the list of ABIs that Gradle should create APKs for to none.
            reset()
    
            // Specifies a list of ABIs that Gradle should create APKs for.
            // We have included all Android mobile architectures.
            include "armeabi", "armeabi-v7a", "arm64-v8a"
    
            // Specifies that we do not want to also generate a universal APK that includes all ABIs.
            universalApk false
        }
    }
}

Usage

Accessing the Singleton

The Ravelin Android SDK has two static instance accessors:

  1. RavelinSDK.createInstance
  2. RavelinSDK.getInstance

These both access the singleton if it has already been created, however, the instance must be created first. Calling getInstance before an instance has been created will pass a RavelinError to the RavelinCallback#failure callback.

Calling RavelinSDK#createInstance a second time will not create a second instance but will instead return a previously created instance.

Kotlin

First call:

// MyApplication.kt

// using callacks
RavelinSDK.createInstance(this, "publishable_key_live_----", "--|---------------", object : RavelinCallback<RavelinSDK>() {
    override fun success(ravelin : RavelinSDK) {
        Log.e("Ravelin Setup Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("Ravelin Setup Error", error.message)
    }
})

// not using callback
val ravelin: RavelinSDK? = RavelinSDK.createInstance(this, "publishable_key_live_----", "--|---------------")

Subsequent calls:

// MyActivity.kt

// using callback
RavelinSDK.getInstance(object : RavelinCallback<RavelinSDK>() {
    override fun success(ravelin : RavelinSDK) {
        Log.e("Ravelin Setup Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("Ravelin Setup Error", error.message)
    }
})

// not using callback
val ravelin: RavelinSDK? = RavelinSDK.getInstance()

Java

First call:

// MyApplication.java

// null-safe inside callback
RavelinSDK.Companion.createInstance(this, "publishable_key_live_----", "--|---------------", new RavelinCallback<RavelinSDK>() {
    @Override
    public void success(RavelinSDK ravelinSDK) {
        Log.e("Ravelin Setup Success", "hooray");
    }

    @Override
    public void failure(@NotNull RavelinError ravelinError) {
        Log.e("Ravelin Setup Error", ravelinError.getMessage());
    }
});

// no callback, not null safe
RavelinSDK ravelin = RavelinSDK.Companion.createInstance(this, "publishable_key_live_----", "--|---------------");;

Subsequent calls:

// MyActivity.java

// null-safe with handled error message
RavelinSDK.getInstance(object : RavelinCallback<RavelinSDK>() {
    override fun success(ravelin : RavelinSDK) {
        Log.e("Ravelin Setup Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("Ravelin Setup Error", error.message)
    }
})

// nullable, no error message
RavelinSDK ravelin = RavelinSDK.Companion.getInstance();

After first initialisation, the SDK instance can be referenced anywhere using RavelinSDK.getInstance(...).

Encrypting Cards

Validation is performed, confirming that expiry dates are valid and that the PAN is at least 13 characters. Should any validation checks fail, nil is returned from the method. Pass an error by ref to determine the cause of failure if any occurs.

The primary goal of the Ravelin Android SDK is to allow the secure sharing of card information with Ravelin without to handling PCI-compliant data. When collecting the card details, encrypt the values to send to Ravelin using ravelin.encryptCard(pan, month, year, nameOnCard). pan, month, year are required, while nameOnCard is optional and may be an empty string.

Encrypted cards are validated prior to encryption. You can perform this validation yourself by calling CardDetails#validate. The optional callback parameter will inform you of a success or failure. In case of a failure the method returns null, and if successful the newly created valid CardDetails is created with extra values stripped, ready to be encrypted.

The basic checks performed by the validation are:

  1. PAN length must be greater than 12 digits
  2. the month must be between 1 and 12 inclusive
  3. the year must be between the current year and 50 years in the future
  4. the year-month combination date must be in the future

Kotlin

val cardDetails = CardDetails("1234 1234 1234 1234", "02", "18", "Dr Jilly McJillson");

// null safe callback
cardDetails.validate(object : RavelinCallback<CardDetails>() {
    override fun success(validatedCardDetails : CardDetails) {
        Log.e("Card Validation Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("Card Validation Error", error.message)
    }
})

// nullable
final CardDetails valid = cardDetails.validate();

Java

final CardDetails cardDetails = new CardDetails("1234 1234 1234 1234", "02", "18", "Dr Jilly McJillson");

// null safe callback
cardDetails.validate(new RavelinCallback<CardDetails>() {
    @Override
    public void success(CardDetails result) {
        Log.e("Card Validation Success", "hooray");
    }

    @Override
    public void failure(@NotNull RavelinError error) {
        Log.e("Card Validation Error", error.getMessage());
    }
});

// nullable
final CardDetails valid = cardDetails.validate();

Tracking Activity

Using the Ravelin Android SDK, you can capture various built in events along with your own custom ones that can later be viewed in the Ravelin dashboard.

Track events may be called with either a RavelinRequestCallback callback or no callback. Using a callback, the network request will be triggered immediately.

No callback will return the RavelinRequest object which can then be enqueued with a callback for non-blocking, asynchronous calls or executed to block until completion.

trackEvent

Tracks a user or app event.

Required eventType, optional pageTitle, optional payload to be serialized and optional callback

Kotlin

// no callback, then blocking call later
val request : RavelinRequest = ravelin.trackEvent("app open")

request?.execute()

// no callback, then asynchronous call later
val request = ravelin.trackEvent("app open")

request?.enqueue(object : RavelinRequestCallback() {
    override fun success() {
        Log.e("API Request Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("API Request Error", error.message)
    }
})

// immediately call asynchronously
ravelin.trackEvent("app open", "login activity", arrayOf("Custom", "Data"), object : RavelinRequestCallback() {
    override fun success() {
        Log.e("API Request Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("API Request Error", error.message)
    }
})

Java

// no callback, then blocking call later
RavelinRequest request = ravelin.trackEvent("app open")

if(request != null) {
    request.execute()
}

// no callback, then asynchronous call later
RavelinRequest request = ravelin.trackEvent("app open")

request.enqueue(new RavelinRequestCallback() {
    @Override
    public void success() {
        Log.e("API Request Success", "hooray");
    }

    @Override
    public void failure(@NotNull RavelinError error) {
        Log.e("API Request Error", error.getMessage());
    }
});

// immediately call asynchronously
ravelin.trackEvent("app open", "login activity", new String[]{"Custom", "Data"}, new RavelinRequestCallback() {
    @Override
    public void success() {
        Log.e("API Request Success", "hooray");
    }

    @Override
    public void failure(@NotNull RavelinError error) {
        Log.e("API Request Error", error.getMessage());
    }
});

trackLogIn

Tracks a user login event.

Required customerId, optional pageTitle, optional payload to be serialized and optional callback

Kotlin

// no callback, then blocking call later
val request : RavelinRequest = ravelin.trackLogIn("15464811")

request?.execute()

// no callback, then asynchronous call later
val request = ravelin.trackLogIn("15464811")

request?.enqueue(object : RavelinRequestCallback() {
    override fun success() {
        Log.e("API Request Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("API Request Error", error.message)
    }
})

// immediately call asynchronously
ravelin.trackLogIn("15464811", "login activity", mapOf("Class" to "Mage"), object : RavelinRequestCallback() {
    override fun success() {
        Log.e("API Request Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("API Request Error", error.message)
    }
})

Java

// no callback, then blocking call later
RavelinRequest request = ravelin.trackLogIn("app open")

if(request != null) {
    request.execute()
}

// no callback, then asynchronous call later
RavelinRequest request = ravelin.trackLogIn("app open")

request.enqueue(new RavelinRequestCallback() {
    @Override
    public void success() {
        Log.e("API Request Success", "hooray");
    }

    @Override
    public void failure(@NotNull RavelinError error) {
        Log.e("API Request Error", error.getMessage());
    }
});

// immediately call asynchronously
final HashMap<String, String> map = new HashMap<>();
map.put("Class", "Mage");

ravelin.trackLogIn("app open", "login activity", map, new RavelinRequestCallback() {
    @Override
    public void success() {
        Log.e("API Request Success", "hooray");
    }

    @Override
    public void failure(@NotNull RavelinError error) {
        Log.e("API Request Error", error.getMessage());
    }
});

trackLogOut

Tracks a user login event.

pageTitle and payload are optional serialized parameters. callback is optional.

Kotlin

// no callback, then blocking call later
val request : RavelinRequest = ravelin.trackLogOut("15464811")

request?.execute()

// no callback, then asynchronous call later
val request = ravelin.trackLogOut()

request?.enqueue(object : RavelinRequestCallback() {
    override fun success() {
        Log.e("API Request Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("API Request Error", error.message)
    }
})

// immediately call asynchronously
ravelin.trackLogOut("home activity", mapOf("Class" to "Mage"), object : RavelinRequestCallback() {
    override fun success() {
        Log.e("API Request Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("API Request Error", error.message)
    }
})

Java

// no callback, then blocking call later
RavelinRequest request = ravelin.trackLogOut()

if(request != null) {
    request.execute()
}

// no callback, then asynchronous call later
RavelinRequest request = ravelin.trackLogOut()

request.enqueue(new RavelinRequestCallback() {
    @Override
    public void success() {
        Log.e("API Request Success", "hooray");
    }

    @Override
    public void failure(@NotNull RavelinError error) {
        Log.e("API Request Error", error.getMessage());
    }
});

// immediately call asynchronously
final HashMap<String, String> map = new HashMap<>();
map.put("Class", "Mage");

ravelin.trackLogOut("login activity", map, new RavelinRequestCallback() {
    @Override
    public void success() {
        Log.e("API Request Success", "hooray");
    }

    @Override
    public void failure(@NotNull RavelinError error) {
        Log.e("API Request Error", error.getMessage());
    }
});

trackOrder

Tracks a users order event.

Requires payload to be serialized. pageTitle and callback are optional parameters.

In the following example, the order object in the trackOrder function call will be your applications own representation of a purchase order, including any information that is beneficial to fraud detection.

Kotlin

// no callback, then blocking call later
val request : RavelinRequest = ravelin.trackOrder(order)

request?.execute()

// no callback, then asynchronous call later
val request = ravelin.trackOrder(order)

request?.enqueue(object : RavelinRequestCallback() {
    override fun success() {
        Log.e("API Request Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("API Request Error", error.message)
    }
})

// immediately call asynchronously
ravelin.trackOrder(order, "checkout activity", object : RavelinRequestCallback() {
    override fun success() {
        Log.e("API Request Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("API Request Error", error.message)
    }
})

Java

// no callback, then blocking call later
RavelinRequest request = ravelin.trackOrder(order)

if(request != null) {
    request.execute()
}

// no callback, then asynchronous call later
RavelinRequest request = ravelin.trackOrder(order)

request.enqueue(new RavelinRequestCallback() {
    @Override
    public void success() {
        Log.e("API Request Success", "hooray");
    }

    @Override
    public void failure(@NotNull RavelinError error) {
        Log.e("API Request Error", error.getMessage());
    }
});

// immediately call asynchronously
ravelin.trackOrder(order, "login activity", new RavelinRequestCallback() {
    @Override
    public void success() {
        Log.e("API Request Success", "hooray");
    }

    @Override
    public void failure(@NotNull RavelinError error) {
        Log.e("API Request Error", error.getMessage());
    }
});

trackPaste

This method is used primarily by the Ravelin PasteEventListener but can be used directly if required.

Required value, optional pageTitle and optional callback

Kotlin

// no callback, then blocking call later
val request : RavelinRequest = ravelin.trackPaste(pastedCharacters)

request?.execute()

// no callback, then asynchronous call later
val request = ravelin.trackPaste(pastedCharacters)

request?.enqueue(object : RavelinRequestCallback() {
    override fun success() {
        Log.e("API Request Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("API Request Error", error.message)
    }
})

// immediately call asynchronously
ravelin.trackPaste(pastedCharacters, "checkout activity", object : RavelinRequestCallback() {
    override fun success() {
        Log.e("API Request Success", "hooray")
    }
    
    override fun failure(error: RavelinError) {
        Log.e("API Request Error", error.message)
    }
})

Java

// no callback, then blocking call later
RavelinRequest request = ravelin.trackPaste(pastedCharacters)

if(request != null) {
    request.execute()
}

// no callback, then asynchronous call later
RavelinRequest request = ravelin.trackPaste(pastedCharacters)

request.enqueue(new RavelinRequestCallback() {
    @Override
    public void success() {
        Log.e("API Request Success", "hooray");
    }

    @Override
    public void failure(@NotNull RavelinError error) {
        Log.e("API Request Error", error.getMessage());
    }
});

// immediately call asynchronously
ravelin.trackPaste(pastedCharacters, "login activity", new RavelinRequestCallback() {
    @Override
    public void success() {
        Log.e("API Request Success", "hooray");
    }

    @Override
    public void failure(@NotNull RavelinError error) {
        Log.e("API Request Error", error.getMessage());
    }
});

Paste Event Listener

The PasteEventListener is a TextWatcher that reports back when a user has pasted into an input box through a PasteEventListener.OnPaste interface.

Internally, the PasteEventListener will report back to Ravelin the pasted text after obfuscating input.

This is primarily for use on PAN input fields but may be used anywhere.

Kotlin

val view = // Your EditText

view.addTextChangedListener(
    PasteListener("page", object : PasteListener.OnPaste() {
        fun pasted() {
            Log.d("Paste Event", "user has pasted some content")
        }
    })
)

Java

EditText view = // Your EditText

view.addTextChangedListener(
    new PasteListener("page", new PasteListener.OnPaste() {
        @Override
        public void pasted() {
            Log.d("Paste Event", "user has pasted some content");
        }
    })
);

End-to-End Example

In the following form, we collect card details from the customer, encrypt them and send that encrypted values (the cipher) back to your server.

Kotlin

class ShopApplication : Application() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // Create instance referencing the application. This can be done anywhere if the application does not extend Application
        RavelinSDK.createInstance(this, "api_key_123456789", "2|rsakeykeykeykeykeykey", object : RavelinCallback<RavelinSDK>() {
            override fun success(instance : RavelinSDK) {
                Log.e("Ravelin Setup Success", "hooray")
            }
            
            override fun failure(error: RavelinError) {
                Log.e("Ravelin Setup Error", error.message)
            }
        })
    }
}


class ShopActivity: AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val ravelin = RavelinSDK.getInstance(object : RavelinCallback<RavelinSDK>() {
            override fun success(instance : RavelinSDK) {
                ravelin = instance
            }
            
            override fun failure(error: RavelinError) {
                Log.e("Ravelin Setup Error", error.message)
            }
        })
        val cardNumberTextView = findViewById<TextView>(R.id.pan)
        val expirationMonthTextView = findViewById<TextView>(R.id.expiration_month)
        val expirationYearTextView = findViewById<TextView>(R.id.expiration_year)
        val nameOnCardTextView = findViewById<TextView>(R.id.name_on_card)

        findViewById<Button>(R.id.payButton).setOnClickListener {
            val pan = cardNumberTextView.text
            val month = expirationMonthTextView.text
            val year = expirationYearTextView.text
            val name = nameOnCardTextView.text
            
            val cardDetails = CardDetails(pan, month, year, name)

            if(ravelin != null) {
            ravelin.encryptCard(cardDetails, object : RavelinCallback<RavelinSDK>() {
                    override fun success(instance : EncryptedCard) {
                        Log.e("Card Encryption Success", "hooray")
                        sendEncryptedDetailsToYourServer(encryptedCardDetails)
                    }
                    
                    override fun failure(error: EncryptedCard) {
                        Log.e("Card Encryption Error", error.message)
                    }
                })
            }
        }
    }
}

Once the cipher is received by your server, the API request to Ravelin in which a fraud recommendation is requested should use this cipher value:

/* Server-side */

var card = JSON.parse(request.params['ravelinCipherText']);
card.methodType = 'paymentMethodCipher';

var action = request('https://api.ravelin.com/v2/checkout?score=true', {
    // ...
    'paymentMethod': card,
});

Class Methods


createInstance(application: Application, apiKey: String, rsaKey: String, callback: RavelinCallback? = null) : RavelinSDK?

Create a singleton instance of the Ravelin sdk with your public keys.

Parameters

Parameter Type Description
application android.app.Application The Android Application instance
apiKey String The public API key from your Ravelin dashboard
rsaKey String The public RSA key from your Ravelin dashboard
callback com.ravelin.sdk.RavelinCallback Optional the callback to trigger with a null safe instance of the class or an error message

Return value

A nullable singleton instance of the class


encryptCard(cardDetails: CardDetails, callback: RavelinCallback? = null) : EncryptedCard?

Encrypt the provided CardDetails.

Parameters

Parameter Type Description
cardDetails com.ravelin.sdk.models.CardDetails The CardDetails to encrypt
callback com.ravelin.sdk.RavelinCallback Optional the callback to trigger with a null safe EncryptedCard instance or an error message

Return value

A nullable encrypted card details


generateFingerprint(callback: RavelinCallback? = null) : Fingerprint?

Generate a new Fingerprint for the device

Parameters

Parameter Type Description
callback com.ravelin.sdk.RavelinCallback Optional the callback to trigger with a null safe Fingerprint instance or an error message

Return value

A nullable device fingerprint

getInstance(callback: RavelinCallback? = null) : RavelinSDK? {

Returns a previously instantiated RavelinSDK instance.

Parameters

Parameter Type Description
callback com.ravelin.sdk.RavelinCallback Optional the callback to trigger with a null safe Fingerprint instance or an error message

Return value

A nullable RavelinSDK


obfuscateInput(value: String): Map {

Takes a given String and replaces digits with 0 and letters with X.

Parameters

Parameter Type Description
value String The input to Obfuscate.

Return value

A map containing the keys pastedValue and panCleaned for the original and obfuscated representations of the input.


Class Properties

Customer ID

Your chosen customer ID

Kotlin

ravelin.customerId

Java

ravelin.getCustomerId();

Temp Customer ID

A temporary customer ID that will always be non-blank. Setting to a null, blank or empty value will generate a new temporary customer ID.

Kotlin

ravelin.tempCustomerId

Java

ravelin.getTempCustomerId();

Order ID

The ID for this specific order.

Kotlin

ravelin.orderId

Java

ravelin.getOrderId();

Session ID

A temporary session ID that will always be non-blank. Setting to a null, blank or empty value will generate a new session ID.

Kotlin

ravelin.sessionId

Java

ravelin.getSessionId();

Bundled Code

Providing this library would not have been possible without the stellar works upon which it relies: