NodeJS

Building Secure Payment Gateways With Node.js

Nishant Rupareliya
Nishant RupareliyaOct 18, 2024

Introduction

The need for secure and trustworthy payment gateways exist for any eCommerce platform and online service in the present-day digital world. These are considered for new application building or when added to existing applications, which calls for utmost security. This guide would talk about how to build payment gateways securely using Node.js, which is a popular option owing to its inherent ability for scalability and efficiency and a vast ecosystem of libraries.

Prerequisites

  • NodeJS
  • ExpressJS
  • Event-loop
  • mongoDb

Why Choose Node.js for Payment Gateways?

Node.js has an event-driven asynchronous architecture and is capable of handling real-time data such as financial transactions. Here are a few advantages in building a payment gateway using Node.js

:

  • Scalability: Multiple requests can be handled at a time which makes it fit for large applications.
  • Non-blocking I/O: They are capable of processing transactions very smoothly without any block

even under heavy traffic.

  • Extensive Libraries: It has a wide range of libraries that are focused on security such as crypto, jsonwebtoken, helmet, etc.

Core Components of a Payment Gateway

Before we start off with implementation, one must understand the fundamental components involved in a secure payment gateway:

  • Client-Side Integration: Basically setting payment forms on the frontend so that users enter their payment credentials.
  • Server-Side API: Processes the payment transferred to the payment processor with details of the transaction such as Stripe, PayPal, etc., over a secure channel..
  • Transaction Handling: Securely storing and validating transaction details.
  • Error Handling and Logging: To record any failed transactions and system errors while ensuring that sensitive data is not disclosed.

Steps to Build a Secure Payment Gateway with Node.js

To get started with Helmet, follow these simple steps:

Step 1: Setting Up Node.js Environment:

Start by initializing a Node.js project and installing the necessary dependencies:

1mkdir payment-gateway
2cd payment-gateway
3npm init -y
4npm install express body-parser dotenv stripe helmet cors mongodb
5
  • Express: Framework for building web applications.
  • body-parser: Parses incoming request bodies in a middleware.
  • dotenv: For environment variable management (storing API keys securely).
  • Stripe: Example of a payment processor library.
  • Helmet: Enhances HTTP headers for better security.
  • CORS: For handling cross-origin requests securely.
  • MongoDB: For securely storing transaction data.

Step 2: Implementing Environment Variables:

We should never store Sensitive data such as API keys hard-coded in the application. Use environment variables to manage them securely.

Create a ‘.env’ file:

1STRIPE_SECRET_KEY=your_stripe_secret_key
2MONGODB_URI=mongodb://localhost:27017/paymentDB
3


In your code, load the environment variables using `dotenv`:

1require('dotenv').config();
2const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
3

Communication between the server and client should be done using HTTPS to ensure that data is not Attacked by intruders.

Install SSL certificates or utilize a service such as Let's Encrypt to manage this. When deploying to cloud providers such as AWS or Heroku, have HTTPS enabled by default.


1const https = require('https');
2const fs = require('fs');
3const options = {
4  key: fs.readFileSync('server.key'),
5  cert: fs.readFileSync('server.cert')
6};
7
8
9https.createServer(options, app).listen(3000);
10

Step 4: Integrating a Payment Processor (e.g., Stripe)

Once your environment is secure, the next step is to integrate a payment processor. Stripe is a common choice due to its excellent API documentation and ease of integration.

Example of creating a payment route in Express:


1app.post('/payment', async (req, res) => {
2  const { amount, currency, source } = req.body;
3  
4  try {
5    const charge = await stripe.charges.create({
6      amount,
7      currency,
8      source,
9    });
10    res.json({ success: true, charge });
11  } catch (error) {
12    res.status(500).json({ success: false, error: error.message });
13  }
14});
15

Step 5: Validating and Encrypting Payment Data

Use validation libraries like `Joi` or `Validator` to ensure that the data being submitted follows strict validation rules. For example, always validate card numbers, expiration dates, and CVV.

1const Joi = require('joi');
2
3
4const paymentSchema = Joi.object({
5  amount: Joi.number().required(),
6  currency: Joi.string().required(),
7  source: Joi.string().required()
8});
9
10
11app.post('/payment', async (req, res) => {
12  const { error } = paymentSchema.validate(req.body);
13  if (error) {
14    return res.status(400).send(error.details[0].message);
15  }
16
17
18  // Proceed with payment processing...
19});
20

For encryption, use the built-in `crypto` module to encrypt sensitive data before storing or transmitting it.

1const crypto = require('crypto');
2const algorithm = 'aes-256-cbc';
3const key = crypto.randomBytes(32);
4const iv = crypto.randomBytes(16);
5
6
7function encrypt(text) {
8  const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), iv);
9  let encrypted = cipher.update(text);
10  encrypted = Buffer.concat([encrypted, cipher.final()]);
11  return { iv: iv.toString('hex'), encryptedData: encrypted.toString('hex') };
12}
13


Step 6: Preventing Fraud with Strong Authentication:

A 3DS or similar systems should be implemented for an extra layer of security. Most payment gateways like Stripe and PayPal use 3DS verification methods requiring customers to identify themselves with passwords or further verification methods.

1const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
2
3
4app.post('/create-payment-intent', async (req, res) => {
5  const { amount, currency } = req.body;
6
7
8  const paymentIntent = await stripe.paymentIntents.create({
9    amount,
10    currency,
11    payment_method_types: ['card'],
12  });
13
14
15  res.send({
16    clientSecret: paymentIntent.client_secret,
17  });
18});
19


In your code, load the environment variables using `dotenv`:

1require('dotenv').config();
2const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
3

Step 7: Ensuring PCI Compliance:

In light of the fact that PCI DSS serves as the benchmark for ultimate security for the system, this should include:

  • No storage of sensitive card information on the server.
  • Processing information over the secure API of the payment processor.
  • Also perform regular audits on the measures you have in place for security.

Step 8: Implementing Webhooks for Payment Notifications:

Webhooks allow real-time notifications of payment events (e.g., successful payments, failed transactions). Here's how you can set up a webhook endpoint to listen for events from your payment processor:

1pp.post('/webhook', bodyParser.raw({type: 'application/json'}), (req, res) => {
2  const sig = req.headers['stripe-signature'];
3  
4  let event;
5  
6  try {
7    event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
8  } catch (err) {
9    return res.status(400).send(`Webhook Error: ${err.message}`);
10  }
11  
12  // Handle the event
13  if (event.type === 'payment_intent.succeeded') {
14    const paymentIntent = event.data.object;
15    console.log('PaymentIntent was successful!');
16  }
17
18
19  res.json({ received: true });
20});
21


Step 9: Integrating MongoDB for Storing Transaction Data

Use the following code to establish a connection:


1const { MongoClient } = require('mongodb');
2require('dotenv').config();
3
4
5const uri = process.env.MONGODB_URI;
6const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true });
7
8
9async function connectDB() {
10  try {
11    await client.connect();
12    console.log("Connected to MongoDB");
13    const db = client.db('paymentDB');
14    return db;
15  } catch (error) {
16    console.error("MongoDB connection failed:", error);
17  }
18}
19
20
21module.exports = connectDB;
22

Storing Transactions

Once a payment is processed, you can store the transaction details in the database. Create a collection named Transections to store the transaction metadata such as payment ID, amount, and status.

Here’s an example of how you can insert transaction data into MongoDB after processing the payment with Stripe:


1app.post('/payment', async (req, res) => {
2  const { amount, currency, source } = req.body;
3
4
5  try {
6    const charge = await stripe.charges.create({ amount, currency, source });
7
8
9    // Insert transaction data into MongoDB
10    const db = await connectDB();
11    const transactions = db.collection('transactions');
12
13
14    const transactionData = {
15      chargeId: charge.id,
16      amount: charge.amount,
17      currency: charge.currency,
18      status: charge.status,
19      createdAt: new Date(),
20    };
21
22
23    await transactions.insertOne(transactionData);
24
25
26    res.json({ success: true, charge });
27  } catch (error) {
28    res.status(500).json({ success: false, error: error.message });
29  }
30});
31

Retrieving and Managing Transactions

To retrieve transaction history or handle refunds, you can query the transaction collection as needed. Here’s an example of how to implement a route to get all transactions:

1app.get('/transactions', async (req, res) => {
2  try {
3    const db = await connectDB();
4    const transactions = db.collection('transactions');
5    const transactionList = await transactions.find({}).toArray();
6    
7    res.json({ success: true, transactions: transactionList });
8  } catch (error) {
9    res.status(500).json({ success: false, error: error.message });
10  }
11});
12

Step 10: Securing MongoDB Access

Sensitive transaction data should not be stored directly (e.g., with card details), and the MongoDB instance should be under proper access controls. Further, ensure that the MongoDB instance is deployed safely with the use of authentication and encryption.

The built-in mechanisms for MongoDB include TLS/SSL for encryption, and authentication is based on RBAC, which you should configure for even better security.

Conclusion

The creation of a secure payment gateway using Node.js calls for higher insight into security practices, compliance with regulations like PCI DSS, and propriety in integrating with trusted payment processors such as Stripe or PayPal. Following this guide, you'll be able to provide users with a safe and seamless payment experience.


© 2026 IGNEK. All rights reserved.

Ignek on LinkedInIgnek on InstagramIgnek on FacebookIgnek on YouTubeIgnek on X