Build a realtime payment dashboard with Stripe
A basic understanding of Vue.js and Node.js is needed to follow this tutorial.
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.
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
npm -v
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.
Once this step is completed, you will be redirected to your dashboard:
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:
# Initialze
npm init -y
# Install
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
const cors = require('cors')
const Pusher = require('pusher')
const express = require('express')
const bodyParser = require('body-parser')
const multipart = require('connect-multiparty')
const stripe = require('stripe')('STRIPE_API_KEY')
[...]
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
:
[...]
const app = express()
[...]
Load the middleware
We load the middleware in our server.js
by adding the following:
...
app.use(cors());
app.set('view engine', 'ejs');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false}));
const multipartMiddleware = multipart();
...
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:
[...]
const pusher = new Pusher({
appId: 'PUSHERE_APP_ID',
key: 'PUSHER_API_KEY',
secret: 'PUSHER_API_SECRET',
cluster: 'PUSHER_APP_CLUSTER',
encrypted: true
});
[...]
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:
[...]
app.get('/', function(req, res){
res.render('index');
});
app.post('/gen-payment', multipartMiddleware, function(req, res){
let amount = 500;
stripe.customers.create({
email: req.body.stripeEmail,
source: req.body.stripeToken
})
.then(customer =>
stripe.charges.create({
amount,
description: 'One camera bought from shop',
currency: "usd",
customer: customer.id
})
)
.then(charge => {
pusher.trigger('sales', 'payment-completed', {
"time" : new Date().toDateString(),
"value" : `\$${charge.amount/100}`,
"message" : "payment complete...duh!",
"description" : charge.description
});
console.log( charge );
res.render("charge");
});
});
[...]
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:
[...]
app.listen('3120')
[...]
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
:
mkdir views && cd views
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.
<form method="POST" action="http://localhost:3120/gen-payment" enctype="multipart/form-data"/>
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="PUSBLISHABLE_API_KEY"
data-amount="500"
data-name="Pay OG"
data-description="Send money to OG"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-locale="auto">
</script>
</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:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Realtime Payment Dashboard</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
<link rel="stylesheet" href="https://codepen.io/drehimself/pen/VvYLmV.css">
</head>
<body>
<nav>
<div class="container">
<ul class="navbar-left">
<li><a href="#">Home</a></li>
<li><a href="#about">About</a></li>
</ul>
<ul class="navbar-right">
<li><a href="#" id="cart"><i class="fa fa-shopping-cart"></i> Cart <span class="badge">0</span></a></li>
</ul>
</div>
</nav>
<script>
alert("Payment Complete");
</script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
</body>
</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:
cd admin/src/components
touch Dashboard.vue
In the Dashboard.vue
, we need to import the pusher.js
module:
<script>
import Pusher from 'pusher-js'
[...]
We then create some mock payments to populate the dashboard:
[...]
const MOCK_PAYMENTS = [
{time : '12th Dec, 2017', description: "Shoes", value : "$5"},
{time : '12th Dec, 2017', description: "Maga don pay", value : "$12"}
]
[...]
Now we describe our component itself:
[...]
export default {
name: 'Dashboard',
data () {
return {
payments : MOCK_PAYMENTS
}
},
created () {
this.subscribe();
},
methods: {
subscribe () {
let pusher = new Pusher('PUSHER_API_KEY', {
cluster: 'PUSHER_CLUSTER',
encrypted: true
});
pusher.subscribe('sales');
pusher.bind('payment-completed', data => {
this.payments.unshift(data);
});
}
}
}
</script>
[...]
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:
[...]
<template>
<div class="container-fluid">
<table class="table table-striped">
<thead>
<tr>
<td>Time</td>
<td>Value</td>
<td>Description</td>
</tr>
</thead>
<tbody>
<tr v-for="payment in payments">
<td>{{ payment.time }}</td>
<td>{{ payment.value }}</td>
<td>{{ payment.description }}</td>
</tr>
</tbody>
</table>
</div>
</template>
[...]
And some scoped css styling:
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
.table{
background-color : white;
}
</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
[...]
import Dashboard from './components/Dashboard'
export default {
name: 'app',
components: {
Dashboard
}
}
[...]
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:
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.
14 February 2018
by Christian Nwamba