If you're here, I'm assuming that you already know some programming language for Backend and API stuff. Now you want to integrate stripe and make some paid features or in Layman's terms, "putting some API endpoints behind the paywall which only paid user can access". By the end of this article, You'll be able to start that cool projects with free & paid users modals fearlessly. So Let's get started!
Understanding Stripe's Payment Flow
- Stripe's flow starts with initiating a checkout session i.e. customer asking to pay and you send them a means to pay specific amount.
- Customer pays the amount successfully or cancel the payment.
- If payment is successful, an event will trigger and stripe will notify your backend. Now you can take actions as you wish i.e. giving paid feature access to user.
And that's it!
Start Editing Your Source Code
First, the Language I'm going to use for backend will be Javascript (NodeJs RE). But you can use Rust, Ruby or Python etc, whatever suits you.
Second, I'm Not going to copy-paste whole source code here (Because remember I'm assuming you already have simple backend with Authentication and your precious logic ready)
Last but not least, Register to stripe.com, get your API secret and save it asSTRIPE_PRIVATE_KEY
in your.env
file.
Update User Schema
stripeId
: Register your every new user to stripe so if they make any payment, you can distinguish them.isPro
: Identifier that will decide if user has paid or not!
const UserSchema = new mongoose.Schema({
stripeId: {
type: String,
required: true
},
isPro: {
type: Boolean,
default: false
},
// Usual stuff like username, email & password etc...
});
Update Signup Controller
Before moving forward, do
npm i stripe --save
While Register User into your Database, also register him/her to Stripe.
const stripe = require('stripe')(process.env.STRIPE_PRIVATE_KEY);
// @route POST /api/signup
// @access Public
exports.register = async (req, res) => {
const { username, email, password } = req.body;
let user = await User.findOne({email});
if (user)
res.status(400).json({ message: "Email already registered" })
// register user to stripe
const stripeUser = await stripe.customers.create({
email
});
const newUser = new User({
stripeId: stripeUser.id,
username,
email,
password
});
await newUser.save();
res.status(201).json({ message: "User created" });
}
NOTE: Do not trust user and always validate all user inputs, I'm lazy so am not doing it here!
Initiate The Payment [create endpoint]
Let's create an authenticated endpoint which lets users to initiate payment if they're interested in becoming a PRO member!
const stripe = require('stripe')(process.env.STRIPE_PRIVATE_KEY);
// @route POST /api/purchase
// @access Protected
exports.purchase = async (req, res) => {
if (req.user.isPro)
return res.status(400).json({ message: "you're already a pro member" });
const session = await stripe.checkout.sessions.create({
customer: req.user.stripeId,
payment_method_types: ['card'],
line_items: [{
name: 'pro membership',
description: 'with this membership, You will be able to use all those paid feature of this app!',
amount: 1000 * 100, // Rs 1000
currency: 'inr',
quantity: 1,
}],
success_url: `${process.env.HOST}/success`,
cancel_url: `${process.env.HOST}/cancel`,
});
res.json({
url: session.url
});
}
When your frontend receives the URL as response, You should redirect them to the url using javascript. Or just redirect from server-side using 303 See Other redirect.
Stripe Webhook manager [event handler]
Using previous section's URL, user will pay the specified amount. Now How your backend will know if payment was actually completed or not? That's where stripe webhook comes into play.
We'll setup an endpoint (webhook) that will listen to stripe. If any payment related activity happens stripe will send a event to this webhook in form of POST request.
Now there are so many event but we are only interested in payment completion.
What we want is, when payment happens by our user, stripe will inform us and we'll make that user a PRO member.
Before using below code, do
sudo apt get install stripe
&stripe login
then finally,stripe listen --forward-to http://localhost:1337/api/webhook
when you run this last command, it'll give you a webhook secret, save it asSTRIPE_WEBHOOK_SECRET
in your .env file and let this command running. Also make sure you're running development server in another tab!
// @route POST /api/webhook
// @access public
exports.webhook = (req, res) => {
const sig = req.headers['stripe-signature'];
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
let event = req.body;
event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
// Handle the event
switch (event.type) {
case 'charge.succeeded':
const { customer } = event.data.object
console.log('๐ฐ payment succeeded');
// now make user a pro member
const user = await getUserByStripeId(customer);
user.isPro = true;
await user.save();
// send congratulations email to user [optional]
sendProMemberConfirmationMail(user.email);
break;
default:
console.log(`Unhandled event type ${event.type}`);
return res.status(400).send(`Unhandled event type ${event.type}`);
}
// Return a response to acknowledge receipt of the event
res.json({received: true});
}
NOTE: This above webhook doesn't like request body in json format, instead it like raw buffer as request body. So to do that, let's make a little change to our main file index.js
of the server!
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
app.use('/api/webhook', bodyParser.raw({type: "*/*"}));
// continue file
Now everything from stripe is in place. All that's left is protecting your precious features from non-pro members & that we can do using middleware.
// Middleware: check if user is pro member
function isPro(req, res, next) {
if (req.user.isPro) {
next();
} else {
res.status(403).json({
message: 'User is not PRO member'
});
}
}
And This is sample of protected route that's using above middleware.
const router = require("express").Router();
const { isAdmin, isPro } = require('../middlewares/auth');
const { getProjectById, downloadProject } = require('../controllers/project');
// Get project data
router.get('/:id', getProjectById);
// Download project if you're a PRO member
router.get('/:id/download', isPro, downloadProject);
module.exports = router;
Finally, All things are set. Just re-run the server and check if something is broken ๐ฅฒ (i hope not)
Bonus
Code Snippets shown above are taken from one of my projects, it's full source code is available on Github and Project is hosted on Heroku ๐
I've created 3 projects using Stripe while learning and later tweeted about it, they're also deployed and ofc code is preset at Github. I hope it'll help as reference or just for code review maybe.
Conclusion
In this article, we discussed payment flow of Stripe and then implemented Stripe in our Backend. If you followed along & there's still any doubts or error that you can't figure out, feel free to contact me at karansh491 thru twitter.
PS: I had lots of fun while learning and playing around with Stripe & I hope you'll too!
cya โ๐ป