Creating a Simple Payment System in Node.js and MongoDb Using Paystack: A Step-by-Step Guide 💳💸
Tags: paystack, paymentintegration, node, express
Published: 2023-09-12 19:44:52

## Introduction

Hey folks! I am Kizito a Software Engineer at Microsofzz(wakes up). In this article I will be guiding you through developing API endpoints for payment integration on Paystack 😎 which can be used for your client side applications.

If you seek to monetize your applications with Paystack using Node.js seek no further, for you are in the right place 🤩 

**Prerequisites**

- Basic Knowledge of Javascript, Node.js and Express
- You should have VS Code, Node.js, MongoDb and Postman Installed.
- Experience in working with Databases (I'll be using MongoDb for this article).
- A heart willing to learn ❤️‍🔥


![PaystackPaymentJoy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g1jaxrza6f31qsuksuzq.jpg)


## Let's dive in

In today's digital age, where e-commerce is booming and online transactions have become the norm, building a seamless and secure payment system is paramount for businesses and developers alike. But building a payment system can be a daunting task, and that's where Paystack comes into play. Paystack, a widely trusted payment gateway, provides developers with the tools they need to handle payments effortlessly. In this comprehensive guide, we'll embark on a journey to demystify the process of building a cutting-edge payment system in Node.js using Paystack. So, fasten your seatbelts, as we embark on this exciting journey into the world of Node.js and Paystack-powered payment systems.

**Setup**

If you do not have a paystack account you'll need to start by creating one here https://dashboard.paystack.com/#/signup if you already have one then you can proceed to login.
Having done that we can proceed to development 👩‍💻👨‍💻

**I will be making use of couple of the most popular endpoints in this article which will be the _create charge endpoints_ and the _subscription endpoints_** 🥁🥁

Open up your VS Code and create a new folder directly from your terminal as a senior dev 🫡. I'll be calling mine paystack-nodejs-integration

Then you cd into that directory using

```
mkdir paystack-nodejs-integration
cd paystack-nodejs-integration
```

![terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dmgm6cu7hyxbvqpikwcx.png)

Now that we are in that same directory we do

```
npm init -y
```
and we see our package.json file opens up 👏
Next up let us install our dependencies. 
These are the dependencies that we are going to use to develop this awesome system and I'll shed light 💡 on them one after another.
Then we create our index.js, env and gitignore file.

```
npm install cors dotenv express mongoose paystack-api
npm install --save-dev nodemon @ngrok/ngrok
touch index.js .env .gitignore
```
As stated, I will shed light on each of the dependencies.

**cors:** CORS is a node.js package for providing a Connect/Express middleware that can be used to enable cross-origin-resource-sharing with various options for access to our API.
**dotenv:** Dotenv is a zero-dependency module that loads environment variables from a .env file into process.env. Storing configuration in the environment separate from code is based on The Twelve-Factor App methodology and we are going to use it to store our API Secret Keys.
**express:** This is the Nodejs framework we will use to build our API.
**mongoose:** Mongoose is the tool that makes it easier for us to work directly and interact with our MongoDb.
**paystack-api:** The Pariola paystack api wrapper which does all the paystack under the hood connections in our nodejs app and provides functions for our integration.
**nodemon:** nodemon is a tool that helps develop Node.js based applications by automatically restarting the node application when file changes in the directory are detected.
**ngrok:** ngrok delivers instant ingress to your apps in
any cloud, private network, or devices
with authentication, load balancing, and other critical controls. We will be using it for our webhook event testing to connect our localhost to a live URL 🔥
You can create an account here [Ngrok](ngrok.com)

Nodemon and Ngrok were installed as dev dependencies because we wouldn't be needing them live unless you want to use some of ngrok's additional features.

Sooo our folder structure should look similar to this and now we startup our express server in our index.js file.

![PaystackFolderStructure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/drvrti6i2vdps3a378nh.png)

**index.js**
```javascript
// imports
const express = require("express");
const dotenv = require("dotenv");
const cors = require("cors");

// specify the port from our enviroment variable
const PORT = process.env.PORT || 8080;
app.use(cors());
app.use(express.json());
// connect database

// routes

app.get('/', async (req, res) => {
    res.send("Hello World!");
});

app.listen(PORT, () => {
    console.log(
        `Server running in ${process.env.NODE_ENV} mode on port ${PORT}`
    );
});
```

**.env**
```
PORT=8000
NODE_ENV=development
```
**.gitignore**

```
node_modules
.env
```
**package.json**

```json
{
  "name": "paystack-nodejs-integration",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.8.5",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "mongoose": "^7.5.0",
    "paystack-api": "^2.0.6"
  },
  "devDependencies": {
    "@ngrok/ngrok": "^0.6.0",
    "nodemon": "^3.0.1"
  }
}

```
Notice the scripts in the json file. We will be using ``` npm run dev ``` on the terminal for our development mode.

![ConnectedNode](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ss9bjg2u2i1ydh2u7oe.png)
Then we try the url on postman

![PostmanImage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofuneedp0qedqsmlwgkj.png)

Who dey check! (exclaims in joy)

Now let us connect to our MongoDb Compass.
![MongoDbCompass](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9mtn6hmuvg520fy9aj5u.png)

Then we set our MONGODB_URI in our **.env** file

```
MONGODB_URI=mongodb://0.0.0.0:27017/paystack-node-integration
```
After that, we add our MongoDb configuration in the **config/db.js** file and then we save

```javascript
const mongoose = require("mongoose");
mongoose.set("strictQuery", false);

const connectDB = async () => {
  try {
    const conn = await mongoose.connect(process.env.MONGODB_URI, {
      useUnifiedTopology: true,
      useNewUrlParser: true,
    });

    console.log(`MongoDB Connected: ${conn.connection.host}`);
  } catch (err) {
    console.error(`Error: ${err.message}`);
    process.exit(1);
  }
};

module.exports = connectDB;
```
Then we import and call the function in our **index.js**

```javascript
// imports
const connectDB = require("./config/db.js");

// connect database
connectDB();
```
Boom! Our database is connected

![ConnectedMongoDb](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pahnvzvktb55bvqi4qle.png)

**Now let us create a system where we have users and they can donate for a campaign using their cards and can also be able to subscribe to a plan you created.**

Let us start by creating the user model for our database.
We create a folder called models and in the folder, we create a file called userModel.js.

**models/userModel.js**

```javascript
const mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
    fullname: {
        type: String,
    },
    email: {
        type: mongoose.Schema.Types.Mixed,
    },
    paystack_ref: {
        type: String,
    },
    amountDonated: {
        type: Number,
    },
    isSubscribed: {
        type: Boolean,
    },
    planName: {
        type: String,
    },
    timeSubscribed: {
        type: Date,
    },
});

const User = mongoose.model("user", userSchema);

module.exports = User;
```
We are going to be using the common MVC (Models, Views, Controller) architectural pattern except ours will be routes endpoints instead of views.
So let's create two(2) extra folders **controllers** and **routes** to make our app easy to debug, more scalable and to be understood by other developers unless, you go explain tire (you will over explain).

We want to create two endpoints for now. The first will be to create a user and the second will be to get the user.
In our **controllers** we create a new file called **userController.js**

**controllers/userController.js**

```javascript
const User = require("../models/userModel");
// Require paystack library

const createUser = async (req, res) => {
    let { email, fullname } = req.body;

    const user = new User({
        fullname,
        email,
    });
    await user.save();

    res.status(201).send({
        data: user,
        message: "User created successfully",
        status: 0,
    });
}

const getUser = async (req, res) => {
    try {
        let { id } = req.params;
        const user = await User.findById(id);

        res.status(200).send({
            user,
            message: "Found user Details",
            status: 0,
        });
    } catch (err) {
        res.status(500).send({ data: {}, error: err.message, status: 1 });
    }
};

// initialize transaction

module.exports = {
    createUser,
    getUser,
};
```
After that, we create another folder named **routes** and create a file called **userRoutes.js** which will be used to call our controller functions.
**routes/userRoutes.js**

```javascript
const express = require("express");
const userRoute = express.Router();

const {
    getUser,
    createUser,
} = require("../controllers/userController");

userRoute.get("/getUser/:id", getUser);
userRoute.post("/createUser", createUser);

module.exports = {
    userRoute,
};
```
In order to be able to use the routes, we have to import it in our **index.js** file.

```javascript
// routes
const { userRoute } = require("./routes/userRoutes.js");
app.use("/users", userRoute);
```
So far so good! Our folder structure should look like this.
![structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbc8udxqimivc2f1mu84.png)
Now we test our user creation endpoint on postman and see that it works and it reflects in our database 🎈
![usercreationtest](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tlgjvw8vm5gx4gt6v8m8.png)

![db](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k584atebupxr2jho4yor.png)
Congratulations on making it this far👏👏 I guess we both deserve a drink 🍻
This is the moment we all have been waiting for.
Moving on to the next phase, we remember that we want to create a system where we have users and they can donate for a campaign and can also be able to subscribe to a plan you created.

These are two key implementations

1. Campaign donations
2. Plan Subscriptions

**Campaign Donations**
Imagine if we are organizing an event and we need people to monetize by donating some funds, let us create an API endpoint for this.

Before that let's go to paystacks dashboard settings and copy our secret key in order for us to be able to interact with our account. https://dashboard.paystack.com/#/settings/developers
And for this article purposes I will be on **test** mode so no real funds are going to be transacted.
![PaystackSecretKey](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/firhe75sx7yoqra3nla9.png)
⚠️Remember, on no account should you share your secret key to anyone that is why it is going to be stored in an environmental variable which will be omitted on any deployment. In case of any public display which was done mistakenly you should generate a new key. ⚠️

Adding a new line to our **.env** file where we copy and paste our secret key there.
**.env**
```
TEST_SECRET=YOUR_SECRET_KEY
```
In our **controllers/userController.js** we add the following lines to initialize a transaction and we save the transaction reference to get the details about a particular transaction which you must have come across in your regular banking transactions.
https://paystack.com/docs/api/charge/#create

**controllers/userController.js**

```javascript
// Require paystack library
const paystack = require("paystack-api")(process.env.TEST_SECRET);

// initialize transaction
const initializeTrans = async (req, res) => {
    try {
        let { id } = req.params;
        const { email, amount, plan, } = req.body;

        const response = await paystack.transaction.initialize({
            email,
            amount,
            plan, // optional but we'll use for subscription
        });

        const data = {
            paystack_ref: response.data.reference,
        };

        await User.findByIdAndUpdate(id, data);

        res.status(200).send({
            data: response.data,
            message: response.message,
            status: response.status,
        });

    } catch (error) {
        res.status(400).send({ data: {}, error: `${error.message}`, status: 1 });
    }
};

// verify transaction

module.exports = {
    ...,
    initializeTrans,
};
```
If you look at the code above you notice we have 3 inputs taken into the body parameters.
email, amount and plan(which is optional but since we are also going to have subscriptions we will be using it later.

Let's add it to our userRoute
**routes/userRoutes.js**

```javascript
const {
    ...,
    initializeTrans,
} = require("../controllers/userController");

userRoute.post("/initiatetransaction/:id", initializeTrans);
```
Yes! time to test on postman.
I set the amount to be 300000 which is 3000 due to currency two decimal places and the email to be mine as payer.

![postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/es1j57c88uc225ijayri.png)
It works 👏
We are going to click on the authorization_url and see what happens.

![Paystack](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zhnk12u1u8526pwct5mn.png)
Bravo friends 💪 we can see our payment options provided and proceed to payment.
After payment, we want to verify that the payment was successful and we can achieve that in two methods.

1. Using the paystack verify endpoint https://paystack.com/docs/payments/verify-payments/
2. Using webhooks to listen for events
https://paystack.com/docs/payments/webhooks/

In this article we will be covering this two methods 🥁

Let's start by verifying the transaction we just made with the verify endpoint. On verification, we add the amount the user donated and change the transaction reference to success in our database.

**controllers/userController.js**
```javascript
// verify transaction
const verifyTrans = async (req, res) => {
    try {
        let { id } = req.params;

        const user = await User.findById(id);

        if (user.paystack_ref == "success")
            return res.status(401).send({
                data: {},
                message: "Transaction has been verified",
                status: 1,
            });

        const response = await paystack.transaction.verify({
            reference: user.paystack_ref
        });

        if (response.data.status == "success") {
            const data = {
                paystack_ref: response.data.status,
                amountDonated: response.data.amount,
            };
            await User.findByIdAndUpdate(id, data);

            return res
                .status(200)
                .send({
                    data: response.data,
                    message: response.message,
                    status: response.status,
                });
        } else {
            return res
                .status(200)
                .send({
                    data: response.data,
                    message: response.message,
                    status: response.status,
                });
        }

    } catch (error) {
        res.status(400).send({ data: {}, error: `${error.message}`, status: 1 });
    }
};

module.exports = {
    ...,
    verifyTrans,
};
```

**routes/userRoutes.js**

```javascript
const {
    ...,
    verifyTrans,
} = require("../controllers/userController");

userRoute.post("/verifytransaction/:id", verifyTrans);
```
After testing the endpoint on postman, we can verify it works from our response and in our database 🤩🤩

![postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/idjb1ka6xoatbds476dl.png)

![mongodb](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xd6knfpiu2967c4oww6z.png)
Yay! Now we are sure this works, we can go ahead to work on the **Plan Subscriptions** and over here, we will get to see how we can use webhooks for event listening 👂🪝

**Plan Subscriptions**
In a scenario whereby you are the CEO of a kitchen app and you want people to be able to subscribe to give them access to various chefs on the app you have to create several plans for them which can either be **weekly** or **monthly** and so on. Let's create these plans and enable subscription on our app.
We can also create plans directly from our paystack dashboard but I will be doing so programatically.

We create a new file in our **controllers** and call it **planController.js** where we are going to create our createPlan, getPlan and webhook function.

**controllers/planController.js**

```javascript
// Require the library
const paystack = require("paystack-api")(process.env.TEST_SECRET);

const createPlan = async (req, res) => {
    try {
        const { interval, name, amount } = req.body;

        const response = await paystack.plan.create({
            name,
            amount,
            interval,
        });

        res.status(200).send({
            data: response.data,
            message: response.message,
            status: response.status,
        });

    } catch (error) {
        res.status(400).send({ data: {}, error: `${error.message}`, status: 1 });
    }
};

const getPlans = async (req, res) => {
    try {
        const response = await paystack.plan.list();

        res.status(200).send({
            data: response.data,
            message: response.message,
            status: response.status,
        });

    } catch (error) {
        res.status(400).send({ data: {}, error: `${error.message}`, status: 1 });
    }
};

// our webhook function for event listening

module.exports = {
    createPlan,
    getPlans,
};
```
We create a new file in our **routes** call **planRoutes.js**

**routes/planRoutes.js**

```javascript
const express = require("express");
const planRoute = express.Router();

const {
    createPlan,
    getPlans,
} = require("../controllers/planController");

planRoute.get("/getPlans", getPlans);
planRoute.post("/createPlan", createPlan);

module.exports = {
    planRoute,
};
```
Let's also not forget to import it in our **index.js**

**index.js**

```javascript
// routes
const { planRoute } = require("./routes/planRoutes.js");

app.use("/plans", planRoute);
```
Saving our file and testing our **createPlan** endpoint, we see that it works 👍

![createplan](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggj5jmzew8755m9bfifk.png)
We have successfully created a plan for our users to subscribe to 🍻
What next...? Yes you are right, we have to subscribe to the plan but before we do that, we are going to add our webhook functions to listen to transaction events sent by paystack.

So we create a new folder called **helpers** and there we create a file called **webhookHelpers.js**
In this file we have 3 functions that will be triggered when an event is updated **chargeSuccess**, **planChargeSuccess** and **cancelSubscription**. Depending on your choice of usage you can create more.
https://paystack.com/docs/payments/subscriptions/
 
**helpers/webhookHelpers.js**
```javascript
const User = require("../models/userModel");

// Require the library
const paystack = require("paystack-api")(process.env.TEST_SECRET);

// Paystack webhook helpers: Functions that should be called on paystack event updates
// invoicePaymentFailed, invoiceCreation, invoiceUpdate, subscriptionNotRenewed, subscriptionDisabled, chargeSuccess

const chargeSuccess = async (data) => {
    try {
        const output = data.data;
        const reference = output.reference;
        // console.log(output);

        const user = await User.findOne({ paystack_ref: reference });
        const userId = user._id;
        console.log("Updating charge status");

        if (user.paystack_ref == "success")
            return ({
                data: {},
                message: "Transaction has been verified",
                status: 1,
            });

        const response = await paystack.transaction.verify({
            reference: user.paystack_ref
        })

        if (response.data.status == "success") {
            const data = {
                paystack_ref: response.data.status,
                amountDonated: output.amount,
            }
            await User.findByIdAndUpdate(userId, data);

            console.log("Charge Successful");
        } else {
            console.log("Charge Unsuccessful");
        }

    } catch (error) {
        console.log({ data: {}, error: `${error.message}`, status: 1 });
    }
};

// succesful subscription
const planChargeSuccess = async (data) => {
    try {
        const output = data.data;
        const reference = output.reference;
        // console.log(output);

        const user = await User.findOne({ paystack_ref: reference });
        const userId = user._id;
        // console.log(user, reference);

        console.log("Updating charge status");

        // subscribe for user
        if (user.paystack_ref == "success")
            return ({
                data: {},
                message: "Transaction has been verified",
                status: 1,
            });

        const response = await paystack.transaction.verify({
            reference: user.paystack_ref
        })

        if (response.data.status == "success") {
            await User.findByIdAndUpdate(userId, {
                isSubscribed: true,
                paystack_ref: response.data.status,
                planName: output.plan.name,
                timeSubscribed: response.data.paid_at,
            });
            console.log("Charge Successful");
        } else {
            console.log("Charge Unsuccessful");
        }

    } catch (error) {
        console.log({ data: {}, error: `${error.message}`, status: 1 });
    }
};

// invoicePaymentFailed
const cancelSubscription = async (data) => {
    try {
        const output = data.data;
        const reference = output.reference;
        // console.log(output);

        const user = await User.findOne({ paystack_ref: reference });
        const userId = user._id;

        console.log("Cancelling subscription...");

        await User.findByIdAndUpdate(userId, {
            isSubscribed: true,
            paystack_ref: response.data.status,
            planName: "cancelled",
        });
        console.log("User Subscription Cancelled");

    } catch (error) {
        console.log({ data: {}, error: `${error.message}`, status: 1 });
    }
};

module.exports = {
    planChargeSuccess,
    chargeSuccess,
    cancelSubscription,
};
```
In our **planController** we import the helper functions
Once an action like a successful charge is created it triggers an event which we are going to select an endpoint URL for paystack to send the data to.

**controllers/planController.js**
```javascript
// Require the library
const { planChargeSuccess, chargeSuccess, cancelSubscription, } = require("../helpers/webhookHelpers");

// our webhook function for event listening
// you can edit this to your style
const addWebhook = async (req, res) => {
    try {
        let data = req.body;
        console.log('Webhook data: ', data);

        switch (data) {
            case data.event = "invoice.payment_failed":
                await cancelSubscription(data);
                console.log("Invoice Failed");
                break;
            case data.event = "invoice.create":
                console.log("invoice created");
                break;
            case data.event = "invoice.update":
                data.data.status == "success" ?
                    await planChargeSuccess(data) :
                    console.log("Update Failed");
                break;
            case data.event = "subscription.not_renew":
                console.log("unrenewed");
                break;
            case data.event = "subscription.disable":
                console.log("disabled");
                break;
            case data.event = "transfer.success":
                console.log("transfer successful");
                break;
            case data.event = "transfer.failed":
                console.log("transfer failed");
                break;
            case data.event = "transfer.reversed":
                console.log("transfer reversed");
                break;
            case data.event = "subscription.disable":
                console.log("disabled");
                break;

            default:
                // successful charge
                const obj = data.data.plan;
                console.log("Implementing charges logic...");
                // object comparison verifying if its a normal payment or a plan
                // charges for subscription and card
                Object.keys(obj).length === 0 && obj.constructor === Object ?
                    await chargeSuccess(data) :
                    // charge sub
                    await planChargeSuccess(data);
                console.log("Successful");
                break;
        }

    } catch (error) {
        res.status(400).send({ data: {}, error: `${error.message}`, status: 1 });
    }
};
module.exports = {
    ...,
    addWebhook,
};
```
We are going to call it here in the **planRoutes**
```javascript
const {
    ...,
    addWebhook,
} = require("../controllers/planController");

planRoute.post("/paystackWebhook", addWebhook);
```
To us who made it this far we deserve pounded yam, don't we? 😁
One final push!
Before we begin trying out our endpoint we need to use our **ngrok** tool to deploy our localhost live so we can add the url to paystack.
https://dashboard.paystack.com/#/settings/developers
![webhookurl](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ljyypyk0ynyn36wcbu87.png)
Then we login to https://dashboard.ngrok.com/get-started/your-authtoken for our token, copy it and add it to our **.env** file

**.env**
```
NGROK_AUTHTOKEN=YOUR_AUTH_TOKEN
```
We add the following lines of code to our **index.js**

**index.js**
```javascript
// imports
const ngrok = require("@ngrok/ngrok");

// at the bottom
if (process.env.NODE_ENV == "development") {
    (async function () {
        const url = await ngrok.connect({ addr: PORT, authtoken_from_env: true, authtoken: process.env.NGROK_AUTHTOKEN });
        console.log(`Ingress established at: ${url}`);
    })();
} 
```
Then we save and our terminal should look similar to this and if we click on the link we should see our Hello World!
![ngrokterminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4z630ogrppa3oouzqs10.png)
Let's add the link to our test webhook URL on paystack so our events can be triggered. Your link should be different.
https://ce41-102-88-63-21.ngrok-free.app/plans/paystackWebhook

![page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sg824mb9yma2e9tvix78.jpeg)
**Note**: Each time nodemon restarts our server, the webhook URL changes. 

Great! so we test with the **initiatetransaction** endpoint on postman but notice this time we added a plan to the body and that amount will be overridden by the plan amount we created.
![subscribe](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q7bh1k2y3egwbj8avcky.png)
![paystackpay](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5v7ovrws1m9pm536r9fx.png)

![webhook log](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/52xtkncplk8phre63q3u.png)

![mongodbsub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iex6mre097bruhe41gql.png)

Yay 🥳🎉 It works smoothly 🤩
Now we can stand up and stretch our body... Feels good 😋

**Summary**
We have learnt and sharpened up lot from working with paystack transactions, REST APIs, modelling our database, building with a solid architecture, working with tools and libraries like nodemon, dotenv, mongoose, ngrok etc. and we have seen an importance of webhooks and event in developing real world applications and even cases of initializing payment process for a contribution or a subscription plan.

Paystack also has a lot more features you can explore in their documentation.

If you encounter any challenge, feel free to comment here or connect with me on [LinkedIn](https://www.linkedin.com/in/kizito-nwaka-b369691a7/) or [Twitter](https://twitter.com/kelechithe3rd)

Congratulations once again 🎉
Thanks for coming all the way and I wish you success in your endeavours.

[Link to Github Repo](https://github.com/Kizito007/paystack-nodejs-integration)
[Paystack Docs](https://paystack.com/docs)