![BTC SMS Intro]()
Building a full-fledged API service isn’t as hard as you may think. By taking
advantage of some really useful API services and open source libraries, you can
rapidly develop an API service in an incredibly short amount of time!
In this article, I’m going to walk you through the process of building an API
service that uses SMS to keep you up-to-date with current value of Bitcoin:
Bitcoin SMS!
This API service:
- Lets users sign up and register for your site.
- Verifies new user accounts by sending them an email and making them click a
link.
- Generates API keys for developers automatically.
- Bills developers on a per-API call basis.
- Handles credit card billing with Stripe.
- Sends SMS messages via Twilio.
- Finds Bitcoin exchange rates via Bitcoin Charts.
- Stores user account data securely with Stormpath.
If you’re at all interested in building API services, or API companies — this
article is meant specifically for you!
NOTE: Prefer video over a long article? If so, you can actually watch my
screencast where I cover the same things in video form on Youtube here:
https://www.youtube.com/watch?v=THDPG2gH7o0
ALSO: all of the code we’ll be writing here can be found on Github here:
https://github.com/rdegges/btc-sms
What We’re Building: BitCoin SMS API Service
![BTC SMS Demo BTC SMS Demo]()
What we’re going to build today is a publicly available API service
called BTC SMS which allows developers to make API calls that send SMS
messages to specified phone numbers with the current value of Bitcoin.
The idea is that a lot of people might want to know the value of Bitcoin at a
specific point in time — in order to buy or sell their Bitcoin — so this API
service makes that information easy to access by sending SMS messages to a user
with the current value of Bitcoin.
The API service we’re building will allow developers to make requests like this:
POST /api/message
{
"phoneNumber": "+18182223333"
}
In the above example, this request would kick off an SMS message to
+18182223333
that says something along the lines of “1 Bitcoin is currently
worth $525.00 USD.”
Here’s how it’ll look:
![BTC SMS API Call BTC SMS API Call]()
Now, since we’re actually building an entire API service here — and not just a
plain old API, we’ll also be charging money! We’ll be charging developers 2
cents per successful API request in order to cover our costs, and make a little
bit of profit =)
So, now that we’ve briefly discussed what we’re going to be making — let’s
actually make it!
Set Up Twilio, Stripe and Stormpath For Your API
To get started, you’ll need to create some accounts that we’ll be using for the
rest of this guide.
Twilio for SMS
First, you’ll want to go and create an account with Twilio. Twilio is an
API service that lets you do all things telephony related: send and receive
calls and SMS messages.
For the purposes of the application we’re building, we’ll only be using Twilio
to send SMS messages, but there’s a lot more it can do.
Signing up for a Twilio account is free, but if you want to actually follow
through with the rest of this article, you’ll need to purchase a phone number
you can send SMS messages from — and this typically costs $1 USD per month + 1c
per SMS message you send.
Here’s what you’ll want to do:
- Sign up.
- Visit the Numbers Page and click “Buy a number”.
- Purchase any phone number that is SMS ready:
![Twilio Buy a Number Twilio Buy a Number]()
- Now that you have a phone number, write down that phone number, you’ll need
it later.
- Next, go to your Account Page and copy your API key credentials so you can
use them later on. Twilio gives you two API tokens: an “AccountSID” and an
“AuthToken”. Both of these are necessary to have. Make sure these stay
private.
![Twilio API Credentials Twilio API Credentials]()
Stripe for Payments
Next, let’s create a Stripe account. Stripe is a payments provider that
allows you accept Credit Cards, Debit Cards, and even Bitcoin as payment
options on your site.
Since this demo application will be charging users money for the amount of API
calls they make, this is necessary.
Once you’ve created your Stripe account, you’ll want to set your Stripe
dashboard to TEST mode — this lets you view the development mode sandbox
where you can use Stripe like normal, but with fake credit cards and money:
![Stripe Test Dashboard Stripe Test Dashboard]()
Next, you’ll want to visit your Stripe API Keys page to view your Stripe API
keys. The ones you’ll want to use for testing are the Test Secret Key and
Test Publishable Key values:
![Stripe API Keys Image Stripe API Keys Image]()
Be sure to take note of both those API key values — we’ll be needing those
later.
Stormpath for Authentication and API Key Management
Now that we’ve got both Twilio and Stripe setup, go create a Stormpath
account. Stormpath is a free API service that lets you store user accounts and
user data. It makes doing stuff like signing users up for your site, managing
users, and doing things like password reset and authorization really easy.
Instead of needing to run a database to store your user data in, we can use
Stormpath to simplify and speed up the process. It also comes with some nice
pre-built login / registration / password reset pages that make things really
nice.
Once you’ve signed up for Stormpath, you’ll want to create a new API key and
download it locally:
![Stormpath Provision API Key Stormpath Provision API Key]()
When you generate a new Stormpath API key, you’ll automatically download an
apiKey.properties file — this contains your API key information. This file
contains two values: an API Key ID and an API Key Secret — we’ll need both
of these later.
Next, you’ll need to create a new Stormpath Application. Generally, you’ll
want to create one Application per project you work on — since we’re building
this BTC SMS project, we’ll create an Application called BTC SMS:
![Stormpath Create Application Stormpath Create Application]()
After creating your Application, be sure to copy down the REST URL
link —
we’ll be needing this later on to reference our Application when we start coding
=)
Bootstrapping an Express.js Application
Now that we’ve gotten all of our required API service accounts setup and
configured properly, we’re ready to start writing some code!
The first thing we’ll need to do is create a minimal Express.js application that
we can use as we move forward.
Here’s the files / folders we’ll be creating, along with a brief description of
what each of them contains:
btc-sms
├── bower.json
├── index.js
├── package.json
├── routes
│ ├── api.js
│ ├── private.js
│ └── public.js
├── static
│ └── css
│ └── main.css
└── views
├── base.jade
├── dashboard.jade
├── docs.jade
├── index.jade
└── pricing.jade
4 directories, 12 files
btc-sms
: This is the project folder we’ll use to hold all of our code.
bower.json
: This is the bower package file which contains our front-end
dependencies. If you haven’t heard of bower, it’s a package manager for
front-end libraries like Twitter Bootstrap.
index.js
: This is the main ‘entrypoint’ of our Node application. This is
where our Express web app is defined, and our middlewares are configured.
package.json
: This is our Node package file — this contains our library
dependencies, and packaging information.
routes
: This folder holds all of our route code (this is the code that
actually makes stuff happen).
routes/api.js
: This file contains our API code.
routes/private.js
: This file contains the code that renders our private
pages (the customer dashboard).
routes/public.js
: This file contains our public pages code.
static
: This folder holds all of our static assets: CSS, Javascript, etc.
static/css/main.css
: This is our CSS file to make things look decent.
views
: This folder holds our template code.
views/base.jade
: This file holds our base template code. This holds the
basic HTML page outlines, navbar, and stuff like that.
views/dashboard.jade
: This file holds our private dashboard page code. This
includes some billing logic.
views/docs.jade
: This file holds our public API documentation.
views/index.jade
: This is our main home page.
views/pricing.jade
: This is our public pricing page.
The Views
Now that we’ve seen what our app looks like at a structural level, let’s take a
look at the views.
Taking a look at the views first will give you a good understanding of how the
site looks / functions before digging into the backend code.
base.jade
The base.jade
view contains a page outline and navbar that all pages of the
site use. This lets us build a ‘modular’ website with regards to the front-end
of the website:
block vars
doctype html
html(lang='en')
head
meta(charset='utf-8')
meta(http-equiv='X-UA-Compatible', content='IE=edge')
meta(name='viewport', content='width=device-width, initial-scale=1')
title #{siteTitle} - #{title}
link(href='/static/bootswatch/sandstone/bootstrap.min.css', rel='stylesheet')
link(href='/static/css/main.css', rel='stylesheet')
<!--[if lt IE 9]>
script(src='/static/html5shiv/dist/html5shiv.min.js')
script(src='/static/respond/dest/respond.min.js')
<![endif]-->
body
nav.navbar.navbar-default.navbar-static-top
- var nav = {}; nav[title] = 'active'
.container
.navbar-header
button.navbar-toggle.collapsed(type='button', data-toggle='collapse', data-target='#navbar-collapse')
span.sr-only Toggle navigation
span.icon-bar
span.icon-bar
span.icon-bar
a.navbar-brand(href='/') #{siteTitle}
#navbar-collapse.collapse.navbar-collapse
ul.nav.navbar-nav
li(class='#{nav.Home}')
a(href='/') Home
li(class='#{nav.Pricing}')
a(href='/pricing') Pricing
li(class='#{nav.Docs}')
a(href='/docs') Docs
if user
li(class='#{nav.Dashboard}')
a(href='/dashboard') Dashboard
li(class='#{nav.Logout}')
a(href='/logout') Logout
else
li(class='#{nav.Login}')
a(href='/login') Login
li(class='#{nav.Register} create-account')
a(href='/register') Create Account
block body
script(src='/static/jquery/dist/jquery.min.js')
script(src='/static/bootstrap/dist/js/bootstrap.min.js')
Some important things to take note of:
- We are using Twitter bootstrap for design. This lets us build a well
structured site without doing a lot of work.
- We are using a Bootswatch theme for Bootstrap to make the site look a
little bit better than your normal Bootstrap site. The theme we’re using
simply overrides some of the basic Bootstrap styles.
- At the top of our template, there is a block called
vars
— this lets us
define template-specific variables later on.
index.jade
Our index.jade
template renders the home page of our site — it’s just a
simple static page:
extends base
block vars
- var title = 'Home'
block body
.container.index
h1.text-center Get BTC Rates via SMS
.row
.col-xs-12.col-md-offset-2.col-md-8
.jumbotron.text-justify
p.
#{siteTitle} makes it easy to track the value of Bitcoin via SMS.
Each time you hit the API service, we'll SMS you the current Bitcoin
price in a user-friendly way.
a(href='/register')
button.btn.btn-lg.btn-primary.center-block(type='button') Get Started!
You’ll notice that our Get Started! button is linking to a registration page
— this registration page is generated automatically by the Stormpath library
that you’ll see later on.
docs.jade
The docs.jade
template is just a static page that contains API documentation
for developers visiting the site:
extends base
block vars
- var title = 'Docs'
block body
.container.docs
h1.text-center API Documentation
.row
.col-xs-12.col-md-offset-2.col-md-8
p.text-justify
i.
This page contains the documentation for this API service. There is
only a single API endpoint available right now, so this document is
fairly short.
p.text-justify
i.
Questions? Please email <a href="mailto:support@apiservice.com">support@apiservice.com</a>
for help!
h2 REST Endpoints
h3 POST /api/message
span Description
p.description.
This API endpoint takes in a phone number, and sends this phone an
SMS message with the current Bitcoin exchange rate.
span Input
.table-box
table.table.table-bordered
thead
tr
th Field
th Type
th Required
tbody
tr
td phoneNumber
td String
td true
span Success Output
.table-box
table.table.table-bordered
thead
tr
th Field
th Type
th Example
tbody
tr
td phoneNumber
td String
td "+18182223333"
tr
td message
td String
td "1 Bitcoin is currently worth $225.42 USD."
tr
td cost
td Integer
td #{costPerQuery}
span Failure Output
.table-box
table.table.table-bordered
thead
tr
th Field
th Type
th Example
tbody
tr
td error
td String
td "We couldn't send the SMS message. Try again soon!"
span Example Request
pre.
$ curl -X POST \
--user 'id:secret' \
--data '{"phoneNumber": "+18182223333"}' \
-H 'Content-Type: application/json' \
'http://apiservice.com/api/message'
pricing.jade
Like our docs page — the pricing.jade
page is just a static page that tells
users how much our service costs to use:
extends base
block vars
- var title = 'Pricing'
block body
.container.pricing
h1.text-center Pricing
.row
.col-xs-offset-2.col-xs-8.col-md-offset-4.col-md-4.price-box.text-center
h2 #{costPerQuery}¢ / query
p.text-justify.
We believe in simple pricing. Everyone pays the same usage-based
feeds regardless of size.
p.text-justify.end.
<i>Regardless of how many requests you make, BTC exchange rates are
updated once per hour.</i>
.row
.col-xs-offset-2.col-xs-8.col-md-offset-4.col-md-4
a(href='/register')
button.btn.btn-lg.btn-primary.center-block(type='button') Get Started!
dashboard.jade
The dashboard.jade
file is where users land once they’ve either created or
logged into an account.
This page does a few things:
- Displays the user’s API keys.
- Displays the user’s account balance, and lets users deposit money into their
account.
- Displays the user’s usage information (how many API requests has this user
made?).
The way we’re accepting billing information on this page is via the
Stripe Checkout Button. To learn more about how this works, you can visit
the Stripe site.
What happens is essentially this: if a user clicks the Stripe button, a
Javascript popup will appear to collect the user’s payment information.
When the user is done entering their information, this credit card info will be
validated by Stripe, and a unique token will be generated to allow us to bill
this user later on.
Here’s the dashboard code:
extends base
block vars
- var title = 'Dashboard'
block body
.container.dashboard
.row.api-keys
ul.list-group
.col-xs-offset-1.col-xs-10
li.list-group-item.api-key-container
.left
strong API Key ID:
span.api-key-id #{user.apiKeys.items[0].id}
.right
strong API Key Secret:
span.api-key-secret #{user.apiKeys.items[0].secret}
.row.widgets
.col-md-offset-1.col-md-5
.panel.panel-primary
.panel-heading.text-center
h3.panel-title Analytics
.analytics-content.text-center
span.total-queries #{user.customData.totalQueries}
br
span
i.
*total queries
.col-md-5
.panel.panel-primary
.panel-heading.text-center
h3.panel-title Billing
.billing-content.text-center
span.account-balance $#{(user.customData.balance / 100).toFixed(2)}
br
span
i.
*current account balance
form(action='/dashboard/charge', method='POST')
script.stripe-button(
src = 'https://checkout.stripe.com/checkout.js',
data-email = '#{user.email}',
data-key = '#{stripePublishableKey}',
data-name = '#{siteTitle}',
data-amount = '2000',
data-allow-remember-me = 'false'
)
Static Assets
Now that we’ve taken a quick look at the views, let’s take a quick look at the
static assets we’ll be using.
In our case, since there’s not a lot of styling done here — we’ve only got a
single CSS file:
/*
* Navbar settings.
*/
ul.nav.navbar-nav {
float: right;
}
li.create-account > a {
color: #fff !important;
}
/*
* Index page settings.
*/
.index h1 {
margin-top: 2em;
}
.index .jumbotron {
margin-top: 4em;
}
.index button {
margin-top: 4em;
font-size: 1em;
}
/*
* Dashboard page settings.
*/
.dashboard .api-keys {
margin-top: 3em;
}
.dashboard .api-key-container {
min-height: 4em;
}
.dashboard .widgets {
margin-top: 4em;
}
.dashboard .api-key-secret {
color: red;
}
.dashboard h3 {
font-size: 1.2em !important;
}
.dashboard span.api-key-id, .dashboard span.api-key-secret {
font-family: "Lucida Console", Monaco, monospace;
margin-left: .5em;
}
.dashboard .left {
float: left;
}
.dashboard .right {
float: right;
}
.dashboard .panel {
padding-bottom: 2em;
}
.dashboard .panel-heading {
margin-bottom: 2em;
}
.dashboard .analytics-content, .dashboard .billing-content {
padding-left: 2em;
padding-right: 2em;
}
.dashboard .account-balance, .dashboard .total-queries {
font-size: 2em;
}
.dashboard form {
margin-top: 2em;
}
/*
* Pricing page settings.
*/
.pricing .price-box {
border: 2px solid #f8f5f0;
border-radius: 6px;
margin-top: 4em;
margin-bottom: 4em;
}
.pricing h2 {
margin-bottom: 1em;
}
.pricing .end {
margin-bottom: 2em;
}
/*
* Documentation page settings.
*/
.docs h1 {
margin-bottom: 2em;
}
.docs h2 {
margin-top: 2em;
margin-bottom: 2em;
}
.docs h3 {
/*padding-left: 2em;*/
font-weight: bold;
}
.docs span {
font-size: 1.2em;
padding-left: 2.7em;
font-weight: bold;
margin-top: 1em;
margin-bottom: .5em;
display: block;
}
.docs .description {
font-size: 1.2em;
padding-left: 2.6em;
}
.docs .table-box {
padding-left: 3em !important;
margin-top: 1em !important;
}
.docs pre {
margin-left: 3em;
}
package.json and bower.json
Now, let’s get into some real code!
Below is the package.json
file that declares all of our Node.js dependencies,
and makes installing this application simple:
{
"name": "api-service-starter",
"version": "0.0.0",
"description": "An API service starter kit for Node.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"api",
"service",
"starter",
"kit"
],
"author": "Randall Degges",
"license": "UNLICENSE",
"dependencies": {
"async": "^0.9.0",
"body-parser": "^1.12.3",
"express": "^4.12.3",
"express-stormpath": "^1.0.4",
"jade": "^1.9.2",
"request": "^2.55.0",
"stripe": "^3.3.4",
"twilio": "^2.0.0"
}
}
To install this project, you can simply run $ npm install
from the command
line — this will automatically download all Node dependencies for ya =)
Likewise, you can also use bower to automatically download and install all
front-end dependencies via $ bower install
. The bower.json
file makes this
possible:
{
"name": "api-service-starter",
"main": "index.js",
"version": "0.0.0",
"authors": [
"Randall Degges <r@rdegges.com>"
],
"description": "An API service starter kit for Node.",
"keywords": [
"api",
"service",
"starter",
"kit"
],
"license": "UNLICENSE",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"jquery": "~2.1.3",
"bootstrap": "~3.3.4",
"respond": "~1.4.2",
"html5shiv": "~3.7.2",
"bootswatch": "~3.3.4+1"
}
}
Application Setup
Now that we’ve covered the basics, let’s take a look at what makes our project
tick: the index.js
file. This holds the main Express.js web application,
configures our libraries, and initializes our web server:
'use strict';
var async = require('async');
var express = require('express');
var stormpath = require('express-stormpath');
var apiRoutes = require('./routes/api');
var privateRoutes = require('./routes/private');
var publicRoutes = require('./routes/public');
// Globals
var app = express();
// Application settings
app.set('view engine', 'jade');
app.set('views', './views');
app.locals.costPerQuery = parseInt(process.env.COST_PER_QUERY);
app.locals.siteTitle = 'BTC SMS';
app.locals.stripePublishableKey = process.env.STRIPE_PUBLISHABLE_KEY;
// Middlewares
app.use('/static', express.static('./static', {
index: false,
redirect: false
}));
app.use('/static', express.static('./bower_components', {
index: false,
redirect: false
}));
app.use(stormpath.init(app, {
enableAccountVerification: true,
expandApiKeys: true,
expandCustomData: true,
redirectUrl: '/dashboard',
secretKey: 'blah',
postRegistrationHandler: function(account, req, res, next) {
async.parallel([
// Set the user's default settings.
function(cb) {
account.customData.balance = 0;
account.customData.totalQueries = 0;
account.customData.save(function(err) {
if (err) return cb(err);
cb();
});
},
// Create an API key for this user.
function(cb) {
account.createApiKey(function(err, key) {
if (err) return cb(err);
cb();
});
}
], function(err) {
if (err) return next(err);
next();
});
}
}));
// Routes
app.use('/', publicRoutes);
app.use('/api', stormpath.apiAuthenticationRequired, apiRoutes);
app.use('/dashboard', stormpath.loginRequired, privateRoutes);
// Server
app.listen(process.env.PORT || 3000);
The first thing we’ll do is import all of the libraries necessary, as well as
our route code (which we’ll hook up in a bit).
The next thing we’ll do is define our Express.js application, and tell is that
we’re going to be using the Jade template language for our view code:
var app = express();
// Application settings
app.set('view engine', 'jade');
app.set('views', './views');
Once that’s been done, we’ll initialize some global settings:
app.locals.costPerQuery = parseInt(process.env.COST_PER_QUERY);
app.locals.siteTitle = 'BTC SMS';
app.locals.stripePublishableKey = process.env.STRIPE_PUBLISHABLE_KEY;
The COST_PER_QUERY
and STRIPE_PUBLISHABLE_KEY
values are being pulled out of
environment variables. Instead of hard-coding your credentials into your source
code, storing them in environmental variables is typically a better thing to do
as you don’t need to worry about accidentally exposing your credentials.
The COST_PER_QUERY
environment variable tells our app how many cents we should
charge for each successful API request — in our case, we’ll set this to 2
.
The STRIPE_PUBLISHABLE_KEY
environment variable should be set to our Stripe
Publishable Key that we retrieved earlier on when we created a Stripe account.
Here’s an example of how you might set these variables from the command line:
$ export COST_PER_QUERY=2
$ export STRIPE_PUBLISHABLE_KEY=xxx
Next, we’ll use the express.static
built-in middleware to properly serve our
app’s static assets:
app.use('/static', express.static('./static', {
index: false,
redirect: false
}));
app.use('/static', express.static('./bower_components', {
index: false,
redirect: false
}));
And… After that, we’ll initialize the Stormpath library:
app.use(stormpath.init(app, {
enableAccountVerification: true,
expandApiKeys: true,
expandCustomData: true,
redirectUrl: '/dashboard',
secretKey: 'blah',
postRegistrationHandler: function(account, req, res, next) {
async.parallel([
// Set the user's default settings.
function(cb) {
account.customData.balance = 0;
account.customData.totalQueries = 0;
account.customData.save(function(err) {
if (err) return cb(err);
cb();
});
},
// Create an API key for this user.
function(cb) {
account.createApiKey(function(err, key) {
if (err) return cb(err);
cb();
});
}
], function(err) {
if (err) return next(err);
next();
});
}
}));
The express-stormpath library makes securing our website really easy.
I’ll cover the different options, and what they do below.
- The
enableAccountVerification
flag tells the library that when a user signs
up for our site, we should email them a link to click on to verify their email
address.
- Stormpath let’s you provision API keys for each user account. Since we’re
building an API service, this is ideal. The
expandApiKeys
option makes
Stormpath automatically pull down a user’s API key information and keep it
cached to speed up API requests.
- Stormpath also lets you store user data using something called custom data.
Basically, each user can have a big old JSON dictionary stored on their
account with whatever data you want. In our case, we’ll be using this to
store a user’s account balance as well as the total amount of requests they’ve
made to our service. The
expandCustomData
flag makes this operation faster
by automatically downloading this information and keeping it in a local cache.
- The
redirectUrl
setting tells Stormpath where to redirect a user after
they’ve logged into the site.
- The
secretKey
setting should be a long random string that stays private —
this is used to secure the user sessions.
- Lastly, the
postRegistrationHandler
lets us run code after a new user has
signed up on the site. What we’ll do here is initialize a user by creating an
API key for them automatically, as well as setting their account balance and
total queries to 0.
Now that we’ve configured all our middleware, the last thing we need to do is
include our route code:
app.use('/', publicRoutes);
app.use('/api', stormpath.apiAuthenticationRequired, apiRoutes);
app.use('/dashboard', stormpath.loginRequired, privateRoutes);
The way this works is like so:
- First, we’ll tell Express to serve our public routes.
- Then, we’ll tell Express to serve our API routes, but to require that a user
authenticate with their API key before being allowed to access any of these
routes.
- Lastly, we’ll tell Express to render our private (dashboard) routes, but to
require a user to be logged in via the website to access these pages.
The Stormapth middlewares included here automatically handle all of the
authentication logic for us 100%. If we try to access the /dashboard
page
without being logged into the website, for instance, we’ll be immediately
redirected to the login page and forced to authenticate.
If we try to access an API route without using Basic Auth, we’ll get a 401
UNAUTHORIZED message with a nice JSON error.
The Routes
The main part of our application is the routes. This is where all the magic
happens: billing, API code, SMS code, etc.
Let’s take a look at each route, and dissect how exactly they work.
public.js
First, let’s look at our public routes. These routes are responsible for
serving our ‘public’ pages on the website:
'use strict';
var express = require('express');
// Globals
var router = express.Router();
// Routes
router.get('/', function(req, res) {
res.render('index');
});
router.get('/pricing', function(req, res) {
res.render('pricing');
});
router.get('/docs', function(req, res) {
res.render('docs');
});
// Exports
module.exports = router;
As you can see, nothing is happening here except that we’re rendering our
pre-defined Jade templates.
private.js
The private route file contains only a single route: our dashboard page.
Because our BTC SMS app only has a single page for logged-in users (the
dashboard) — this is where that logic is contained.
If we were building a larger site, which had many private pages that only logged
in users could access, they’d be included here also:
'use strict';
var bodyParser = require('body-parser');
var express = require('express');
var stormpath = require('express-stormpath');
var stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
// Globals
var router = express.Router();
// Middlewares
router.use(bodyParser.urlencoded({ extended: true }));
// Routes
router.get('/', function(req, res) {
res.render('dashboard');
});
router.post('/charge', function(req, res, next) {
stripe.charges.create({
amount: 2000,
currency: 'usd',
source: req.body.stripeToken,
description: 'One time deposit for ' + req.user.email + '.'
}, function(err, charge) {
if (err) return next(err);
req.user.customData.balance += charge.amount;
req.user.customData.save(function(err) {
if (err) return next(err);
res.redirect('/dashboard');
});
});
});
// Exports
module.exports = router;
Let’s see how this works.
First, after creating an Express router, we’re using the bodyParser
middleware to decode form data.
On this page, we’ll have a form that allows us to accept payment from a user,
and because of this, we’ll need the ability to read the form data we’re
receiving. This is what the bodyParser middleware is used for:
router.use(bodyParser.urlencoded({ extended: true }));
This middleware let’s us access form data via req.body
. So, for instance, if
a form field called username
was posted to us, we could access that data by
saying req.body.username
.
Next, we’ll register a router handler for the GET requests to our dashboard
page:
router.get('/', function(req, res) {
res.render('dashboard');
});
This code simply renders the dashboard page if a user visits the /dashboard
URL in their browser.
Next, we’ll register a POST handler for the dashboard page:
router.post('/charge', function(req, res, next) {
// stuff
});
This code will get run if a user attempts to deposit money into their account:
![Stripe Deposit Money Stripe Deposit Money]()
What happens in our template code is all of the card collection and verification
stuff. When we receive this POST request from the browser, what that means is
that the user’s card is valid, and Stripe has given us permission to actually
charge this user some money.
In our case, we’ll be charging users a flat fee of 20$.
Using the stripe library, we’ll then charge the user’s card:
stripe.charges.create({
amount: 2000,
currency: 'usd',
source: req.body.stripeToken,
description: 'One time deposit for ' + req.user.email + '.'
}, function(err, charge) {
if (err) return next(err);
req.user.customData.balance += charge.amount;
req.user.customData.save(function(err) {
if (err) return next(err);
res.redirect('/dashboard');
});
});
Once the user’s card has been successfully charged, we’ll also update the user
account’s balance, so that we now know how much money this user has paid us.
And… That’s it for billing! Quite easy, right?
api.js
The last route we need to cover is the API route. Since our API service only
has a single API call, this file only holds one API route. If we were building
a more complex API service, however, this file might be a lot longer:
'use strict';
var bodyParser = require('body-parser');
var express = require('express');
var request = require('request');
var twilio = require('twilio')(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
// Globals
var router = express.Router();
var BTC_EXCHANGE_RATE;
var COST_PER_QUERY = parseInt(process.env.COST_PER_QUERY);
// Middlewares
router.use(bodyParser.json());
// Routes
router.post('/message', function(req, res) {
if (!req.body || !req.body.phoneNumber) {
return res.status(400).json({ error: 'phoneNumber is required.' });
} else if (!BTC_EXCHANGE_RATE) {
return res.status(500).json({ error: "We're having trouble getting the exchange rates right now. Try again soon!" });
} else if (req.user.customData.balance < COST_PER_QUERY) {
return res.status(402).json({ error: 'Payment required. You need to deposit funds into your account.' });
}
var message = '1 Bitcoin is currently worth $' + BTC_EXCHANGE_RATE + ' USD.';
twilio.sendMessage({
to: req.body.phoneNumber,
from: process.env.TWILIO_PHONE_NUMBER,
body: message
}, function(err, resp) {
if (err) return res.status(500).json({ error: "We couldn't send the SMS message. Try again soon!" });
req.user.customData.balance -= COST_PER_QUERY;
req.user.customData.totalQueries += 1;
req.user.customData.save();
res.json({ phoneNumber: req.body.phoneNumber, message: message, cost: COST_PER_QUERY });
});
});
// Functions
function getExchangeRates() {
request('http://api.bitcoincharts.com/v1/weighted_prices.json', function(err, resp, body) {
if (err || resp.statusCode !== 200) {
console.log('Failed to retrieve BTC exchange rates.');
return;
}
try {
var data = JSON.parse(body);
BTC_EXCHANGE_RATE = data.USD['24h'];
console.log('Updated BTC exchange rate: ' + BTC_EXCHANGE_RATE + '.');
} catch (err) {
console.log('Failed to parse BTC exchange rates.');
return;
}
});
}
// Tasks
getExchangeRates();
setInterval(getExchangeRates, 60000);
// Exports
module.exports = router;
Like our private.js routes, we’ll also be using the bodyParser middleware
here to read in API request data.
We’ll also be making use of the twilio library to send SMS messages to
users, as well as the request library to fetch the current Bitcoin exchange
rates from bitcoincharts.
The bitcoincharts site provides a publicly available API that lets you grab the
current Bitcoin exchange rates. This is where we’ll be grabbing our Bitcoin
value information from =) You can find more information on this here:
http://api.bitcoincharts.com/v1/weighted_prices.json
So, once we’ve defined our Express router, the first thing we’ll do is declare
some globals:
var BTC_EXCHANGE_RATE;
var COST_PER_QUERY = parseInt(process.env.COST_PER_QUERY);
The BTC_EXCHANGE_RATE
variable will be set to the current value of Bitcoin in
USD, and updated frequently. This is what we’ll use when we send out SMS
messages to users.
The COST_PER_QUERY
variable is the amount of money (in cents) that we’ll
charge a user for each successful API request made.
Next, we’ll define a helper function called getExchangeRates
which queries the
bitcoin charts API service to find the current value of a Bitcoin:
function getExchangeRates() {
request('http://api.bitcoincharts.com/v1/weighted_prices.json', function(err, resp, body) {
if (err || resp.statusCode !== 200) {
console.log('Failed to retrieve BTC exchange rates.');
return;
}
try {
var data = JSON.parse(body);
BTC_EXCHANGE_RATE = data.USD['24h'];
console.log('Updated BTC exchange rate: ' + BTC_EXCHANGE_RATE + '.');
} catch (err) {
console.log('Failed to parse BTC exchange rates.');
return;
}
});
}
This function simply makes the request, then extracts the data. Finally it
assigns the current value to the global BTC_EXCHANGE_RATE
variable defined
earlier.
After that’s done, we’ll invoke this function in two ways:
getExchangeRates();
setInterval(getExchangeRates, 60000);
First, we’ll call it immediately so that as soon as our program starts, we get
the current BTC value.
Next, we’ll call it on a setInterval
job, which executes once per hour (in
milliseconds). This ensures that every hour we’ll update the BTC exchange rate
to the latest values.
Finally, we’ll implement our API route /api/message
, which is what developers
will be using to send SMS messages with the current BTC exchange rate
information:
router.post('/message', function(req, res) {
if (!req.body || !req.body.phoneNumber) {
return res.status(400).json({ error: 'phoneNumber is required.' });
} else if (!BTC_EXCHANGE_RATE) {
return res.status(500).json({ error: "We're having trouble getting the exchange rates right now. Try again soon!" });
} else if (req.user.customData.balance < COST_PER_QUERY) {
return res.status(402).json({ error: 'Payment required. You need to deposit funds into your account.' });
}
var message = '1 Bitcoin is currently worth $' + BTC_EXCHANGE_RATE + ' USD.';
twilio.sendMessage({
to: req.body.phoneNumber,
from: process.env.TWILIO_PHONE_NUMBER,
body: message
}, function(err, resp) {
if (err) return res.status(500).json({ error: "We couldn't send the SMS message. Try again soon!" });
req.user.customData.balance -= COST_PER_QUERY;
req.user.customData.totalQueries += 1;
req.user.customData.save();
res.json({ phoneNumber: req.body.phoneNumber, message: message, cost: COST_PER_QUERY });
});
});
This API route will:
- Check to ensure that the incoming request data is formatted properly. Each
incoming request must supply a phone number so we know who to SMS.
- If no phone number is supplied, we’ll return an HTTP 400 response, along with
an appropriate error.
- If the BTC exchange rate isn’t available for some reason (maybe the bitcoin
charts API is down), we’ll return a 500 status code along with an appropriate
JSON error message.
- Lastly, if the user doesn’t have enough money in their account to pay for this
request, we’ll reject the request with a 402 status code an another
appropriate error message.
Once we’ve done the error handling stuff, we’ll use the Twilio library to send
an SMS message from our pre-purchased phone number
(process.env.TWILIO_PHONE_NUMBER
), with our pre-formatted message.
If, for any reason, the SMS message sending fails, we’ll return a 500 with an
error message.
If the SMS message succeeds, we’ll subtract 2 cents from the user’s account
balance, increment the user’s total queries counter, and then return a
successful JSON response message.
It’s that simple!
Running the App
To run the app, as you saw through the code explanations, you’ll need to define
some environment variables.
Here is a full list of the required environment variables you need to set to run
this thing:
$ export COST_PER_QUERY=2
$ export STORMPATH_API_KEY_ID=xxx
$ export STORMPATH_API_KEY_SECRET=xxx
$ export STORMPATH_APPLICATION=https://api.stormpath.com/v1/applications/xxx
$ export STRIPE_SECRET_KEY=xxx
$ export STRIPE_PUBLISHABLE_KEY=xxx
$ export TWILIO_ACCOUNT_SID=xxx
$ export TWILIO_AUTH_TOKEN=xxx
$ export TWILIO_PHONE_NUMBER=+18882223333
These variables will be used automatically in the project code to make things
work as needed.
Once these variables have been defined, you can then run your own instance of
the BTC SMS app by saying:
$ node index.js
And then visiting http://localhost:3000
.
To deposit money into your account using Stripe (in test mode), you can use
the credit card number 424242424242
, with any fake expiration date and CVC
number. This will deposit funds into your account.
Lastly, to make successful API requests, you can use the cURL command line
tool like so:
$ curl -v —user ‘API_KEY_ID:API_KEY_SECRET’ -H ‘Content-Type: application/json’ —data ‘{“phoneNumber”: “+18882223333”}’ ‘http://127.0.0.1:3000/api/message’
Be sure to substitute in your own phone number and API credentials (taken from
the BTC SMS dashboard page).
What Did We Learn?
Building a simple API service isn’t really all that hard. In just a few hours
you can structure, plan, and implement a full-fledged API company with only a
few small, free-to-use services.
The old days where launching a company took a lot of time and effort are long
gone. Using API services to speed up your development can save you a bunch of
time, effort, and problems.
I hope that this tutorial gave you a little bit of inspiration, taught you
something new, and hopefully gave you some new ideas for your own cool projects.
Be sure to check out Stormpath, Twilio, and Stripe for your next
projects =)
Oh — and if you have any questions, leave us a comment below!
PS: If you’re currently learning how to build API services and do stuff with
Node.js, I’d recommend really writing this code out and playing around with it
yourself. There’s no better way to learn this stuff than by messing around with
it on your own =)
-Randall