Ethereum Shopping Smartcontract Development Walkthrough

Ethereum Shopping Smart Contract Development Walkthrough

A Photo by Artem Beliaikin at Pexels.com

During the first half of 2019, So many things happened in favor of crypto believers. While Bitcoin crossed $11000 mark, entire crypto market crossed $300 billion, Facebook launched its own Cryptocurrency libra and Ethereum Racing to $350 mark. Major financial firms, Banks are adopting Blockchain technology. it Seems Promising Days ahead for all crypto believers and Blockchain Enthusiastics.

Sticking to Ethereum, even though Ethereum has excellent use cases, currently, DApps are facing scalability issues. but founder Vitalik Buterin says he’s close to fixing the scalability issues.

One of the promising use cases of Ethereum is Escrow Management (e-commerce). In this article, I am going to walk you through Creating Smarcontract for e-commerce business.

Who This Article is for?

This article is for anyone Who Wants to build Smartcontracts and DApps. basic knowledge on Ethereum Eco-System and Solidity Language is necessary to understand These Concepts.

I’m writing this step-by-step guide and I wish it is the best way to explain what Smart Contracts are. So let’s get started.

E-commerce involves buying and selling of goods or services. So mainly it has buyers and sellers.

Seller Perspective:

  • Before selling his goods on an e-commerce platform, he must be registered as a seller and have to pay some amount for using the platform.
  • List his Product on Platform with all Required details like Product Name, Price, description, warranty etc.
  • Track All placed orders by buyers and Ship Product to their delivery address and Update shipment details so that they can track Status.
  • Refund Cancelled orders.

Buyer’s Perspective:

  • Buyer must be Registered to Buy any Product on the platform.
  • Order a Product and pay Amount.
  • If Not satisfied with a product, he can Cancel Order. Refund will be Proceeded by Seller.
  • Track Order Status
  • Product Delivery by Seller Courier Medium.

The Smartcontract Pieces

Seller Perspective:

  1. Registering as a Seller and Paying fixed bank Guarantee of 5 ETH
pragma solidity ^0.5.4;
contract shoppy {

address payable public owner;

constructor() public {
owner=msg.sender;
}
struct seller {
string name;
address addr;
uint bankGuaraantee;
bool bgPaid;
}
mapping(address=> seller) public sellers;

function sellerSignUp(string memory _name) public payable{
require(!sellers[msg.sender].bgPaid);
require(msg.value==5 ether);
owner.transfer(msg.value);
sellers[msg.sender].name= _name;
sellers[msg.sender].addr= msg.sender;
sellers[msg.sender].bankGuaraantee = msg.value;
sellers[msg.sender].bgPaid=true;
}
}

Here is What I did:

I created a Public address variable owner and made deployer address as owner in the constructor. Owner address is able to receive ether from Seller. so I made it payable

In order to Store sellers details in bulk, we are going to use address mappings pointing to our struct seller. Here seller struct consists of all Required details about the seller

Added a function sellerSignup with require statements. Here is What they’ll do,

a). Checks if seller is already Registered. if registered, condition will be false and it reverts back to initial state

b). Checks if msg.value along with function is equal to 5ETH or not. if not, it will revert back to initial state

require(!sellers[msg.sender].bgPaid);
require(msg.value==5 ether);
Selecting value of 5ETH
Transaction Succeeds if value is 5ETH

If both conditions are true, sellerSignup function will get executed and it sets seller details in a mapping called sellers with address index. since it is public You can check seller details by pasting seller address

mapping(address=> seller) public sellers;

2. Listing Product With All Required Details

Note: No worries, I’m highlighting code that needed to be added in a step with bold text. We are going to talk about the highlighted code in each step.

pragma solidity ^0.5.4;
contract shoppy {

address payable public owner;

constructor() public {
owner=msg.sender;
}
struct seller {
string name;
address addr;
uint bankGuaraantee;
bool bgPaid;
}
struct product{
string productId;
string productName;
string Category;
uint price;
string description;
address payable seller;
bool isActive;
}

mapping(address=> seller) public sellers;
mapping (string => product) products;
product[] public allProducts;


function sellerSignUp(string memory _name) public payable{
require(!sellers[msg.sender].bgPaid);
require(msg.value==5 ether);
owner.transfer(msg.value);
sellers[msg.sender].name= _name;
sellers[msg.sender].addr= msg.sender;
sellers[msg.sender].bankGuaraantee = msg.value;
sellers[msg.sender].bgPaid=true;
}
function addProduct(string memory _productId, string memory _productName, string memory _category, uint _price, string memory _description) public {
require(!products[_productId].isActive);
require(sellers[msg.sender].bgPaid);

product memory product = product(_productId, _productName, _category, _price, _description, msg.sender, true);
products[_productId].productId= _productId;
products[_productId].productName= _productName;
products[_productId].Category= _category;
products[_productId].description= _description;
products[_productId].price= _price;
products[_productId].seller= msg.sender;
products[_productId].isActive = true;
allProducts.push(product);

}

}

Here is What I did

I added a struct product with all required variables and made string mapping to struct. so that you can update or buy Product with productId String .I made seller address payable because it receive ether from buyer directly.

Added a bool parameter isActive to check if product is already active with these details.

Added a function with following require statements

a). To check whether seller paid bank guarantee or not

b).To check if product with the same productId is active already

require(!products[_productId].isActive);
require(sellers[msg.sender].bgPaid);

if both conditions are passed, it executes addProduct function and add product details into the allProducts[] array and products mapping so that we can list all products by using array looping or by index.

function addProduct(string memory _productId, string memory _productName, string memory _category, uint _price, string memory _description) public {
require(!products[_productId].isActive);
require(sellers[msg.sender].bgPaid);

product memory product = product(_productId, _productName, _category, _price, _description, msg.sender, true);
products[_productId].productId= _productId;
products[_productId].productName= _productName;
products[_productId].Category= _category;
products[_productId].description= _description;
products[_productId].price= _price;
products[_productId].seller= msg.sender;
products[_productId].isActive = true;
allProducts.push(product);

3. Tracking Orders Placed By Buyers

pragma solidity ^0.5.4;
contract shoppy {

address payable public owner;

constructor() public {
owner=msg.sender;
}
struct seller {
string name;
address addr;
uint bankGuaraantee;
bool bgPaid;
}
struct product{
string productId;
string productName;
string Category;
uint price;
string description;
address payable seller;
bool isActive;

}
struct ordersPlaced {
string productId;
uint purchaseId;
address orderedBy;
}

mapping(address=> seller) public sellers;
mapping (string => product) products;
product[] public allProducts;
mapping (address=> ordersPlaced[]) sellerOrders;

function sellerSignUp(string memory _name) public payable{
require(!sellers[msg.sender].bgPaid);
require(msg.value==5 ether);
owner.transfer(msg.value);
sellers[msg.sender].name= _name;
sellers[msg.sender].addr= msg.sender;
sellers[msg.sender].bankGuaraantee = msg.value;
sellers[msg.sender].bgPaid=true;
}
function addProduct(string memory _productId, string memory _productName, string memory _category, uint _price, string memory _description) public {
require(!products[_productId].isActive);
require(sellers[msg.sender].bgPaid);

product memory product = product(_productId, _productName, _category, _price, _description, msg.sender, true);
products[_productId].productId= _productId;
products[_productId].productName= _productName;
products[_productId].Category= _category;
products[_productId].description= _description;
products[_productId].price= _price;
products[_productId].seller= msg.sender;
products[_productId].isActive = true;
allProducts.push(product);
}
function getOrdersPlaced(uint _index) public view returns(string memory, uint, address, string memory) { return(sellerOrders[msg.sender][_index].productId, sellerOrders[msg.sender][_index].purchaseId, sellerOrders[msg.sender][_index].orderedBy, sellerShipments[msg.sender][sellerOrders[msg.sender][_index].purchaseId].shipmentStatus);
} 
function getShipmentDetails(uint _purchaseId) public view returns(string memory,string memory,address,string memory) {

return(sellerShipments[msg.sender][_purchaseId].productId, sellerShipments[msg.sender][_purchaseId].shipmentStatus, sellerShipments[msg.sender][_purchaseId].orderedBy,sellerShipments[msg.sender][_purchaseId].deliveryAddress);
}
}

Here is What I did:

Added a struct ordersPlaced with required Tracking Detail variables and mapped struct array with seller address so that every seller can Track all his listed product orders placed by buyers.

mapping (address=> ordersPlaced[]) sellerOrders;

Order details will be pushed to mapping sellerOrders when buyer placed an order. we are going to have a look at it next in buyer Perspective. For now, We can retrieve Orders placed by buyers with array index.

function getOrdersPlaced(uint _index) public view returns(string memory, uint, address, string memory) {
return(sellerOrders[msg.sender][_index].productId, sellerOrders[msg.sender][_index].purchaseId, sellerOrders[msg.sender][_index].orderedBy, sellerShipments[msg.sender][sellerOrders[msg.sender][_index].purchaseId].shipmentStatus); 
}

We can Also track Orders Placed by buyers with purchaseId. A unique purchaseId will be allocated to buyer on particular purchase. so we can track Shipments with this PurchaseId too..

function getShipmentDetails(uint _purchaseId) public view returns(string memory,string memory,address,string memory) {

return(sellerShipments[msg.sender][_purchaseId].productId, sellerShipments[msg.sender][_purchaseId].shipmentStatus, sellerShipments[msg.sender][_purchaseId].orderedBy,sellerShipments[msg.sender][_purchaseId].deliveryAddress);
}

4. Shipping Products And Updating Shipment Details:

pragma solidity ^0.5.4;
contract shoppy {

address payable public owner;

constructor() public {
owner=msg.sender;
}
struct seller {
string name;
address addr;
uint bankGuaraantee;
bool bgPaid;
}
struct product{
string productId;
string productName;
string Category;
uint price;
string description;
address payable seller;
bool isActive;

}
struct ordersPlaced {
string productId;
uint purchaseId;
address orderedBy;
}
struct sellerShipment {
string productId;
uint purchaseId;
string shipmentStatus;
string deliveryAddress;
address payable orderedBy;
bool isActive;
bool isCanceled;
}
mapping(address=> seller) public sellers;
mapping (string => product) products;
product[] public allProducts;
mapping (address=> ordersPlaced[]) sellerOrders;
mapping (address=> mapping(uint=>sellerShipment))sellerShipments;

function sellerSignUp(string memory _name) public payable{
require(!sellers[msg.sender].bgPaid);
require(msg.value==5 ether);
owner.transfer(msg.value);
sellers[msg.sender].name= _name;
sellers[msg.sender].addr= msg.sender;
sellers[msg.sender].bankGuaraantee = msg.value;
sellers[msg.sender].bgPaid=true;
}
function addProduct(string memory _productId, string memory _productName, string memory _category, uint _price, string memory _description) public {
require(!products[_productId].isActive);
require(sellers[msg.sender].bgPaid);

product memory product = product(_productId, _productName, _category, _price, _description, msg.sender, true);
products[_productId].productId= _productId;
products[_productId].productName= _productName;
products[_productId].Category= _category;
products[_productId].description= _description;
products[_productId].price= _price;
products[_productId].seller= msg.sender;
products[_productId].isActive = true;
allProducts.push(product);

}
function updateShipment(uint _purchaseId, string memory  _shipmentDetails) public {
require(sellerShipments[msg.sender][_purchaseId].isActive);

sellerShipments[msg.sender][_purchaseId].shipmentStatus= _shipmentDetails;

}

}

Here is What I did:

Created a Struct sellerShipment with all required variables for Tracking and Updating Details and mapped this struct to address and nested uint.

mapping (address=> mapping(uint=>sellerShipment))sellerShipments;

So, Every seller can update shipment details with unique purchaseId and he can also track orders with this Id as said in earlier step.

These Shipment details are Pushed to mapping only when Buyer Placed his order. Once after order is placed, we can Update Shipment details with function updateShipments with purchaseId alone. here it is

function updateShipment(uint _purchaseId, string memory  _shipmentDetails) public {
require(sellerShipments[msg.sender][_purchaseId].isActive);

sellerShipments[msg.sender][_purchaseId].shipmentStatus= _shipmentDetails;

}

Above function takes purchaseId as argument and checks if product on particular purchaseId is active or not. if active, shipment Details according to seller will be updated by seller.

5. Refunding Cancelled Orders

pragma solidity ^0.5.4;
contract shoppy {

address payable public owner;

constructor() public {
owner=msg.sender;
}
struct seller {
string name;
address addr;
uint bankGuaraantee;
bool bgPaid;
}
struct product{
string productId;
string productName;
string Category;
uint price;
string description;
address payable seller;
bool isActive;

}
struct ordersPlaced {
string productId;
uint purchaseId;
address orderedBy;
}
struct sellerShipment {
string productId;
uint purchaseId;
string shipmentStatus;
string deliveryAddress;
address payable orderedBy;
bool isActive;
bool isCanceled;
}
mapping(address=> seller) public sellers;
mapping (string => product) products;
product[] public allProducts;
mapping (address=> ordersPlaced[]) sellerOrders;
mapping (address=> mapping(uint=>sellerShipment))sellerShipments;

function sellerSignUp(string memory _name) public payable{
require(!sellers[msg.sender].bgPaid);
require(msg.value==5 ether);
owner.transfer(msg.value);
sellers[msg.sender].name= _name;
sellers[msg.sender].addr= msg.sender;
sellers[msg.sender].bankGuaraantee = msg.value;
sellers[msg.sender].bgPaid=true;
}
function addProduct(string memory _productId, string memory _productName, string memory _category, uint _price, string memory _description) public {
require(!products[_productId].isActive);
require(sellers[msg.sender].bgPaid);

product memory product = product(_productId, _productName, _category, _price, _description, msg.sender, true);
products[_productId].productId= _productId;
products[_productId].productName= _productName;
products[_productId].Category= _category;
products[_productId].description= _description;
products[_productId].price= _price;
products[_productId].seller= msg.sender;
products[_productId].isActive = true;
allProducts.push(product);

}
function updateShipment(uint _purchaseId, string memory _shipmentDetails) public {
require(sellerShipments[msg.sender][_purchaseId].isActive);

sellerShipments[msg.sender][_purchaseId].shipmentStatus= _shipmentDetails;

}
function refund(string memory _productId, uint _purchaseId)public payable {
require (!sellerShipments[products[_productId].seller][purchaseId].isActive);

require (sellerShipments[msg.sender[_purchaseId].isCanceled);
require(msg.value==products[_productId].price);
sellerShipments[msg.sender[_purchaseId].orderedBy.transfer(msg.value);
sellerShipments[products[_productId].seller][_purchaseId].shipmentStatus= "Order Canceled By Buyer, Payment Refunded";

}

function getShipmentDetails(uint _purchaseId) public view returns(string memory,string memory,address,string memory) {

return(sellerShipments[msg.sender][_purchaseId].productId, sellerShipments[msg.sender][_purchaseId].shipmentStatus, sellerShipments[msg.sender][_purchaseId].orderedBy,sellerShipments[msg.sender][_purchaseId].deliveryAddress);
}

}

Here is What I did:

I created a function refund with following require statements.they will,

a).Check if Product with particular purchaseId is active or not

b).Check if Buyer Cancelled Order or not

if both conditions are passed, it will check

c). If seller Released amount equal to product price

require (!sellerShipments[products[_productId].seller[purchaseId].isActive);
require (sellerShipments[msg.sender[_purchaseId].isCanceled);
require(msg.value==products[_productId].price);

If all these conditions are passed, payment will be directly refunded to buyer’s address and sets Shipment Status to “Order Cancelled by buyer, Payent Refunded..”

function refund(string memory _productId, uint _purchaseId)public payable {
require (!sellerShipments[products[_productId].seller][purchaseId].isActive);

require (sellerShipments[msg.sender[_purchaseId].isCanceled);
require(msg.value==products[_productId].price);
sellerShipments[msg.sender[_purchaseId].orderedBy.transfer(msg.value);
sellerShipments[products[_productId].seller][_purchaseId].shipmentStatus= "Order Canceled By Buyer, Payment Refunded";

Buyers Perspective:

  1. Creating Account:
pragma solidity ^0.5.4;
contract shoppy {

address payable public owner;

constructor() public {
owner=msg.sender;
}
struct seller {
string name;
address addr;
uint bankGuaraantee;
bool bgPaid;
}
struct product{
string productId;
string productName;
string Category;
uint price;
string description;
address payable seller;
bool isActive;

}
struct ordersPlaced {
string productId;
uint purchaseId;
address orderedBy;
}
struct sellerShipment {
string productId;
uint purchaseId;
string shipmentStatus;
string deliveryAddress;
address payable orderedBy;
bool isActive;
bool isCanceled;
}
struct user{
string name;
string email;
string deliveryAddress;
bool isCreated;
}

mapping(address=> seller) public sellers;
mapping (string => product) products;
product[] public allProducts;
mapping (address=> ordersPlaced[]) sellerOrders;
mapping (address=> mapping(uint=>sellerShipment))sellerShipments;
mapping (address=> user) users;
function sellerSignUp(string memory _name) public payable{
require(!sellers[msg.sender].bgPaid);
require(msg.value==5 ether);
owner.transfer(msg.value);
sellers[msg.sender].name= _name;
sellers[msg.sender].addr= msg.sender;
sellers[msg.sender].bankGuaraantee = msg.value;
sellers[msg.sender].bgPaid=true;
}
function createAccount(string memory _name, string memory _email, string memory _deliveryAddress) public {

users[msg.sender].name= _name;
users[msg.sender].email= _email;
users[msg.sender].deliveryAddress= _deliveryAddress;
users[msg.sender].isCreated= true;
}
function addProduct(string memory _productId, string memory _productName, string memory _category, uint _price, string memory _description) public {
require(!products[_productId].isActive);
require(sellers[msg.sender].bgPaid);

product memory product = product(_productId, _productName, _category, _price, _description, msg.sender, true);
products[_productId].productId= _productId;
products[_productId].productName= _productName;
products[_productId].Category= _category;
products[_productId].description= _description;
products[_productId].price= _price;
products[_productId].seller= msg.sender;
products[_productId].isActive = true;
allProducts.push(product);

}
function updateShipment(uint _purchaseId, string memory _shipmentDetails) public {
require(sellerShipments[msg.sender][_purchaseId].isActive);

sellerShipments[msg.sender][_purchaseId].shipmentStatus= _shipmentDetails;

}
function refund(string memory _productId, uint _purchaseId)public payable {
require (sellerShipments[msg.sender[_purchaseId].isCanceled);
require (!sellerShipments[products[_productId].seller][purchaseId].isActive);
require(msg.value==products[_productId].price);
sellerShipments[msg.sender][_purchaseId].orderedBy.transfer(msg.value);
sellerShipments[products[_productId].seller][_purchaseId].shipmentStatus= "Order Canceled By Buyer, Payment Refunded";

}
function getShipmentDetails(uint _purchaseId) public view returns(string memory,string memory,address,string memory) {

return(sellerShipments[msg.sender][_purchaseId].productId, sellerShipments[msg.sender][_purchaseId].shipmentStatus, sellerShipments[msg.sender][_purchaseId].orderedBy,sellerShipments[msg.sender][_purchaseId].deliveryAddress);
}

}

Here is What I did:

Created a Struct user with required user details and mapped it to address.

mapping(address=>user) users;

Added a function createAccount and pushed function arguments(user details) to the users mapping

function createAccount(string memory _name, string memory _email, string memory _deliveryAddress) public {

users[msg.sender].name= _name;
users[msg.sender].email= _email;
users[msg.sender].deliveryAddress= _deliveryAddress;
users[msg.sender].isCreated= true;
}

bool parameter isCreated is helpful in checking if the user is registered or not while buying.

2. Placing An Order And Tracking Orders

pragma solidity ^0.5.4;
contract shoppy {

address payable public owner;

constructor() public {
owner=msg.sender;
}
uint id;
uint purchaseId;
struct seller {
string name;
address addr;
uint bankGuaraantee;
bool bgPaid;
}
struct product{
string productId;
string productName;
string Category;
uint price;
string description;
address payable seller;
bool isActive;

}
struct ordersPlaced {
string productId;
uint purchaseId;
address orderedBy;
}
struct sellerShipment {
string productId;
uint purchaseId;
string shipmentStatus;
string deliveryAddress;
address payable orderedBy;
bool isActive;
bool isCanceled;
}
struct user{
string name;
string email;
string deliveryAddress;
bool isCreated;
}
struct orders{
string productId;
string orderStatus;
uint purchaseId;
string shipmentStatus;
}

mapping(address=> seller) public sellers;
mapping (string => product) products;
product[] public allProducts;
mapping (address=> ordersPlaced[]) sellerOrders;
mapping (address=> mapping(uint=>sellerShipment))sellerShipments;
mapping (address=> user) users;
mapping (address=>orders[]) userOrders;
function sellerSignUp(string memory _name) public payable{
require(!sellers[msg.sender].bgPaid);
require(msg.value==5 ether);
owner.transfer(msg.value);
sellers[msg.sender].name= _name;
sellers[msg.sender].addr= msg.sender;
sellers[msg.sender].bankGuaraantee = msg.value;
sellers[msg.sender].bgPaid=true;
}
function createAccount(string memory _name, string memory _email, string memory _deliveryAddress) public {

users[msg.sender].name= _name;
users[msg.sender].email= _email;
users[msg.sender].deliveryAddress= _deliveryAddress;
users[msg.sender].isCreated= true;
}
function buyProduct(string memory _productId) public payable {

require( users[msg.sender].isCreated);
require(msg.value == products[_productId].price);


products[_productId].seller.transfer(msg.value);

purchaseId = id++;
orders memory order = orders(_productId, "Order Placed With Seller",purchaseId, sellerShipments[products[_productId].seller][purchaseId].shipmentStatus);
userOrders[msg.sender].push(order);
ordersPlaced memory ord = ordersPlaced(_productId, purchaseId, msg.sender);
sellerOrders[products[_productId].seller].push(ord);

sellerShipments[products[_productId].seller][purchaseId].productId=_productId;
sellerShipments[products[_productId].seller][purchaseId].orderedBy= msg.sender;
sellerShipments[products[_productId].seller][purchaseId].purchaseId= purchaseId;
sellerShipments[products[_productId].seller][purchaseId].deliveryAddress = users[msg.sender].deliveryAddress;
sellerShipments[products[_productId].seller][purchaseId].isActive= true;
}
function myOrders (uint _index) public view returns(string memory, string memory, uint, string memory) {                return(userOrders[msg.sender][_index].productId, userOrders[msg.sender][_index].orderStatus, userOrders[msg.sender][_index].purchaseId, sellerShipments[products[userOrders[msg.sender][_index].productId].seller][userOrders[msg.sender][_index].purchaseId].shipmentStatus);                  }
function addProduct(string memory _productId, string memory _productName, string memory _category, uint _price, string memory _description) public {
require(!products[_productId].isActive);
require(sellers[msg.sender].bgPaid);

product memory product = product(_productId, _productName, _category, _price, _description, msg.sender, true);
products[_productId].productId= _productId;
products[_productId].productName= _productName;
products[_productId].Category= _category;
products[_productId].description= _description;
products[_productId].price= _price;
products[_productId].seller= msg.sender;
products[_productId].isActive = true;
allProducts.push(product);

}
function updateShipment(uint _purchaseId, string memory _shipmentDetails) public {
require(sellerShipments[msg.sender][_purchaseId].isActive);

sellerShipments[msg.sender][_purchaseId].shipmentStatus= _shipmentDetails;

}
function refund(string memory _productId, uint _purchaseId)public payable {
require (sellerShipments[msg.sender][_purchaseId].isCanceled);
require (!sellerShipments[products[_productId].seller][purchaseId].isActive); require(msg.value==products[_productId].price);
sellerShipments[msg.sender][_purchaseId].orderedBy.transfer(msg.value);
sellerShipments[products[_productId].seller][_purchaseId].shipmentStatus= "Order Canceled By Buyer, Payment Refunded";

}
function getShipmentDetails(uint _purchaseId) public view returns(string memory,string memory,address,string memory) {

return(sellerShipments[msg.sender][_purchaseId].productId, sellerShipments[msg.sender][_purchaseId].shipmentStatus, sellerShipments[msg.sender][_purchaseId].orderedBy,sellerShipments[msg.sender][_purchaseId].deliveryAddress);
}

}

Here is What I did:

I created a struct orders with order detail variables and mapped struct to address. so that each individual user can easily track their orders. The problem here is, everyone can view other people orders if we make mapping public. so the other alternative here is that make it private and call variables from mapping (current call only). so the user only see his orders.

mapping (address=>orders[]) userOrders;
function myOrders (uint _index) public view returns(string memory, string memory, uint, string memory) {                
return(userOrders[msg.sender][_index].productId, userOrders[msg.sender][_index].orderStatus, userOrders[msg.sender][_index].purchaseId, sellerShipments[products[userOrders[msg.sender][_index].productId].seller][userOrders[msg.sender][_index].purchaseId].shipmentStatus);
}

Added a function buyProduct with productId as an argument with following require statements to check,

a). If buyer is Registered or not,

b). if msg.value is equal to product price or not

if these two conditions passed, the amount will be transferred to the seller and a unique purchaseId will be allocated to purchase.

products[_productId].seller.transfer(msg.value);
purchaseId=Id++;

Order details will be pushed to userOrders, sellerOrders and sellerShipments Maps in order to track orders by buyer and seller for updating shipment details**.

User Orders

3. Cancelling An Order

pragma solidity ^0.5.4;
contract shoppy {

address payable public owner;

constructor() public {
owner=msg.sender;
}
uint id;
uint purchaseId;
struct seller {
string name;
address addr;
uint bankGuaraantee;
bool bgPaid;
}
struct product{
string productId;
string productName;
string Category;
uint price;
string description;
address payable seller;
bool isActive;

}
struct ordersPlaced {
string productId;
uint purchaseId;
address orderedBy;
}
struct sellerShipment {
string productId;
uint purchaseId;
string shipmentStatus;
string deliveryAddress;
address payable orderedBy;
bool isActive;
bool isCanceled;
}
struct user{
string name;
string email;
string deliveryAddress;
bool isCreated;
}
struct orders{
string productId;
string orderStatus;
uint purchaseId;
string shipmentStatus;
}
mapping(address=> seller) public sellers;
mapping (string => product) products;
product[] public allProducts;
mapping (address=> ordersPlaced[]) sellerOrders;
mapping (address=> mapping(uint=>sellerShipment))sellerShipments;
mapping (address=> user) users;
mapping (address=>orders[]) userOrders;
function sellerSignUp(string memory _name) public payable{
require(!sellers[msg.sender].bgPaid, "You are Already Registered");
require(msg.value==5 ether, "Bank Guarantee of 5ETH Required");
owner.transfer(msg.value);
sellers[msg.sender].name= _name;
sellers[msg.sender].addr= msg.sender;
sellers[msg.sender].bankGuaraantee = msg.value;
sellers[msg.sender].bgPaid=true;
}
function createAccount(string memory _name, string memory _email, string memory _deliveryAddress) public {

users[msg.sender].name= _name;
users[msg.sender].email= _email;
users[msg.sender].deliveryAddress= _deliveryAddress;
users[msg.sender].isCreated= true;
}
function buyProduct(string memory _productId) public payable {


require(msg.value == products[_productId].price, "Value Must be Equal to Price of Product");
require( users[msg.sender].isCreated, "You Must Be Registered to Buy");

products[_productId].seller.transfer(msg.value);

purchaseId = id++;
orders memory order = orders(_productId, "Order Placed With Seller",purchaseId, sellerShipments[products[_productId].seller][purchaseId].shipmentStatus);
userOrders[msg.sender].push(order);
ordersPlaced memory ord = ordersPlaced(_productId, purchaseId,
msg.sender);
sellerOrders[products[_productId].seller].push(ord);

sellerShipments[products[_productId].seller][purchaseId].productId=_productId;
sellerShipments[products[_productId].seller][purchaseId].orderedBy= msg.sender;
sellerShipments[products[_productId].seller][purchaseId].purchaseId= purchaseId;
sellerShipments[products[_productId].seller][purchaseId].deliveryAddress = users[msg.sender].deliveryAddress;
sellerShipments[products[_productId].seller][purchaseId].isActive= true;
}
function addProduct(string memory _productId, string memory _productName, string memory _category, uint _price, string memory _description) public {
require(sellers[msg.sender].bgPaid,"You are not Registered as Seller");
require(!products[_productId].isActive, "Product With this Id is already Active. Use other UniqueId");


product memory product = product(_productId, _productName, _category, _price, _description, msg.sender, true);
products[_productId].productId= _productId;
products[_productId].productName= _productName;
products[_productId].Category= _category;
products[_productId].description= _description;
products[_productId].price= _price;
products[_productId].seller= msg.sender;
products[_productId].isActive = true;
allProducts.push(product);

}
function cancelOrder(string memory _productId, uint _purchaseId) public payable {
require(sellerShipments[products[_productId].seller][_purchaseId].orderedBy==msg.sender, "Aww Crap.. You are not Authorized to This Product PurchaseId");
require (sellerShipments[products[_productId].seller][purchaseId].isActive, "Aww crap..You Already Canceled This order");


sellerShipments[products[_productId].seller][_purchaseId].shipmentStatus= "Order Canceled By Buyer, Payment will Be Refunded";
sellerShipments[products[_productId].seller][_purchaseId].isCanceled= true;
sellerShipments[products[_productId].seller][_purchaseId].isActive= false;
}

function updateShipment(uint _purchaseId, string memory _shipmentDetails) public {
require(sellerShipments[msg.sender][_purchaseId].isActive, "Order is either inActive or cancelled");

sellerShipments[msg.sender][_purchaseId].shipmentStatus= _shipmentDetails;

}
function refund(string memory _productId, uint _purchaseId)public payable {
require (sellerShipments[msg.sender][_purchaseId].isCanceled, "Order is not Yet Cancelled");
require (!sellerShipments[products[_productId].seller][purchaseId].isActive,"Order is Active and not yet Cancelled");
require(msg.value==products[_productId].price,"Value Must be Equal to Product Price");
sellerShipments[msg.sender][_purchaseId].orderedBy.transfer(msg.value);
sellerShipments[products[_productId].seller][_purchaseId].shipmentStatus= "Order Canceled By Buyer, Payment Refunded";

}
function myOrders (uint _index) public view returns(string memory, string memory, uint, string memory) {
return(userOrders[msg.sender][_index].productId, userOrders[msg.sender][_index].orderStatus, userOrders[msg.sender][_index].purchaseId, sellerShipments[products[userOrders[msg.sender][_index].productId].seller][userOrders[msg.sender][_index].purchaseId].shipmentStatus);
}
function getOrdersPlaced(uint _index) public view returns(string memory, uint, address, string memory) {
return(sellerOrders[msg.sender][_index].productId, sellerOrders[msg.sender][_index].purchaseId, sellerOrders[msg.sender][_index].orderedBy, sellerShipments[msg.sender][sellerOrders[msg.sender][_index].purchaseId].shipmentStatus);
}
function getShipmentDetails(uint _purchaseId) public view returns(string memory,string memory,address,string memory) {

return(sellerShipments[msg.sender][_purchaseId].productId, sellerShipments[msg.sender][_purchaseId].shipmentStatus, sellerShipments[msg.sender][_purchaseId].orderedBy,sellerShipments[msg.sender][_purchaseId].deliveryAddress);
}

}

Here is What I did:

Added a function to cancel order. it takes purchaseId as argument and checks

a).if product with particular purchaseId is ordered by the cureent caller or not,

b). if product with particular purchaseId is active or not.

If these conditions are passed, that means Product is ordered by current caller and it is active now. then cancelOrder function will be executed and sets isCanceled flag to true, isActive flag to false and shipmentStatus to “Order Canceled by Buyer , Payment Will be Refunded” it will intimates to the seller for refund.

function cancelOrder(string memory _productId, uint _purchaseId)  public payable {
require(sellerShipments[products[_productId].seller][_purchaseId].orderedBy==msg.sender, "Aww Crap.. You are not Authorized to This Product PurchaseId");
require (sellerShipments[products[_productId].seller][purchaseId].isActive, "Aww crap..You Already Canceled This order");


sellerShipments[products[_productId].seller][_purchaseId].shipmentStatus= "Order Canceled By Buyer, Payment will Be Refunded";
sellerShipments[products[_productId].seller][_purchaseId].isCanceled= true;
sellerShipments[products[_productId].seller][_purchaseId].isActive= false;
}
Order Cancel Status

Done. Are you still with me? In “Ethereum Shopping SmartContract Walkthrough”, we examined the business logic behind e-commerce SmartContract . Since I don’t want this article to be too long, I’m finishing it up.

The final SmartContract

Test this contract manually using Remix IDE . This is the official tool to write, test and deploy contracts. go to https://remix.ethereum.org. Paste Contract code and Compile Contract using Solidity Compiler and deploy on Javascript Vm to See how it works. I Recommend you to use new interface because in the near future old user interface will be removed. Try practicing with a new interface from now on.

Interface Of Remix IDE

Final Words

Here, Smart Contract did all the job of ensuring both seller and buyer played their roles in the deal. There was no need for a middleman, and neither was there any fear that any parties will pull out from the deal, taking money or item with him.

  • The whole process runs on the Blockchain so every transaction is transparently documented. Unlike a typical e-commerce platform, where the operator could shut down, or money could be lost . the Blockchain is decentralized, maintained by every ‘node’ that participate in the platform. Nobody can shut it down or hack.

Finishing Off

You can see the complete and updated contract in my github or you can find above in final Smartcontract Section.

I also made A Demo DApp on e-commerce business here. full source code for DApp can be found here

Cheers…..❤


Ethereum Shopping Smartcontract Development Walkthrough was originally published in Data Driven Investor on Medium, where people are continuing the conversation by highlighting and responding to this story.