Creating HMAC BodyHash Sha256 nodejs - node.js

I am trying to integrate with a service (paysera https://developers.paysera.com/) that uses HMAC Authentication. I've manage to integrate every GET endpoint. Nevertheless, when it comes to the POST request it seems the code is not working.
createHMACAuth(data) {
let bodyHash = crypto.createHash('sha256').update(JSON.stringify(data)).digest('base64');
let method = 'POST';
let path = `/rest/v1/transfers`;
let ts = moment().unix();
let nonce = GUID(32);
let port = 443;
let macString = `${ts}\n${nonce}\n${method}\n${path}\n${host}\n${port}\n${bodyHash || ''}\n`;
let my_mac_key = 'my_mac_key';
let my_mac_id = 'my_mac_id';
let mac = crypto.createHmac('sha256', my_mac_key).update(macString).digest('base64');
let headerString = `MAC id="${my_mac_id}", ts="${ts}", nonce="${nonce}", mac="${mac}", ext="body_hash=${bodyHash}"`;
return headerString;
}
let data = {
key: 'value',
otherkey: 'othervalue'
};
let headers = {
Host: 'wallet.paysera.com',
'User-Agent': `Paysera node.js library`,
mac_id: 'my_mac_id',
Authorization: createHMACAuth(data);
};
POST_REQUEST(`${headers.host}/rest/v1/transfers`, data, headers, (err, res) => console.log(res))
The response I get is:
{
error: 'unauthorized',
error_description: 'Given MAC content body hash does not match actual hash of content'
}
Any help will be much appreciated!

I had an error when generating the body_hash
Fix
createHMACAuth(method, path, data) {
let bodyHash, ext;
if (method === 'POST') {
ext = `body_hash=${encodeURIComponent(this.createHash(JSON.stringify(data)))}`;
}
this.method = method;
this.path = `/${path}`;
let nonce = `${U.GUID(32)}`;
let port = 443;
let macString = `${this.ts}\n${nonce}\n${this.method}\n${this.path}\n${this.host}\n${port}\n${ext || ''}\n`;
let mac = this.createHMACHash(macString);
let headerString = `MAC id="${this.mac_id}", ts="${this.ts}", nonce="${nonce}", mac="${mac}"`;
if (method === 'POST') headerString += `, ext="${ext}"`
return headerString;
}

Your answer helped me make it work. Full code for other people that might need this.
import crypto from 'crypto'
import fetch from 'node-fetch'
function createHash(data) {
return crypto.createHash('sha256').update(data).digest('base64');
}
function createHMACHash (macKey, macString) {
return crypto.createHmac('sha256', macKey).update(macString).digest('base64');
}
async function createHMACAuth() {
const macId = 'your-mac-id';
const macKey = 'your-mac-key';
const ts = new Date().getTime();
const nonce = 'nQnNaSNyubfPErjRO55yaaEYo9YZfKYU';
const method = 'POST';
const uri = '/rest/v1/transaction'
const host = 'wallet.paysera.com'
const port = 443;
const data = {
"payments": [
{
"description": "Payment for order No. 1234",
"price": 1299,
"currency": "EUR",
"parameters": {
"orderid": 1234
}
}
],
"redirect_uri": "http://www.example.com/somePage"
}
let ext;
if (method === 'POST') {
ext = `body_hash=${encodeURIComponent(createHash(JSON.stringify(data)))}`;
}
let macString = `${ts}\n${nonce}\n${method}\n${uri}\n${host}\n${port}\n${ext || ''}\n`;
let mac = createHMACHash(macKey, macString);
let headerString = `MAC id="${macId}", ts="${ts}", nonce="${nonce}", mac="${mac}"`;
if (method === 'POST') headerString += `, ext="${ext}"`
const response = await fetch(`https://${host}${uri}`,{
method:method,
headers:{
Authorization: headerString,
'Content-Type':'application/json'
},
body: JSON.stringify(data)
})
return headerString;
}
await createHMACAuth()

Related

How to speed up Fetching Google Place and Photos

I currently have the following code to fetch matching Google Places according to a received query as shown below:
async function searchGoogleBusiness(req, res) {
let { name } = req.query;
const apiKey = process.env.API_KEY;
const searchUrl = `https://maps.googleapis.com/maps/api/place/textsearch/json?query=`;
try {
let { data } = await axios.get(`${searchUrl}${name}&key=${apiKey}`)
let { status, error_message, results } = data;
if (status === 'OK') {
let businessResults = [];
if ((results ?? []).length > 0) {
for (let business of results) {
let businessDetails = {
....
}
if ((business.photos ?? []).length > 0) {
let { width = 1200, height = 1200, photo_reference } = business.photos[0];
let photoUrl = `https://maps.googleapis.com/maps/api/place/photo?photoreference=${photo_reference}&sensor=false&maxheight=${height}&maxwidth=${width}&key=${apiKey}`
try {
let businessPhotoResponse = await axios.get(photoUrl, { responseType: 'arraybuffer' });
let imageBuffer = businessPhotoResponse.data;
let base64Image = Buffer.from(imageBuffer, 'binary').toString('base64');
businessDetails.photo = `data:${businessPhotoResponse.headers['content-type']};base64,${base64Image}`;
} catch (e) {
businessDetails.photo = business.icon;
}
} else {
businessDetails.photo = business.icon;
}
businessResults.push(businessDetails);
}
}
...//Omitted
}
...//Omitted
} catch (e) {
...//Omitted
}
}
As you can immediately notice, the function takes forever to return when the results are more than 5 and the reason is because I'm looping through each business to make another api call to fetch each photo.
I don't like this approach at all.
This idea of making another network call using photoReferences is really affecting my site speed and basically just makes my users angry.
Is there no way to automatically fetch the photo urls along just in the first request?

How to add utm tags to a wix form?

I am currently attempting to include UTM tags in a webhook that I am sending from a wix form. However, upon sending the webhook, I am either receiving an empty response or receiving "google.co.il" source. I am expecting specifically Google Ads or email sources.
I have added the code that I am using in an attempt to make this function properly. Any assistance or guidance would be greatly appreciated..
export function wixForms1_wixFormSubmitted(event) {
var obj = {};
var i = 0;
for (i=0;i<event.fields.length;i++){
obj[event.fields[i].id] = event.fields[i].fieldValue;
}
//console.log(obj);
fetch( "https://hook.eu1.make.com/62tsltjotop6iwboufggguuxhhqrjaen", {
"method": "post",
"headers": {
"Content-Type": "application/json"
},
"body": JSON.stringify(obj)
} )
.then( (httpResponse) => {
if (httpResponse.ok) {
return httpResponse.json();
} else {
return Promise.reject("Fetch did not succeed");
}
} )
.then( (json) => console.log(json) )
.catch(err => console.log(err));
}
$w.onReady(function () {
const query = wixLocation.query;
if (query) {
$w('#refbox').value = JSON.stringify(query);
}
let params = wixLocation.query
console.log(params)
$w('#campaign').value = params.campaign_name
$w('#source').value = params.utm_source
});
import wixWindow from 'wix-window';
import wixLocation from 'wix-location';
});
import wixWindow from 'wix-window';
import wixLocation from 'wix-location';
import {local, session, memory} from 'wix-storage';
let referrer = wixWindow.referrer;
let previousPageURL;
$w.onReady(function () {
previousPageURL = session.getItem("page");
session.setItem("page", wixLocation.url);
});
$w.onReady(function () {
// Write your JavaScript here
const prvurl = wixLocation.url;
if (prvurl) {
$w('#prvurl').value = JSON.stringify(prvurl);
}
// To select an element by ID use: $w("#elementID")
// Click "Preview" to run your code
const query = wixWindow.referrer;
if (query) {
$w('#refbox').value = JSON.stringify(query);
}
}
);

How to convert a JSON into a Hashmap in NodeJS

I am attempting to convert a HTTP request body in JSON format into a hashmap. Below is how my request looks:
{
"car": [{
"ford": "focus",
"audi": "a6"
}],
"food": [{
"soup": "leek"
}]
}
And below is the code I have tried so far:
const express = require('express');
const router = express.Router();
router.post('/items', async (req, res) => {
res.setHeader('Content-Type', 'application/json');
let jsonString = JSON.stringify(req.body);
let jsonObject = JSON.parse(jsonString);
let jsonMap = new Map(Object.entries(jsonObject));
var mainArray = {};
var carKey = 'car';
mainArray[carKey] = [];
for (var entry of jsonMap.entries()) {
let key = entry[0], value = entry[1];
let innerJson = JSON.stringify(value);
let innerJsonObj = JSON.parse(innerJson);
for(var innerKey in innerJsonObj) {
let tempStr = JSON.stringify(innerJsonObj[innerKey]);
let tempVal = JSON.parse(tempStr);
let attr = '';
let param = '';
for(var k in tempVal) {
attr = k;
param = tempVal[k];
logger.info(k + "=" + tempVal[k]);
}
let result = someMethod(param);
var carData = {
attr : JSON.stringify(result);
}
mainArray[carKey].push(carData);
}
}
});
I want to display the results in the same way as received in the request, but with the use of the binary result of someMethod(). Below is what im trying to create as a response.
{
"car": [
{
"ford": "binarydata",
"audi": "binarydata"
}
],
"food": [
{
"soup": "binarydata"
}
]
}
I have managed to achieve this using the code below. This allows me to specify the key and value in the JSON I add to the array via the push() command. Thank you for the guidance.
var carData = {};
carData[attr] = JSON.parse(JSON.stringify(`${result}`));
mainArray[carKey].push(carData);

Unable to deploy NFT in terminal

I already deployed my smart contract to my wallet and connected it to my Alchemy account.
Here are my codings (Note that my contract address, PUBLIC_KEY, PRIVATE_KEY, API_URL and alchemy address are edited for security purposes).
mint-nft.js
require('dotenv').config();
const API_URL = process.env.API_URL;
const PUBLIC_KEY = process.env.PUBLIC_KEY;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json");
const contractAddress = "My_Contract_Adress";
const nftContract = new web3.eth.Contract(contract.abi, contractAddress);
async function mintNFT(tokenURI) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce
//the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': 500000,
'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI()
};
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY);
signPromise.then((signedTx) => {
web3.eth.sendSignedTransaction(signedTx.rawTransaction, function(err, hash) {
if (!err) {
console.log("The hash of your transaction is: ", hash, "\nCheck Alchemy's Mempool to view the status of your transaction!");
} else {
console.log("Something went wrong when submitting your transaction:", err)
}
});
}).catch((err) => {
console.log(" Promise failed:", err);
});
}
mintNFT("https://gateway.pinata.cloud/ipfs/My_NFT_Picture_Hash");
alchemyContext.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeAlchemyContext = void 0;
var tslib_1 = require("tslib");
var sturdy_websocket_1 = tslib_1.__importDefault(require("sturdy-websocket"));
var websocket_1 = require("websocket");
var jsonRpc_1 = require("../util/jsonRpc");
var version_1 = require("../version");
var alchemySendHttp_1 = require("./alchemySendHttp");
var alchemySendWebSocket_1 = require("./alchemySendWebSocket");
var httpProvider_1 = require("./httpProvider");
var sendPayload_1 = require("./sendPayload");
var webSocketProvider_1 = require("./webSocketProvider");
var NODE_MAX_WS_FRAME_SIZE = 100 * 1024 * 1024; // 100 MB
function makeAlchemyContext(url, config) {
var makePayload = jsonRpc_1.makePayloadFactory();
if (/^https?:\/\//.test(url)) {
var alchemySend = alchemySendHttp_1.makeHttpSender(url);
var _a = sendPayload_1.makePayloadSender(alchemySend, config), sendPayload = _a.sendPayload, setWriteProvider = _a.setWriteProvider;
var senders = jsonRpc_1.makeSenders(sendPayload, makePayload);
var provider = httpProvider_1.makeAlchemyHttpProvider(sendPayload);
return { provider: provider, senders: senders, setWriteProvider: setWriteProvider };
}
else if (/^wss?:\/\//.test(url)) {
var protocol = isAlchemyUrl(url) ? "alchemy-web3-" + version_1.VERSION : undefined;
var ws = new sturdy_websocket_1.default(url, protocol, {
wsConstructor: getWebSocketConstructor(),
});
var alchemySend = alchemySendWebSocket_1.makeWebSocketSender(ws);
var _b = sendPayload_1.makePayloadSender(alchemySend, config), sendPayload = _b.sendPayload, setWriteProvider = _b.setWriteProvider;
var senders = jsonRpc_1.makeSenders(sendPayload, makePayload);
var provider = new webSocketProvider_1.AlchemyWebSocketProvider(ws, sendPayload, senders);
return { provider: provider, senders: senders, setWriteProvider: setWriteProvider };
}
else {
throw new Error("Alchemy URL protocol must be one of http, https, ws, or wss. Recieved: " + url);
}
}
exports.makeAlchemyContext = makeAlchemyContext;
function getWebSocketConstructor() {
return isNodeEnvironment()
? function (url, protocols) {
return new websocket_1.w3cwebsocket(url, protocols, undefined, undefined, undefined, {
maxReceivedMessageSize: NODE_MAX_WS_FRAME_SIZE,
maxReceivedFrameSize: NODE_MAX_WS_FRAME_SIZE,
});
}
: WebSocket;
}
function isNodeEnvironment() {
return (typeof process !== "undefined" &&
process != null &&
process.versions != null &&
process.versions.node != null);
}
function isAlchemyUrl(url) {
return url.indexOf("alchemyapi.io") >= 0;
}
.env
API_URL = "https://eth-rinkeby.alchemyapi.io/v2/KEY"
PRIVATE_KEY = "MY_PRIVATE_KEY"
PUBLIC_KEY = "MY_PUBLIC_KEY"
But then I was trying to deploy my NFT with metadata and nft-mint.js, I got these error.
Can anyone please tell me what was the error about?
Your issue may be with dotenv not reading in the values in your .env.
If you add console.log(API_URL), is it correct or is it undefined?
If it is undefined, I was able to resolve the issue by adding the path to my .env like so:
require('dotenv').config({path:"../.env"});
(In my case my mint-nft.js was in scripts/mint-nft.js
and .env is in the root directory.)

.push is not a function in web crawler

I am writing a node JS web crawler class, and I have encountered the following error, this.textInvertedIndex[word].push is not a function. Upon further inspection I realised that for some reason this.textInvertedIndex[word] was written as a native object, function Object({ [native code] }). For the first few iterations, by console logging this.textInvertedIndex everything seemed fine as it was an object of arrays. But then suddenly this error occurred. Is there any part of the code where I am implicitly rewriting textInvertedIndex?
Here is the relevant class:
function Crawler(queue, maxIndexSize) {
this.queue = queue;
this.maxIndexSize = maxIndexSize;
this.findChunks = () => {
let currentChunk;
let minimumDistance = Infinity;
for (i = 1; i <= this.maxIndexSize; i++) {
if (this.maxIndexSize % i === 0) {
const newDistance = Math.abs(i - 30);
if (newDistance < minimumDistance) {
minimumDistance = newDistance;
currentChunk = i;
} else {
return currentChunk
};
};
};
};
this.chunks = this.findChunks();
this.chunkSize = this.maxIndexSize / this.chunks;
this.totalWordOccurances = {};
this.imageInvertedIndex = {};
this.textInvertedIndex = {};
this.images = [];
this.sites = [];
this.seen = {};
this.write = (url, html) => {
const documentId = this.sites.length;
const website = new Website(url, html);
const title = website.title();
const content = website.content(title);
const words = content.filter(item => typeof item !== "object");
const wordsLength = words.length;
const query = new Query(words);
const individualWords = query.individualize(words);
this.seen[url] = true;
this.sites.push({
url,
title,
description: website.description()
});
for (word of individualWords) {
const normalizedTf = query.count(word) / wordsLength;
const textInvertedIndexEntry = {
documentId,
normalizedTf
};
if (this.textInvertedIndex[word]) {
this.textInvertedIndex[word].push(textInvertedIndexEntry);
} else {
this.textInvertedIndex[word] = [textInvertedIndexEntry];
};
if (this.totalWordOccurances[word]) {
this.totalWordOccurances[word] += 1;
} else {
this.totalWordOccurances[word] = 1;
};
};
for (i = 0; i < content.length; i++) {
const item = content[i];
if (typeof item === "object") {
const imageId = this.images.length;
this.images.push(item);
for (word of individualWords) {
const imageScore = getImageScore(i, word, content);
const imageInvertedIndexEntry = {
imageId,
imageScore
};
if (this.imageInvertedIndex[word]) {
this.imageInvertedIndex[word].push(imageInvertedIndexEntry);
} else {
this.imageInvertedIndex[word] = [imageInvertedIndexEntry];
};
};
};
};
};
this.crawl = async () => {
while (this.sites.length !== this.maxIndexSize) {
let nextQueue = [];
const websitesUnfiltered = await Promise.all(this.queue.map((url) => {
const website = new Website(url);
return website.request();
}));
const websitesToAdd = this.maxIndexSize - this.sites.length;
let websites = websitesUnfiltered.filter(message => message !== "Failure")
.slice(0, websitesToAdd);
for (site of websites) {
const url = site.url;
const htmlCode = site.htmlCode;
const website = new Website(url, htmlCode);
this.write(url, htmlCode);
nextQueue = nextQueue.concat(website.urls());
};
nextQueue = new Query(nextQueue.filter(url => !this.seen[url]))
.individualize();
this.queue = nextQueue;
};
};
};
Called like this
const crawler = new Crawler(["https://stanford.edu/"], 25000000);
crawler.crawl();
this.textInvertedIndex = {}; is defining an Object of which push is not a valid function. you can change it to an array by defining it as this.textInvertedIndex = []; otherwise you can add key/value entries to the object as it is defined like this: this.textInvertedIndex[key] = value;
Turns out, my key was accessing this.textInvertedIndex[word]. And word was constructor. constructor is already a built in object property so it can never be rewritten as an array with .push defined. To solve this problem, make all object keys capital, so constructor will become CONSTRUCTOR, thus making sure that already existing object properties are never called.

Resources