Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 8811

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 60k
Author
Asked: November 28, 20242024-11-28T04:35:10+00:00 2024-11-28T04:35:10+00:00

How to Build a Wallet System with Flutterwave Payment Integration Into NodeJs Application

  • 60k

Today, many website owners want online payment gateways, especially as the industry has developed by leaps and bounds. Creating a wallet system is a bit of a nightmare when designing an app for the health sector, finance, or other sectors.

We will learn how to create a wallet system and how to integrate the Flutterwave payment system in this tutorial.

Prerequisites

To follow along with this tutorial, we will need:

  • A working knowledge of JavaScript.
  • A good understanding of Node.js.
  • A basic understanding of MongoDB or any database of our choice.
  • Postman and some knowledge on how to use Postman.

We also need a Flutterwave account to receive or accept payments from our application.

What is a Wallet System?

A digital wallet, often known as an e-wallet, is an electronic device, internet service, or software application that allows one party to exchange digital currency units for products and services with another party. This can include utilizing a computer to purchase products online or using a smartphone to buy in a store.

Before any transactions, money can be deposited in the digital wallet, or an individual's bank account can be linked to the digital wallet in other situations.

What Are the Benefits of a Digital Wallet?

  • Instead of having to fish your card out of your likely bulging wallet every time you want to make an online transaction, you can simply hold your smartphone, log into your account, and you're ready to go.

  • Most applications allow you to organize all of your information in a readily accessible manner, saving you time from rummaging through your wallet for the things you need.

  • Many digital wallet apps offer a variety of bonuses and prizes to their users, which might help you get more “bang for your buck” on specific transactions.

What is Flutterwave?

From online collections to payouts and everything in between, Flutterwave can help you develop any form of payment flow for your specific use case.

They also provide several services that allow you to transfer and receive money over the world in a matter of minutes.

Create a directory, Install dependencies, and set up authentication

To get started, we'll need to set up our project.

Open Visual Studio Code by navigating to a directory of our choice on our machine and opening it on the terminal.

Then execute:

   code.  
Enter fullscreen mode Exit fullscreen mode

Note: code . won't work if we don't have Visual Studio Code installed on our system.

Create a directory and initialize npm.

Create a directory and initialize npm by typing the following command:

  • Windows power shell
    mkdir wallet-demo-with-flutterwave     cd wallet-demo-with-flutterwave     npm init -y  
Enter fullscreen mode Exit fullscreen mode

  • Linux
    mkdir wallet-demo-with-flutterwave     cd wallet-demo-with-flutterwave     npm init -y  
Enter fullscreen mode Exit fullscreen mode

Create files and directories

In the earlier step, we initialized npm with the command npm init -y, which automatically created a package.json.

We need to create the model, config directory, and files, for example, wallet.js, wallet_transaction.js, transaction.js, database.js using the commands below.

    mkdir model config     touch config/database.js model/wallet.js     model/wallet_transaction.js model/transaction.js     model/user.js  
Enter fullscreen mode Exit fullscreen mode

We can now create the index.js and app.js files in the root directory of our project with the command.

 touch app.js index.js  
Enter fullscreen mode Exit fullscreen mode

As shown in the image below:

Flutterwave demo

Install dependencies

We'll install several dependencies like mongoose, jsonwebtoken, express, dotenv, axios, bcryptjs, and development dependency like nodemon to restart the server as we make changes automatically.

We will install mongoose because I will be using MongoDB in this tutorial.

User credentials will be checked against what we have in our database. As a result, the entire authentication process isn't limited to the database we'll use in this tutorial.

   npm install jsonwebtoken dotenv mongoose express bcryptjs axios    npm install nodemon -D  
Enter fullscreen mode Exit fullscreen mode

Create a Node.js server and connect your database

By adding the following snippets to our app.js, index.js, database.js, .env in that order, we can now create our Node.js server and connect it to our database.

In our database.js.

config/database.js:

 const mongoose = require("mongoose");  const { MONGO_URI } = process.env;  exports.connect = () => {   // Connecting to the database   mongoose     .connect(MONGO_URI, {       useNewUrlParser: true,       useUnifiedTopology: true,     })     .then(() => {       console.log("Successfully connected to database");     })     .catch((error) => {       console.log("database connection failed. exiting now...");       console.error(error);       process.exit(1);     }); };  
Enter fullscreen mode Exit fullscreen mode

In our app.js:

wallet-demo-with-flutterwave/app.js

 require("dotenv").config(); require("./config/database").connect(); const express = require("express"); const bcrypt = require("bcryptjs"); const jwt = require("jsonwebtoken");  const app = express();  app.use(express.json());  // Logic here  module.exports = app; 
Enter fullscreen mode Exit fullscreen mode

In our index.js:

wallet-demo-with-flutterwave/index.js

const http = require("http"); const app = require("./app"); const server = http.createServer(app);  const { API_PORT } = process.env; const port = process.env.PORT || API_PORT;  // server listening  server.listen(port, () => {   console.log(`Server running on port ${port}`); });  
Enter fullscreen mode Exit fullscreen mode

If you notice, our file needs some environment variables. We will create a new .env file and add our variables before starting our application.

In our .env.

API_PORT=4001  MONGO_URI= //Your database URI here 
Enter fullscreen mode Exit fullscreen mode

To start our server, edit the scripts object in our package.json to look like the below one.

"scripts": {     "start": "node index.js",     "dev": "nodemon index.js",     "test": "echo "Error: no test specified" && exit 1"   } 
Enter fullscreen mode Exit fullscreen mode

The snippet above has been successfully inserted into app.js, index.js, and database.js. First, we built our node.js server in index.js and imported the app.js file with routes configured.

Then, as indicated in database.js, we used mongoose to create a connection to our database.

Execute the command npm run dev.

Both the server and the database should be up and running without crashing.

Create user model and route

We'll define our schema for the user details when signing up for the first time and validate them against the saved credentials when logging in.

Add the following snippet to user.js inside the model folder.

model/user.js

const mongoose = require("mongoose");  const userSchema = new mongoose.Schema({   first_name: { type: String, default: null },   last_name: { type: String, default: null },   email: { type: String, unique: true },   password: { type: String }, });  module.exports = mongoose.model("user", userSchema); 
Enter fullscreen mode Exit fullscreen mode

Now let's create the routes for register and login, respectively.

app.js file in the root directory, we will add the following snippet for user registration and login.

// importing user context const User = require("./model/user");  // Register app.post("/register", (req, res) => { // our register logic goes here... });  // Login app.post("/login", (req, res) => { // our login logic goes here }); 
Enter fullscreen mode Exit fullscreen mode

Implement register and login functionality

We'll be implementing these two routes in our application. We will be using JWT to sign the credentials and bycrypt to encrypt the password before storing them in our database.

From the /register route, we will:

  • Get user input.
  • Validate user input.
  • Validate if the user already exists.
  • Encrypt the user password.
  • Create a user in our database.
  • And finally, create a signed JWT token.

Modify the /register route structure we created earlier to look as shown below.

// ...  app.post("/register", async (req, res) => {    // Our register logic starts here   try {     // Get user input     const { first_name, last_name, email, password } = req.body;      // Validate user input     if (!(email && password && first_name && last_name)) {       res.status(400).send("All input is required");     }      // check if user already exist     // Validate if user exist in our database     const oldUser = await User.findOne({ email });      if (oldUser) {       return res.status(409).send("User Already Exist. Please Login");     }      //Encrypt user password     encryptedPassword = await bcrypt.hash(password, 10);      // Create user in our database     const user = await User.create({       first_name,       last_name,       email: email.toLowerCase(), // sanitize: convert email to lowercase       password: encryptedPassword,     });      // Create token     const token = jwt.sign(       { user_id: user._id, email },       process.env.TOKEN_KEY,       {         expiresIn: "2h",       }     );     // save user token     user.token = token;      // return new user     res.status(201).json(user);   } catch (err) {     console.log(err);   }   // Our register logic ends here });  // ... 
Enter fullscreen mode Exit fullscreen mode

Note: Update your .env file with a TOKEN_KEY, which can be a random string.

Using Postman to test the endpoint, we'll get the response shown below after successful registration.

Register user result - Flutterwave Demo

/login

// ...  app.post("/login", async (req, res) => {    // Our login logic starts here   try {     // Get user input     const { email, password } = req.body;      // Validate user input     if (!(email && password)) {       res.status(400).send("All input is required");     }     // Validate if user exist in our database     const user = await User.findOne({ email });      if (user && (await bcrypt.compare(password, user.password))) {       // Create token       const token = jwt.sign(         { user_id: user._id, email },         process.env.TOKEN_KEY,         {           expiresIn: "2h",         }       );        // save user token       user.token = token;        // user       res.status(200).json(user);     }     res.status(400).send("Invalid Credentials");   } catch (err) {     console.log(err);   }   // Our login logic ends here });  // ... 
Enter fullscreen mode Exit fullscreen mode

Click here to learn more about How to Build an Authentication API with JWT Token in Node.js

Build a Wallet System with Flutterwave Payment Integration

We can now utilize the user details to build wallets, save wallet transactions, and perform other operations in the system now that we have successfully created a user collection and authentication functionality in the previous step.

Let's update our wallet.js, wallet_transaction, and transaction using the following code.

model/wallet.js

const { Schema, model } = require("mongoose");  const walletSchema = Schema(   {     balance: { type: Number, default: 0 },     userId: {       type: Schema.Types.ObjectId,       required: true,       ref: "users",     },   },   { timestamps: true } );  module.exports = model("wallet", walletSchema);  
Enter fullscreen mode Exit fullscreen mode

model/wallet_transaction.js

const mongoose = require("mongoose");  const walletTransactionSchema = new mongoose.Schema(   {     amount: { type: Number, default: 0 },      // Even though user can be implied from wallet, let us     // double save it for security     userId: {       type: String,       ref: "users",       required: true,     },      isInflow: { type: Boolean },      paymentMethod: { type: String, default: "flutterwave" },      currency: {       type: String,       required: [true, "currency is required"],       enum: ["NGN", "USD", "EUR", "GBP"],     },      status: {       type: String,       required: [true, "payment status is required"],       enum: ["successful", "pending", "failed"],     },   },   { timestamp: true } );  module.exports = mongoose.model("walletTransaction", walletTransactionSchema);  
Enter fullscreen mode Exit fullscreen mode

model/transaction.js

const mongoose = require("mongoose");  const transactionSchema =new mongoose.Schema(   {     userId: {       type: Schema.Types.ObjectId,       ref: "user",     },     transactionId: {       type: Number,       trim: true,     },     name: {       type: String,       required: [true, "name is required"],       trim: true,     },     email: {       type: String,       required: [true, "email is required"],       trim: true,     },     phone: {       type: String,     },     amount: {       type: Number,       required: [true, "amount is required"],     },     currency: {       type: String,       required: [true, "currency is required"],       enum: ["NGN", "USD", "EUR", "GBP"],     },     paymentStatus: {       type: String,       enum: ["successful", "pending", "failed"],       default: "pending",     },     paymentGateway: {       type: String,       required: [true, "payment gateway is required"],       enum: ["flutterwave"], // Payment gateway might differs as the application grows     },   },   {     timestamps: true,   } );  module.exports = mongoose.model("Transaction", transactionSchema);  
Enter fullscreen mode Exit fullscreen mode

We've created our wallet, wallet transaction, and transaction schemas, which means we can now receive funds from the client, validate payment using flutterwave on the backend, record and update payment details in the wallet, wallet transaction, and transaction collections, respectively.

Let's get the front-end client ready to accept payments from clients.

We will use the following command to create an index.html file in our root directory.

touch index.html 
Enter fullscreen mode Exit fullscreen mode

Update index.html that we just created with the snippet below:

<!DOCTYPE html> <html lang="en">   <head>     <meta charset="UTF-8" />     <meta name="viewport" content="width=device-width, initial-scale=1.0" />     <title>Receive Payment</title>   </head>   <body>     <form>       <script src="https://checkout.flutterwave.com/v3.js"></script>       <button type="button" onClick="makePayment()">Pay Now</button>     </form>      <script>       function makePayment() {         FlutterwaveCheckout({           public_key: "YOUR_PUBLIC_KEY_HERE",           tx_ref: "hooli-tx-1920bbtyt",           amount: 1000,           currency: "NGN",           country: "NG",           payment_options: "card",            // specified redirect URL           redirect_url: "http://localhost:4001/response",            // use customer details if user is not logged in, else add user_id to the request           customer: {             email: "demomail@gmail.com",             phone_number: "08088098622",             name: "Idris Olubisi",           },           callback: function (data) {             console.log(data);           },           onclose: function () {             // close modal           },           customizations: {             title: "Flutterwave Demo",             description: "Flutterwave Payment Demo",             logo: "https://cdn.iconscout.com/icon/premium/png-256-thumb/payment-2193968-1855546.png",           },         });       }     </script>   </body> </html>  
Enter fullscreen mode Exit fullscreen mode

You can learn more about the snippet above from the Flutterwave documentation

Updating our app.js to render the index.html file from the server-side, we will use the following snippet:

const path = require('path');  // ...  app.post("/login", async (req, res) => { //... }  // Add the route below app.get("/pay", (req, res) => {   res.sendFile(path.join(__dirname + "/index.html"));   //__dirname : It will resolve to your project folder. });  //...  
Enter fullscreen mode Exit fullscreen mode

Before we test our app, we'll see the phrase 'YOUR_PUBLIC_KEY_HERE' in the 'index.html' we created earlier, which implies we'll need a public key from our flutterwave dashboard. Let's go to our dashboard to retrieve our public key.

Flutterwave dashboard

Let's test it out by going to our browser and typing http://localhost:4001/pay. We should see something similar to what we have below after clicking the Pay Now button:

Flutterwave Pay

We will use the test card number:4242424242424242, expiry date: 04/25 and CVV: 202. We will be redirected to the page below to enter OTP, which is 12345 because we are using a test card.

Flutterwave OTP

We'll be redirected to localhost:3000/response after entering the OTP, but nothing will happen. Let's address that by implementing the logic to handle what happens next.

Creating the. /response endpoint, we will update our app.js with the snippet below:

//...  app.get("/response", async (req, res) => {   const { transaction_id } = req.query;    // URL with transaction ID of which will be used to confirm transaction status   const url = `https://api.flutterwave.com/v3/transactions/${transaction_id}/verify`;    // Network call to confirm transaction status   const response = await axios({     url,     method: "get",     headers: {       "Content-Type": "application/json",       Accept: "application/json",       Authorization: `${process.env.FLUTTERWAVE_V3_SECRET_KEY}`,     },   });    console.log(response.data.data) }); 
Enter fullscreen mode Exit fullscreen mode

We get the transaction_id from the query params of our callback in the code above, and we verify the transaction by sending a request to the flutterwave endpoint. We should see something like the screenshot below in our log.

Flutterwave response

Let us create a logic to manage operations such as validating a user's wallet, creating wallet transactions and transactions, among others.

In our app.js let's import Wallet, Wallet transaction, and transaction model.

//...  // importing user context const User = require("./model/user");  const Wallet = require("./model/wallet"); const WalletTransaction = require("./model/wallet_transaction"); const Transaction = require("./model/transaction");  //... 
Enter fullscreen mode Exit fullscreen mode

Updating app.js with the logic to manage operations:

app.get("/response", async (req, res) => {  //.... });  // Validating User wallet const validateUserWallet = async (userId) => {   try {     // check if user have a wallet, else create wallet     const userWallet = await Wallet.findOne({ userId });      // If user wallet doesn't exist, create a new one     if (!userWallet) {       // create wallet       const wallet = await Wallet.create({         userId,       });       return wallet;     }     return userWallet;   } catch (error) {     console.log(error);   } };  // Create Wallet Transaction const createWalletTransaction = async (userId, status, currency, amount) => {   try {     // create wallet transaction     const walletTransaction = await WalletTransaction.create({       amount,       userId,       isInflow: true,       currency,       status,     });     return walletTransaction;   } catch (error) {     console.log(error);   } };  // Create Transaction const createTransaction = async (   userId,   id,   status,   currency,   amount,   customer ) => {   try {     // create transaction     const transaction = await Transaction.create({       userId,       transactionId: id,       name: customer.name,       email: customer.email,       phone: customer.phone_number,       amount,       currency,       paymentStatus: status,       paymentGateway: "flutterwave",     });     return transaction;   } catch (error) {     console.log(error);   } };  // Update wallet  const updateWallet = async (userId, amount) => {   try {     // update wallet     const wallet = await Wallet.findOneAndUpdate(       { userId },       { $inc: { balance: amount } },       { new: true }     );     return wallet;   } catch (error) {     console.log(error);   } };  
Enter fullscreen mode Exit fullscreen mode

We can now update the /response endpoint with all the functions we created to manage different operations.

//...  app.get("/response", async (req, res) => {   const { transaction_id } = req.query;    // URL with transaction ID of which will be used to confirm transaction status   const url = `https://api.flutterwave.com/v3/transactions/${transaction_id}/verify`;    // Network call to confirm transaction status   const response = await axios({     url,     method: "get",     headers: {       "Content-Type": "application/json",       Accept: "application/json",       Authorization: `${process.env.FLUTTERWAVE_V3_SECRET_KEY}`,     },   });    const { status, currency, id, amount, customer } = response.data.data;    // check if customer exist in our database   const user = await User.findOne({ email: customer.email });    // check if user have a wallet, else create wallet   const wallet = await validateUserWallet(user._id);    // create wallet transaction   await createWalletTransaction(user._id, status, currency, amount);    // create transaction   await createTransaction(user._id, id, status, currency, amount, customer);    await updateWallet(user._id, amount);    return res.status(200).json({     response: "wallet funded successfully",     data: wallet,   }); });  //... 
Enter fullscreen mode Exit fullscreen mode

Voila 🥳 We're almost done, so let's test our application. After completing payment, we should get something similar to what we have below:

Fluttwerwave wallet funding result

Because of several trials, while trying to fund our wallet, we have a balance of 10,000 in the screenshot above; however, the number may differ if the procedures are done attentively.

Let's create an endpoint to retrieve a user balance with the code snippet below:

//...  app.get("/wallet/:userId/balance", async (req, res) => {   try {     const { userId } = req.params;      const wallet = await Wallet.findOne({ userId });     // user     res.status(200).json(wallet.balance);   } catch (err) {     console.log(err);   } });  //... 
Enter fullscreen mode Exit fullscreen mode

Testing our endpoint that returns the user balance:

Wallet Balance

Note: We may have noticed that if we reload the page after being redirected to the '/response' endpoint, the same transaction with the same amount is updated in our wallet. To prevent this, we must verify that such a transaction_id does not exist in our system; otherwise, we will receive a duplicate transaction error.

We can modify our logic as shown below:

//...  app.get("/response", async (req, res) => {   const { transaction_id } = req.query;    //...    const { status, currency, id, amount, customer } = response.data.data;    // check if transaction id already exist   const transactionExist = await Transaction.findOne({ transactionId: id });    if (transactionExist) {     return res.status(409).send("Transaction Already Exist");   }    //...    return res.status(200).json({     response: "wallet funded successfully",     data: wallet,   }); });  
Enter fullscreen mode Exit fullscreen mode

Next, we should see something similar to the screenshot below when we refresh the page.

Transaction Exist

Complete code is available on GitHub

Conclusion

In this article, we learned how to build a simple authentication, wallet system, and flutterwave payment integration into our nodeJs application

References

  • Flutterwave
  • Flutterwave Docs

I'd love to connect with you at Twitter | LinkedIn | GitHub | Portfolio

See you in my next blog article. Take care!!!

javascriptnodetutorialwebdev
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 2k
  • Popular
  • Answers
  • Author

    ES6 - A beginners guide - Template Literals

    • 0 Answers
  • Author

    Understanding Higher Order Functions in JavaScript.

    • 0 Answers
  • Author

    Build a custom video chat app with Daily and Vue.js

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.