Full Stack, Car Booking Web App with Celo as Payment Method Part 2

Full Stack, Car Booking Web App with Celo as Payment Method Part 2 https://celo.academy/uploads/default/optimized/2X/e/e67b15cc99053277ba73c74d2b2937b31ca89598_2_1024x576.png
none 0.0 0

Introduction

In the first part of the tutorial, We built and explored a car booking web app that allows users to select locations and book rides. In this article we will implement a payment feature using celo Contract kit where users can pay them in CELO tokens. This article will guide you through the key components of such an app, including the following:

  • Payment Confirmation: Using the transaction hash to check if a user has paid.

  • Front-end development: Handling routing when payment is confirmed.

  • Celo payment API integration: How to enable secure and speedy payment transactions with Celo.

By the end of this tutorial article series, you will have an understanding of how to add a celo payment method to a car booking web app like Uber Complete, the development of a full stack car booking application.

Here is a completed and deployed version of the application you’ll be building in this part of this tutorial series.
Here is the link to the complete code on GitHub.

Prerequisites

This tutorial will guide you on how to add a payment method using celo contract kit. You will need solid and prior knowledge of the following:

  • Part 1: Reading part one of the article will help you understand how we created the car booking application, here is the link to the article .

  • Javascript: JavaScript is a versatile, high-level programming language that is primarily used to add interactivity, functionality, and dynamic behavior to websites. It is supported by all major web browsers. And is the language you’ll be using in this tutorial.

  • React: You should also have experience with creating and editing React applications.

  • Blockchain concepts like Celo: A good understanding of basic blockchain technologies like Celo and their useful tool for development will suffice to take you through this tutorial.

Requirement

To complete this article

  • Node.js and npm should be installed on your machine: node

  • Celo Wallet Extension: You will need to have the celo wallet extension installed in google chrome.

  • Fund Celo Wallet: Click on the link below and paste your wallet address to Fund Your Testnet Account.

Let’s Begin

To get started, you will need to clone the starter file from Github.

In your terminal:

 git clone https://github.com/kinyichukwu/Decentralized--Rides-booking-application.git

Once cloning is done, install all the project dependencies

npm install

Here is a screenshot of what our application will look like once we are done:

Celo Contract Kit

ContractKit is a library to help developers and validators to interact with the Celo blockchain and is well suited to developers looking for an easy way to integrate Celo Smart Contracts within their applications. Go to your terminal and input the command.

npm install web3 @celo/contractkit 

After installing web3 and the celo contract kit, go to the BookOrderDetails3 component in the book rides page and import Web3 and newKitFromWeb3. To start working with ContractKit you need a kit instance and a valid net to connect with.

import Web3 from "web3";

import { newKitFromWeb3 } from "@celo/contractkit";

Error

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

After installation, our web application gives a ton of errors. To fix this we will need to uninstall and install another version of react-scripts then restart your application/ system.

npm uninstall react-scripts
npm install react-scripts@4.0.3

After restarting your system everything should be working fine.

Connecting To Wallet

Great, our next step will be to connect our web application to your Celo wallet. To do this we use the window.celo.enable(). This prompts you to open the celo wallet extension where you will need to click on allow to connect to your wallet.

// connect to celo wallet
  const connectToWallet = async () => {
    if (window.celo) {
      try {
        await window.celo.enable();
        const web3 = new Web3(window.celo);
        let kit = newKitFromWeb3(web3);
 
        const accounts = await kit.web3.eth.getAccounts();
        const user_address = accounts[0];
        kit.defaultAccount = user_address;
 
        await setAddress(user_address);
        await setKit(kit);
      } catch (error) {
        console.log(error);
      }
    } else {
      alert("Error Occurred");
    }
  };


We initialize a function called connectToWallet in our bookOrdersDetails3 component which is an async function that will allow you to connect to your wallet. The function checks if you have a celo wallet, after confirmation we use our user state hook to set the address variable to the users address and also the kit to the users kit. We want this function to run whenever the page loads this page so we run this function using a use effect hook.

  useEffect(() => {
    connectToWallet();
  }, []);

Ride Payment

After connecting to the Celo wallet we will need to initialize a function sendCELOTx that will deduct money from the customer’s wallet. This function uses Celo newKitFromWeb3, a transaction object CeloTx. To send a transaction we use the kit.sendTransaction method and pass in the transaction object. After the transaction has been completed we will route the user to the next page by checking the transaction hash.

// send money to other account
async function sendCELOTx() {
    // Connect to the network and get the current tx count
    let nonce = await kit.web3.eth.getTransactionCount(kit.defaultAccount);
 
    // Send 0.1 CELO
    let amount = kit.web3.utils.toWei("0.1", "ether");
 
    let CeloTx = {
      to: "0x78820ee969c7C3264817723779b1D780f1aD0C13",
      // omit recipient for a contract deployment
      from: address,
      gas: 2000000, // surplus gas will be returned to the sender
      nonce: nonce,
      chainId: "44787", // Alfajores chainId
      data: "0x0", // data to send for smart contract execution
      value: amount,
      // The following fields can be omitted and will be filled by ContractKit, if required
      gasPrice: "30000000000",
      // gatewayFee: 0,
      // gatewayFeeRecipient: "",
      // feeCurrency: ""
    };
 
    let tx = await kit.sendTransaction(CeloTx);
    let receipt = await tx.waitReceipt();
 
    if (receipt.transactionHash.length > 0) {
      clickForward();
    }else{
      alert('transaction failed')
    }
 
    console.log(
      `CELO tx: https://alfajores-blockscout.celo-testnet.org/tx/${receipt.transactionHash}`
    );
  }


The full BookOrderDetails3 component should look like this

import React, { useEffect, useState } from "react";
import { useBookOrder } from "../../contexts/BookOrderContext";
import { useUserData } from "../../contexts/DataContext";
import { useMap } from "../../contexts/MapContext";
import Web3 from "web3";
import { newKitFromWeb3 } from "@celo/contractkit";
 
const BookOrderDetails3 = ({ clickBack, clickForward }) => {
  const {
    transferType,
    locations,
    pickupDateTime,
    totalDistance,
    totalTime,
    drivers,
    driver,
  } = useBookOrder();
  const { userInfo } = useUserData();
 
  const { price } = useMap();
 
  const [address, setAddress] = useState(null);
  const [kit, setKit] = useState(null);
 
  // connect to celo wallet
  const connectToWallet = async () => {
    if (window.celo) {
      try {
        await window.celo.enable();
        const web3 = new Web3(window.celo);
        let kit = newKitFromWeb3(web3);
 
        const accounts = await kit.web3.eth.getAccounts();
        const user_address = accounts[0];
        kit.defaultAccount = user_address;
 
        await setAddress(user_address);
        await setKit(kit);
      } catch (error) {
        console.log(error);
      }
    } else {
      alert("Error Occurred");
    }
  };
 
  useEffect(() => {
    connectToWallet();
  }, []);
 
  // send money to other account
  async function sendCELOTx() {
    // Connect to the network and get the current tx count
    let nonce = await kit.web3.eth.getTransactionCount(kit.defaultAccount);
 
    // Send 0.1 CELO
    let amount = kit.web3.utils.toWei("0.1", "ether");
 
    let CeloTx = {
      to: "0x78820ee969c7C3264817723779b1D780f1aD0C13", // omit recipient for a contract deployment
      from: address,
      gas: 2000000, // surplus gas will be returned to the sender
      nonce: nonce,
      chainId: "44787", // Alfajores chainId
      data: "0x0", // data to send for smart contract execution
      value: amount,
 
      // The following fields can be omitted and will be filled by ContractKit, if required
 
      gasPrice: "30000000000",
      // gatewayFee: 0,
      // gatewayFeeRecipient: "",
      // feeCurrency: ""
    };
 
    let tx = await kit.sendTransaction(CeloTx);
    let receipt = await tx.waitReceipt();
 
    if (receipt.transactionHash.length > 0) {
      clickForward();
    }else{
      alert(‘Transaction failed’)
      clickBack()
    }
 
    console.log(
      `CELO tx: https://alfajores-blockscout.celo-testnet.org/tx/${receipt.transactionHash}`
    );
  }
 
  return (
    <div className="order-details3-container">
      <div className="summary">
        <h2>Summary</h2>
        <div className="details">
          <div>
            <h3>SERVICE TYPE</h3>
            <p>Distance</p>
          </div>
          <div>
            <h3>TRANSFER TYPE</h3>
            <p>{transferType}</p>
          </div>
          <div>
            <h3>PICKUP LOCATION</h3>
            <p>
              {locations?.filter((loc) => loc.type === "pickup")[0]?.address}
            </p>
          </div>
          <div>
            <h3>PICKUP TIME, DATE</h3>
            <p>
              {pickupDateTime.time}, {pickupDateTime.date}
            </p>
          </div>
          <div>
            <span>
              <h3>Total Distance</h3>
              <p>{totalDistance}</p>
            </span>
            <span>
              <h3>Total Time</h3>
              <p>{totalTime}</p>
            </span>
          </div>
          <div>
            <h3>DRIVER</h3>
            <p>{drivers.filter((d) => d.uid === driver)[0]?.fullName}</p>
          </div>
          <span className="price">
            <p>Total</p>
            <p>1CELO</p>
          </span>
          <span className="price">
            <p></p>
            <p>1CELO</p>
          </span>
        </div>
      </div>
      <div className="contact-details">
        <div className="heading">
          <h3>Contact Details</h3>
        </div>
        <div className="scan-user">
          <div className="user-info">
            <div>
              <p>First Name*</p>
              <h3>{userInfo?.fullName?.split(" ")[0]}</h3>
            </div>
            <div>
              <p>Last Name*</p>
              <h3>
                {!userInfo?.fullName?.split(" ")[1]
                  ? "---"
                  : userInfo?.fullName?.split(" ")[1]}
              </h3>
            </div>
            <div>
              <p>Email address*</p>
              <h3>{userInfo?.email}</h3>
            </div>
            <div>
              <p>Phone Number*</p>
              <h3>{userInfo.phoneNumber}</h3>
            </div>
          </div>
          <div className="scan">
            <img
              src="https://www.pngitem.com/pimgs/m/120-1202125_qr-code-png-transparent-background-qr-code-png.png"
              alt=""
            />
            <h3>Celo Wallet</h3>
          </div>
        </div>
        <p id="pay-with">Pay with</p>
        <div className="img-list">
          <p>Celo</p>
        </div>
 
        <p id="info">
          *your information cannot be tampered with by inputting your details
        </p>
      </div>
      <div className="buttons">
        <div onClick={() => clickBack()} className="back">
          <p> &#8592; Go back to last Step </p>
        </div>
 
        <div
          onClick={() => {
            sendCELOTx();
          }}
          style={{ opacity: driver !== "" ? 1 : 0.5 }}
          className="next"
        >
          <p>Pay &#8594;</p>
        </div>
      </div>
    </div>
  );
};
 
export default BookOrderDetails3;

Awesome, now our app is up and running and users will be able to book rides and pay for trips using Celo.

Booking A Ride

Now let us test our application by booking a ride.

Conclusion

Congratulations, you have completed your car booking web app. Using React for the front end, Firebase for the back end, and Google Map API. We have also implemented booking rides, choosing drivers, phone number verification, and added Celo as a payment method and Celo SDK for blockchain integration.

Next Steps

To test your skills, you can create more features such as using the distance between two locations to calculate the price of a trip.

About the Author

Oselukwue Kinyichukwu is a Fullstack developer with a passion for learning, building, and teaching. You can follow me on Twitter, You can check out my profile on LinkedIn, and see what I’m building on Github.

10 Likes

I’m guessing you meant to push to review @kinyichukwu
If so I’ll be reviewing your piece

2 Likes

Yes Thank you I have corrected it.

Hi @kinyichukwu the link to part one of this piece you mentioned hasn’t been written? i remember it was published , perhaps you sent the linke to the wrong proposal.

Grammer check
Requirement> first point
Change to: * Node.js and npm should be installed on your machine: node

2 Likes

Alright Thank you. I have already written part 1 and it has been published I will ask for it to be migrated to the academy right away.

Awesome :clinking_glasses:
Looks good, I have to say your piece looks really good @kinyichukwu
I’ll be moving to publish :clinking_glasses:

2 Likes

Thanks a lot :sparkles:

1 Like

I love your article, it s very detailed

6 Likes

I am glad you liked it @Lanacreates :grin:

2 Likes

Nice piece @kinyichukwu

3 Likes

Thank you @Balq :tada::tada:

2 Likes

A nice work. Thanks @kinyichukwu for contributing this.

2 Likes

My pleasure @Samuel_Okoro :innocent:

3 Likes

Good one here bro :clap:

1 Like

Well done @kinyichukwu,
Giving you some feedbacks in dm.

5 Likes

Thanks a lot Mahn.:sparkles:

1 Like

You’re welcome @kinyichukwu

6 Likes

Thanks’ Mahn :tada:

2 Likes

This tutorial worth an accolade!