Now you can Subscribe using RSS

Submit your Email

Saturday

Complete Braintree Integration in Android Tutorial.

Vishal Shrestha
In this post we will completely integrate Braintree payment in an Android app. We will also create our own PHP server side scripts to fully understand the functioning. If you are not familiar with PHP you don't need to worry, we will just create two PHP methods and it's fairly simple, you will understand it in no time. You can download the full source code at the bottom of this post.
Let's first discuss how Braintree Payments in an App works. There are two parts to it:
1. The Client Side (Android App)
2. The Server Side.

Let's first discuss the client side, the client side includes the following steps:

Braintree-integration-android-client-side
Client Side for Braintree Integration. (Pic Courtesy: Braintree)
  1. Our app requests the token from Server. 
  2. The server generates a token using Braintree's SDK and sends it back to the android app). 
  3. The app then gets the user's credit card info and then contacts the Braintree server to receive a payment nonce.
  4. After the app receives the payment nonce from the Braintree server, it will contact our own server with payment amount and Nonce. (The server then handles payment and has to send information to the app whether the payment was successful of not)
Now in the server side, we do the following tasks:
Braintree integration in Android app server side (Pic courtesy: Braintree)

  1. The server receives token request from the client (Our Android app).
  2. Server generates the token and sends back the token to app.
  3. (Step 4 in image above)The server receives the payment details and nonce from the client app.
  4. (Step 5 in image above)Our server contacts the Braintree server with Nonce and get response if the payment was successful or not.
Now that we have understood how the payment process works, let's start with some actual coding, first let's do the client side.

Steps to Integrate Braintree Payment in Android App (Client Side). 



First of all, let's add dependencies for using Braintree. We will also add dependency for using Volley using which we will create API requests. Add the dependencies in app level Gradle file as shown below. Dependencies are :


          compile 'com.braintreepayments.api:drop-in:3.+
     compile 'com.android.volley:volley:1.0.0'

Braintree-dependency-for-Android-integration

Now let's create layout for the app, we have a simple layout with an EditText where the user will enter the amount to pay and a button to make the payment.

Braintree-android-integration

Here's the code for the layout main_activity.xml.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="16dp">


    <LinearLayout
        android:id="@+id/llHolder"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        android:visibility="gone">

        <TextView
            android:id="@+id/tvMessage"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="Ready for transaction"
            android:textSize="18sp"
            android:textStyle="bold" />

        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="16dp">

            <EditText
                android:id="@+id/etPrice"
                android:layout_width="match_parent"
                android:inputType="text"
                android:imeOptions="actionDone"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:hint="Choose the amount to pay" />

        </android.support.design.widget.TextInputLayout>

        <Button
            android:id="@+id/btnPay"
            android:layout_width="148dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:background="@color/colorPrimaryDark"
            android:text="Pay"
            android:textColor="@android:color/white" />
    </LinearLayout>

</LinearLayout>



Notice that we've added visibility:gone in our nested linear layout as we first want to get the token from server, only then we want to show form to user to make payments as we need token for payments and we cannot make payments through Braintree without tokens. Once we get the token, we set its visibility to visible.

Now we will create the MainActivity.java. Here's the code for it:
Note: As we have not created APIs yet for getting token, we will leave that variable empty for now. After we create APIs we will add our API.
Steps in this Activity:
  1. We first call our API that will get the client token using an Async class(API is created at the end of this post).
  2. Once we've successfully got the client token, we can use that to make payments using Braintree. So we set the visibility of the form to Visibile so that user can enter the amount to pay.
  3. Once user click's the pay button, we present user a DropInForm. DropInForm is Braintree's default form where user need to enter their credit card details. It's Braintree's default implementation to get user card data. Once the user enters his/her details, the details are directly sent to Braintree's server and the server returns a Nonce to the app.
  4. Once the Nonce is received from the Braintree server, we send the Nonce along with the amount that user has entered in the 2nd step to our own server using an API that we'll create in the end of this post.
  5. Finally we receive reply from our server regarding the payment.

public class MainActivity extends AppCompatActivity {

    final int REQUEST_CODE = 1;
    final String get_token = "YOUR-API-TO-GET-TOKEN";
    final String send_payment_details = "YOUR-API-FOR-PAYMENTS";
    String token, amount;
    HashMap<String, String> paramHash;

    Button btnPay;
    EditText etAmount;
    LinearLayout llHolder;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        llHolder = (LinearLayout) findViewById(R.id.llHolder);
        etAmount = (EditText) findViewById(R.id.etPrice);
        btnPay = (Button) findViewById(R.id.btnPay);
        btnPay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onBraintreeSubmit();
            }
        });
        new HttpRequest().execute();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CODE) {
            if (resultCode == Activity.RESULT_OK) {
                DropInResult result = data.getParcelableExtra(DropInResult.EXTRA_DROP_IN_RESULT);
                PaymentMethodNonce nonce = result.getPaymentMethodNonce();
                String stringNonce = nonce.getNonce();
                Log.d("mylog", "Result: " + stringNonce);
                // Send payment price with the nonce
                // use the result to update your UI and send the payment method nonce to your server
                if (!etAmount.getText().toString().isEmpty()) {
                    amount = etAmount.getText().toString();
                    paramHash = new HashMap<>();
                    paramHash.put("amount", amount);
                    paramHash.put("nonce", stringNonce);
                    sendPaymentDetails();
                } else
                    Toast.makeText(MainActivity.this, "Please enter a valid amount.", Toast.LENGTH_SHORT).show();

            } else if (resultCode == Activity.RESULT_CANCELED) {
                // the user canceled
                Log.d("mylog", "user canceled");
            } else {
                // handle errors here, an exception may be available in
                Exception error = (Exception) data.getSerializableExtra(DropInActivity.EXTRA_ERROR);
                Log.d("mylog", "Error : " + error.toString());
            }
        }
    }

    public void onBraintreeSubmit() {
        DropInRequest dropInRequest = new DropInRequest()
                .clientToken(token);
        startActivityForResult(dropInRequest.getIntent(this), REQUEST_CODE);
    }

    private void sendPaymentDetails() {
        RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
        // Request a string response from the provided URL.
        StringRequest stringRequest = new StringRequest(Request.Method.POST, send_payment_details,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        if(response.contains("Successful"))
                        {
                            Toast.makeText(MainActivity.this, "Transaction successful", Toast.LENGTH_LONG).show();
                        }
                        else Toast.makeText(MainActivity.this, "Transaction failed", Toast.LENGTH_LONG).show();
                        Log.d("mylog", "Final Response: " + response.toString());
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.d("mylog", "Volley error : " + error.toString());
            }
        }) {
            @Override
            protected Map<String, String> getParams() {
                if (paramHash == null)
                    return null;
                Map<String, String> params = new HashMap<>();
                for (String key : paramHash.keySet()) {
                    params.put(key, paramHash.get(key));
                    Log.d("mylog", "Key : " + key + " Value : " + paramHash.get(key));
                }

                return params;
            }

            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> params = new HashMap<>();
                params.put("Content-Type", "application/x-www-form-urlencoded");
                return params;
            }
        };
        queue.add(stringRequest);
    }

    private class HttpRequest extends AsyncTask {
        ProgressDialog progress;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            progress = new ProgressDialog(MainActivity.this, android.R.style.Theme_DeviceDefault_Dialog);
            progress.setCancelable(false);
            progress.setMessage("We are contacting our servers for token, Please wait");
            progress.setTitle("Getting token");
            progress.show();
        }

        @Override
        protected Object doInBackground(Object[] objects) {
            HttpClient client = new HttpClient();
            client.get(get_token, new HttpResponseCallback() {
                @Override
                public void success(String responseBody) {
                    Log.d("mylog", responseBody);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, "Successfully got token", Toast.LENGTH_SHORT).show();
                            llHolder.setVisibility(View.VISIBLE);
                        }
                    });
                    token = responseBody;
                }

                @Override
                public void failure(Exception exception) {
                    final Exception ex = exception;
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, "Failed to get token: " + ex.toString(), Toast.LENGTH_LONG).show();
                        }
                    });
                }
            });
            return null;
        }

        @Override
        protected void onPostExecute(Object o) {
            super.onPostExecute(o);
            progress.dismiss();
        }
    }
}

Steps to Integrate Braintree Payment in Android App (Server Side - PHP). 

Getting Braintree API Key Credentials:

To get started with back-end first we need to create a Braintree account. Go to Braintree and create a new account if you don't have one already, then login to the Braintree control panel. Then hover over account on top navigation bar and click on my user. You will see a page similar to this. Then click on view right next to the public key.

Braintree-API-Key-Credentials-for-android

Once you clicked on view you will see a page similar to this, select PHP as the language above the details that are provided. You will need these details in the next step.

Braintree-API-Key-Credentials

Server Side coding in PHP: 


Steps involved in server side development:
Note: I've used  XAMPP for server side functionality. The following will assume you have installed XAMPP.
  1. Download Braintree's PHP SDK, I do it using composer.
  2. Create API for sending token.
  3. Create API for receiving payment details and sending Braintree server's reply.
Let's use composer for downloading Braintree's PHP SDK. Download composer from here: Composer.
After downloading, install composer. Create a folder inside C:\xampp\htdocs. I've named it BraintreePayments. Now create a composer.json file in your root directory (BraintreePayments). Copy this inside the file.

{
  "require" : {
    "braintree/braintree_php" : "3.23.0"
  }
}

Once you copied and saved the code above inside composer.json, Open the terminal in your root directory(Directory with composer.json) and execute composer install. It will download Braintree's PHP SDK and you will finally be ready to code API for you Andoird App. Once everything is finished. Create a new folder inside BraintreePayments, name it include and create a new file braintree_init.php inside it. Copy the following code in it, these are just your API credentials that you will need:


<?php
session_start();
require_once("vendor/autoload.php");
if(file_exists(__DIR__ . "/../.env")) {
    $dotenv = new Dotenv\Dotenv(__DIR__ . "/../");
    $dotenv->load();
}
Braintree_Configuration::environment('sandbox');
Braintree_Configuration::merchantId('YOUR-MERCHAND-IT');
Braintree_Configuration::publicKey('YOUR-PUBLIC-KEY');
Braintree_Configuration::privateKey('YOUR-PRIVATE-KEY');
?>
Check the step above this one to check how to get you API credentials. Once your saved this file, go back to the root directory. Create a new file, call it main.php and write the following code in it:

<?php
require_once ("include/braintree_init.php");
require_once 'vendor/braintree/braintree_php/lib/Braintree.php';
echo ($clientToken = Braintree_ClientToken::generate());
?>


This code simply echos (Displays) the clientToken, but when we call this file from our Android Device we will receive this token and we can use it further for contacting Braintree's server.

After main.php is ready, create a new file name it checkout.php. This file will deal with getting payment amount and Nonce from the client and sending it to the Braintree server. It will receive the reply from Braintree server and we can take steps accordingly.  In this example, we don't parse the reply from server. We will straight away send the response to our App and if it contains the text successful, we will toast payments was successful, or else, payment failed.
Here's the code for checkout.php:
<?php
require_once ("include/braintree_init.php");
require_once 'vendor/braintree/braintree_php/lib/Braintree.php';

$nonce = $_POST['nonce'];
$amount = $_POST['amount'];
$result = Braintree_Transaction::sale([
  'amount' => $amount,
  'paymentMethodNonce' => $nonce,
  'options' => [
    'submitForSettlement' => True
  ]
]);
echo $result;
?>


Finally we are ready, we have created the app and we have also done the server side coding. But to use test this app we need to do one more thing, Start XAMPP and find out you ip address(Type ipconfig in terminal) and set the get_token variable that we created in the MainActivity.java to http://YOURIP/YOUR-ROOT-FOLDER/main.php and set send_payment_details variable to http://YOURIP/YOUR-ROOT-FOLDER/checkout.php. Bravo, we done it. Now build and test you app!

Download full Android app with Braintree Integration: BraintreePayments
Server Side Code: BraintreePaymentsServer.
In case you are wondering what the Braintree's server replies to our server, here's what it looks like, in actual production this needs to be properly parsed by the server, but we are just sending it to our Android app and if it contains the word successful, we show a toast message as Payment successful.

Braintree-reply-for-android-app

Vishal Shrestha / Author & Founder

A developer by profession, a born Adventurer. I mainly do Android but like to get my hands dirty with web development and a little bit of Python. I would't rather go on a Trek than a party and you can find me having a few rounds with the heavy bag to let out the steam ;)

For Business info : My Portfolio Site.

0 comments:

Post a Comment

Coprights @ 2017 | The Code City by Vishal Shrestha Vishal Shrestha