Client SDK

Migrationanchor

Migrating from v3 to v4anchor

In Android v4, we decoupled the Braintree SDK from Android to offer more integration flexibility.

Why upgrade to v4?anchor

  • First class Kotlin support
  • Better support for resource-constrained Android devices
  • Simplified Pay with PayPal integration
  • Increased modularity

Installationanchor

The features of the Braintree SDK are now organized into modules and can each be imported as dependencies in your build.gradle file. You must remove the com.braintreepayments.api:braintree:3.x.x dependency when migrating to v4.

The examples below show the required dependencies for each feature.

Browser Switchanchor

In v3, com.braintreepayments.api.BraintreeBrowserSwitchActivity was the designated deep link destination activity maintained by the Braintree SDK. In v4, we've removed BraintreeBrowserSwitchActivity to give apps more control over their deep link configuration.

In the AndroidManifest.xml, migrate the intent-filter from your v3 integration into an activity you own:

  1. XML
<activity android:name="com.company.app.MyPaymentsActivity"
    android:exported="true">
    ...
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <data android:scheme="${applicationId}.braintree"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
    </intent-filter>
</activity>
note

You must also add android:exported to your Activity with the intent-filter if your app compile SDK version is API 31 (Android 12) or later.

If your app has multiple browser switch targets, you can specify multiple intent filters and use the BraintreeClient constructor that allows you to specify a customUrlScheme:

  1. Xml
<activity android:name="com.company.app.MyPaymentsActivity1"
    android:exported="true">
    ...
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <data android:scheme="custom-url-scheme-1"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
    </intent-filter>
</activity>

<activity android:name="com.company.app.MyPaymentsActivity2"
    android:exported="true">
    ...
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <data android:scheme="custom-url-scheme-2"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
    </intent-filter>
</activity>

Then when constructing your BraintreeClient make sure to pass the appropriate custom url scheme for each deep link target Activity:

  1. Kotlin
  2. Java
// MyPaymentsActivity1.kt
val braintreeClient1 = BraintreeClient(this, "TOKENIZATION_KEY_OR_CLIENT_TOKEN", "custom-url-scheme-1")

// MyPaymentsActivity1.kt
val braintreeClient2 = BraintreeClient(this, "TOKENIZATION_KEY_OR_CLIENT_TOKEN", "custom-url-scheme-2")

BraintreeFragmentanchor

BraintreeFragment has been replaced by a Client for each respective payment feature. See the below payment method sections for examples of instantiating and using the feature clients.

Authorizationanchor

A BraintreeClient is needed to construct a feature client for any payment method.

When creating a BraintreeClient, you can provide a tokenization key or a ClientTokenProvider. When given a ClientTokenProvider, the SDK will fetch a client token on your behalf when it is needed. This makes it possible to construct a BraintreeClient instance using client token authorization in onCreate.

The example below shows the initialization with a tokenization key authorization:

  1. Kotlin
  2. Java
class MyActivity : AppCompatActivity() {

  private lateinit var braintreeClient: BraintreeClient

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    braintreeClient = BraintreeClient(this, "<#TOKENIZATION_KEY#>")
  }
}

Client Token Provideranchor

Below is an example ClientTokenProvider implementation using Retrofit 2.x. This example assumes that you have a server that supports GET https://www.my-api.com/client_token and receives the following json response:

  1. JSON
{
  "value": "<CLIENT_TOKEN>"
}
  1. Kotlin
  2. Java
// ClientToken.kt
data class ClientToken(val value: String = "")

// Api.kt
interface Api {

    @GET("/client_token")
    fun getClientToken(): Call<ClientToken>
}

// ExampleClientTokenProvider.kt
internal class ExampleClientTokenProvider : ClientTokenProvider {
    override fun getClientToken(callback: ClientTokenCallback) {
        val call = createService().getClientToken()
        call.enqueue(object: Callback<ClientToken> {
            override fun onResponse(call: Call<ClientToken>?, response: Response<ClientToken>?) {
                response?.body()?.value?.let {
                    callback.onSuccess(it)
                }
            }

            override fun onFailure(call: Call<ClientToken>?, t: Throwable?) {
                callback.onFailure(Exception(t))
            }

        })
    }

    companion object {
        private val builder = Retrofit.Builder()
            .baseUrl("https://my-api.com")
            .addConverterFactory(GsonConverterFactory.create())
        private val httpClient = OkHttpClient.Builder()
        fun createService(): Api {
            builder.client(httpClient.build())
            val retrofit = builder.build()
            return retrofit.create(Api::class.java)
        }
    }
}

In an Activity or Fragment, create an instance of BraintreeClient using your ClientTokenProvider:

  1. Kotlin
  2. Java
class ExampleActivity : AppCompatActivity() {

    private var braintreeClient: BraintreeClient? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        braintreeClient = BraintreeClient(this, ExampleClientTokenProvider())
    }
}

Event Handlinganchor

In v3, there were several interfaces that would be called when events occurred: PaymentMethodNonceCreatedListener, ConfigurationListener, BraintreeCancelListener, and BraintreeErrorListener. In v4, these listeners have been replaced by payment method specific listeners for payment flows that require an app or browser switch, and callbacks for non-switching methods.

Handling PaymentMethodNonce Resultsanchor

For payment methods that do not require leaving the application, the result will be returned via the callback passed into the tokenization method. For example, using the CardClient:

  1. Kotlin
  2. Java
cardClient.tokenize(card) { cardNonce, error ->
  // send cardNonce.string to your server or handle error
}

For payment methods that require a browser switch or an app switch, the result will be returned to the payment method specific listener. For example, using the PayPalClient and PayPalListener:

  1. Kotlin
  2. Java
class PayPalActivity : AppCompatActivity(), PayPalListener {
  override fun onPayPalSuccess(payPalAccountNonce: PayPalAccountNonce) {
    // send nonce to server
  }

  override fun onPayPalFailure(error: Exception) {
    if (error is UserCanceledException) {
      // user canceled 
    else {
      // handle error
    } 
  }
}

Handling Cancellationanchor

When the customer cancels out of a payment flow, a UserCanceledException will be returned in the on<PaymentMethod>Failure listener method.

Handling Errorsanchor

Errors will be returned to the callback of the invoked method or the on<PaymentMethod>Failure listener method.

Fetching Configurationanchor

If you need to fetch configuration, use BraintreeClient#getConfiguration().

Previously, this was done via adding a ConfigurationListener to BraintreeFragment.

Builder Patternanchor

The builder pattern has been removed in v4 to allow for consistent object creation across Java and Kotlin. Classes have been renamed without the Builder postfix, method chaining has been removed, and setters have been renamed with the set prefix.

For example, CardBuilder in v3 becomes Card in v4:

  1. Kotlin
  2. Java
val card = Card()
card.number = "4111111111111111"
card.expirationDate = "12/2022"

Builder classes that have been renamed:

  • CardBuilder is now Card

American Expressanchor

The American Express feature is now supported by implementing the following dependencies:

  1. Groovy
dependencies {
  implementation 'com.braintreepayments.api:american-express:4.39.0'
  implementation 'com.braintreepayments.api:card:4.39.0'
}

To use the feature, instantiate an AmericanExpressClient:

  1. Kotlin
  2. Java
class MyActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    braintreeClient = BraintreeClient(this, "<#CLIENT_AUTHORIZATION#>")
    americanExpressClient = AmericanExpressClient(braintreeClient)

    // you will also need a card client for tokenization in this example
    cardClient = CardClient(braintreeClient)
  }

  private fun tokenizeCard() {
    val card = Card()
    card.number = "378282246310005"
    card.expirationDate = "12/2022"

    cardClient.tokenize(card) { cardNonce, error ->
      cardNonce?.let {
        getAmexRewardsBalance(it)
    }
  }

  private fun getAmexRewardsBalance(cardNonce: CardNonce) {
    val nonceString = cardNonce.string
    americanExpressClient.getRewardsBalance(nonceString, "USD") { rewardsBalance, error ->
      rewardsBalance?.let {
        // display rewards amount to user
        val rewardsAmount = it.getRewardsAmount()
      }
    }
  }
}

Cardanchor

The Card feature is now supported in a single dependency:

  1. Groovy
dependencies {
  implementation 'com.braintreepayments.api:card:4.39.0'
}

To use the feature, instantiate a CardClient:

  1. Kotlin
  2. Java
class MyActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    braintreeClient = BraintreeClient(this, "<#CLIENT_AUTHORIZATION#>")
    cardClient = CardClient(braintreeClient)
  }

  private fun tokenizeCard() {
    val card = Card()
    card.number = "4111111111111111"
    card.expirationDate = "12/2022"

    cardClient.tokenize(card) { cardNonce, error ->
      cardNonce?.let {
        // send it.string to your server
      }
    }
  }
}

Data Collectoranchor

The Data Collector feature is now supported in the following dependency:

  1. Groovy
dependencies {
  implementation 'com.braintreepayments.api:data-collector:4.39.0'
}

To use the feature, instantiate a DataCollector:

  1. Kotlin
  2. Java
class MyActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    braintreeClient = BraintreeClient(this, "<#CLIENT_AUTHORIZATION#>")
    dataCollector = DataCollector(braintreeClient)
  }

  private fun collectDeviceData() {
    dataCollector.collectDeviceData(this) { deviceData, error ->
      // send deviceData to your server
    }
  }
}

Local Paymentanchor

The Local Payment feature is now supported in a single dependency:

  1. Groovy
dependencies {
  implementation 'com.braintreepayments.api:local-payment:4.39.0'
}

To use the feature, instantiate a LocalPaymentClient:

  1. Kotlin
  2. Java
class MyActivity : AppCompatActivity(), LocalPaymentListener {
  
  private lateinit var braintreeClient: BraintreeClient
  private lateinit var localPaymentClient: LocalPaymentClient

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    braintreeClient = BraintreeClient(this, ExampleClientTokenProvider())
    localPaymentClient = LocalPaymentClient(this, braintreeClient)
    localPaymentClient.setListener(this)
  }

  override fun onNewIntent(newIntent: Intent?) {
    super.onNewIntent(newIntent)
    // required if your activity's launch mode is "singleTop", "singleTask", or "singleInstance"
    intent = newIntent
  }

  private fun startLocalPayment() {
    val address = PostalAddress()
    address.streetAddress = "836486 of 22321 Park Lake"
    address.countryCodeAlpha2 = "NL"
    address.locality = "Den Haag"
    address.postalCode = "2585 GJ"

    val request = LocalPaymentRequest()
    request.paymentType = "ideal"
    request.amount = "1.01"
    request.address = address
    request.phone = "639847934"
    request.email = "joe@getbraintree.com"
    request.givenName = "Jon"
    request.surname = "Doe"
    request.shippingAddressRequired = true
    request.currencyCode = "EUR"

    localPaymentClient.startPayment(request) { localPaymentTransaction, error ->
      localPaymentTransaction?.let { transaction ->
        // do any pre-processing transaction.paymentId
        localPaymentClient.approvePayment(this@MyActivity, transaction)
      }
    }
  }
  override fun onLocalPaymentSuccess(paymentMethod: PaymentMethodNonce) {
    // send nonce to server
  }

  override fun onLocalPaymentFailure(error: Exception) {
    if (error is UserCanceledException) {
      // user canceled
    } else {
      // handle error
    }
  }
}

Google Payanchor

The Google Pay feature is now supported in a single dependency:

  1. Groovy
dependencies {
  implementation 'com.braintreepayments.api:google-pay:4.39.0'
}

Note: The following wallet enabled metadata tag is now included by the SDK and is no longer required in your AndroidManifest.xml:

  1. XML
<meta-data android:name="com.google.android.gms.wallet.api.enabled" android:value="true"/>

To use the feature, instantiate a GooglePayClient:

  1. Kotlin
  2. Java
class MyActivity : AppCompatActivity(), GooglePayListener {

  private lateinit var braintreeClient: BraintreeClient
  private lateinit var googlePayClient: GooglePayClient

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    braintreeClient = BraintreeClient(this, ExampleClientTokenProvider())
    googlePayClient = GooglePayClient(this, braintreeClient)
    googlePayClient.setListener(this)
  }

  private fun checkIfGooglePayIsAvailable() {
    googlePayClient.isReadyToPay(this) { isReadyToPay, error ->
      if (isReadyToPay) {
        // Google Pay is available
    }
  }

  private fun makeGooglePayRequest() {
    val googlePayRequest = GooglePayRequest()
    googlePayRequest.transactionInfo = TransactionInfo.newBuilder()
      .setTotalPrice("1.00")
      .setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_FINAL)
      .setCurrencyCode("USD")
      .build()
    googlePayRequest.billingAddressRequired = true

    googlePayClient.requestPayment(this, googlePayRequest) 
  }

  override fun onGooglePaySuccess(paymentMethodNonce PaymentMethodNonce) {
    // send nonce to server
  }

  override fun onGooglePayFailure(error: Exception) {
    if (error is UserCanceledException) {
      // user canceled
    } else {
      // handle error
    }
  }
}

PayPalanchor

The PayPal feature is now supported in a single dependency:

  1. Groovy
dependencies {
  implementation 'com.braintreepayments.api:paypal:4.39.0'
}

To use the feature, instantiate a PayPalClient:

  1. Kotlin
  2. Java
class MyActivity : AppCompatActivity(), PayPalListener {

  private lateinit var braintreeClient: BraintreeClient
  private lateinit var payPalClient: PayPalClient

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    braintreeClient = BraintreeClient(this, ExampleClientTokenProvider())
    payPalClient = PayPalClient(this, braintreeClient)
    payPalClient.setListener(this)
  }

  override fun onNewIntent(newIntent: Intent?) {
    super.onNewIntent(newIntent)
    // required if your activity's launch mode is "singleTop", "singleTask", or "singleInstance"
    intent = newIntent
  }

  private fun myTokenizePayPalAccountWithCheckoutMethod() {
    val request = PayPalCheckoutRequest("1.00")
    request.currencyCode = "USD"
    request.intent = PayPalPaymentIntent.AUTHORIZE

    payPalClient.tokenizePayPalAccount(this, request) 
  }

  private fun myTokenizePayPalAccountWithVaultMethod() {
    val request = PayPalVaultRequest()
    request.billingAgreementDescription = "Your agreement description"

    payPalClient.tokenizePayPalAccount(this, request) 
  }

  override fun onPayPalSuccess(payPalAccountNonce: PayPalAccountNonce) {
    // send nonce to server
  }

  override fun onPayPalFailure(error: Exception) {
    if (error is UserCanceledException) {
      // user canceled
    } else {
      // handle error
    }
  }
}

PayPal Requestanchor

v4 introduces two subclasses of PayPalRequest:

  • PayPalCheckoutRequest, for checkout flows
  • PayPalVaultRequest, for vault flows

The setters on the request classes have been updated to remove method chaining.

The requestOneTimePayment and requestBillingAgreement methods on PayPalClient have been updated to expect instances of PayPalCheckoutRequest and PayPalVaultRequest, respectively.

However, requestOneTimePayment and requestBillingAgreement have been deprecated in favor of tokenizePayPalAccount.

Samsung Payanchor

This feature is not yet supported in v4.

Secure Remote Commerce (Visa Checkout)anchor

This feature is not yet supported in v4.

Venmoanchor

The Venmo feature is now supported in a single dependency:

  1. Groovy
dependencies {
  implementation 'com.braintreepayments.api:venmo:4.39.0'
}

To use the feature, instantiate a VenmoClient:

  1. Kotlin
  2. Java
class MyActivity : AppCompatActivity(), VenmoListener {

  private lateinit var braintreeClient: BraintreeClient
  private lateinit var venmoClient: VenmoClient

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    braintreeClient = BraintreeClient(this, ExampleClientTokenProvider())
    venmoClient = VenmoClient(this, braintreeClient)
    venmoClient.setListener(this)
  }

  // The authorizeAccount() method has been replaced with tokenizeVenmoAccount()
  private fun tokenizeVenmoAccount() {
    val request = VenmoRequest(VenmoPaymentMethodUsage.MULTI_USE)
    request.profileId = "your-profile-id"
    request.shouldVault = false

    venmoClient.tokenizeVenmoAccount(this, request) 
  }

  override fun onVenmoSuccess(venmoAccountNonce: VenmoAccountNonce) {
    // send nonce to server
  }

  override fun onVenmoFailure(error: Exception) {
    if (error is UserCanceledException) {
      // user canceled
    } else {
      // handle error
    }
  }

}

3D Secureanchor

The 3D Secure feature is now supported in a single dependency:

  1. Groovy
dependencies {
  implementation 'com.braintreepayments.api:three-d-secure:4.39.0'
}

Additionally, add the following Maven repository and (non-sensitive) credentials to your app-level gradle:

  1. Groovy
repositories {
    maven {
        url "https://cardinalcommerceprod.jfrog.io/artifactory/android"
        credentials {
            username 'braintree_team_sdk'
            password 'AKCp8jQcoDy2hxSWhDAUQKXLDPDx6NYRkqrgFLRc3qDrayg6rrCbJpsKKyMwaykVL8FWusJpp'
        }
    }
}

To use the feature, instantiate a ThreeDSecureClient:

  1. Kotlin
  2. Java
class MyActivity : AppCompatActivity(), ThreeDSecureListener {

  private lateinit var braintreeClient: BraintreeClient
  private lateinit var threeDSecureClient: ThreeDSecureClient

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    braintreeClient = BraintreeClient(this, ExampleClientTokenProvider())
    threeDSecureClient = ThreeDSecureClient(this, braintreeClient)
    threeDSecureClient.setListener(this)

    // you will also need a card client for tokenization in this example
    cardClient = CardClient(braintreeClient)
  }

  override fun onNewIntent(newIntent: Intent?) {
    super.onNewIntent(newIntent)
    // required if your activity's launch mode is "singleTop", "singleTask", or "singleInstance"
    intent = newIntent
  }

  private fun tokenizeCard() {
    val card = Card()
    card.number = "378282246310005"
    card.expirationDate = "12/2022"

    cardClient.tokenize(card) { cardNonce, error ->
      cardNonce?.let {
        performThreeDSecureVerification(cardNonce)
      }
    }
  }

  private fun performThreeDSecureVerification(cardNonce: CardNonce) {
    ThreeDSecurePostalAddress billingAddress = new ThreeDSecurePostalAddress()
    billingAddress.givenName = "Jill"
    billingAddress.surname = "Doe"
    billingAddress.phoneNumber = "5551234567"
    billingAddress.streetAddress = "555 Smith St"
    billingAddress.extendedAddress = "#2"
    billingAddress.locality = "Chicago"
    billingAddress.region = "IL"
    billingAddress.postalCode = "12345"
    billingAddress.countryCodeAlpha2 = "US"

    val additionalInformation = ThreeDSecureAdditionalInformation()
    additionalInformation.accountId = "account-id"

    val threeDSecureRequest = ThreeDSecureRequest()
    threeDSecureRequest.amount = "10"
    threeDSecureRequest.email = "test@email.com"
    threeDSecureRequest.billingAddress = billingAddress
    threeDSecureRequest.nonce = cardNonce.string
    threeDSecureRequest.shippingMethod = ThreeDSecureShippingMethod.GROUND
    threeDSecureRequest.additionalInformation = additionalInformation

    threeDSecureClient.performVerification(this, threeDSecureRequest) { result, error ->
      result?.let { -> threeDSecureResult
        // examine lookup response (if necessary), then continue verification
        threeDSecureClient.continuePerformVerification(this@MyActivity, threeDSecureRequest, threeDSecureResult)
      }
    }
  }

  override fun onThreeDSecureSuccess(threeDSecureResult: ThreeDSecureResult) {
    // send this nonce to your server
    val nonce = threeDSecureResult.tokenizedCard?.string  
  }

  override fun onThreeDSecureFailure(error: Exception) {
    if (error is UserCanceledException) {
      // user canceled
    } else {
      // handle error
    }
  }
}

3DS1 UI Customizationanchor

The ThreeDSecureV1UiCustomization class setters have been updated to remove method chaining and follow standard Java getter/setter pattern.

3DS2 UI Customizationanchor

On ThreeDSecureRequest the uiCustomization property was replaced with v2UiCustomization of type ThreeDSecureV2UiCustomization. For 3DS2 UI customization, use the following new classes:

  • ThreeDSecureV2UiCustomization
  • ThreeDSecureV2ButtonCustomization
  • ThreeDSecureV2LabelCustomization
  • ThreeDSecureV2TextBoxCustomization
  • ThreeDSecureV2ToolbarCustomization

Default 3DS Versionanchor

Previously, the versionRequested property on ThreeDSecureRequest defaulted to VERSION_1. It now defaults to VERSION_2.

Shipping Methodanchor

The shippingMethod property on ThreeDSecureRequest is now an enum rather than a string. Possible values:

  • SAME_DAY
  • EXPEDITED
  • PRIORITY
  • GROUND
  • ELECTRONIC_DELIVERY
  • SHIP_TO_STORE