Client SDK

Migration

Migrating from v3 to v4AnchorIcon

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

Why upgrade to v4?AnchorIcon

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

InstallationAnchorIcon

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 SwitchAnchorIcon

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")
val braintreeClient2 = BraintreeClient(this, "TOKENIZATION_KEY_OR_CLIENT_TOKEN", "custom-url-scheme-2")

BraintreeFragmentAnchorIcon

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.

AuthorizationAnchorIcon

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 ProviderAnchorIcon

Below is an example ClientTokenProvider implementation using Retrofit 2.x. This example assumes that you have a server that supports GEThttps://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 HandlingAnchorIcon

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.

HandlingPaymentMethodNonceResultsAnchorIcon

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 CancellationAnchorIcon

When the customer cancels out of a payment flow, a UserCanceledException will be returned in the onFailure listener method.

Handling ErrorsAnchorIcon

Errors will be returned to the callback of the invoked method or the onFailure listener method.

Fetching ConfigurationAnchorIcon

If you need to fetch configuration, use BraintreeClient#getConfiguration(). Previously, this was done via adding a ConfigurationListener to BraintreeFragment.

Builder PatternAnchorIcon

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 ExpressAnchorIcon

The American Express feature is now supported by implementing the following dependencies:
  1. Groovy
dependencies {
    implementation 'com.braintreepayments.api:american-express:4.49.1'
    implementation 'com.braintreepayments.api:card:4.49.1'
}
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()
            }
        }
    }
}

CardAnchorIcon

The Card feature is now supported in a single dependency:
  1. Groovy
dependencies {
    implementation 'com.braintreepayments.api:card:4.49.1'
}
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 CollectorAnchorIcon

The Data Collector feature is now supported in the following dependency:
  1. Groovy
dependencies {
    implementation 'com.braintreepayments.api:data-collector:4.49.1'
}
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 PaymentAnchorIcon

The Local Payment feature is now supported in a single dependency:
  1. Groovy
dependencies {
    implementation 'com.braintreepayments.api:local-payment:4.49.1'
}
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().apply { 
            streetAddress = "836486 of 22321 Park Lake"
            countryCodeAlpha2 = "NL"
            locality = "Den Haag"
            postalCode = "2585 GJ"
        }

        val request = LocalPaymentRequest().apply { 
            paymentType = "ideal"
            amount = "1.01"
            this.address = address
            phone = "639847934"
            email = "joe@getbraintree.com"
            givenName = "Jon"
            surname = "Doe"
            shippingAddressRequired = true
            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 PayAnchorIcon

The Google Pay feature is now supported in a single dependency:
  1. Groovy
dependencies {
    implementation 'com.braintreepayments.api:google-pay:4.49.1'
}
Note: The following wallet enabled metadata tag is now included by the SDK and is no longer required in your AndroidManifest.xml:
  1. XML
undefined
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().apply {
            transactionInfo = TransactionInfo.newBuilder()
                .setTotalPrice("1.00")
                .setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_FINAL)
                .setCurrencyCode("USD")
                .build()
            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
        }
    }
}

PayPalAnchorIcon

The PayPal feature is now supported in a single dependency:
  1. Groovy
dependencies {
    implementation 'com.braintreepayments.api:paypal:4.49.1'
}
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").apply {
            currencyCode = "USD"
            intent = PayPalPaymentIntent.AUTHORIZE
        }
        payPalClient.tokenizePayPalAccount(this, request)
    }

    private fun myTokenizePayPalAccountWithVaultMethod() {
        val request = PayPalVaultRequest().apply {
            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 RequestAnchorIcon

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 PayAnchorIcon

Samsung Pay has been deprecated.
Important
This guide has been deprecated. Please use the pay later offers guide instead.

Secure Remote Commerce (Visa Checkout)AnchorIcon

This feature is not yet supported in v4.

VenmoAnchorIcon

The Venmo feature is now supported in a single dependency:
  1. Groovy
dependencies {
    implementation 'com.braintreepayments.api:venmo:4.49.1'
}
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).apply {
            profileId = "your-profile-id"
            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 SecureAnchorIcon

The 3D Secure feature is now supported in a single dependency:
  1. Groovy
dependencies {
    implementation 'com.braintreepayments.api:three-d-secure:4.49.1'
}
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 'cmVmdGtuOjAxOjIwMzgzMzI5Nzg6Q3U0eUx5Zzl5TDFnZXpQMXpESndSN2tBWHhJ'
        }
    }
}
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().apply {
            number = "378282246310005"
            expirationDate = "12/2022"
        }

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

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

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

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

        threeDSecureClient.performVerification(this, threeDSecureRequest) { result, error ->
            result?.let { ->
                // 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 CustomizationAnchorIcon

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

3DS2 UI CustomizationAnchorIcon

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 VersionAnchorIcon

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

Shipping MethodAnchorIcon

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

If you accept cookies, we’ll use them to improve and customize your experience and enable our partners to show you personalized PayPal ads when you visit other sites. Manage cookies and learn more