Requirements

Installation

Add io.elements.pay:core-module to your build.gradle dependencies.

Gradle

dependencies {
    implementation 'io.elements.pay:core-module:1.0.7'
}

Usage

ElementsApiClient

The ElementsApiClient handles the low level api communications to Elements server. i.e. Card Tokenization

Initialize the API client.

The api client requires clientToken fetched from your backend server. Once you have obtained your clientToken you can initialize the api client in the following way.

// Environment -> The environment that ElementsApiClient runs.
// StripeTestKey -> Optional if you want to take fall back to Stripe tokenization
// if elements tokenization failed
val configuration = ElementsApiClientConfiguration(
  Environment.sandbox(clientToken)
)
val elementsApiClient = ElementsApiClient(configuration = configuration)

In order to call the tokenize api, you need to create and pass in ElementsCardParams object, example:

val cardParams = ElementsCardParams(
  cardNumber = "424242424242",
  expiryMonth = 4,
  expiryYear = 24,
  securityCode = "242",
  holderName = "Test"
)

Once you have created the ElementsAPIClient and ElementsCardParams you can call the following to tokenize a card.

elementsApiClient.tokenizeCard(cardParams, callback = object : ApiResultCallback<VaultToken> {
  override fun onSuccess(result: VaultToken) {
    Log.d("Your Logger Tag", "Tokenization succeeded $result")
    result.elementsToken?.let {
      // Now you can pass this token object to your backend server
    }
    result.fallbackStripToken?.let { 
      // If Elements tokenization failed, a stripe token will be generated if you have provided the `stripePublishableKey`
      // You can pass the stripe key to your backend server as a fall back.
    }
  }

  override fun onError(e: Exception) {
    // Check ElementsException for tokenization failure.
    Log.d("Your Logger Tag", "Tokenization failed $e")
  }
})

ElementsToken

The ElementsToken model returns the response received from Elements server once tokenization succeeded. It contains the corresponded elements token matching the ElementsCardParams you passed in the method. It will also contain an ElementsCard object that has the tokenized card info.

ElementsToken(
  id = tok_xxxxxxxxxxxxxxxx
  card = ElementsCard(
    id = card-9a06775f-d4d7-4413-8fed-379ac610283e, 
    last4 = 4242, 
    expiryMonth= 4,
    expiryYear = 2024, 
    brand = VISA, 
    fingerprint = mhahtPiD6Yns0Nnyn82206DG1l17mxYIedFkWuQ6GUg=
  )
)

3DS2 Flow

ElementsAPIClient also supports tokenize card with 3DS2 auth flow enabled. In order to handle 3DS2 flow correctly you need to pass an activity param to the tokenization method. This activity will be the host of all 3DS flow activities from Elements.

private fun tokenizeCard(cardParams: ElementsCardParams) {
    client.tokenizeCard(cardParams, this, callback = object : ApiResultCallback<VaultToken> {
        override fun onSuccess(result: VaultToken) {
            result.elementsToken?.let {
                Log.d("Tokenization succeeded", parseElementsTokenToDisplayString(it)).show()
            }
            result.fallbackStripToken?.let {
                Log.d("Tokenization succeeded", "Stripe token $it").show()
            }
        }

        override fun onError(e: Exception) {
            Log.d("Your Logger Tag", "Tokenization failed $e")
        }
    })
}

You will also need to listen for 3DS result from onActivityResult and obtain result from apiClient.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    client.onTokenizationSetupResult(requestCode, data, object : ApiResultCallback<ElementsToken> {
        override fun onSuccess(result: ElementsToken) {
            Log.d("Your Logger Tag", "Tokenization succeeded $result")
        }

        override fun onError(e: Exception) {
            Log.d("Your Logger Tag", "Tokenization failed $e")
        }
    })
}

Now the token you obtained from onActivityResult will be a token validated through 3DS.

ElementsActionDriver

The ElemenetsActionDriver handles Elements redirect payment methods, i.e. Paypal/Kakao/Klarna etc...

Initialize the action driver.

The action driver requires clientToken fetched from your backend server. Once you have obtained your client key you can initialize the action driver in the following way.

class YourActivity : AppCompatActivity() {
  private val environment = Environment.sandbox(clientToken)
  private lateinit var actionDriver: ElementsActionDriver

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    //...
    actionDriver = ElementsActionDriver(
      this@YourActivity,
      environment,
      WeakReference(this)
    )
    //...
  }
}

Setting up the required components.

Add return URL scheme

  1. You need to config the redirect URL scheme in your application manifest file.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="io.elements.sampleApp">

  <application
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:roundIcon="@mipmap/ic_launcher_round"
      android:supportsRtl="true"
      android:theme="@style/Theme.MyApplication">
      <activity
          android:name=".MainActivity"
          android:exported="true">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />

              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>

        // Add this block into your manifest file
          <intent-filter>
              <action android:name="android.intent.action.VIEW" />

              <category android:name="android.intent.category.DEFAULT" />
              <category android:name="android.intent.category.BROWSABLE" />
            
            // This is important!!
              <data
                  android:host="${applicationId}"
                  android:scheme="elements">
              </data>
          </intent-filter>
      </activity>
  </application>

</manifest>
  1. Handle redirect intent
private fun handleIntent(intent: Intent) {
  when (intent.action) {
    // Redirect response
    Intent.ACTION_VIEW -> {
      val data = intent.data
      Logger.d("Your Logger Tag", "scheme: ${RedirectUtil.REDIRECT_RESULT_SCHEME}")
      if (data != null && data.toString().startsWith(RedirectUtil.REDIRECT_RESULT_SCHEME)) {
        actionDriver.handleRedirectResponse(data)
      } else {
        Logger.e("Your Logger Tag", "Unexpected response from ACTION_VIEW - ${intent.data}")
      }
    }
    else -> {
      Logger.e("Your Logger Tag", "Unable to find action")
    }
  }
}

call handleIntent in your activity

class MainActivity : AppCompatActivity() {
	// ...
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
      
    handleIntent(intent)
  }
  // ...
}

Implement ElementsActionDriver.ActionCompletionListener

// Elements SDK will trigger this function when redirect failed.
override fun elementsActionFailed(error: Exception) {
  Logger.d("Your Logger Tag", "Action failed $error")
}

// Elements SDK will trigger this function when redirect succeeded and returns an element token.
override fun elementsActionSucceeded(chargeToken: String?) {
  Logger.d("Your Logger Tag", "Action succeeded $chargeToken")
}

Start ElementsActionDriver

actionDriver.requestPaymentSetup("vipps") // Starts Vipps payment method
actionDriver.requestPaymentSetup("paypal") // Start Paypal payment method

Example App

Clone this repo and run the app. The demo app demonstrated how to use ElementsApiClient.