Braintree Integration in Android - Kotlin

Let’s learn how to integrate Braintree in Android app with Kotlin. Braintree is a subsidary of PayPal. It focuses in helping businesses of all size and to help maximize business growth. Braintree accepts the following payments:

* Credit Cards
                            * Venmo
                            * Android Pay
                            * Apple Pay
                            * PayPal


Operation Flow:

1. App have to request token from the server. 2. Server have to generate a token using Braintree’s SDK and send that to the requested App. 3. With that Token, app should allow user to pay, which will be validated by Braintree’s server and
then the payment nonce will be received. 4. That received payment nonce and paid amount will be sent to our own server. There the payment will be checked whether its successful or not and the response will be sent to the app.

Create Braintree Sandbox Account:

(a) Go to https://www.braintreepayments.com/sandbox and signup with your details and proceed with login once setup.










(b) After login, you will get Merchant ID, Public Key and Private Key. You can make a note of it to use those credentials.(For Server)




To enable PayPal as payment option:

(a) Navigate to Account → My User




(b) Then click on Enable button below Login with PayPal.



(c) You will be prompted  to enter and login the PayPal account, you would like to be linked. Once done, It will be integrated to use with BrainTree.






Code

Let’s start with gradle. Beware that, BrainTree requires minSdkVersion as 16.


build.gradle



apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
   compileSdkVersion 26
   defaultConfig {
       applicationId "com.developer.braintreepay"
       minSdkVersion 16
       targetSdkVersion 26
       versionCode 1
       versionName "1.0"
       testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
   }
   buildTypes {
       release {
           minifyEnabled false
           proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
       }
   }
}


dependencies {
   implementation fileTree(dir: 'libs', include: ['*.jar'])
   implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
   implementation 'com.android.support:appcompat-v7:26.0.0'
   implementation 'com.android.support.constraint:constraint-layout:1.0.2'
   testImplementation 'junit:junit:4.12'
   androidTestImplementation 'com.android.support.test:runner:1.0.1'
   androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'


   compile 'com.braintreepayments.api:drop-in:3.1.0'
   compile 'com.loopj.android:android-async-http:1.4.9'


}


AndroidManifest.xml:



<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.developer.braintreepay">

   <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/AppTheme">
       <activity android:name=".MainActivity">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />


               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
       </activity>
<!-- This is required for PayPal, since the page will be opened in Brower -->
       <activity android:name="com.braintreepayments.api.BraintreeBrowserSwitchActivity"
           android:launchMode="singleTask">
           <intent-filter>
               <action android:name="android.intent.action.VIEW" />
               <category android:name="android.intent.category.DEFAULT" />
               <category android:name="android.intent.category.BROWSABLE" />
               <data android:scheme="${applicationId}.braintree" />
           </intent-filter>
       </activity>

   </application>

</manifest>


activity_main.xml:



<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context="com.developer.braintreepay.MainActivity">


   <TextView
       android:id="@+id/price"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Price : $10"
       android:padding="10dp"
       android:textAppearance="@android:style/TextAppearance.Medium"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent" />


   <TextView
       android:id="@+id/pay"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="Pay Now"
       android:background="@color/colorPrimary"
       android:padding="15dp"
       android:gravity="center"
       android:textColor="@color/white"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"/>


</android.support.constraint.ConstraintLayout>


MainActivity.kt:

First, the api should be called to get the client token from Server. After that, the payment can be done either by using PayPal or credit/debit cards. Paypal payment will be redirected to browser.


Also note that,  


public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)


here the parameter,  data: Intent should be declared with '?'. Otherwise, it will throw exception 'Parameter specified as non-null is null for data in onActivityResult'.


class MainActivity : AppCompatActivity() {

   val API_URL = "YOUR_API_URL" // Replace your url here
   val REQUEST_CODE = 101
   var token = ""


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


       getTokenFromServer()


       pay.setOnClickListener {
           if (!token.isEmpty())
               payNow()
       }
   }


   private fun getTokenFromServer() {
       val androidClient = AsyncHttpClient()
       androidClient.get(API_URL, object : TextHttpResponseHandler() {
           override fun onFailure(statusCode: Int, headers: Array<Header>, responseString: String, throwable: Throwable) {
               Log.v("Failure!", responseString)
           }


           override fun onSuccess(statusCode: Int, headers: Array<Header>, responseToken: String) {
               Log.d("Success!!", "Client Token== " + responseToken)
               token = responseToken
           }
       })
   }


   private fun payNow() {
       val dropInRequest = DropInRequest().clientToken(token)
               .amount("$10.00") // Here you can pass Amount to be paid.
       startActivityForResult(dropInRequest.getIntent(this), REQUEST_CODE)
   }


   public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
       super.onActivityResult(requestCode, resultCode, data)
       Log.v("resultCode","resultCode=="+resultCode);
       if (requestCode == REQUEST_CODE) {
           when (resultCode) {
               Activity.RESULT_OK -> {
                   val result = data!!.getParcelableExtra<DropInResult>(DropInResult. EXTRA_DROP_IN_RESULT)
                   val paymentMethodNonce = result.paymentMethodNonce!!.nonce
                   Log.v("PayNonce", "PayNonce==" + paymentMethodNonce)
                   Toast.makeText(this@MainActivity,"Payment Success!", LENGTH_SHORT). show()
                   // Send paymentMethodNonce to your server here..
               }


               Activity.RESULT_CANCELED -> Log.v("Cancelled", "Cancelled!!")


               else -> {
                   Log.v("Error", "Error!! ")

                   val error = data!!.getSerializableExtra(DropInActivity.EXTRA_ERROR) as Exception
               }
           }
       }
   }
}


Sample Inputs:

You can use the sample data for testing. 
  • Token : "eyJ2ZXJzaW9uIjoyLCJhdXRob3JpemF0aW9uRmluZ2VycHJpbnQiOiJjMDhiZmQzOGFlYmZkYTcyYjY4MWY0NWFhNTNkZjMyMGQ0Nzg4Zjk0OGUxNTdlNGJlZDY2YjZjNzE4NWJlZWYzfGNyZWF0ZWRfYXQ9MjAxNy0xMi0yMFQxMzowMjozNy4yNjIxODM5MzcrMDAwMFx1MDAyNm1lcmNoYW50X2lkPTM0OHBrOWNnZjNiZ3l3MmJcdTAwMjZwdWJsaWNfa2V5PTJuMjQ3ZHY4OWJxOXZtcHIiLCJjb25maWdVcmwiOiJodHRwczovL2FwaS5zYW5kYm94LmJyYWludHJlZWdhdGV3YXkuY29tOjQ0My9tZXJjaGFudHMvMzQ4cGs5Y2dmM2JneXcyYi9jbGllbnRfYXBpL3YxL2NvbmZpZ3VyYXRpb24iLCJjaGFsbGVuZ2VzIjpbXSwiZW52aXJvbm1lbnQiOiJzYW5kYm94IiwiY2xpZW50QXBpVXJsIjoiaHR0cHM6Ly9hcGkuc2FuZGJveC5icmFpbnRyZWVnYXRld2F5LmNvbTo0NDMvbWVyY2hhbnRzLzM0OHBrOWNnZjNiZ3l3MmIvY2xpZW50X2FwaSIsImFzc2V0c1VybCI6Imh0dHBzOi8vYXNzZXRzLmJyYWludHJlZWdhdGV3YXkuY29tIiwiYXV0aFVybCI6Imh0dHBzOi8vYXV0aC52ZW5tby5zYW5kYm94LmJyYWludHJlZWdhdGV3YXkuY29tIiwiYW5hbHl0aWNzIjp7InVybCI6Imh0dHBzOi8vY2xpZW50LWFuYWx5dGljcy5zYW5kYm94LmJyYWludHJlZWdhdGV3YXkuY29tLzM0OHBrOWNnZjNiZ3l3MmIifSwidGhyZWVEU2VjdXJlRW5hYmxlZCI6dHJ1ZSwicGF5cGFsRW5hYmxlZCI6dHJ1ZSwicGF5cGFsIjp7ImRpc3BsYXlOYW1lIjoiQWNtZSBXaWRnZXRzLCBMdGQuIChTYW5kYm94KSIsImNsaWVudElkIjpudWxsLCJwcml2YWN5VXJsIjoiaHR0cDovL2V4YW1wbGUuY29tL3BwIiwidXNlckFncmVlbWVudFVybCI6Imh0dHA6Ly9leGFtcGxlLmNvbS90b3MiLCJiYXNlVXJsIjoiaHR0cHM6Ly9hc3NldHMuYnJhaW50cmVlZ2F0ZXdheS5jb20iLCJhc3NldHNVcmwiOiJodHRwczovL2NoZWNrb3V0LnBheXBhbC5jb20iLCJkaXJlY3RCYXNlVXJsIjpudWxsLCJhbGxvd0h0dHAiOnRydWUsImVudmlyb25tZW50Tm9OZXR3b3JrIjp0cnVlLCJlbnZpcm9ubWVudCI6Im9mZmxpbmUiLCJ1bnZldHRlZE1lcmNoYW50IjpmYWxzZSwiYnJhaW50cmVlQ2xpZW50SWQiOiJtYXN0ZXJjbGllbnQzIiwiYmlsbGluZ0FncmVlbWVudHNFbmFibGVkIjp0cnVlLCJtZXJjaGFudEFjY291bnRJZCI6ImFjbWV3aWRnZXRzbHRkc2FuZGJveCIsImN1cnJlbmN5SXNvQ29kZSI6IlVTRCJ9LCJtZXJjaGFudElkIjoiMzQ4cGs5Y2dmM2JneXcyYiIsInZlbm1vIjoib2ZmIn0="



  • Card: 4111 1111 1111 1111



Run Application:


Comments

  1. What is this API _ URL here

    ReplyDelete
    Replies
    1. That's our server url, where we configured the braintree to generate Access Token.

      Delete
  2. Hi, I tried your tutorial their is no error in the project but nothing happened in the logcat I found that the token has pitched but still not working. pls do help

    ReplyDelete
  3. Hi, Can't Able to add Google Pay option.
    Also enable in braintree. Still getting only two option
    1) Paypal
    2) Credit or Debit Card
    I want Google Pay also in my app,

    val googlePaymentRequest = GooglePaymentRequest()
    .transactionInfo(
    TransactionInfo.newBuilder()
    .setTotalPrice("1.00")
    .setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_FINAL)
    .setCurrencyCode("USD")
    .build()
    )
    .billingAddressRequired(true)

    val dropInRequest = DropInRequest().clientToken(getString(R.string.paypal_token))
    .googlePaymentRequest(googlePaymentRequest)

    ReplyDelete
  4. working fine in Card payment
    but the paypal option is not working

    ReplyDelete

Post a Comment

Popular posts from this blog

SOAP Client using ksoap2 in Android - Kotlin

RecyclerView with different number of columns using SpanSizeLookup

Using Camera in Android - Kotlin

Databinding in RecyclerView - Android - Kotlin

Map, Location update and AutoComplete Places - Kotlin

Room with LiveData, ViewModel - Android Architecture Components - Kotlin

Stripe Integration in Android - Kotlin

Android JetPack - Scheduling Tasks with WorkManager

Using RxJava, Retrofit in Android - Kotlin