Blockchain-Powered Ecommerce Marketplace Workshop

Blockchain-Powered Ecommerce Marketplace Workshop

in Tutorial

In this comprehensive workshop, you will build a functional ecommerce marketplace web application using blockchain-powered microservices.

This workshop is intended for developers who have little or no prior blockchain experience. It is recommended that you have some familiarity with JavaScript, Node and NPM. Experience with Solidity, the language used to write smart contracts, will be helpful.

This workshop is made up of a series of tutorials which build on each other so it is recommended that you go through the tutorials in order. The tutorials are:

This workshop can be downloaded from GitHub at https://github.com/blockmason/ecommerce-workshop

On GitHub:

Finished product

Blockmason Link & Blockchain Network Token Setup

Goal: Setup your Link account and obtain the test utility tokens for your blockchain network of choice.

Blockmason Link setup

Follow the simple setup steps outlined in the following section of the Link onboarding guide: https://github.com/blockmason/link-onboarding#signing-up

When you see the code editor in Link with the Demo smart contract code, you have successfully setup your Link account!

Blockchain network token setup

In order to store data on a public blockchain network, you will need to obtain some of its corresponding utility tokens in order to digitally pay for that transaction. This is generally known as a ‘gas fee’.

For example, the Ethereum blockchain uses Ether or ETH as it’s underlying utility token. Another blockchain network, GoChain, uses GO tokens.

Tokens are specific to a network and this also applies between test and mainnet (production) networks. For example, you can obtain ETH tokens specific for the Ethereum Ropsten testnet for free since it is designed for test purposes. Mainnet tokens, however, usually need to be bought from a cryptocurrency exchange.

For the public Ethereum blockchain, follow the setup steps outlined here: https://github.com/blockmason/link-onboarding/blob/master/Ethereum.md#setup . The setup requires 3 steps:

  1. Instead of running your own Ethereum node, you can just setup an Infura account, which provides a hosted, cloud-based, Ethereum node that you can connect to.
  2. Obtain 1 ETH, recorded on the Ethereum Ropsten testnet from a testnet ‘faucet’ which drips tokens.
  3. Confirm you have received the test 1 ETH by visiting the Ethereum Ropsten testnet block explorer at: https://ropsten.etherscan.io/ . Search your wallet address to see your balance.

For the public GoChain blockchain, follow the setup steps outlined here: https://github.com/blockmason/link-onboarding/blob/master/GoChain.md#setup . The setup requires only 2 steps since GoChain provides public node APIs that Link can connect to:

  1. Obtain test GO tokens from the GO faucet https://faucet.gochain.io/.
  2. Confirm you have received the testnet GO tokens by searching your Link default wallet address with the GoChain testnet block explorer: https://testnet-explorer.gochain.io/ .

Now you are all setup with Link and some test tokens and ready to proceed with the next tutorial!

images/tutorial_2_video.png

Create and Deploy Smart Contracts

Goal: Create a ‘purchasing’, ‘custom token’ and ‘comments’ smart contracts and deploy them to the blockchain.

Create a Smart Contract

Smart contracts are essentially lines of code which control the execution of a transaction between blockchain accounts based on events. Smart contracts are regarded as self-executing and intended to avoid the need of intermediaries or 3rd parties.

We will use the Solidity (https://solidity.readthedocs.io/en/latest/) programming language throughout these tutorials to program Ethereum-blockchain compatible smart contracts. Please refer to this documentation if any Solidity syntax used below is unfamiliar.

Solidity has influences from C++, Python and JavaScript and you will see some of the similar syntax used in the following examples.

A ‘Purchasing’ Smart Contract

In this series, we are going to build a simple blockchain-powered ecommerce marketplace, where buyers are limited to only purchase 1 of any item.

We will start with creating a basic Purchasing smart contract to record products, quantity, and buyers on the blockchain.

Open up the contract template file contracts/Purchasing-template.sol.

pragma solidity ^0.5.8;

contract Purchasing {
    event Product(string product, uint quantity, string url, string price, string description, string company, string id);
    struct ProductDetails {
        uint quantity;
        address[] purchasers;
        string url;
        string description;
        string price;
        string company;
        string id;
        bool isSet;
    }
    mapping(string => ProductDetails) public productList;
    address public authority;
  
    constructor() public {
        authority = msg.sender;
    }
    
    ...functions...
}

Things to note:

  • A struct is custom defined object type in Solidity which can group together different types of variables. In this contract, we have a ProductDetails custom object which contains a bunch of variables defining the various attributes of the product.
  • Events are inheritable members of contracts. When you call them, they cause the arguments to be stored in the transaction’s log – a special data structure in the blockchain. We can access these events from a tool like Blockmason Link. To trigger an event, we use the emit keyword followed by the event class.
  • The product details are mapped to a product in the productList mapping object, which can be publicly accessed.
  • The authority of this smart contract will be the address which deploys the contract (i.e. the msg.sender). When deploying a contract using Blockmason Link, the Link default wallet is the deploying address and will thus be the contract’s authority.
  • The functions we need to complete are: #addProduct(), #addProductQuantity(), #purchaseProduct(), and #getPurchasers().
#addProduct()
function addProduct(string memory product, uint addQuantity, string memory url, string memory description, string memory price, string memory company, string memory id) public {
    //TODO
}

This is the most involved function out of the list, but is still relatively quite simple. We just need to add our arguments to the corresponding product in the productList mapping. We complete the function by emitting the Product event.

function addProduct(string memory product, uint addQuantity, string memory url, string memory description, string memory price, string memory company, string memory id) public {
    productList[product].quantity = addQuantity;
    productList[product].url = url;
    productList[product].price = price;
    productList[product].description = description;
    productList[product].company = company;
    productList[product].id = id;
    productList[product].isSet = true;
    emit Product(product, addQuantity, url, price, description, company, id);
}
#addProductQuantity()
function addProductQuantity(string memory product, uint addQuantity) public {
    //TODO
}

Here we want to add quantity to an existing product so we need to first check that the product exists in the productList before cumulatively adding the quantity. We will use the require() Solidity keyword to check for this requirement first.

function addProductQuantity(string memory product, uint addQuantity) public {
    require(productList[product].isSet);
    productList[product].quantity += addQuantity;
}
#getPurchasers()
function getPurchasers(string memory product) public view returns (address[] memory purchasers) {
    //TODO
}

Here, we simply need to return the purchasers array for a particular product from the productList:

function getPurchasers(string memory product) public view returns (address[] memory purchasers) {
    return productList[product].purchasers;
}
#purchaseProduct()
function purchaseProduct(string memory product, address purchaser) public {
    //TODO
}

Here we need to do 2 things:

  1. First check if there is product available for purchase. Since each buyer is limited to 1 of any item for our example, we can simply check that the product quantity is greater than the length of the purchasers array. We will use the require() Solidity keyword to check for this requirement first.
  2. Add the buyer to the purchasers array for the product.
function purchaseProduct(string memory product, address purchaser) public {
    require(productList[product].purchasers.length < productList[product].quantity);
    productList[product].purchasers.push(purchaser);
}

See the completed smart contract code in contracts/Purchasing.sol.

Deploy the Purchasing Smart Contract using Blockmason Link

The process for connecting and interacting with external blockchains using Link is relatively straightforward using the Link Project Wizard. In general, the process flow looks something like this:

Link public blockchain setup flow
  1. Create your smart contract in Link
  2. Label your public blockchain
  3. Setup your network connector by identifying the network’s public JSON-RPC API endpoint.
  4. Deploy your smart contract onto the public blockchain
  5. Label your generated APIs
  6. Label your API Consumer (e.g. the name of your app using the APIs)
  7. Obtain your OAuth API authentication (automatically generated)

Let us now deploy the Purchasing.sol smart contract to a public blockchain.

  1. In Link, click on the Create new button which starts the new project wizard.
Link New Project
  1. Under Which contract would you like to use?, select Create new and then copy and paste the Purchasing.sol code into the Source Code field. Set the Display Name as Purchasing. Press Save and Next.
Link New Contract
  1. Under Which Ethereum account would you like to use?, use the Default Account. This is the Link default wallet we seeded with test utility tokens for our specific blockchain as part of the setup.
Default Link Wallet
  1. Under Which network would you like to use?, select Create new and name it based on the network you are using. Keep the Block Confirmations Needed at 0 (unless you are using a public blockchain Mainnet, in which case use 1) and the default EVM Version. Press Save and Next.
New Network Link
  1. Under Which connector would you like to use?, select Create new. Enter in a connector Display Name and provide the appropriate network RPC URL. For example, if you are using Ethereum, you can use an Infura hosted node URL to connect with that network. If you are using GoChain Testnet, you can use the URL https://testnet-rpc.gochain.io. See the Link Onboarding section on using public blockchains. Ensure you are using the correct Network and press Save and Next.
New Network Connector Link
  1. Now we just need to label our Deployment. Under Where is your contract deployed?, select Create new. Call this deployment Purchasing Testnet Deployment. Since we do not have an existing contract deployment, leave the Address field blank. Ensure the Account is the Default Account, the Contract is the Purchasing contract and the desired Network. Press Save and Next.
Contract Deployment Prep
  1. Now we are ready to deploy our contract to the desired network. When asked Are you ready to perform the following deployment?, press Deploy and you should get a deployment in progress indicator icon. This might take a few seconds to complete. If deployed correctly, you will automatically proceed to the next step to setup your API.

*Note: if you get an insufficient gas fee funds related error, you need to seed your Link default wallet with test utility tokens of your selected blockchain network. Refer to the Blockchain network token setup section of Tutorial 1.

Link Confirm Deployment
  1. Now we label our Purchasing contract API. Under Name, call it purchasing-testnet-api Also add in a human-readable display name. Ensure you are using the correct Contract Deployment. Press Save and Next.
Link API Setup
  1. Now we label our Purchasing API Consumer. This is usually the name of the app or service calling the API. For example, let us call the consumer E-Commerce App. Ensure you are using the correct API and Account. Press Save and Next.
Link Consumer Setup
  1. Lastly, your consumer needs to authenticate with the Purchasing API. An OAuth2.0 Client Secret is automatically generated. Ensure you are using the correct Principal/Consumer. Press Save, Next and then Finish.
Link Setup OAuth

Once you hit Finish, you should end up back at Home page and see your Purchasing code in the code editor, the API tab and a gear icon which toggles the client_id and client_secret auth credentials at the bottom of the page. You will use these credentials to interact with you newly created APIs!

Link API Setup Completed

Also, by clicking on the Code/API toggle, you will see the full list of available API endpoints generated from the smart contract code:

Link API Setup Completed APIs

Note: All calls to these API endpoints must be made asynchronously!

Let us also double check that our Purchasing contract deployed successfully on the blockchain.

Click on the Ethereum Contract Deployments menu item to see a list of contract deployments and their addresses. In the following example, the contract deployment address is 0x3ea042fa32f731422d6147bb31515395413986c6

List of Contract Deployments

Copy and paste this address into the selected blockchain’s explorer to see the details of your contract deployment.

Congrats! You have successfully deployed your first smart contract to the blockchain!

Deploy your ‘Custom Token’ Smart Contract

Following the same steps as before, we will now deploy a custom token which will be used ‘under the hood’ in our ecommerce marketplace app.

In contracts/, open up BasicToken.sol. Note this is a simplified contract adapted from the the ERC20 standard token contract (you can read more about the Ethereum token standard here: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md)

There are only a few customizations you need to do to this contract before deploying it:

//Change the contract name to your token name
contract BasicToken {
    // Name your custom token
    string public constant name = "Basic Token";

    // Set your custom token symbol
    string public constant symbol = "BASIC";

Replace Basic in the contract definition and the public constant name variable.

Then replace the BASIC token symbol with your custom symbol. This will be the token ticker symbol.

And then down in the constructor() public {... , customize the following lines:

// Add your wallet address here which will contain your total token supply
treasury = address(<some address>);

// Use the following for the treasury if you want the Link default account to be the treasury
// treasury = msg.sender;

// Set your total token supply (default 1000)
totalSupply = 1000 * 10**uint(decimals);

So for example if you want to use a custom wallet address, such as 0xFeE9813A4B268793D4Edc6DF11A760C3c07a2c98 as your treasury wallet holding all the custom tokens you will mint, simply change the above line to:

treasury = address(`0xFeE9813A4B268793D4Edc6DF11A760C3c07a2c98`);

If you want to use the Link default account as the treasury, delete treasury = address(<some address>); and uncomment treasury = msg.sender;.

Lastly, change the 1000 supply figure to your desired supply amount.

Your custom token contract is ready to go! Follow the previous deployment steps in Blockmason Link to deploy the custom token contract.

Use the blockchain explorer to confirm you have successfully deployed your token contract.

Deploy your ‘Comments’ Smart Contract

The final smart contract we will deploy is for Comments, with the full Solidity code in contracts/Comments.sol.

The contract is quite straight-forward using just a Comment event emitter every time a comment is made about a product:

pragma solidity ^0.5.10;

contract Comments {
    event Comment(string asset, string comment);
    address public authority;

    constructor() public {
        authority = msg.sender;
    }

    function postComment(string memory asset, string memory comment) public {
        emit Comment(asset, comment);
    }
}

Follow the previous deployment steps in Blockmason Link to deploy the Comments contract to your blockchain.

Use the blockchain explorer to confirm you have successfully deployed your token contract.

Congrats! You have successfully created and deployed multiple smart contracts! In the next tutorial, we will use our Link APIs and Link SDKs to interact with these contracts.

images/tutorial_3_video.png

Interact with Smart Contract Functions

Goal: Use Link APIs to interact with the previously deployed Purchasing, custom token and Comments smart contracts.

Install Link JavaScript SDK

Recall that on the Link homepage, clicking on the gear icon next to Code/API toggles the client_id and client_secret at the bottom of the code editor screen.

Link API Client ID and Secret

Clicking on the Code/API toggle also displays the available API endpoints:

Link API endpoints

We will use client_id and client_secret credentials to interact with these API endpoints using the Link JavaScript SDK.

The Link JavaScript SDK is available using Yarn or NPM (https://www.npmjs.com/package/@blockmason/link-sdk).

We will also use a basic node script to interact with the APIs. To use the Link JavaScript SDK with node, we also need to install the node-fetch library.

From the root project folder, run:

npm install @blockmason/link-sdk

npm install node-fetch

Interact with the Link APIs

Open Tutorial_3/demo/purchasing-demo-script-template.js. Here we have a template script file with several functions that need to be completed. We will go through the process of adding code for a few of these functions.

First, let us look at the dependencies and Link SDK initialization:

const fetch = require('node-fetch');
const { link } = require('@blockmason/link-sdk');

// Authenticate with Link API
const project = link({
    clientId: '',
    clientSecret: ''
  }, {
    fetch
});

The first two lines import our node-fetch and link-sdk libraries as fetch and link objects respectively.

We then instantiate the link object by passing in the clientId and clientSecret which we get from Link as shown above.

Enter in your Link clientId and clientSecret found at the bottom of the code editor or API endpoint screen in Link.

Note – because we are using node, we pass in the fetch object into link to help manage our http requests.

Now our project object is correctly initialized and connected with Link to make GET and POST requests with simply project.get(..) and project.post(..) respectively!

Remember: All calls to the API endpoints are async!

addProduct()

async function addProduct(product, addQuantity, url, description, price, company, id) {
    //TODO
}

Let us take a look at the POST /addProduct API endpoint in Link:

Link API POST addProduct

This tells us all the request parameters we need to send in the POST request and their data type.

Our code might look something like this:

async function addProduct(product, addQuantity, url, description, price, company, id) {
  const reqBody = {
    "product": product,
    "addQuantity": addQuantity,
    "url": url,
    "description": description,
    "price": price,
    "company": company,
    "id": id
  }
    
  await project.post('/addProduct', reqBody);
}

If you are not familiar with JavaScript’s async/await syntax, please take a look at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

addProductQuantity()

async function addProductQuantity(product, quantity) {
    //TODO
}

Recall that our smart contract code for adding quantity is:

function addProductQuantity(string memory product, uint addQuantity) public {
  require(productList[product].isSet);
  productList[product].quantity += addQuantity;
}

And the Link API endpoint is:

Link API POST add product quantity

This should be pretty straightforward but there is one subtlety that we must factor in! The Link API documentation tells us that no response is provide. While this is true for the smart contract function not returning anything, we will still get a response at the http layer. This is important because if the require(productList[product].isSet) requirement fails, we will only see a error sent at the http level!

So our code might look something like this:

async function addProductQuantity(product, quantity) {
  const reqBody = {
    "product": product,
    "addQuantity": quantity
  }
  
  const response = await project.post('/addProductQuantity', reqBody);
  console.log('POST /addProductQuantity called with request data ', reqBody);
  if (response.errors) {
    console.log('Error with adding quantity of ', quantity, '. Check that product exists!');
  }
}

Now if the http response object contains an error, at the function level, it would be because the require(...) statement failed which helps provide context for the error handling.

purchaseProduct()

async function purchaseProduct(product) {
    //TODO 
}

Similar to #addProductQuantity()above, the smart contract code contains a require(..) statement:

function purchaseProduct(string memory product, address purchaser) public {
  require(productList[product].purchasers.length < productList[product].quantity); 
  productList[product].purchasers.push(purchaser);
}

For the purpose of this demo script, we will fix the purchasers wallet address to 0xaFf485B0dd5D2c2851FDf374D488379F75403663 (it can be any Ethereum wallet address).

So our code might look something like this:

async function purchaseProduct(product) {
  const reqBody = {
    "product": product,
    "purchaser": "0xaFf485B0dd5D2c2851FDf374D488379F75403663"
  }
  
  const response = await project.post('/purchaseProduct', reqBody);
  console.log('POST /purchaseProduct called with request data ', reqBody);
  if (response.errors) {
    console.log('Error with purchasing product. Check that product and sufficient quantity exists');
  }
}

Go ahead and complete the #getProductDetails function, which will call the GET /productList endpoint.

Go ahead and complete the #getProducts() function, which will call the GET /events/Product. Note – since these are events, a list object is returned.

See the complete code in Tutorial_3/demo/purchasing-demo-script.js

Running the script

At the bottom, of the script file, we create a test product and then below that, we make our function calls.

Uncomment the function you want to call, and then simply run: node <filename> from your Terminal/Command Prompt.

For example, if you:

  • Execute the #addProduct(..) function
  • Uncomment the previous function and execute the #getProducts() function

you will see an output like:

Terminal Output Link JavaScript SDK

Congrats! You have succesfully interacted with your Purchasing smart contract using web APIs through Link!

In the next tutorial, we will create a few microservices, which will be short, concise, javascript files each running one specific service. These microservices will then be used by our front-end ecommerce marketplace app.

Create Blockchain-Powered Microservices

Goal: To create portable microservices that give our application a series of independently deployable features. We create these with JavaScript files that will interact with our Smart Contracts on the blockchain using the Link JavaScript SDK.

What microservices do we need?

Payments Service

The payment microservice controls the transfer of funds from a purchaser to a seller when a product is bought.

Details: The payment microservice executes transfers of a custom token, which we created in an earlier tutorial, and held by a treasury. When products are purchased, tokens are transferred between the appropriate wallets. These custom tokens are used instead of the typical credit card.

Purchase Service

The purchase microservice controls: the creation and storage of products (including the details of those products), changes to product inventory, and records which products are bought and by whom.

Comments Service

The Comments microservice records and retrieves customer commentsfor a particular product.


Using Environment Variables when Interacting with your Link APIs

Recall in the previous tutorial, we used basic node scripts to interact with our Purchasing smart contract. In these scripts, we copied/pasted the required client_id and client_secret directly into our script.

  const service = link ({
    clientId: "<CLIENT_ID>",
    clientSecret: "<CLIENT_SECRET>" });

However, in creating our microservices, we will be using environment variables to protect our client ID and secret from the public and being stored in version control.

Before we move on let us set up some environment variables in a .env file

  • Create a new file in your project folder named .env
  • Add your .env to your .gitignore
  • In your .env file you can store your client IDs and client secrets for each microservice. So for example, for the Purchasing smart contract APIs, you may have:
PURCHASE_CLIENT_ID=abcdefg..
PURCHASE_CLIENT_SECRET=abcefg..

Then in your script file, you can use the environment variables with:

  const service = link ({
    clientId: process.env.PURCHASE_CLIENT_ID,
    clientSecret: process.env.PURCHASE_CLIENT_SECRET });

Building portable microservices

In this section we will break down how each microservice is constructed using JavaScript.

Payments Service

First we need to set up the SDK.

const { link } = require('@blockmason/link-sdk');

const paymentMicroservice = link({
    clientId: "<CLIENT_ID>",
    clientSecret: "<CLIENT_SECRET>"
});

Next we need two functions which will:

  • Check the balance of a user’s wallet to ensure that the customer has enough money in their wallet to make the purchase when they are checking out
  • Transfer funds from the customer to the producer when a purchase is made
const paymentService = {

    balanceOf: async function (address) {
        const reqBody = {
            "_tokenholder": address
        }
        const { balance } = await paymentMicroservice.get('/balanceOf', reqBody);
        return(parseInt(balance, 16) / Math.pow(10, 18));
    },

    transferFrom: async function transferFunds(sender, recipient, amount) {
        const funds = (amount*Math.pow(10, 18)).toString(16);
        const transferBody = {
          "_from": sender,
          "_to": recipient,
          "_value": funds
        }
        try {
          console.log('transfering funds now');
          await paymentMicroservice.post('/transferFrom', transferBody);
          console.log('transferring funds complete');
        }
        catch(err) {
          console.log(err);
        }
       },
}

Finally we export our payment service as a module that can be used elsewhere:

module.exports = paymentService;

Purchase Service

The following Purchasing microservice setup code should be very familiar, if not identical, to the scripts we built in the previous tutorial.

First we set up the SDK.

const { link } = require('@blockmason/link-sdk');

// Purchse Service API
const purchaseMicroservice = link({
    clientId: "<CLIENT_ID>",
    clientSecret: "<CLIENT_SECRET>"
});

Next, we need to add a function that will allow for the creation of new products. In the addProduct function we create an object with all the product details and use the Link SDK to send a post request to store those details.

purchaseService = {
    addProduct: async function (product, quantity, url, description, price, company, id) {
        const reqBody = {
            "product": product.toString(),
            "addQuantity": quantity.toString(),
            "url": url.toString(),
            "description": description.toString(),
            "price": price.toString(),
            "company": company.toString(),
            "id": id.toString(),
        };
        return await purchaseMicroservice.post('/addProduct', reqBody);
    },

We also need a function that will be called when a product is purchased. This function records which product is purchased and by whom.

    purchaseProduct: async function (product, buyerAddress) {
        console.log(product);
        console.log(buyerAddress);
        const reqBody = {
            "product": product,
            "purchaser": buyerAddress
        };
        const response = await project.post('/purchaseProduct', reqBody);
        console.log('POST /purchaseProduct called with request data ', reqBody);
        if (response.errors) {
            console.log('Error with purchasing product. Check that product and sufficient quantity exists');
        }
    }

Finally, we want to be able to retrieve all the products that we have stored on the blockchain. Using the getProducts function we can retrieve an array of products and information about these products.

    getProducts: async function () {
        const productList = await purchaseMicroservice.get('/events/Product');
        return productList.data
    }
}

Finally we export our purchasing service as a module that can be used elsewhere:

module.exports = purchaseService;

Comments Service

First we set up the SDK.

const { link } = require('@blockmason/link-sdk');

const commentsMicroservice = link({
    clientId: "<CLIENT_ID>",
    clientSecret: "<CLIENT_SECRET>"
});

The comments service is relatively simple. It consists of making a post request to store a comment about a specific product, and making a get request to retrieve all the comments.

commentsService = {
    getComments: async function () {
        const comments = await commentsMicroservice.get('/events/Comment');
        comments.data.forEach((data) => {
            this.addCommentsInMemory(data)
        });
    },

    addCommentsInMemory: function(commentData) {
        this.commentsInMemory.push(commentData);
    },
}

module.exports = commentsService;

And that’s it for our 3 microservices! Each represents a simple service containing a few key functions that our front-end application can now use.

In the next and final tutorial, we tie it all together and run our ecommerce marketplace app!

images/tutorial_5_video.png

Run Your Ecommerce Marketplace App

Goal: Bringing all the microservices together in one application and getting it running.

Let’s look at an overview of what we have built so far

The application can be broken down into 3 basic sections: Product, Currency, and Comments. And a fourth being the front-end.

  • The Payments Service: This controls the transfer of funds from a buyer to a seller upon purchase of a product. The Smart Contract used to create this service creates the tokens that represent value, distributes those tokens, and manages the transfer of tokens on behalf of the users. These transactions are then recorded on the blockchain and can be confirmed by the appropriate blockchain explorer.
images/payments-service.png
  • The Comments Service: This allows customers to leave reviews and comments on particular products. These comments are then recorded to the appropriate blockchain. The Smart Contract used to create this service records the comment text and the product id associated with the comment. The contract can then recall all the past comments for each product.
images/comments-service.png
  • The Purchase Service: This is a basic product management system. It controls the creation of new products including the details of the product such as product name, quantity, description, price etc. it then stores that data. This contract also manages product stock, and contains a ledger of product purchase history including the customers who purchased the product.
images/purchase-service.png

Once we have our front-end set up, we can pull each service together in the index.js file.

Front-End Breakdown

Instead of breaking down the entire index.html and index.js files let’s take a look at some of the general concepts behind these files and the way they interact.

The core piece of the index.html file that we interact with is the product template. Inside the product template we create and store most of our data. The bulk of the DOM manipulation is achieved through the JavaScript library Jquery.

images/jquery.png

on the index.js side whenever you see $('something in here') that is using the Jquery library.

images/product-template.png

Now stepping inside the index.js file there are several key actions we want to respond to when a user is interacting with our application on the front-end.

The first is logging in. When a user enters their credentials at the top of the page it maps their username to a specific wallet in a series of wallet addresses. This wallet will then be associated with any interactions the user has such as purchasing a product.

The next key action is purchasing a product. When a user clicks the ‘Buy Now’ button on a product card, a function is called to the Payment service and Purchase service. If the payment is successful (if the user has enough money to buy the product) and if their is enough stock of that particular item then the purchase is recorded, and the funds are transferred. The index.js file gets the appropriate id of the product a customer intends to purchase from the data and id attributes associated with the DOM elements created during from the product template. Which brings us to our next component, printing each product and creating new products.

images/purchasing.png

Products are generated on the front-end by retrieving an array of product data that has been stored on the blockchain. The appropriate data is then distributed on a unique copy of the product template including images, description, and price, and then displayed on the page as a product card. More products can be added by using filling in the “add new product” form. Once submitted the data in the form is sent through the Purchase service to the blockchain to be added to the list of products available.

images/products.png

Finally customers can leave comments and reviews on specific products. When a comment is submitted, in the index.js file using Jquery we can retrieve the text that has been submitted, and the id of the product that the comment is associated with. This data is then sent using the Comments service to the Link API and finally recorded on the blockchain. All the appropriate comments are retrieved from the blockchain when the page loads and displayed.

images/comments.png

Application Overview

Now that we have the front-end and microservices figured out let’s take a look at how they all tie together. It’s pretty easy to follow: Each microservice acts independently from the other services and from the front-end. This means that the service can be transported to other applications with ease (say if you wanted to build a mobile app instead). The index.js file then acts as the glue that ties the interactions of each microservice and the front-end HTML together. This keeps the front-end independent from each individual component, and will make it easier to add new features in the future.

Each microservice also interacts with a blockchain network via Link as we have seen in past tutorials.

The flow of interactions looks something like this:

index.html <—–> index.js <—–> Payment Service <—–> Link <—–> Blockchain Network

index.html <—–> index.js <—–> Purchase Service <—–> Link <—–> Blockchain Network

index.html <—–> index.js <—–> Comment Service <—–> Link <—–> Blockchain Network

Let’s get it running!

The last thing to do is get our application running.

Now open up your terminal and run the command npm start or alternatively parcel src/index.html. This should start up our parcel server and run the application at localhost port 1234, and thats it! If everything is up and running, you should see the following front-end:

App Front-end

Great! Now let us add a new product and record it on the blockchain. The Add New Product modal looks like:

Add new product

Let us add a new soccer ball with this image URL from Unsplash. Fill in the modal as follows:

Add new product filled

And see your new product added to the marketplace!

App front-end new product added

Refresh the website to ensure your product is being correctly retrieved from the blockchain!

To purchase a product, you need to ‘log in’. What this really means, is using one of the following names we have mapped to accounts in index.js in the log in field:

App wallet mappings
App front-end login

Go ahead and add products to this marketplace, buy products, and add comments – all of these microservices powered by blockchains!

Congratulations you are done!