Pay for light (Part 2)
Tags: blockchain, iot
Published: 2020-09-17 08:13:00

This tutorial continues from ***Part 1*** which can be found [here](https://dev.to/peterokwara/pay-for-light-part-1-1nec) make sure you go through it before going through this.

## Step 7: Api Setup

The code for the api can be found [here](https://github.com/peterokwara/pay-for-light/tree/master/api). All is needed is to download the folder and run ***npm run install*** within the folder. In the following sections we will walk through the major files within the api.

## Step 8: TextHelper
The [TextHelper](https://github.com/peterokwara/pay-for-light/blob/master/api/src/utils/textHelper.ts) class holds helper functions to help store information on the Tangle.

```javascript
/**
 * Helper functions for use with text.
 */
export class TextHelper {
}
``` 
The *encodeNonASCII* function encodes non ASCII characters to escaped characters.

```javascript
/**
     * Encode Non ASCII characters to escaped characters.
     * @param value The value to encode.
     * @returns The encoded value.
     */
    public static encodeNonASCII(value: string): string | undefined {
        return typeof (value) === "string" ?
            value.replace(/[\u007F-\uFFFF]/g, chr => `\\u${(`0000${chr.charCodeAt(0).toString(16)}`).substr(-4)}`)
            : undefined;
    }
``` 

The *decodeNonASCII* function decodes escaped characters to non ASCII characters.

```javascript
/**
     * Decode escaped Non ASCII characters.
     * @param value The value to decode.
     * @returns The decoded value.
     */
    public static decodeNonASCII(value: string): string | undefined {
        return typeof (value) === "string" ?
            value.replace(/\\u([\d\w]{4})/gi, (match, grp) => String.fromCharCode(parseInt(grp, 16))) :
            undefined;
    }
``` 

## Step 9: TrytesHelper

The necessary functions were imported to the [TrytesHelper class](https://github.com/peterokwara/pay-for-light/blob/master/api/src/utils/trytesHelper.ts). This will help convert objects to trytes and trytes to objects. In case we need to generate a hash, we also create a function to generate a given hash.

```javascript
import { asciiToTrytes, TRYTE_ALPHABET, trytesToAscii } from "@iota/converter";
import * as crypto from "crypto";
import { TextHelper } from "./textHelper";
``` 

The create class helps with all the trytes operations we will deal with.

```javascript
/**
 * Helper functions for use with trytes.
 */
export class TrytesHelper {

}
``` 

The *toTrytes* function converts an object to trytes.

```javascript
/**
     * Convert an object to Trytes.
     * @param obj The obj to encode.
     * @returns The encoded trytes value.
     */
    public static toTrytes(obj: unknown): string {
        const json = JSON.stringify(obj);
        const encoded = TextHelper.encodeNonASCII(json);
        return encoded ? asciiToTrytes(encoded) : "";
    }

``` 
The *fromTrytes* function converts trytes to a given object.

```javascript
/**
     * Convert an object from Trytes.
     * @param trytes The trytes to decode.
     * @returns The decoded object.
     */
    public static fromTrytes<T>(trytes: string): T {
        if (typeof (trytes) !== "string") {
            throw new Error("fromTrytes can only convert strings");
        }

        // Trim trailing 9s
        const trimmed = trytes.replace(/\9+$/, "");

        if (trimmed.length === 0) {
            throw new Error("fromTrytes trytes does not contain any data");
        }

        const ascii = trytesToAscii(trimmed);
        const json = TextHelper.decodeNonASCII(ascii);
        return json ? JSON.parse(json) : undefined;
    }
``` 

The *generateHash* function generates a random hash for the length provided.

```javascript
  /**
     * Generate a random hash.
     * @param length The length of the hash.
     * @returns The hash.
     */
    public static generateHash(length: number = 81): string {
        let hash = "";

        while (hash.length < length) {
            const byte = crypto.randomBytes(1);
            if (byte[0] < 243) {
                hash += TRYTE_ALPHABET.charAt(byte[0] % 27);
            }
        }

        return hash;
    }
``` 

## Step 10: MQTT
The necessary files were imported using the code below. One is the *Mqtt* library. The other is the configuration files.

```javascript
import mqtt from "mqtt";
import config from "../data/config.local.json";
``` 
The *MqttHelper* class handles all the Mqtt functions.

```javascript
/**
 * Class to handle Mqtt functions
 */
export class MqttHelper {
}
``` 

Inside the class, variables were set which represent the configuration files for the *Mqtt* connection to Adafruit IO, which acts like our broker in the cloud. This was done using the code below. 

```javascript
/**
     * The mqtt client
     */
    protected _mqttClient: mqtt.MqttClient;

    /**
     * The mqtt host
     */
    protected readonly _host: string;

    /**
     * The username for the mqtt host/broker
     */
    protected readonly _username: string;

    /**
     * The password for the mqtt host/broker
     */
    protected readonly _password: string;

    /**
     * The feed/topic to subscribe to
     */
    protected readonly _feed: string;

    /**
     * The port to use when connecting to the mqtt host/broker
     */
    protected readonly _port: number;

    
``` 
In our constructor, we set up our variables to the values we stored in our configuration files.

```javascript
constructor() {
        this._mqttClient = null;
        this._host = `mqtts://${config.MQTT_CONFIG.ADAFRUIT_IO_URL}`;
        this._username = config.MQTT_CONFIG.ADAFRUIT_USERNAME;
        this._password = config.MQTT_CONFIG.ADAFRUIT_IO_KEY;
        this._feed = `${config.MQTT_CONFIG.ADAFRUIT_USERNAME}/f/${config.MQTT_CONFIG.ADAFRUIT_IO_FEEDNAME}`;
        this._port = config.MQTT_CONFIG.ADAFRUIT_IO_PORT;
    }
``` 

We then created a function that will help us connect to our broker. In this case Adafruit IO .

```javascript
 /**
   * Function to connect to a mqtt host/broker and subscribe to events coming from it
   */
public connect(): void {

}
``` 
Connect to the cloud broker with username, password and port to use.

```javascript
// Connect mqtt with credentials (in case of needed, otherwise we can omit 2nd param)
        this._mqttClient = mqtt.connect(
            this._host, { username: this._username, password: this._password, port: this._port });
``` 
In case of error we want to log the error and break off the connection.

```javascript
 // Mqtt error calback
        this._mqttClient.on("error", err => {
            console.log(err);
            this._mqttClient.end();
        });
``` 
In case we successfully connect, we print out a message that our computer has successfully connected.

```javascript
// Connection callback
        this._mqttClient.on("connect", () => {
            console.log(`mqtt client connected`);
        });
``` 

We specify the feed we want to connect to and subscribe to it.

```javascript
// mqtt subscriptions
        this._mqttClient.subscribe(this._feed);
``` 
In case we get a message, we will just show the message we received.
```javascript
  // When a message arrives, console.log it
        this._mqttClient.on("message", message => {
            console.log(message.toString());
        });
``` 

On close, we will just close the *Mqtt* client.

```javascript
this._mqttClient.on("close", () => {
            console.log(`mqtt client disconnected`);
        });
``` 
We created another function for publishing messages to *Mqtt*.

```javascript
 /**
     * Function  to send messages to the mqtt client/broker
     * @param message The message to be sent
     */
    public sendMessage(message: string | Buffer): void {
        this._mqttClient.publish(this._feed, message);
    }
``` 

## Step 11: MAM
We import the necessary libraries.

```javascript
import { composeAPI } from "@iota/core";
import { createChannel, createMessage, mamAttach } from "@iota/mam.js";
import fs from "fs";
import config from "../data/config.local.json";
import { INodeConfiguration } from "../models/configuration/INodeConfiguration";
import { TrytesHelper } from "./trytesHelper";
``` 

A class called *MamHelper* was created which will helps us with our connection to the Mam channel.

```javascript
export class MamHelper {
}
``` 
In it we import the configuration settings for the node we want to use.

```javascript
/**
  * Node configuratin settins
  */
  private readonly _nodeConfig: INodeConfiguration;

  constructor() {
       this._nodeConfig = config.node;
   }
``` 
We created a function to store messages on the mam channel.

```javascript
/**
 * Function to store information on the mam channel
 * @param asciiMessage The message to be stored on the mam channel
 */
public async create(asciiMessage: object): Promise<void> {
}
``` 

We then created a try-catch to catch errors if the whole process of storing the messages in the mam channel fails.

```javascript
try {

} catch (error) {
            throw new Error(`Could not store the message on the mam channel ${error} `);
}
``` 

We then try to load the channel state and parse it.


```javascript
 let channelState;

            // Try and load the channel state from json file
            try {
                const currentState = fs.readFileSync("./channelState.json");
                if (currentState) {
                    channelState = JSON.parse(currentState.toString());
                }
            } catch (e) { }
``` 
If we are unable to load the channel state, we create a new one.


```javascript
// If we couldn't load the details then create a new channel.
if (!channelState) {

    // set up details for the channel
    const mode = "public";

    // create a new mam channel
    channelState = createChannel(TrytesHelper.generateHash(81), 2, mode);
}
``` 

We create a Mam message using the channel state


```javascript
// Create a MAM message using the channel state.
const mamMessage = createMessage(channelState, TrytesHelper.toTrytes(asciiMessage));
``` 
Then display mam channel information

```javascript
// Display the details for the MAM message.
console.log("Seed:", channelState.seed);
console.log("Address:", mamMessage.address);
console.log("Root:", mamMessage.root);
console.log("NextRoot:", channelState.nextRoot);
``` 
Setup node configuration for the provider.

```javascript
 const iota = composeAPI({
          provider: this._nodeConfig.provider
 });
``` 

Then attach the message to the mam channel.

```javascript
// Attach the message.
console.log("Attaching to tangle, please wait...");

await mamAttach(iota, mamMessage, this._nodeConfig.depth, this._nodeConfig.mwm);
``` 
We provide a link where we can see all the information from the Mam channel.

```javascript
console.log(`You can view the mam channel here https://utils.iota.org/mam/${mamMessage.root}/devnet`);

``` 

We then store the state of the Mam channel json file


```javascript
 // Store the channel state.
 try {
         fs.writeFileSync("./channelState.json", JSON.stringify(channelState, undefined, "\t"));
 } catch (e) {
         console.error(e);
 }
``` 

## Step 12: Iota-Pay

We import the necessary libraries and files for the project. 

```javascript
import bodyParser from "body-parser";
import express from "express";
import paymentModule from "iota-payment";
import scheduler from "node-schedule";
import { MamHelper } from "./utils/mamHelper";
import { MqttHelper } from "./utils/mqttHelper";
``` 

We then set up an express server.

```javascript
const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
``` 

We import the MqttHelper function. We then connect and first of all turn the relay off by sending an "OFF" message.

```javascript
const mqttClient = new MqttHelper();
mqttClient.connect();
mqttClient.sendMessage("OFF");
``` 

We then set up our options for the payment module. This involves enabling the api option, the dashboard option  and the web socket option.

```javascript
const options = {
    api: true,
    dashboard: true,
    websockets: true
};
``` 

 Set up an express server and run it. This provides us with two routes. The **/iotapay** route and the **/iotapay/api** route.

```javascript
const server = paymentModule.createServer(app, options);

// Start server with iota-payment dashboard on '/iotapay' and api on '/iotapay/api'
server.listen(5000, () => {
    console.log(`Server started on http://localhost:5000 `);
    console.info(`Please visit http://localhost:5000/iotapay in your browser`);
});
``` 
Set up a time variable that will return the current date and time.

```javascript
const time = () => {
    return new Date();
};
``` 
We then create a payment handler. The payment handler will be called once a payment is successful.

```javascript
//Create an event handler which is called, when a payment was successful
const onPaymentSuccess = async payment => {
}
``` 

We store all the payment information into variable. This also includes the payment event itself.

```javascript
const transaction = {
        paid: payment.paid,
        type: payment.type,
        value: payment.value,
        address: payment.address,
        index: payment.index,
        id: payment.id,
        txInfo: {
            timestamp: payment.txInfo.timestamp,
            hash: payment.txInfo.hash,
            value: payment.txInfo.value,
            message: payment.txInfo.message
        },
        event: [`User has paid ${payment.txInfo.value} tokens for lights`]
    };
``` 

Set up the mam channel and create a message.

``` javascript
const mamHelper = new MamHelper();

await mamHelper.create(transaction);
``` 

We then wait for 3 seconds before adding the time we turn the lights on and push the whole transaction to the mam channel.

```javascript
setTimeout(async () => {
        // send the event that the lights are now on
        transaction.event.push(`Lights are on ${time()}`);
        await mamHelper.create(transaction);

        // tslint:disable-next-line: align
    }, 3000);
``` 

Let's make the amount of time (in minutes) the lights will be on be the amount of  IOTA tokens multiplied by 60000.

```javascript
 const seconds: number = payment.txInfo.value * 60000;
``` 

Let's set up the start time and the end time, the lights will be on.

```javascript
 const startTime = new Date(Date.now());
 const endTime = new Date(startTime.getTime() + seconds);
``` 

To keep the lights on, we send a Mqtt message every 10 seconds for the amount of time we have set.

```javascript
await scheduler.scheduleJob({ start: startTime, end: endTime, rule: `*/10 * * * * *` }, () => {
        mqttClient.sendMessage("ON");
        console.log("😊");
   });
``` 
Once the time is over, we set the end time for which which the off message will be sent.

```javascript
 const newEndTime = new Date(endTime.getTime() + 30000);
``` 
We send an OFF mesage for another 10 seconds for a period of 30 seconds to ensure the relay is off.


```javascript
await scheduler.scheduleJob({ start: endTime, end: newEndTime, rule: `*/10 * * * * *` }, () => {
        mqttClient.sendMessage("OFF");
        console.log("😐");
    });
``` 
We then immediately send that the lights are off to the mam channel.

```javascript
await scheduler.scheduleJob({ start: endTime, end: newEndTime, rule: `*/30 * * * * *` }, async () => {

        // send the event that the lights are now off
        transaction.event.push(`Lights are off ${time()}`);
        await mamHelper.create(transaction);
    });
``` 

At the end of this, we now call the function onPaymentSuccess that we just created.

```javascript
paymentModule.onEvent("paymentSuccess", onPaymentSuccess);
``` 

## Step 13: Configuration
There are two configurations we need to set up. One is for the MQTT and the IOTA Node, to store transaction information using MAM. The other one is a seed for generating an address for payment and connecting to the IOTA Nodes only to do payments.

Same with the configuration on step 4, we would only need to change the ***adafruit io username*** and ***adafruit io*** key. This configuration is found [here](https://github.com/peterokwara/pay-for-light/tree/master/api/dist/data). The file should be saved as **config.local.json**.

```
{
    "MQTT_CONFIG": {
        "ADAFRUIT_IO_URL": "io.adafruit.com",
        "ADAFRUIT_USERNAME": "",
        "ADAFRUIT_IO_KEY": "",
        "ADAFRUIT_IO_FEEDNAME": "pay-for-light.bulb-relay",
        "ADAFRUIT_IO_PORT": 8883
    },
    "node": {
        "provider": "https://nodes.devnet.iota.org:443",
        "depth": 3,
        "mwm": 9
    }
}
```

Create another file called a **.env** file in the api folder. Add the following configuration.
```
seed=
iotaNodes=["https://nodes.comnet.thetangle.org:443"]
```
Generate a seed by heading over to https://docs.iota.org/docs/getting-started/0.1/tutorials/create-a-seed and running one of the commands to generate a seed. After that you can paste the seed as it is in the ***seed=*** parameter.

## Step 14: Testing
We can now run the api server by running the command below.

```cd api && npm run start```

This boots up the api server as shown below.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pw56vfud89bjrpgnph0w.png)

**Note: Running npm run start-dev puts its in a loop. Don't use it.**

Once the api server has boot up, you can now go to the storefront to get your IOTA address. This is highlighted in red in the above image as.

***http://localhost:5000/iotapay/#/***


Once you go to the url on your machine or server, the page looks like this.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/tmsfylm8tn1yt03u9wgf.png)

Clicking on Show QR Code displays the QR code. The QR code can be scanned to get the IOTA address to make payments to.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9nvojqaylf9buva39ak1.png)

Clicking on Copy address copies the IOTA address to the clipboard. Once we have the IOTA address, we can now make payments to the given address.

Go to the IOTA *Comnet* *Fauchet*site here. The site provides free tokens for the community testent. The tokens will be used to simulate a payment process.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/l2d6e3l73z330n1zxv5d.png)

Enter the address to send tokens on the IOTA Comnet address to and specify the amount.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/l1ogmvcbab5696phfbdl.png)

For now we will send 3 IOTA tokens. Once we enter the amount, we can now hit send. One can also add an optional message to accompany the payment.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pun56aq0rdqkg2lsd6th.png)


Once the payment is successfull, a message will pop up, Transaction sent: with a link to the transaction on the tangle. This also shows up on the console as Payment Successful! Address XXXXX


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fjoe3hkddhehsdnerfqr.png)


Going to the link shown above (You can view the mam channel here), we can see the in the mam explorer, the payment information and the events. This is shown in the photos below.


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/c0xk3w89qmh0uc4e5b41.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9m1v0zybgq2k8ltiy4qr.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/56nyom6duye5pl5d3jap.png)

The event are:
- User has paid 3 tokens for lights
- Lights are on
- Lights are off

The lights will then turn on and then turn off after 3 minutes.

## Limitations
Limitations of the project are:
- Security has not been implemented in the MAM channel for this project. At the moment, anyone can access the information.  Security can be enhanced by using private mode or restricted mode.
- There is no mechanism for handling multiple payments at the same time. One can only do one payment at a time for it to work correctly. Probably having a deposit account and checking the amount would help. Or queue the payments
- Maximum amount of time the lights can be turned on is 59 minutes. This limitation is easy to fix, it just involves modifying the *npm *module, node schedule to allow for more date and time.
- One cannot get a specific transaction. It shows the compete list of transactions from which one has to manually search for a specific transaction.

## References
Some of the references for this code is found here:
- The payment module https://github.com/iota-pay/iota-payment-module
- Masked Authenticated Messaging (MAM) https://github.com/iotaledger/mam.js
- Connecting to Adafruit IO https://github.com/adafruit/adafruit-io-node
- Building management system https://github.com/peterokwara/BuildingManagement
- Energy boss must wake up Kenya Power https://www.businessdailyafrica.com/analysis/columnists/Energy-boss-must-wake-up-Kenya-Power/4259356-5618224-cm7w40z/index.html