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> ← Go back to last Step </p>
</div>
<div
onClick={() => {
sendCELOTx();
}}
style={{ opacity: driver !== "" ? 1 : 0.5 }}
className="next"
>
<p>Pay →</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.