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* Credit Cards
* 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.
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
What is this API _ URL here
ReplyDeleteThat's our server url, where we configured the braintree to generate Access Token.
DeleteHi, 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
ReplyDeleteHi, Can't Able to add Google Pay option.
ReplyDeleteAlso 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)
working fine in Card payment
ReplyDeletebut the paypal option is not working