Build a realtime payment dashboard with Stripe

Introduction

In this article, we are going to be looking at how to accept payments from a user using Stripe and displaying sales on an admin dashboard in realtime usingtext in italic Pusher.

How it works

Stripe is used to handle user payments and Pusher adds realtime functionality to our application.

realtime-payment-dashboard-stripe-demo

Dependencies

Before you begin, you need Node and Node Package Manager( npm ) installed on your machine. To verify your installation, please run the following commands on your terminal

1npm -v
2    node -v

If you get version numbers as your response, then it means that you already have them installed and you are good to go.

Setting up a Stripe account

Stripe is a platform that helps process online payment. We will use this to process payments in our store.

To set up a Stripe account, head over here and then fill out the form.

realtime-payment-dashboard-stripe-signup

Once this step is completed, you will be redirected to your dashboard:

realtime-payment-dashboard-stripe-dashboard
realtime-payment-dashboard-stripe-api-keys

Note your STRIPE_PUBLISHABLE_KEY and STRIPE_SECRET_KEY. We are going to use them later on as we build our application

Setting up a Pusher account

Pusher allows you to incorporate realtime functionality into your applications. To get started with Pusher, head over here.

Once you're signed in, you will be redirected to your dashboard. You then need to create a new app. After your new app is created, you need to note your PUSHER_APP_ID, PUSHER_API_KEY, PUSHER_API_SECRET, PUSHER_API_CLUSTER .

Once you have these details, you are ready to begin building your app.

Note : All source code is available here

Setting up the backend server

To handle your API calls to Stripe and Pusher, we will use an Express server.

Install node modules

We need some node modules that are essential for our application to work:

  • cors - to enable cross origin resource sharing on our app
  • express - this is our web server
  • pusher - this package enables us and makes it easy to make calls to pushers api
  • body-parser - used in parsing the contents of a request in a json format
  • multipart - to enable multipart on our app
  • stripe - to allow us communicate seamlessly with our stripe api
  • ejs - this will be our view engine for the user facing side of the application

Make a new directory and change directory into it:

    mkdir realtime-dashboard && cd realtime-dashboard

Then initialize a node project and install the node modules:

1# Initialze
2    npm init -y
3    # Install
4    npm install cors express ejs body-parser connect-multiparty pusher stripe --save

You have now installed all the modules necessary for you to build the project.

Create our server.js file

Now we need to create a file that will contain the instructions needed for our server to work In your realtime-dashboard directory:

    touch server.js

This is the start up file that will be referenced when your server is running In your server.js file, you need to:

Import the node modules

1const cors  = require('cors')
2    const Pusher = require('pusher')
3    const express = require('express')
4    const bodyParser = require('body-parser')
5    const multipart = require('connect-multiparty')
6    const stripe = require('stripe')('STRIPE_API_KEY')
7        [...]

Once you have imported your node modules, you can then use them freely all through your script.

Create your express app

Now we create our express app by adding the following to the server.js:

1[...]
2    const app = express()
3    [...]

Load the middleware

We load the middleware in our server.js by adding the following:

1...
2    app.use(cors());
3    app.set('view engine', 'ejs');
4    app.use(bodyParser.json());
5    app.use(bodyParser.urlencoded({ extended: false}));
6    const multipartMiddleware = multipart();
7    ...

Here, we set our app to use cors and set the view engine to ejs. We also instructed the app the parse the requests in JSON format.

Create the Pusher client

We need to create our Pusher client to enable us to trigger events from our app to a specific channel. Our admin dashboard will also be listening for events on the same channel (more details on this as we progress). We create the client by adding this to our file:

1[...]
2    const pusher = new Pusher({
3        appId: 'PUSHERE_APP_ID',
4        key: 'PUSHER_API_KEY',
5        secret: 'PUSHER_API_SECRET',
6        cluster: 'PUSHER_APP_CLUSTER',
7        encrypted: true
8    });
9    [...]

Once this is done, we have successfully created our Pusher client.

Create app routes

We need to decide what the user sees when visiting different parts of our app. Since this is a simple application, we only need two routes for the user facing side:

  • Route that loads the checkout page.
  • Route that handles the payment and communicated with the stripe API.

Earlier on, we set our view engine to ejs and we will use this here:

1[...]
2    app.get('/', function(req, res){
3        res.render('index');
4    });
5    
6    app.post('/gen-payment', multipartMiddleware, function(req, res){
7        let amount = 500;
8        stripe.customers.create({
9            email: req.body.stripeEmail,
10            source: req.body.stripeToken
11        })
12        .then(customer =>
13            stripe.charges.create({
14                amount,
15                description: 'One camera bought from shop',
16                currency: "usd",
17                customer: customer.id
18            })
19        )
20        .then(charge => {
21            pusher.trigger('sales', 'payment-completed', {
22                "time" : new Date().toDateString(),
23                "value" : `\$${charge.amount/100}`,
24                "message" : "payment complete...duh!",
25                "description" : charge.description
26            });
27            console.log( charge );
28            res.render("charge");
29        });
30    });
31    [...]

Let's shed more light on the gen-payment route. We accept the stripeEmail and the stripeToken which would be passed as part of the body in the post request to the route. We then create a new customer using the stripeEmail and the stripeToken. The create function returns a promise and so once the customer is created, we initiate a new charge for the customer. If this charge is successful, i.e we are able to completely charge the customer, then we trigger a payment-completed event to the sales channel.

Assign a port to your app

You need to choose a port you want your app to run on. You do this by adding this following to your server.js file:

1[...]
2    app.listen('3120')
3    [...]

At this point the backend server is all set up. Now we need to go to the views to see how data is passed to the server.

Setting up the frontend

Create a views directory. In the views directory create your index.ejs and charge.ejs:

1mkdir views && cd views
2    touch index.ejs charge.ejs

In our index.ejs we need to accept user payment. To make it more secure when handling card information, Stripe has an embedded payment form called Checkout which we will use to collect user's card data securely. The following is a truncated version of the form that sends data to our server. You can see the complete code on github.

1<form method="POST" action="http://localhost:3120/gen-payment" enctype="multipart/form-data"/>
2        <script
3            src="https://checkout.stripe.com/checkout.js" class="stripe-button"
4            data-key="PUSBLISHABLE_API_KEY"
5            data-amount="500"
6            data-name="Pay OG"
7            data-description="Send money to OG"
8            data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
9            data-locale="auto">
10        </script>
11    </form>

We submit the form to the /gen-payment route on our server. We include the checkout.js script and the following :

  • data-key: your publishable API_KEY gotten from your dashboard
  • data-amount: the amount you plan on charging
  • data-name: name of the store
  • data-description: description of your payment
  • data-image: store image

Once this is sent to the server and the request is completed successfully, we render the charge.ejs view to the user telling the use that their payment is complete:

1<!DOCTYPE html>
2    <html lang="en">
3    <head>
4        <meta charset="UTF-8">
5        <meta name="viewport" content="width=device-width, initial-scale=1.0">
6        <meta http-equiv="X-UA-Compatible" content="ie=edge">
7        <title>Realtime Payment Dashboard</title>
8        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous"> 
9        <link rel="stylesheet" href="https://codepen.io/drehimself/pen/VvYLmV.css"> 
10    </head>
11    <body>
12        <nav>
13            <div class="container">
14                <ul class="navbar-left">
15                <li><a href="#">Home</a></li>
16                <li><a href="#about">About</a></li>
17                </ul>
18            
19                <ul class="navbar-right">
20                <li><a href="#" id="cart"><i class="fa fa-shopping-cart"></i> Cart <span class="badge">0</span></a></li>
21                </ul>
22            </div> 
23        </nav>
24                
25        <script>
26            alert("Payment Complete");
27        </script>
28        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
29    </body>
30    </html>

Setting up the admin dashboard

We want our admin dashboard to show completed payments as they are made without having to refresh the page. To do this, we are going to make use of Vue.js and Pusher.

Before we begin, we need to install the vue-cli by running the following command:

    npm install -g vue-cli

This will install it globally on our local machine. To confirm your installation of the vue-cli you can run:

    vue --version

If you get the version number as a result then you're all set!

To create the admin server, run the following command in the realtime-dashboard directory:

    vue init webpack admin

This will ask you a few questions on project name, description, author, etc. then it will create a new Vue project for us with some boilerplate already set up.

Installing the pusher-js module

We need to install pusher-js module that allows us to use pusher with our vue frontend. To do this, change directory into the admin directory and run the following command:

    npm install -S pusher-js

Creating the dashboard component

Now we want to create our dashboard component:

1cd admin/src/components
2    touch Dashboard.vue

In the Dashboard.vue, we need to import the pusher.js module:

1<script>
2    import Pusher from 'pusher-js'
3    [...]

We then create some mock payments to populate the dashboard:

1[...]
2    const MOCK_PAYMENTS = [
3      {time : '12th Dec, 2017', description: "Shoes", value : "$5"},
4      {time : '12th Dec, 2017', description: "Maga don pay", value : "$12"}
5    ]
6    [...]

Now we describe our component itself:

1[...]
2    export default {
3      name: 'Dashboard',
4      data () {
5        return {
6          payments : MOCK_PAYMENTS
7        }
8      },
9      created () {
10        this.subscribe();
11      },
12      methods: {
13        subscribe () {
14          let pusher = new Pusher('PUSHER_API_KEY', {
15              cluster: 'PUSHER_CLUSTER',
16              encrypted: true
17          });
18          pusher.subscribe('sales');
19          pusher.bind('payment-completed', data => {
20            this.payments.unshift(data);
21          });
22        }
23      }
24    }
25    </script>
26    [...]

In the subscribe method above, we subscribe to the sales channel and then listen for the payment-completed event. When a new payment-completed event is broadcast from the backend server on the sales, our frontend server picks it up and the adds it to the payments array of the component.

The Dashboard.vue also has a template which looks like this:

1[...]
2    <template>
3      <div class="container-fluid">
4        <table class="table table-striped">
5          <thead>
6            <tr>
7              <td>Time</td>
8              <td>Value</td>
9              <td>Description</td>
10            </tr>
11          </thead>
12    
13          <tbody>
14            <tr v-for="payment in payments">
15              <td>{{ payment.time }}</td> 
16              <td>{{ payment.value }}</td>   
17              <td>{{ payment.description }}</td>   
18            </tr> 
19          </tbody>
20        </table>
21      </div>
22    </template>
23    [...]

And some scoped css styling:

1<style scoped>
2    h1, h2 {
3      font-weight: normal;
4    }
5    ul {
6      list-style-type: none;
7      padding: 0;
8    }
9    li {
10      display: inline-block;
11      margin: 0 10px;
12    }
13    a {
14      color: #42b983;
15    }
16    .table{
17      background-color : white;
18    }
19    </style>

Importing the dashboard component in our App.vue

Now that our dashboard component is ready, we need to import it in our App.vue so that I would be picked up when the view is being compiled

1[...]
2    import Dashboard from './components/Dashboard'
3    export default {
4      name: 'app',
5      components: {
6        Dashboard
7      }
8    }
9    [...]

Once this is done, you should be ready to run your frontend server. You can can do this using the command:

    npm run dev

Now you can run the Dashboard side-by-side with the Store and then see as purchases are being made in realtime:

realtime-payment-dashboard-stripe-demo

Conclusion

We have seen how to build a realtime payment dashboard using Pusher, Stripe and some Vue.js. There are many more use cases where realtime functionality will give you an edge. A big advantage is that you get to obtain realtime insights as to how users interact with your application.