Using Stripe Payments into Android Apps

Introduction

Stripe is a Tech Company, operating in over 25 countries, that allows both private individuals and businesses to accept payments over the Internet. Stripe focuses on providing the technical, fraud prevention, and banking infrastructure required to operate online payment systems.

This tutorial will walk you through creating an example app for Android and on how to set up and do the integration of Stripe API to your Parse Server.

We are going to create a Purchase Button that, when clicked, will make an order to buy one item from the database and do a fake financial transaction via Stripe Payments. You will be guided on how to make the whole process and how to test if the connection to the server and to the Stripe API is working properly.

Prerequisites

To use the Stripe API, your company should be from one of the following operating countries available on Stripe: Countries Available on Stripe.

Before beginning this tutorial, you will need to have Android Studio installed, preferably at the newer version, and some familiarity with Android, Parse Server and Cloud Code development.

You can find tutorials for downloading and setting up Android Studio, Parse Server and Cloud Code in the links below:

Android Studio Official Website

Android Guide With Parse Server

Cloud Code with Back4App

Step 1 — Create a Stripe Account

Go to Stripe Website and create an account. Proceed to fill your information as needed. If it all went well you will end on the main dashboard page.

Stripe main dashboard page. It has informations on how to get started.

After that, you should click on the Activate your Account button in the left. You will be request to verify your email.

Verify email for Stripe button

Then, you need to fill basic contact info about your account. For that, you need to click on Activate your account button again and you will go to the following page:

Account application details that need to be provided forms

Step 2 — Get your Stripe API key

Navigate to the menu at the left, click on API button and you will go to a dashboard that has your Publishable key and your secret key. Click on Reveal test key token to see your secret key. The Publishable key will be the value that is blurred in the image below.

Main API dashboard. Shows your Publishable and your Secret key

 

Step 3 — Setup your Object Class

Go to your Parse dashboard and create a new class called Item with the following columns:

  • ItemName (String)
  • price (Number)
  • quantityAvailable (Number)

Then, create new object with the following information:

  • ItemName: test
  • price: 10
  • quantityAvailable: 5

Parse dashboard on class item

After this is done, create an Order class with the columns:

  • name (String)
  • email (String)
  • address (String)
  • zip (String)
  • city_state (String)
  • item (String)
  • size (Number)
  • fulfilled (Boolean)
  • charged (Boolean)
  • stripePaymentId (String)

Parse dashboard on class Order

If the setup was well done your dashboard menu should look like the one below, with one row in Item class.

Parse dashboard menu

Step 4 — Implement Cloud Code

We need to create a function that purchases an item in the Cloud Code named “purchaseItem” and call it from the Application. The function works by using the item class made in your Parse dashboard.

var Stripe = require("stripe")("your Stripe API Secret Key here");
    Parse.Cloud.define("purchaseItem", function(request, response) {
    var item, order;
    Parse.Promise.as().then(function() {

    var itemQuery = new Parse.Query('Item');
    itemQuery.equalTo('ItemName', request.params.ItemName);
    return itemQuery.first(null,{useMasterKey: true}).then(null, function(error) {
        return Parse.Promise.error('Sorry, this item is no longer available.');
    });

    },{useMasterKey: true}).then(function(result) {
        if (!result) {
          return Parse.Promise.error('Sorry, this item is no longer available.');
        } else if (result.get('quantityAvailable') <= 0) {
          return Parse.Promise.error('Sorry, this item is out of stock.');
        }
        item = result;
        item.increment('quantityAvailable', -1);
        return item.save(null,{useMasterKey: true}).then(null, function(error) {
          console.log('Decrementing quantity failed. Error: ' + error);
          return Parse.Promise.error('An error has occurred. Your credit card was not charged. 1');
        });

    },{useMasterKey: true}).then(function(result) {
        if (item.get('quantityAvailable') < 0) { // can be 0 if we took the last
          return Parse.Promise.error('Sorry, this item is out of stock.');}
        order = new Parse.Object("Order");
        order.set('name', request.params.name);
        order.set('email', request.params.email);
        order.set('address', request.params.address);
        order.set('zip', request.params.zip);
        order.set('city_state', request.params.city_state);
        order.set('item', item.get('ItemName'));
        order.set('fulfilled', false);
        order.set('charged', false);
        return order.save(null,{useMasterKey:true}).then(null, function(error) {
            item.increment('quantityAvailable', 1);
            return Parse.Promise.error('An error has occurred. Your credit card was not charged.');
        });

    },{useMasterKey:true}).then(function(order) {
        return Stripe.charges.create({
          amount: item.get('price')*100, // It needs to convert to cents
          currency: "usd",
          source: request.params.cardToken,
          description: "Charge for " + request.params.email
        }, function(err, charge) {
          // asynchronously called
          console.log(charge.id);
        });

    },{useMasterKey:true}).then(function(purchase) {
        order.set('stripePaymentId', purchase.id);
        order.set('charged', true);
        order.save(null,{useMasterKey:true});
    },{useMasterKey:true}).then(function() {
        console.log('mail session '+order.id);
        //your email logic
    },{useMasterKey:true}).then(function() {
        response.success('Success');
    }, function(error) {
        response.error(error);
  });
});

Step 5 — Create your Android Client Application

First, add stripe package to your app by going to build.gradle, adding the following code and then syncing your activity.

compile 'com.stripe:stripe-android:4.1.5'

Then, you will need to call the function purchaseItem of your Cloud Code from the main application. You can do that by adding the following code to the main:

import android.widget.Button;
import android.app.ProgressDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.parse.FunctionCallback;
import com.parse.Parse;
import com.parse.ParseCloud;
import com.parse.ParseException;
import com.stripe.android.Stripe;
import com.stripe.android.TokenCallback;
import com.stripe.android.model.Card;
import com.stripe.android.model.Token;
import java.util.HashMap;

public class StripeApp extends AppCompatActivity {

    public static final String PUBLISHABLE_KEY = "your Stripe API Publishable Key here";
    public static final String APPLICATION_ID = "your Back4App Aplication ID here";
    public static final String CLIENT_KEY = "your Back4App Cliente Key here";
    public static final String BACK4APP_API = "https://parseapi.back4app.com/";
    private Card card;
    private ProgressDialog progress;

    private Button purchase;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stripe_app);

        // Connect to Your Back4app Account
        Parse.initialize(new Parse.Configuration.Builder(this)
                .applicationId(APPLICATION_ID)
                .clientKey(CLIENT_KEY)
                .server(BACK4APP_API).build());
        Parse.setLogLevel(Parse.LOG_LEVEL_VERBOSE);

        // Create a demo test credit Card
        // You can pass the payment form data to create a Real Credit card
        // with your client information

        card = new Card(
                "4242424242424242", //card number
                12, //expMonth
                2025,//expYear
                "123"//cvc
        );
        progress = new ProgressDialog(this);

        purchase = (Button) findViewById(R.id.purchase);
        purchase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                buy();
            }
        });
    }

    // Will perform the buy of a item action
    // Uses the credit card that you initialized before
    private void buy(){
        boolean validation = card.validateCard();
        if(validation){
            startProgress("Validating Credit Card");
            new Stripe(this).createToken(
                    card,
                    PUBLISHABLE_KEY,
                    new TokenCallback() {
                        @Override
                        public void onError(Exception error) {
                            Log.d("Stripe",error.toString());
                        }

                        @Override
                        public void onSuccess(Token token) {
                            finishProgress();
                            charge(token);
                        }
                    });
        } else if (!card.validateNumber()) {
            Log.d("Stripe","The card number that you entered is invalid");
        } else if (!card.validateExpiryDate()) {
            Log.d("Stripe","The expiration date that you entered is invalid");
        } else if (!card.validateCVC()) {
            Log.d("Stripe","The CVC code that you entered is invalid");
        } else {
            Log.d("Stripe","The card details that you entered are invalid");
        }
    }

    // Will make the request for the purchaseItem at your Cloud Code
    private void charge(Token cardToken){
        // Create the parameters that will be passed to your
        // Cloud Code function
        // Configure your parameters here
        HashMap<String, Object> params = new HashMap<String, Object>();
        params.put("ItemName", "test");
        params.put("cardToken", cardToken.getId());
        params.put("name","ClientName");
        params.put("email","[email protected]");
        params.put("address","HIHI");
        params.put("zip","99999");
        params.put("city_state","CA");
        startProgress("Purchasing Item");
        ParseCloud.callFunctionInBackground("purchaseItem", params, new FunctionCallback<Object>() {
            public void done(Object response, ParseException e) {
                finishProgress();
                if (e == null) {
                    Log.d("Cloud Response", "There were no exceptions! " + response.toString());
                    Toast.makeText(getApplicationContext(),
                            "Item Purchased Successfully ",
                            Toast.LENGTH_LONG).show();
                } else {
                    Log.d("Cloud Response", "Exception: " + e);
                    Toast.makeText(getApplicationContext(),
                            e.getMessage().toString(),
                            Toast.LENGTH_LONG).show();
                }
            }
        });
    }

    private void startProgress(String title){
        progress.setTitle(title);
        progress.setMessage("Please Wait");
        progress.show();
    }
    private void finishProgress(){
        progress.dismiss();
    }
}

To make the layout, go to the res/layout main file and paste the following code:

<?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.example.igor.stripeapp.StripeApp">

    <LinearLayout
        android:id="@+id/activity_main"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="dominwong4.scm.back4app_stripe_android_tutorial.MainActivity"
        android:weightSum="1"
        tools:layout_constraintTop_creator="1"
        tools:layout_constraintRight_creator="1"
        tools:layout_constraintBottom_creator="1"
        android:layout_marginStart="9dp"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginEnd="9dp"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="8dp"
        tools:layout_constraintLeft_creator="1"
        android:layout_marginBottom="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginLeft="9dp"
        android:layout_marginRight="9dp">

        <Button
            android:id="@+id/purchase"
            android:layout_width="327dp"
            android:layout_height="wrap_content"
            android:text="Purchase Button" />
    </LinearLayout>

</android.support.constraint.ConstraintLayout>

Then, go to res/values directory. Create a file named dimens.xml and add the code below:

<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
</resources>

Step 6 — Testing 

Now your android code should be working properly, your app should look like the picture below. By clicking the Purchase Button, it should pass through a loading phase and then display a message if the payment of the item was successful or not.

The main page of your application when you click Purchase Button.

If all the integration was done correctly, you can check if your function is working by looking at your Parse dashboard.

In the Item class, you should be able to see that the stock got decreased.

The parse dashboard on item class after the purchaseItem function was called.

In the Order class, you can see that there are new orders.

Parse dashboard on order class after the purchaseItem function was called.

At Stripe dashboard, you can see all the history of your transactions that were done with your account.

Stripe main dashboard after the transaction.

Conclusion

Your basic configuration for using Stripe is now ready. You can explore the complete Stripe API documentation for more references.

Feel free to reach out to us through our website chat if you have any questions.