Cloud Code Functions

How to integrate Stripe on Android App using Cloud Functionss

Introduction

In this guide, we are going to show you how to integrate a hosted backend in Back4App with Stripe API. The best architecture choice to build it on Back4App is using a very powerful feature called Cloud Functions. Once you finish the cloud function integration with Stripe you’ll be able to use this integration for all your front-end projects (Web, iOS, Android). This guide presents a complete Stripe integration using an Android App example.

Prerequisites

To complete this tutorial, you will need:

What is Stripe?

Stripe is a Tech Company, operating in over 25 countries, which allows both 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 the steps of creating a function and integrating Stripe API to your Parse Server into your Android App.

To be precise, we will create a Purchase Button that, whenever clicked will be accountable to place an order when purchasing a product from the database and generate a fake (demo) financial transaction via Stripe Payments to confirm whether it’s working appropriately or not. Also, you will be guided on how to set up the whole process as well as test if the connection to the server and the Stripe API is working properly.

After following this tutorial, you will be able to do this:

App working in Back4App!

Step 1 - Create a Stripe Account

Go to Stripe and click on the sign up to create an account. There, you just need to provide your personal information, as shown below:

Create a New Account in Stripe

Next, verify your Stripe Account (you’ll receive an email compromising a verification link from Stripe). Click on that link, and then follow the steps to confirm your Stripe email address.

Step 2 - Set up your Purchase Class

After configuring the Stripe environment for Step 1, go to Back4App Website, log in, find your app and click on Dashboard > Create a class, then enter with the name Item. Check below:

Inside that class, we will need to create the following columns:

  • ItemName (String)
  • Price (Number)
  • quantityAvailable (Number)

Afterward, create a new object with the following information:

  • ItemName: test
  • Price: 10
  • quantityAvailable: 5

Step 3 - Set up your Order Class

Now, let’s create a class to receive the order details from the payment gateway. This class will be called Order with the columns:

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

Order Class

To this point, we’ve successfully configured the Stripe Account and set up the classes Order and Item, plus the columns for both.

Step 4 - Implementing Cloud Code

In this section, let’s configure the Cloud Code Function in the app, we will install the Stripe module and deploy the Code.

If you want to better understand the Cloud Code enviroment, check this guide.

4.1 - Get your Stripe Key

Now, open the Stripe Dashboard and navigate to the menu list at the left, and click on Developers and API Keys. In that section, you will be able to see your Publishable key and your secret key. Next, click on the Reveal test key token to get your secret key. The Publishable key is the first value that is blurred in the image below.

Account Settings

After completing this step, you can easily copy your Stripe Keys.

4.2 - Cloud Code files

On your computer, create the following files, that will be responsible to install the module and to purchase an item using a Cloud Code Function.

package.json

1
2
3
4
5
{
  "dependencies": {
    "stripe": "*"
  }
}

In the code below, don’t forget to paste your Secret key, more specifically in space: PASTE_YOUR_SECRECT_KEY_HERE.

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
let stripe_key = "PASTE_YOUR_SECRECT_KEY_HERE"; //Secret Key here
let Stripe = require("stripe")(stripe_key);

Parse.Cloud.define("purchaseItem", async (request) => {
    let item, order;
    let itemQuery = new Parse.Query('Item');    
    itemQuery.equalTo('ItemName', request.params.ItemName);

    const result = await itemQuery.first(null,{useMasterKey: true});

    if(!result){
        throw new Error('Sorry, this item is no longer available.');
    } else if(result.get('quantityAvailable') <= 0){
        throw new Error('Sorry, this item is out of stock.');
    } else {
        item = result;
        const charge = await 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
        })
                
        item.increment('quantityAvailable', -1);
        const object = await item.save(null,{useMasterKey: true})
        const 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', true);
        order.set('charged', false);
        order.set('stripePaymentId', charge.id);            
        order.set('charged', true);
        await order.save(null,{useMasterKey:true});
    }
    return 'OK';
});

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
let stripe_key = "PASTE_YOUR_SECRECT_KEY_HERE";
let Stripe = require("stripe")(stripe_key);

Parse.Cloud.define("purchaseItem", function(request, response) {
  let item, order;

  Parse.Promise.as().then(function() {

    let 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.');
        }
        //Setting the columns to Order class
        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', true);
        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) {
        if (!err){
            order.set('stripePaymentId', charge.id);            
            order.set('charged', true);
            order.save(null,{useMasterKey:true});
        }
    });
    },{useMasterKey:true}).then(function() {
        response.success('Success');
    }, function(error) {
        response.error(error);
    });
});

Step 5 - Upload function to Cloud Code

Go to Back4App website, log in and then find your app. After that, click on Dashboard link and you will end up in the page shown below. To deploy your Cloud Code, simply click on the + ADD button and select the main.js and package.json file that you created at previous step, then click on the DEPLOY button.

Until this step, you have configured the Cloud Code function that you can use to call on other platforms! Check the Deploy & call functions guide to learn how to call it. After the next step, you will work with a Android Project.

Step 6 - Let’s configure our Project

Make sure that you have installed an IDE that will help you with this process (see prerequisites). Let’s start with an Android Template from Back4App to install the necessary packages.

Step 6.1 - Add Stripe package to your project

./app/build.grandle

dependencies {
    /* Other lines */

    // Stripe Package
    implementation 'com.stripe:stripe-android:+'

    /* Other lines */
}

Step 6.2 - Checking internet permission

Don’t forget to add the below line to your code, as it’ll ensure that the device has an active internet connection.

./app/src/main/AndroidManifest.xml

1
2
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.INTERNET"/>

Step 7 - Let’s configure our Layout

In this step, we will configure the Layout and use it to insert a Button on the view. The following code is a template file to use in the Layout.

./app/src/main/res/layout/activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?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=".MainActivity">

    <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"
        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="365dp"
        android:layout_height="60dp"
        android:text="Purchase Button"
        android:visibility="visible"
        android:layout_marginTop="5dp"
        android:layout_marginLeft="5dp"
        tools:layout_editor_absoluteX="11dp"
        tools:layout_editor_absoluteY="5dp" />

    </LinearLayout>

</android.support.constraint.ConstraintLayout>

Now, create a file named dimens.xml and add the code written below:

./app/src/main/res/values/dimens.xml

1
2
3
4
5
<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 8 - Implementing the Logic code!

It’s not over yet! Let’s not forget about the process that will send all data to our function created above in step 4.2.



./app/src/main/java/back4app/android_stripe_payment/MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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.Button;
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;
import java.util.Map;

import java.util.HashMap;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

    public static final String PUBLISHABLE_KEY = "PASTER_YOUR_PUBLISHABLE_KEY_HERE";
    private Card card;
    private ProgressDialog progress;
    private Button purchase;

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

        // Create a demo test credit Card
        // You can pass the payment form data to create a Real Credit card
        // But you need to implement youself.

        card = new Card(
                "4242424242424242", //card number
                12, //expMonth
                2030,//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();
            }
        });
    }

    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) {
                            Toast.makeText(MainActivity.this,
                                    "Stripe -" + error.toString(),
                                    Toast.LENGTH_LONG).show();
                        }

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

    private void charge(Token cardToken){
        HashMap<String, Object> params = new HashMap<String, Object>();
        params.put("ItemName", "test");
        params.put("cardToken", cardToken.getId());
        params.put("name","Dominic Wong");
        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();
    }
}

An important point to remember: You will need to replace your keys (App Id and Client Key) from Back4App in the string.xml file and also, the Publishable Key in the Code above.

Step 9 - Testing

By this time, your Android code should be working properly. After clicking the Purchase Button, it should easily pass through the loading phase and then display a message if the payment made for the item was successful or not. Your app will look like the picture below:

App working in Back4App!

If the integration was done correctly, your Parse Dashboard will display that the function is working properly. In the Item class, you should be able to see that the product quantity has been updated.

Item class

Also, in the Order class, you can see the newly created orders.

Order class

Step 10 - It’s done!

With the guide described above, you can use Stripe with a Cloud Code Function in Back4App as well as integrate payments to your apps!

In case you face any trouble while integrating Stripe or a function doesn’t work, please contact our team via chat!