<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Samir Pokhrel</title>
    <description>The latest articles on DEV Community by Samir Pokhrel (@invisibleworld).</description>
    <link>https://dev.to/invisibleworld</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F683507%2F01b83caa-fd90-4713-984e-599bb1b7c5e9.jpeg</url>
      <title>DEV Community: Samir Pokhrel</title>
      <link>https://dev.to/invisibleworld</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/invisibleworld"/>
    <language>en</language>
    <item>
      <title>Integrating eSewa Payment in a MERN Stack Web Application(part 2)</title>
      <dc:creator>Samir Pokhrel</dc:creator>
      <pubDate>Wed, 28 Aug 2024 12:22:32 +0000</pubDate>
      <link>https://dev.to/invisibleworld/final-part-integrating-esewa-payment-in-a-mern-stack-web-application-113k</link>
      <guid>https://dev.to/invisibleworld/final-part-integrating-esewa-payment-in-a-mern-stack-web-application-113k</guid>
      <description>&lt;p&gt;In the final part of our eSewa payment integration tutorial, we’ll cover three crucial steps:serving configuration files from .env , setting up a MongoDB database to store transactions and verifying responses from the payment gateway. But first, let’s recap what we’ve accomplished so far:&lt;/p&gt;

&lt;p&gt;In the first part of the tutorial, we:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set Up a MERN Stack Application&lt;/strong&gt;: We initialized a new MERN stack project, set up a Node.js and Express backend, and created a React frontend. This foundational setup allowed us to build a robust application for integrating payment features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initiated Payment Integration&lt;/strong&gt;: We implemented basic payment integration by configuring routes in our backend to handle payment requests and redirect users to the eSewa payment gateway.&lt;/p&gt;

&lt;p&gt;You can check out &lt;a href="https://dev.to/invisibleworld/integrating-esewa-payment-in-a-mern-stack-web-application-part-1-35bi"&gt;Part 1&lt;/a&gt; following this link.&lt;/p&gt;

&lt;p&gt;Let's begin...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Serving Configuration Files from .env&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To manage sensitive information such as API keys and secrets, it’s essential to use environment variables stored in a .env file. This approach keeps your configuration secure and prevents sensitive data from being hard-coded into your application.&lt;/p&gt;

&lt;p&gt;Step-by-Step:&lt;/p&gt;

&lt;p&gt;1.Create a .env File: &lt;br&gt;
In the root of your project directory, create a file named .env. This file will store your environment variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo &amp;gt; .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.Add Configuration Variables: &lt;br&gt;
Open the .env file and add your configuration variables. For eSewa, you you will require:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT=3000
MONGO_URI=mongodb://127.0.0.1:27017/mydatabase
MERCHANT_ID=EPAYTEST
SUCCESS_URL=http://localhost:5173/payment-success
FAILURE_URL=http://localhost:5173/payment-failure
ESEWAPAYMENT_URL=https://rc-epay.esewa.com.np/api/epay/main/v2/form
SECRET=8gBm/:&amp;amp;EnhH.1/q
ESEWAPAYMENT_STATUS_CHECK_URL=https://uat.esewa.com.np/api/epay/transaction/status/

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.Load Environment Variables in Your Application: &lt;br&gt;
Use the dotenv package to load these variables. Ensure dotenv is installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4.At the beginning of your app.js file, load the environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require('dotenv').config();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5.Access the environment variables in your code using process.env. For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;process.env.PORT&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting up a MongoDB database to store transactions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To track and manage transactions, you need to set up MongoDB to store transaction records. This setup allows you to maintain a history of payments and manage order states effectively.&lt;/p&gt;

&lt;p&gt;Step-by-Step:&lt;/p&gt;

&lt;p&gt;This blog post assumes that the MongoDB Community Server and MongoDB Compass is already installed and running on your machine. If not, please install it from the MongoDB official website before proceeding.&lt;br&gt;
&lt;a href="https://www.mongodb.com/try/download/community" rel="noopener noreferrer"&gt;MongoDB Community Server&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;1.Install Mongoose package: Install Mongoose for MongoDB object modeling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install mongoose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.Configure the MongoDB Database:&lt;br&gt;
 Create a &lt;em&gt;config&lt;/em&gt; folder in the root of the project(server) directory and navigate into the folder and create a file &lt;em&gt;db.config.js&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir config
cd config
echo &amp;gt; db.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.Set Up the MongoDB Connection: &lt;br&gt;
Use Mongoose to connect to your MongoDB instance. This instance could be a local MongoDB server or a cloud-based MongoDB Atlas server.&lt;br&gt;
Here, we will use the local MongoDB server for the sake of simplicity.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;db.config.js&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//later we will serve 'mongodb_connection_url' from .env  
const { connect } = require("mongoose");
const connectDB = () =&amp;gt; {
  try {
    connect('mongodb://127.0.0.1:27017/mydatabase').then((res) =&amp;gt; {
      console.log("MongoDB connected successfully");
    });
  } catch (error) {
    console.error("MongoDB connection error:", error);
  }
};

module.exports = connectDB;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4.Import the &lt;em&gt;db.config.js&lt;/em&gt; on the app.js file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const connectDB = require("./config/db.config");
connectDB()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5.Create a Mongoose Model: &lt;br&gt;
Define a Mongoose model for transactions. Create a file named Transaction.model.js in your models directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//navigate to the root of the directory
mkdir models
cd models
echo &amp;gt; Transaction.model.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Define the Schema
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const mongoose = require("mongoose");

// Define the Transaction schema
const transactionSchema = new mongoose.Schema(
  {

    product_id: {
      type: String, 
      required: true,
    },
    amount: {
      type: Number,
      required: true,
      min: 0, // Amount should not be negative
    },

    status: {
      type: String,
      required: true,
      enum: ["PENDING", "COMPLETE", "FAILED", "REFUNDED"], // Example statuses
      default: "PENDING",
    },

  },
  {
    timestamps: true, // Adds createdAt and updatedAt fields automatically
  }
);

// Create the Transaction model from the schema
const Transaction = mongoose.model("Transaction", transactionSchema);

// Export the model
module.exports = Transaction;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;6.Save Transactions: In your payment route, save transaction details to MongoDB. Update the /initiate-payment route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post("/initiate-payment", async (req, res) =&amp;gt; {
  const { amount, productId } = req.body;

  let paymentData = {
    amount,
    failure_url: process.env.FAILURE_URL,
    product_delivery_charge: "0",
    product_service_charge: "0",
    product_code: process.env.MERCHANT_ID,
    signed_field_names: "total_amount,transaction_uuid,product_code",
    success_url: process.env.SUCCESS_URL,
    tax_amount: "0",
    total_amount: amount,
    transaction_uuid: productId,
  };

  const data = `total_amount=${paymentData.total_amount},transaction_uuid=${paymentData.transaction_uuid},product_code=${paymentData.product_code}`;

  const signature = generateHmacSha256Hash(data, process.env.SECRET);

  paymentData = { ...paymentData, signature };
  try {
    const payment = await axios.post(process.env.ESEWAPAYMENT_URL, null, {
      params: paymentData,
    });
    const reqPayment = JSON.parse(safeStringify(payment));
    if (reqPayment.status === 200) {
      const transaction = new Transaction({
        product_id: productId,
        amount: amount,

      });
      await transaction.save();

      return res.send({
        url: reqPayment.request.res.responseUrl,
      });
    }
  } catch (error) {
    res.send(error);
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verifying Responses from the Payment Gateway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s essential to verify the responses from the payment gateway to ensure that transactions are legitimate and to update your transaction records accordingly.&lt;/p&gt;

&lt;p&gt;Step-by-Step:&lt;br&gt;
1.Verify payment transaction (/payment-status): : &lt;br&gt;
Implement routes to handle payment responses from eSewa.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Route to handle payment status update
app.post("/payment-status", async (req, res) =&amp;gt; {
  const { product_id } = req.body; // Extract data from request body
  try {
    // Find the transaction by its id
    const transaction = await Transaction.findOne({ product_id });

    if (!transaction) {
      return res.status(400).json({ message: "Transaction not found" });
    }

    const paymentData = {
      product_code: process.env.MERCHANT_ID,
      total_amount: transaction.amount,
      transaction_uuid: transaction.product_id,
    };

    const response = await axios.get(
      process.env.ESEWAPAYMENT_STATUS_CHECK_URL,
      {
        params: paymentData,
      }
    );
    const paymentStatusCheck = JSON.parse(safeStringify(response));
    if (paymentStatusCheck.status === 200) {
      // Update the transaction status
      transaction.status = paymentStatusCheck.data.status;
      await transaction.save();
      res
        .status(200)
        .json({ message: "Transaction status updated successfully" });
    }
  } catch (error) {
    console.error("Error updating transaction status:", error);
    res.status(500).json({ message: "Server error", error: error.message });
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;app.js&lt;/em&gt; file after update  look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const dotenv = require("dotenv");
const express = require("express");
const bodyParser = require("body-parser");
const axios = require("axios");
const cors = require("cors");
const { generateHmacSha256Hash, safeStringify } = require("./utils");

const Transaction = require("./model/TransactionModel");
const connectDB = require("./config/db.config");

// Load environment variables from .env file
dotenv.config();

const app = express();
const PORT = process.env.PORT || 3000;
connectDB();

const corsOptions = {
  origin: ["http://localhost:5173"],
};

app.use(cors(corsOptions));

// Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// Route to initiate payment
app.post("/initiate-payment", async (req, res) =&amp;gt; {
  const { amount, productId } = req.body;

  let paymentData = {
    amount,
    failure_url: process.env.FAILURE_URL,
    product_delivery_charge: "0",
    product_service_charge: "0",
    product_code: process.env.MERCHANT_ID,
    signed_field_names: "total_amount,transaction_uuid,product_code",
    success_url: process.env.SUCCESS_URL,
    tax_amount: "0",
    total_amount: amount,
    transaction_uuid: productId,
  };

  const data = `total_amount=${paymentData.total_amount},transaction_uuid=${paymentData.transaction_uuid},product_code=${paymentData.product_code}`;

  const signature = generateHmacSha256Hash(data, process.env.SECRET);

  paymentData = { ...paymentData, signature };
  try {
    const payment = await axios.post(process.env.ESEWAPAYMENT_URL, null, {
      params: paymentData,
    });
    const reqPayment = JSON.parse(safeStringify(payment));
    if (reqPayment.status === 200) {
      const transaction = new Transaction({
        product_id: productId,
        amount: amount,

      });
      await transaction.save();

      return res.send({
        url: reqPayment.request.res.responseUrl,
      });
    }
  } catch (error) {
    res.send(error);
  }
});

// Route to handle payment status update
app.post("/payment-status", async (req, res) =&amp;gt; {
  const { product_id } = req.body; // Extract data from request body
  try {
    // Find the transaction by its signature
    const transaction = await Transaction.findOne({ product_id });

    if (!transaction) {
      return res.status(400).json({ message: "Transaction not found" });
    }

    const paymentData = {
      product_code: "EPAYTEST",
      total_amount: transaction.amount,
      transaction_uuid: transaction.product_id,
    };

    const response = await axios.get(
      process.env.ESEWAPAYMENT_STATUS_CHECK_URL,
      {
        params: paymentData,
      }
    );
    const paymentStatusCheck = JSON.parse(safeStringify(response));
    if (paymentStatusCheck.status === 200) {
      // Update the transaction status
      transaction.status = paymentStatusCheck.data.status;
      await transaction.save();
      res
        .status(200)
        .json({ message: "Transaction status updated successfully" });
    }
  } catch (error) {
    console.error("Error updating transaction status:", error);
    res.status(500).json({ message: "Server error", error: error.message });
  }
});

app.listen(PORT, () =&amp;gt; {
  console.log(`Server is running on http://localhost:${PORT}`);
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, lets update the &lt;em&gt;Success.jsx&lt;/em&gt; page in the front-end to verify the payment status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import axios from "axios";

const Success = () =&amp;gt; {
  const [isSuccess, setIsSuccess] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const navigate = useNavigate();
  const location = useLocation();

  // Create a new URLSearchParams object using the search string from location
  const queryParams = new URLSearchParams(location.search);
  const token = queryParams.get("data");

  // Decode the JWT without verifying the signature
  const decoded = base64Decode(token);

  const verifyPaymentAndUpdateStatus = async () =&amp;gt; {
    try {
      const response = await axios.post(
        "http://localhost:3000/payment-status",
        {
          product_id: decoded.transaction_uuid,
        }
      );
      if (response.status === 200) {
        setIsLoading(false);
        setIsSuccess(true);
      }
    } catch (error) {
      setIsLoading(false);
      console.error("Error initiating payment:", error);
    }
  };
  useEffect(() =&amp;gt; {
    verifyPaymentAndUpdateStatus();
  }, []);

  if (isLoading &amp;amp;&amp;amp; !isSuccess) return &amp;lt;&amp;gt;Loading...&amp;lt;/&amp;gt;;
  if (!isLoading &amp;amp;&amp;amp; !isSuccess)
    return (
      &amp;lt;&amp;gt;
        &amp;lt;h1&amp;gt;Oops!..Error occurred on confirming payment&amp;lt;/h1&amp;gt;
        &amp;lt;h2&amp;gt;We will resolve it soon.&amp;lt;/h2&amp;gt;
        &amp;lt;button onClick={() =&amp;gt; navigate("/")} className="go-home-button"&amp;gt;
          Go to Homepage
        &amp;lt;/button&amp;gt;
      &amp;lt;/&amp;gt;
    );

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Payment Successful!&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;Thank you for your payment. Your transaction was successful.&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; navigate("/")} className="go-home-button"&amp;gt;
        Go to Homepage
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Success;
//using the utility fucntion in the same page for simplicity
//you can create a separate directory and serve it 
function base64Decode(base64) {
  // Convert Base64Url to standard Base64
  const standardBase64 = base64.replace(/-/g, "+").replace(/_/g, "/");
  // Decode Base64 to UTF-8 string
  const decoded = atob(standardBase64);
  return JSON.parse(decoded);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this final part of our tutorial, we have ensured that our eSewa payment integration is complete and secure. By serving configuration files from .env, setting up MongoDB to store transactions, and verifying payment responses, we have laid a strong foundation for a reliable payment system in your MERN stack application.&lt;/p&gt;

&lt;p&gt;Feel free to explore further enhancements and integrations, and as always, stay tuned for more tutorials. Happy coding!&lt;/p&gt;

</description>
      <category>mern</category>
      <category>esewa</category>
      <category>paymentintegration</category>
      <category>webapp</category>
    </item>
    <item>
      <title>Integrating eSewa Payment in a MERN Stack Web Application (part 1)</title>
      <dc:creator>Samir Pokhrel</dc:creator>
      <pubDate>Sat, 24 Aug 2024 13:23:03 +0000</pubDate>
      <link>https://dev.to/invisibleworld/integrating-esewa-payment-in-a-mern-stack-web-application-part-1-35bi</link>
      <guid>https://dev.to/invisibleworld/integrating-esewa-payment-in-a-mern-stack-web-application-part-1-35bi</guid>
      <description>&lt;p&gt;Integrating a payment gateway is an essential feature for any web application that deals with financial transactions. In this blog post, we'll walk through the process of integrating the eSewa payment gateway into a MERN stack web application. eSewa is a popular digital payment platform in Nepal that allows secure online transactions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overview of the MERN Stack&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The MERN stack is a robust combination of technologies used to build full-stack web applications. It comprises:&lt;br&gt;
MongoDB: A NoSQL database for storing application data.&lt;br&gt;
Express.js: A minimal web application framework for Node.js.&lt;br&gt;
React.js: A JavaScript library for building interactive user interfaces.&lt;br&gt;
Node.js: A JavaScript runtime that executes JavaScript code on the server side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting Up the Project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To get started, we'll set up a basic MERN stack application. If you already have a MERN application, you can skip ahead to the eSewa Integration section.&lt;br&gt;
Step 1: Initialize the Backend with Express.js&lt;br&gt;
Create a New Directory and Initialize NPM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir esewa-payment-integration
cd esewa-payment-integration
mkdir server
cd server
npm init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install Necessary Packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install express cors body-parser axios
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set Up Express Server:&lt;/p&gt;

&lt;p&gt;Create a file named app.js in the root directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const bodyParser = require("body-parser");

const app = express();
const PORT = 3000;

app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', (req, res) =&amp;gt; {
  res.send('eSewa Payment Integration');
});
app.listen(PORT, () =&amp;gt; {
  console.log(`Server running on http://localhost:${PORT}`);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Initialize the Frontend with React&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set Up React App:&lt;/p&gt;

&lt;p&gt;Navigate back to the root directory(esewa-payment-integration) and create a new React app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite client --template react
cd client
npm install axios react-router-dom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set Up Basic Routing:&lt;/p&gt;

&lt;p&gt;Edit src/App.jsx to set up basic routing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import Failure from "./components/Failure";
import PaymentForm from "./components/PaymentForm";
import Success from "./components/Success";

function App() {
  return (
    &amp;lt;Router&amp;gt;
        &amp;lt;div className="App"&amp;gt;
          &amp;lt;Routes&amp;gt;
            &amp;lt;Route path="/" element={&amp;lt;PaymentForm /&amp;gt;} /&amp;gt;
            &amp;lt;Route path="/payment-success" element={&amp;lt;Success /&amp;gt;} /&amp;gt;
            &amp;lt;Route path="/payment-failure" element={&amp;lt;Failure /&amp;gt;} /&amp;gt;
          &amp;lt;/Routes&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/Router&amp;gt;
    );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create Payment, Success, Failure Components and CSS:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the src/components folder, create and PaymentForm.jsx:&lt;br&gt;
PaymentForm.jsx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from "react";
import axios from "axios";

const PaymentComponent = () =&amp;gt; {
  const [amount, setAmount] = useState("");

  const handlePayment = async (e) =&amp;gt; {
    e.preventDefault();

  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;eSewa Payment Integration&amp;lt;/h1&amp;gt;

      &amp;lt;div className="form-container" onSubmit={handlePayment}&amp;gt;
        &amp;lt;form className="styled-form"&amp;gt;
          &amp;lt;div className="form-group"&amp;gt;
            &amp;lt;label htmlFor="Amount"&amp;gt;Amount:&amp;lt;/label&amp;gt;
            &amp;lt;input
              type="number"
              value={amount}
              onChange={(e) =&amp;gt; setAmount(e.target.value)}
              required
              placeholder="Enter amount"
            /&amp;gt;
          &amp;lt;/div&amp;gt;

          &amp;lt;button type="submit" className="submit-button"&amp;gt;
            Pay with eSewa
          &amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default PaymentComponent;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. Success.jsx :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";
import { useNavigate } from "react-router-dom";

const Success = () =&amp;gt; {
  const navigate = useNavigate();
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Payment Successful!&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;Thank you for your payment. Your transaction was successful.&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; navigate("/")} className="go-home-button"&amp;gt;
        Go to Homepage
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Success;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. Failure.jsx :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";
import { useNavigate } from "react-router-dom";

const Failure = () =&amp;gt; {
  const navigate = useNavigate();
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Payment Failed!&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;There was an issue with your payment. Please try again.&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; navigate("/")} className="go-home-button"&amp;gt;
        Go to Homepage
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Failure;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. index.css&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  font-weight: 400;

  color-scheme: light dark;
  color: rgba(255, 255, 255, 0.87);
  background-color: #242424;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  font-weight: 500;
  color: #646cff;
  text-decoration: inherit;
}
a:hover {
  color: #535bf2;
}

body {
  margin: 0;
  display: flex;
  place-items: center;
  min-width: 320px;
  min-height: 100vh;
}

h1 {
  font-size: 3.2em;
  line-height: 1.1;
}

button {
  border-radius: 8px;
  border: 1px solid transparent;
  padding: 0.6em 1.2em;
  font-size: 1em;
  font-weight: 500;
  font-family: inherit;
  background-color: #1a1a1a;
  cursor: pointer;
  transition: border-color 0.25s;
}
button:hover {
  border-color: #646cff;
}
button:focus,
button:focus-visible {
  outline: 4px auto -webkit-focus-ring-color;
}

#root {
  max-width: 1280px;
  margin: 0 auto;
  padding: 2rem;
  text-align: center;
}

/* Hide spinner for Chrome, Safari, Edge, and Opera */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* Hide spinner for Firefox */
input[type="number"] {
  -moz-appearance: textfield;
}

/* FormComponent.css */

.form-container {
  max-width: 500px;
  margin: 0 auto;
  padding: 20px;
  /* background-color: #f9f9f9; */
  border-radius: 8px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.styled-form {
  display: flex;
  flex-direction: column;
}

.form-group {
  margin-bottom: 15px;
}

.form-group label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}

.form-group input,
{
  width: 100%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  font-size: 16px;
  box-sizing: border-box;
}

.submit-button {
  padding: 10px 15px;
  background-color: #007bff;
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
  transition: background-color 0.3s ease;
}

.submit-button:hover {
  background-color: #0056b3;
}

.go-home-button {
  padding: 10px 20px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
  transition: background-color 0.3s ease;
}

.go-home-button:hover {
  background-color: #0056b3;
}

@media (prefers-color-scheme: light) {
  :root {
    color: #213547;
    background-color: #ffffff;
  }
  a:hover {
    color: #747bff;
  }
  button {
    background-color: #f9f9f9;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Creating eSewa Payment Gateway route&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, let's create the eSewa payment endpoint functionality into our application.&lt;br&gt;
Step 3: Implement Payment Endpoint in Express&lt;br&gt;
Add a payment route to app.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// eSewa Configuration //Later we will serve it from .env 
const esewaConfig = {
  merchantId: "EPAYTEST", // Replace with your eSewa Merchant ID
  successUrl: "http://localhost:5173/payment-success", //Replace with front-end success route page
  failureUrl: "http://localhost:5173/payment-failure", //Replace with front-end failure route page
  esewaPaymentUrl: "https://rc-epay.esewa.com.np/api/epay/main/v2/form",
  secret: "8gBm/:&amp;amp;EnhH.1/q",
};

// Route to initiate payment
app.post("/initiate-payment", async (req, res) =&amp;gt; {
  const { amount, productId } = req.body;

  let paymentData = {
    amount,
    failure_url: esewaConfig.failureUrl,
    product_delivery_charge: "0",
    product_service_charge: "0",
    product_code: esewaConfig.merchantId,
    signed_field_names: "total_amount,transaction_uuid,product_code",
    success_url: esewaConfig.successUrl,
    tax_amount: "0",
    total_amount: amount,
    transaction_uuid: productId,
  };

  const data = `total_amount=${paymentData.total_amount},transaction_uuid=${paymentData.transaction_uuid},product_code=${paymentData.product_code}`;

  const signature = generateHmacSha256Hash(data, esewaConfig.secret); 

  paymentData = { ...paymentData, signature };
  try {
    const payment = await axios.post(esewaConfig.esewaPaymentUrl, null, {
      params: paymentData,
    });
    const reqPayment = JSON.parse(safeStringify(payment));
    if (reqPayment.status === 200) {
      return res.send({
        url: reqPayment.request.res.responseUrl,
      });
    }
  } catch (error) {
    res.send(error);
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const crypto = require("crypto");

/**
 * Generates a Base64-encoded HMAC SHA256 hash.
 *
 * @param {string} data - The data to be hashed.
 * @param {string} secret - The secret key used for hashing.
 * @returns {string} The Base64-encoded HMAC SHA256 hash.
 */
function generateHmacSha256Hash(data, secret) {
  if (!data || !secret) {
    throw new Error("Both data and secret are required to generate a hash.");
  }

  // Create HMAC SHA256 hash and encode it in Base64
  const hash = crypto
    .createHmac("sha256", secret)
    .update(data)
    .digest("base64");

  return hash;
}

function safeStringify(obj) {
  const cache = new Set();
  const jsonString = JSON.stringify(obj, (key, value) =&amp;gt; {
    if (typeof value === "object" &amp;amp;&amp;amp; value !== null) {
      if (cache.has(value)) {
        return; // Discard circular reference
      }
      cache.add(value);
    }
    return value;
  });
  return jsonString;
}

module.exports = { generateHmacSha256Hash, safeStringify };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Running the Application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before running the node server include the nodemon package to watch the server everytime the file changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install nodemon --save-dev

//Update the package.json
{
  "name": "server",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "node app.js",
    "dev": "nodemon app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^1.7.5",
    "body-parser": "^1.20.2",
    "cors": "^2.8.5",
    "express": "^4.19.2"
  },
  "devDependencies": {
    "nodemon": "^3.1.4"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.** Run the node server **&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Integrating eSewa Payment api on frontend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;let's update the PaymentForm.jsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from "react";
import axios from "axios";
import { generateUniqueId } from "../utils/generateUniqueId";

const PaymentComponent = () =&amp;gt; {
  const [amount, setAmount] = useState("");

  const handlePayment = async (e) =&amp;gt; {
    e.preventDefault();
    try {
      const response = await axios.post(
        "http://localhost:3000/initiate-payment", //server payment route
        {
          amount,
          productId: generateUniqueId(),
        }
      );

      window.location.href = response.data.url;
    } catch (error) {
      console.error("Error initiating payment:", error);
    }
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;eSewa Payment Integration&amp;lt;/h1&amp;gt;

      &amp;lt;div className="form-container" onSubmit={handlePayment}&amp;gt;
        &amp;lt;form className="styled-form"&amp;gt;
          &amp;lt;div className="form-group"&amp;gt;
            &amp;lt;label htmlFor="Amount"&amp;gt;Amount:&amp;lt;/label&amp;gt;
            &amp;lt;input
              type="number"
              value={amount}
              onChange={(e) =&amp;gt; setAmount(e.target.value)}
              required
              placeholder="Enter amount"
            /&amp;gt;
          &amp;lt;/div&amp;gt;

          &amp;lt;button type="submit" className="submit-button"&amp;gt;
            Pay with eSewa
          &amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default PaymentComponent;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create utils folder on the root of the project and generate a file generateUniqueId.js for generating unique id for product&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function generateUniqueId() {
  return `id-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Part 1 of this tutorial series, we set up a basic MERN stack application and integrated the eSewa payment gateway. In the next part, we will explore advanced features such as handling payment status callbacks, storing transaction data in MongoDB, serving config from .env file and improving security measures.&lt;br&gt;
Stay tuned for Part 2 where we'll dive deeper into handling payment confirmations and improving the overall user experience!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Getting Started with React: A Beginner’s Guide</title>
      <dc:creator>Samir Pokhrel</dc:creator>
      <pubDate>Sat, 24 Aug 2024 05:47:28 +0000</pubDate>
      <link>https://dev.to/invisibleworld/getting-started-with-react-a-beginners-guide-1f8p</link>
      <guid>https://dev.to/invisibleworld/getting-started-with-react-a-beginners-guide-1f8p</guid>
      <description>&lt;p&gt;React is a powerful JavaScript library for building user interfaces, particularly single-page applications requiring a fast, interactive experience. Developed and maintained by Facebook, React has become a popular choice among developers due to its component-based architecture, reusable code, and the ability to manage the state of an application efficiently.&lt;/p&gt;

&lt;p&gt;If you’re new to React, this guide will help you understand the basics and get started on your first React application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is React?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;React is a JavaScript library used to create user interfaces (UIs) by breaking them down into components. A component in React is a reusable piece of code that defines how a section of the UI should look and behave. Components can be nested, managed, and handled independently, making complex UIs more manageable.&lt;/p&gt;

&lt;p&gt;React uses a virtual DOM (Document Object Model) to enhance performance. The virtual DOM is a lightweight copy of the actual DOM. When a component’s state changes, React updates the virtual DOM first, compares it with a previous version, and only updates the actual DOM with the differences. This process, called reconciliation, makes React applications fast and efficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Concepts in React&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Components: The building blocks of a React application. They can be class-based or functional. Functional components are simple JavaScript functions, while class components are ES6 classes that extend from React.Component.&lt;/li&gt;
&lt;li&gt;JSX (JavaScript XML): A syntax extension for JavaScript that looks similar to HTML and is used in React to describe what the UI should look like. JSX makes it easier to write and add HTML in React.&lt;/li&gt;
&lt;li&gt;State: An object that holds some information that may change over the component’s lifecycle. State is managed within the component (using useState in functional components or this.state in class components) and can influence what gets rendered.&lt;/li&gt;
&lt;li&gt;Props (Properties): Read-only attributes passed from parent to child components. Props allow you to pass data and event handlers to child components, making components reusable and modular.&lt;/li&gt;
&lt;li&gt;Lifecycle Methods: Special methods in class components that run at specific points in a component’s life (e.g., mounting, updating, unmounting). Functional components can use React Hooks like useEffect to mimic these lifecycle methods.&lt;/li&gt;
&lt;li&gt;Hooks: Functions that let you “hook into” React state and lifecycle features from functional components. Some popular hooks are useState, useEffect, useContext, and useReducer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Getting Started with React&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To get started with React, you need to set up a development environment. Here’s how:&lt;/p&gt;

&lt;p&gt;Step 1: Install Node.js and npm&lt;/p&gt;

&lt;p&gt;React relies on Node.js, a JavaScript runtime, and npm (Node Package Manager) to manage dependencies. You can download and install Node.js from nodejs.org. npm is included with Node.js.&lt;/p&gt;

&lt;p&gt;To check if you have Node.js and npm installed, run the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node -v
npm -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If these commands return version numbers, you’re good to go.&lt;/p&gt;

&lt;p&gt;Step 2: Set Up a React Application Using Create React App&lt;/p&gt;

&lt;p&gt;Create React App is an officially supported tool to create a single-page React application. It sets up your development environment so you can use the latest JavaScript features and provides a nice developer experience.&lt;/p&gt;

&lt;p&gt;To create a new React application, run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app my-app
cd my-app
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;npx create-react-app my-app sets up a new React project named "my-app" with all necessary configurations.&lt;br&gt;
cd my-app moves into the project directory.&lt;br&gt;
npm start runs the app in development mode and opens it in your default browser.&lt;br&gt;
You should see a React welcome page in your browser, confirming that your setup is correct.&lt;/p&gt;

&lt;p&gt;Step 3: Understand the Project Structure&lt;/p&gt;

&lt;p&gt;Here’s a quick overview of the files and folders created by Create React App:&lt;/p&gt;

&lt;p&gt;/public: Contains the index.html file, the entry point of your React application.&lt;br&gt;
/src: Contains the JavaScript code for your application. The main files here are:&lt;br&gt;
index.js: The JavaScript entry point, which renders the React application into the DOM.&lt;br&gt;
App.js: The main component of your application.&lt;br&gt;
You’ll mostly work inside the src folder, creating new components, managing state, and handling events.&lt;/p&gt;

&lt;p&gt;Step 4: Create Your First Component&lt;/p&gt;

&lt;p&gt;Let’s create a simple component to display a welcome message. Open the App.jsx file and modify it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';

function App() {
  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;h1&amp;gt;Welcome to React!&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;This is your first React component.&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code defines a functional component called App that returns some JSX. Save the file and check your browser. You should see your welcome message.&lt;/p&gt;

&lt;p&gt;Step 5: Add More Components&lt;/p&gt;

&lt;p&gt;To understand how components work, let’s add another component. Create a new file named Greeting.jsx in the src folder and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';

function Greeting(props) {
  return &amp;lt;h2&amp;gt;Hello, {props.name}!&amp;lt;/h2&amp;gt;;
}

export default Greeting;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, modify App.jsx to include the Greeting component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import Greeting from './Greeting';

function App() {
  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;h1&amp;gt;Welcome to React!&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;This is your first React component.&amp;lt;/p&amp;gt;
      &amp;lt;Greeting name="John" /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save both files and check your browser. You should now see “Hello, John!” beneath your welcome message.&lt;/p&gt;

&lt;p&gt;Step 6: Use State and Handle Events&lt;/p&gt;

&lt;p&gt;Let’s add a button that changes the greeting message when clicked. Modify the Greeting component to use state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from 'react';

function Greeting(props) {
  const [message, setMessage] = useState('Hello');
  const handleClick = () =&amp;gt; {
    setMessage('Hi');
  };
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h2&amp;gt;{message}, {props.name}!&amp;lt;/h2&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;Change Greeting&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default Greeting;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code introduces a message state with a default value of "Hello." When the button is clicked, the handleClick function updates the state to "Hi," causing the component to re-render with the new message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! You’ve just built your first React application and learned the basics of React components, state management, and event handling. From here, you can explore more advanced concepts like context, routing, and Redux for state management.&lt;/p&gt;

&lt;p&gt;React’s flexibility and simplicity make it a powerful tool for building modern web applications. Keep experimenting, building, and learning, and soon you’ll be creating complex, interactive UIs with ease.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
