Welcome folks today in this blog post we will be building a ecommerce shopping cart using stripe payment gateway in node.js and express using javascript. All the full source code of the application is shown below.
Get Started
In order to get started you need to initialize a new react.js project by typing the below command
npx create-react-app shoppingcart
cd shoppingcart
npm i bootstrap
npm i react-router-dom
Directory structure of App
As you know see we are installing the bootstrap library and also we are installing the react-router-dom library. After that we will be copy paste the below code in App.js
as shown below
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import './App.css'; import 'bootstrap/dist/css/bootstrap.min.css'; import NavbarComponent from './components/Navbar'; import { Container } from 'react-bootstrap'; import { BrowserRouter, Routes, Route} from "react-router-dom"; import Cancel from './pages/Cancel'; import Store from './pages/Store'; import Success from './pages/Success'; import CartProvider from './CartContext'; // localhost:3000 -> Home // localhost:3000/success -> Success function App() { return ( <CartProvider> <Container> <NavbarComponent></NavbarComponent> <BrowserRouter> <Routes> <Route index element={<Store />} /> <Route path="success" element={<Success />} /> <Route path="cancel" element={<Cancel />} /> </Routes> </BrowserRouter> </Container> </CartProvider> ); } export default App; |
As you can see in the above code we have the react router in which we are adding all the routes to different pages. As we are adding the success and error routes. So when we go to success page then success component will be loaded some goes for error page also. And also we are wrapping all the routes using BrowserRouter. And at the top we are importing the bootstrap cdn and also react-bootstrap package as well. And also we are wrapping all the jsx using the CartProvider context. We are importing the context at the very top.
Creating the Context and Storing Data
Now we will be creating context or storing all the data which will be used inside this ecommerce shopping cart application. First of all in the root directory we will be making the CartContext.js
file as shown below
Creating the Products inside Stripe
Now first of all go to your stripe account dashboard you need to create the products that you want to sell. Just give the name of the product and give the pricing to the product. And after creating the product we will get the price Id.
And after that as you can see we will get the price_id of the particular product. And you will similarly created unlimited products that you want to create and you will copy paste the price_id. And after that inside the root directory we need to make a file called Productsstore.js file and copy paste the below code
ProductsStore.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
// Coffee: price_1LnUTFDM1jwCEz8OGoOSXiSM // Sunglasses: price_1LnUTxDM1jwCEz8OAqHYTwKQ // Camera: price_1LnUUoDM1jwCEz8OvxIcJ7to const productsArray = [ { id: "price_1LvS2aSCXzUC9mHj6ZcjnqZQ", title: "Coffee", price: 15.00 }, { id: "price_1LvS3ISCXzUC9mHjhEBjuwLz", title: "Sunglasses", price: 50.00 }, { id: "price_1LvS3fSCXzUC9mHjyb5xVbKF", title: "Camera", price: 50.00 } ]; function getProductData(id) { let productData = productsArray.find(product => product.id === id); if (productData == undefined) { console.log("Product data does not exist for ID: " + id); return undefined; } return productData; } export { productsArray, getProductData }; |
As you can see we are declaring three products that we will be selling inside this shopping cart. We have the array of objects which contain three properties which will be id,title and price of the particular product. And then we will have the method which is called getProductData() and we will be passing the id to this method. And lastly we are exporting these two things.
Now we will be making the CartContext.js file as shown below
CartContext.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
import { createContext, useState } from "react"; import { productsArray, getProductData } from "./productsStore"; export const CartContext = createContext({ items: [], getProductQuantity: () => {}, addOneToCart: () => {}, removeOneFromCart: () => {}, deleteFromCart: () => {}, getTotalCost: () => {} }); export function CartProvider({children}) { const [cartProducts, setCartProducts] = useState([]); // [ { id: 1 , quantity: 3 }, { id: 2, quantity: 1 } ] function getProductQuantity(id) { const quantity = cartProducts.find(product => product.id === id)?.quantity; if (quantity === undefined) { return 0; } return quantity; } function addOneToCart(id) { const quantity = getProductQuantity(id); if (quantity === 0) { // product is not in cart setCartProducts( [ ...cartProducts, { id: id, quantity: 1 } ] ) } else { // product is in cart // [ { id: 1 , quantity: 3 }, { id: 2, quantity: 1 } ] add to product id of 2 setCartProducts( cartProducts.map( product => product.id === id // if condition ? { ...product, quantity: product.quantity + 1 } // if statement is true : product // if statement is false ) ) } } function removeOneFromCart(id) { const quantity = getProductQuantity(id); if(quantity == 1) { deleteFromCart(id); } else { setCartProducts( cartProducts.map( product => product.id === id // if condition ? { ...product, quantity: product.quantity - 1 } // if statement is true : product // if statement is false ) ) } } function deleteFromCart(id) { // [] if an object meets a condition, add the object to array // [product1, product2, product3] // [product1, product3] setCartProducts( cartProducts => cartProducts.filter(currentProduct => { return currentProduct.id != id; }) ) } function getTotalCost() { let totalCost = 0; cartProducts.map((cartItem) => { const productData = getProductData(cartItem.id); totalCost += (productData.price * cartItem.quantity); }); return totalCost; } const contextValue = { items: cartProducts, getProductQuantity, addOneToCart, removeOneFromCart, deleteFromCart, getTotalCost } return ( <CartContext.Provider value={contextValue}> {children} </CartContext.Provider> ) } export default CartProvider; |
As you can see we are importing the productsStore
file at the very top. From that file we are importing the getProductData method and also we are importing the list of products which will be sold inside this app
Creating the Pages for this Shopping Cart
Now we will be creating the pages for this react.js App. For this you need to make the pages
directory and inside that we will be making the success.js as shown below
pages/Success.js
1 2 3 4 5 6 7 |
function Success() { return ( <h1>Thank you for your purchase!</h1> ) } export default Success; |
And similarly we will making the error page template as shown below in the pages directory
pages/Cancel.js
1 2 3 4 5 6 7 |
function Cancel() { return ( <h1>Sorry to see you cancelled your Stripe payment!</h1> ) } export default Cancel; |
As you can we have a simple error message in this component saying sorry to see you cancelled your stripe payment to the user.
Rendering All the Products on the Screen
Now just make the store.js
file in the pages directory and copy paste the below codoe
pages/store.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import {Row, Col} from 'react-bootstrap'; import { productsArray } from '../productsStore'; import ProductCard from '../components/ProductCard'; function Store() { return ( <> <h1 align="center" className="p-3">Welcome to the store!</h1> <Row xs={1} md={3} className="g-4"> {productsArray.map((product, idx) => ( <Col align="center" key={idx}> <ProductCard product={product}/> </Col> ))} </Row> </> ) } export default Store; |
As you can see we are importing the productsArray from the store file which we have created. And in jsx we are using the loop to render all the products. For rendering the product we are making a special component called ProductCard.js
inside the components folder.
Now we need to make the components
folder and inside it we will be making the ProductCard.js
file and copy paste the below code
components/ProductCard.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import { Card, Button, Form, Row, Col } from 'react-bootstrap'; import { CartContext } from '../CartContext'; import { useContext } from 'react'; function ProductCard(props) { // props.product is the product we are selling const product = props.product; const cart = useContext(CartContext); const productQuantity = cart.getProductQuantity(product.id); console.log(cart.items); return ( <Card> <Card.Body> <Card.Title>{product.title}</Card.Title> <Card.Text>${product.price}</Card.Text> { productQuantity > 0 ? <> <Form as={Row}> <Form.Label column="true" sm="6">In Cart: {productQuantity}</Form.Label> <Col sm="6"> <Button sm="6" onClick={() => cart.addOneToCart(product.id)} className="mx-2">+</Button> <Button sm="6" onClick={() => cart.removeOneFromCart(product.id)} className="mx-2">-</Button> </Col> </Form> <Button variant="danger" onClick={() => cart.deleteFromCart(product.id)} className="my-2">Remove from cart</Button> </> : <Button variant="primary" onClick={() => cart.addOneToCart(product.id)}>Add To Cart</Button> } </Card.Body> </Card> ) } export default ProductCard; |
As you can see inside this component we are rendering all the information about a particular product which is the name of the product, the price of the product and also some buttons available to increase or decrease the number of products to the cart as shown below
Adding the Navigation Bar Component
Now we will be adding the navigation bar component inside the components folder. First of all we will be making the Navbar.js
file inside the folder as shown below
components/Navbar.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
import {Button, Container, Navbar, Modal} from 'react-bootstrap'; import { useState, useContext } from 'react'; import { CartContext } from "../CartContext"; import CartProduct from './CartProduct'; function NavbarComponent() { const cart = useContext(CartContext); const [show, setShow] = useState(false); const handleClose = () => setShow(false); const handleShow = () => setShow(true); const checkout = async () => { await fetch('http://localhost:4000/checkout', { method: "POST", headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({items: cart.items}) }).then((response) => { return response.json(); }).then((response) => { if(response.url) { window.location.assign(response.url); // Forwarding user to Stripe } }); } const productsCount = cart.items.reduce((sum, product) => sum + product.quantity, 0); return ( <> <Navbar expand="sm"> <Navbar.Brand href="/">Ecommerce Store</Navbar.Brand> <Navbar.Toggle /> <Navbar.Collapse className="justify-content-end"> <Button onClick={handleShow}>Cart ({productsCount} Items)</Button> </Navbar.Collapse> </Navbar> <Modal show={show} onHide={handleClose}> <Modal.Header closeButton> <Modal.Title>Shopping Cart</Modal.Title> </Modal.Header> <Modal.Body> {productsCount > 0 ? <> <p>Items in your cart:</p> {cart.items.map( (currentProduct, idx) => ( <CartProduct key={idx} id={currentProduct.id} quantity={currentProduct.quantity}></CartProduct> ))} <h1>Total: {cart.getTotalCost().toFixed(2)}</h1> <Button variant="success" onClick={checkout}> Purchase items! </Button> </> : <h1>There are no items in your cart!</h1> } </Modal.Body> </Modal> </> ) } export default NavbarComponent; |
As you can see first of all we have the header component in which we are displaying the logo and title of the application and then we have the simple cart button where we hit the button to see the products in the cart. This cart will automatically get updated when we add products or remove products. And this is a toggle navigation bootstrap component. And this is completely responsive. When we view on mobile devices it will be taking the shape of a hamburger menu icon.
At the top we are declaring some state variables which is useful in showing and hide the cart button in small devices. And also showing the modal window of cart. All the cart products will be shown in the modal window and sum of the total products will be shown at the bottom as shown below. And also when we click the checkout button we are calling the backend api and making a simple POST request passing the list of items to be purchased.
As you can see above as we select products to buy the shopping cart will get updated to show the number of products present. And inside the modal window we are looping through all the products to show information. For showing the information we are again another component called CartProduct passing some props in the form of id,key and price as well to that component. Now build CartProduct.js file inside the components folder as shown below
components/CartProduct.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import Button from 'react-bootstrap/Button'; import { CartContext } from "../CartContext"; import { useContext } from "react"; import { getProductData } from "../productsStore"; function CartProduct(props) { const cart = useContext(CartContext); const id = props.id; const quantity = props.quantity; const productData = getProductData(id); return ( <> <h3>{productData.title}</h3> <p>{quantity} total</p> <p>${ (quantity * productData.price).toFixed(2) }</p> <Button size="sm" onClick={() => cart.deleteFromCart(id)}>Remove</Button> <hr></hr> </> ) } export default CartProduct; |
As you can see we are rendering all the information about the product such as the name and the price and also we have the remove button to remove the product from the cart as shown below
As you can see the frontend of the application is complete now we need to write the backend code in node.js and express to actually carry out the checkout process to actually make the payment.
Making the Backend
Now first of all we need to initialize the package.json file using the below command
npm init -y
npm i express
npm i stripe
npm i cors
As you can see we are installing cors so that we can make our backend api accessible in all origins. Now you need to make the index.js
file inside the root of the project
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
const express = require('express'); var cors = require('cors'); const stripe = require('stripe')('##yourstripesecretkey##'); const app = express(); app.use(cors()); app.use(express.static("public")); app.use(express.json()); app.post("/checkout", async (req, res) => { console.log(req.body); const items = req.body.items; let lineItems = []; items.forEach((item)=> { lineItems.push( { price: item.id, quantity: item.quantity } ) }); const session = await stripe.checkout.sessions.create({ line_items: lineItems, mode: 'payment', success_url: "http://localhost:3000/success", cancel_url: "http://localhost:3000/cancel" }); res.send(JSON.stringify({ url: session.url })); }); app.listen(4000, () => console.log("Listening on port 4000!")); |
As you can see we are including all the packages at the very top of the file. For this you will need the stripe secret key from your dashboard as shown below
And after it we are making a new POST Request to the route /checkout and inside that we are inserting the price and the quantity. And lastly we are creating a checkout session to purchase these products. So from where the user will be redirected to the stripe payment page where there is no restriction that we built. Now if you run the node.js app by the below command
nodemon index.js
The app will listen port:4000. After you click the checkout button it will redirect the user to the homepage.
As you can see we have the stripe payment page where all the product will be shown that you are purchasing and then in the test mode you can write the test credit card details to test the app.
As you can see after successful payment we are redirecting the user back to the success page.