PayFast integration in NodeJS / ReactJS - node.js

I am trying to integrate PayFast into my React / NodeJS app. Using Express, my NodeJS successfully retrieves a payment uuid from the PayFast endpoint (I see this uuid in my console log) -
app.get("/api", async (req, res) => {
paymentData["signature"] = generateSignature(paymentData, phrase);
console.log(paymentData["signature"])
const str = dataToString(paymentData)
const id = await getPaymentId(str)
res.json({uuid: id})
})
However, in my front end (ReactJS) I am getting an undefined response & possible CORS issue from my backend API end point when trying to retrieve this uuid -
My custom fetch hook:
export default function useFetch(baseUrl) {
const [loading, setLoading] = useState(true);
function get() {
return new Promise((resolve, reject) => {
fetch(baseUrl)
.then(res => {
console.log(res)
res.json()
})
.then(data => {
console.log(data);
if (!data) {
setLoading(false);
return reject(data);
}
setLoading(false);
resolve(data);
})
.catch(error => {
setLoading(false);
reject(error);
});
});
}
return { get, loading };
};
The error:
Response {type: 'cors', url: 'http://localhost:3001/api', redirected: false, status: 200, ok: true, …}
undefined
If I test my NodeJS end point from my browser, it successfully comes back with my payment uuid. Any one have any ideas why my React app is acting up?

Update your CORS config to accept connections from the React app host.
app.use(cors({
origin: 'http://localhost:3000',
}));

Open package.json of your react app and add a line on the bottom of the json file:
"proxy":"http://localhost:3001"
3001 is the PORT that your Node http server is running on locally, if it's another PORT just change it accordingly.
This will redirect all http traffic from your webpack dev server running on PORT 3000, to your Node server running on 3001.

For those others who might encounter a similar type of an issue, I have attached a blog post with the method that I have used to solve the CORS issue, as well as integrate with the PayFast API.
https://codersconcepts.blogspot.com/2022/04/nodejs-payfast-integration.html

Related

Network Request Failed while Integrating React Native(using Expo) to Node js

I developed a React Native app and then i devloped Node js as a backend and write all the required REST apis.
But whenever i tried to fetch data from my backend it says Network Request Failed
I am starting my web server using command :-
npm start --host 0.0.0.0
And then i am checking whether i'm getting data from server or not by typing my ip address : port in the browser of both my android phone and laptop and there i'm getting my data.
But i don't know why is my react native app unable to fetch data...
My code is :-
export const fetchDishes = () => (dispatch) => {
dispatch(dishesLoading(true));
console.log("baseUrl", baseUrl);
return fetch('https://192.168.43.182:3443/dishes')
.then(response => {
if (response.ok) {
return response;
}
else {
var error = new Error('Error ' + response.status + ': ' + response.statusText);
error.response = response;
throw error;
}
},
error => {
var errmess = new Error(error.message);
throw errmess;
})
.then(response => response.json())
.then(dishes => dispatch(addDishes(dishes)))
.catch(error => dispatch(dishesFailed(error.message)));
}
export const dishesLoading = () => ({
type: ActionTypes.DISHES_LOADING
});
export const dishesFailed = (errmess) => ({
type: ActionTypes.DISHES_FAILED,
payload: errmess
});
export const addDishes = (dishes) => ({
type: ActionTypes.ADD_DISHES,
payload: dishes
});
As u can see i have put the same ip and port in the fetch() method and still getting Network Request Failed error.
Note:- I am running app on my physical device
Edit - My app is successfully fetching data on http but gives error in https.

How to deal with backend dynamically allocated ports on react.js side using axios?

This morning I deployed a MERN stack login app in heroku successfully. But, when I tried to login
GET http://localhost:5000/user/login/email/password net::ERR_CONNECTION_REFUSED
in the console.
I understood that that the error is because I am making get request in axios using
axios.get("http://localhost:5000/user/login/" + this.state.email + "/" + this.state.password).then((res) => {
if (res.status === 200) {
this.setState({ status: res.status, name: res.data.name });
console.log(res.data);
}
else
throw new Error(res.status);
}).catch((err) => {
this.setState({ isInvalid: true });
})
But, the port is being dynamically allocated on the server side.
const port = process.env.PORT||5000;
app.listen(port, () => {
console.log("Server started on port:" + port);
});
Tried allocating only hardcoded value to the port. Still no luck
There are lots of mistakes in your code. You have deployed your app but your URL is still localhost which is not Heroku URL. First of all you need to setup env variables for your application like this.
You can put this in some constant file from where you get your end point. Don't write END POINTS directly in the ajax calls. Use constant and create a single file for from where you do all the ajax calls of the application.
You can set the env for both frontend and backend and this is how you should work. The development env should be separate from production one.
if (process.env.NODE_ENV === "development") {
API = "http://localhost:8000";
} else if (process.env.NODE_ENV === "production") {
API = "https://be-prepared-app-bk.herokuapp.com";
}
Don't use GET for the login and sending email and password in parameters. You should use POST and send all the data in body.
Here's how you single ajax file should look alike:
import { API_HOST } from "./constants";
import * as auth from "../services/Session";
const GlobalAPISvc = (endPoint, method, data) => {
const token = auth.getItem("token");
const uuid = auth.getItem("uuid");
return new Promise((resolve, reject) => {
fetch(`${API_HOST}${endPoint}`, {
method: method,
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
"x-authentication": token,
uuid: uuid
}
})
.then(res => {
return res.json();
})
.then(json => {
resolve(json);
})
.catch(error => {
reject(error);
});
}).catch(error => {
return error;
});
};
export default GlobalAPISvc;
I have created an application in MERN which I made public on GitHub. Feel free to take help from that. Repository Link
Firstly, I would suggest you, not to use get request method for login.
Secondly, if you've deployed your backend code then use dynamic url provided by heroku for login request.
e.g. if your url is xyz.heroku.com then axios.get('xyz.heroku.com/user/login/'+email+'/'+password);
as now you don't need to hard-code the port or use localhost.

Using Node and Express, How to Call remote API from inside server.get(..)

Because of CORS problems, I want to call an external REST API from inside my node express server. That is, I have code like this that obviously does not work because it does not return.
How can I make this work and return the results of my external call?
const server = express();
server.put('/callme',(req,res) => {
axios
('http://weather.com/restapi', 'put', { zip: 10530 })
.then((resp: any) => {
console.log(' success' + resp.data);
})
.catch(function(error: any) {
console.log(error.message);
});
}
Axios returns a Promise which is resolved in the .then(). In order to get the response data back to the client you need to return it with res.send().
const server = express();
server.get('/callme', (req, res) => {
axios
.get('http://weather.com/restapi?zip=10530')
.then((resp: any) => {
res.send(resp.data);
})
.catch(function(error: any) {
console.log(error.message);
});
}
It would be a good idea to cache the weather API response for a period of time and serve the cached response for subsequent requests.

How do you handle CORS in an electron app?

I'm building an electron app and need to call APIs where the API provider has not enabled CORS. The typically proposed solution is to use a reverse proxy which is trivial to do when running locally by using node and cors-anywhere like this:
let port = (process.argv.length > 2) ? parseInt (process.argv[2]) : 8080;
require ('cors-anywhere').createServer ().listen (port, 'localhost');
The app can then be configured to proxy all requests through the reverse proxy on localhost:8080.
So, my questions are:
Is it possible to use node and cors-anywhere in an electron app to create a reverse proxy? I don't want to force the app to make calls to a remote server.
Is there a better or standard way of doing this in an Electron app? I'm assuming I'm not the first to run into CORS issues. :)
Just overide header before send request using webRequest.onBeforeSendHeaders
const filter = {
urls: ['*://*.google.com/*']
};
const session = electron.remote.session
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
details.requestHeaders['Origin'] = null;
details.headers['Origin'] = null;
callback({ requestHeaders: details.requestHeaders })
});
put these codes in renderer process
In my application, it wasn't sufficient to remove the Origin header (by setting it to null) in the request. The server I was passing the request to always provided the Access-Control-Allow-Origin header in the response, regardless of it the Origin header is present in the request. So the embedded instance of Chrome did not like that the ACAO header did not match its understanding of the origin.
Instead, I had to change the Origin header on the request and then restore it on the Access-Control-Allow-Origin header on the response.
app.on('ready', () => {
// Modify the origin for all requests to the following urls.
const filter = {
urls: ['http://example.com/*']
};
session.defaultSession.webRequest.onBeforeSendHeaders(
filter,
(details, callback) => {
console.log(details);
details.requestHeaders['Origin'] = 'http://example.com';
callback({ requestHeaders: details.requestHeaders });
}
);
session.defaultSession.webRequest.onHeadersReceived(
filter,
(details, callback) => {
console.log(details);
details.responseHeaders['Access-Control-Allow-Origin'] = [
'capacitor-electron://-'
];
callback({ responseHeaders: details.responseHeaders });
}
);
myCapacitorApp.init();
});
Try this if you are running web apps in localhost
const filter = {
urls: ['http://example.com/*'] // Remote API URS for which you are getting CORS error
}
browserWindow.webContents.session.webRequest.onBeforeSendHeaders(
filter,
(details, callback) => {
details.requestHeaders.Origin = `http://example.com/*`
callback({ requestHeaders: details.requestHeaders })
}
)
browserWindow.webContents.session.webRequest.onHeadersReceived(
filter,
(details, callback) => {
details.responseHeaders['access-control-allow-origin'] = [
'capacitor-electron://-',
'http://localhost:3000' // URL your local electron app hosted
]
callback({ responseHeaders: details.responseHeaders })
}
)
Just had this issue today API calls with axios inside a React app bundled in Electron is returning 400
From what I can see Electron calls act as normal calls to the API urls meaning they are not affected by CORS.
Now when you wrap your calls with a CORS proxy and make a regular call to the proxy, it should error with a 400 error because it's not a CORS call.
This thread explains why cors-anywhere responds like that => https://github.com/Rob--W/cors-anywhere/issues/39
I actually removed my CORS proxies from the app before the Electron build. I still need the CORS proxy for development since I'm testing in the browser.
Hope this helps.
You can have the main process, the NodeJS server running Electron, send the request. This avoids CORS because this is a server-to-server request. You can send an event from the frontend (the render process) to the main process using IPC. In the main process you can listen to this event, send the HTTP request, and return a promise to the frontend.
In main.js (the script where the Electron window is created):
import { app, protocol, BrowserWindow, ipcMain } from ‘electron’
import axios from 'axios'
ipcMain.handle('auth', async (event, ...args) => {
console.log('main: auth', event, args) const result = await axios.post(
'https://api.com/auth',
{
username: args[0].username,
password: args[0].password,
auth_type: args[1],
},
) console.log('main: auth result', result)
console.log('main: auth result.data', result.data) return result.data
})
In your frontend JS:
import { ipcRenderer } from 'electron'
sendAuthRequestUsingIpc() {
return ipcRenderer.invoke('auth',
{
username: AuthService.username,
password: AuthService.password,
},
'password',
).then((data) => {
AuthService.AUTH_TOKEN = data['access_token']
return true
}).catch((resp) => console.warn(resp))
}
I wrote an article that goes into more depth here.
While I have struggled a while with the existing answers I will provide here the solution that finally worked for me, assuming that you are on the main process.
Here are the steps involved:
You need to have access to the session object which can be obtained by one of two ways:
A) via the global session.defaultSession which is available after the app is ready.
const { session } = require('electron');
const curSession = session.defaultSession;
B) The other method is via the session on the BrowserWindow, this assumes that the windnows is already created.
win = new BrowserWindow({});
const curSession = win.webContents.session;
Once you have the session object you set the response header to the site you are sending the request from.
For example, let's say your electron BrowserWindow is loaded from http://localhost:3000 and you are making a request to example.com, here would be some sample code:
const { app, BrowserWindow, session } = require('electron');
app.whenReady().then(_ => {
// If using method B for the session you should first construct the BrowserWindow
const filter = { urls: ['*://*.example.com/*'] };
session.defaultSession.webRequest.onHeadersReceived(filter, (details, callback) => {
details.responseHeaders['Access-Control-Allow-Origin'] = [ 'http://localhost:3000' ];
callback({ responseHeaders: details.responseHeaders });
}
// Construct the BrowserWindow if haven't done so yet...
});
Have you tried using fetch()
Check how to use fetch to make a no-cors request here
https://developers.google.com/web/updates/2015/03/introduction-to-fetch?hl=en

Using the watson-developer-cloud node SDK directly on the client?

I have a client based on react and I bundle it with webpack 2. But the moment I import/require const SpeechToTextV1 = require('watson-developer-cloud/speech-to-text/v1'); I got some trouble. After I fixed it that it does not break the build, it still throws some warning like:
Module not found: Error: Can't resolve '../build/Release/validation' in '/Users/denbox/Desktop/schedulebot/web-interface/node_modules/websocket/lib'
# ./~/websocket/lib/Validation.js 9:21-59
# ./~/websocket/lib/WebSocketConnection.js
# ./~/websocket/lib/websocket.js
# ./~/websocket/index.js
# ./~/watson-developer-cloud/speech-to-text/recognize_stream.js
# ./~/watson-developer-cloud/speech-to-text/v1.js
# ./src/components/chat.jsx
# ./src/components/chat-page.js
# ./src/index.js
# multi (webpack)-dev-server/client?http://localhost:8080 ./src/index.js
Is it even possible to use the watson-developer-cloud node sdk for the speech-to-text service on the client or only directly on the nodejs server? Thank you.
The Watson Node.js SDK has growing compatibility for client-side usage, but it's not all the way there yet. However, for speech services, there is a separate SDK targeted at client-side usage: https://www.npmjs.com/package/watson-speech
I just added a Webpack example and confirmed that it works: https://github.com/watson-developer-cloud/speech-javascript-sdk/blob/master/examples/webpack.config.js
Update: I also went and added a Webpack example to the Node.js SDK - with the configuration there, it can build for the entire library, and actually works for a subset set of the modules as documented: https://github.com/watson-developer-cloud/node-sdk/tree/master/examples/webpack
Only in Node,js. The mechanism for using Speech To Text from the browser is to use websockets, but to do that you need a token, which will require a server side request. Once you have the token you can use the websockets interface.
With the answers above found a solution for my problem and it might help others who want to get started with the API:
import axios from 'axios';
import recognizeMicrophone from 'watson-speech/speech-to-text/recognize-microphone';
axios.get(`${BACKEND_ROOT_URL}/watsoncloud/stt/token`)
.then((res) => {
console.log('res:', res.data);
const stream = recognizeMicrophone({
token: res.data.token,
continuous: false, // false = automatically stop transcription the first time a pause is detected
});
stream.setEncoding('utf8');
stream.on('error', (err) => {
console.log(err);
});
stream.on('data', (msg) => {
console.log('message:', msg);
});
})
.catch((err) => {
console.log(`The following gUM error occured: ${err}`);
});
In the backend I create a proxy service that get's a token for the watson speech to text service so I don't have to save my credentials on the client:
const watson = require('watson-developer-cloud');
const express = require('express');
const cors = require('cors');
app.use(cors());
const stt = new watson.SpeechToTextV1({
// if left undefined, username and password to fall back to the SPEECH_TO_TEXT_USERNAME and
// SPEECH_TO_TEXT_PASSWORD environment properties, and then to VCAP_SERVICES (on Bluemix)
username: process.env.STT_SERVICE_USER,
password: process.env.STT_SERVICE_PW,
});
const authService = new watson.AuthorizationV1(stt.getCredentials());
// Endpoint to retrieve an watson speech to text api token
// Get token using your credentials
app.get('/watsoncloud/stt/token', (req, res, next) => {
// TODO check jwt at the auth service
authService.getToken((err, token) => {
if (err) {
next(err);
} else {
res.send({ token });
}
});
});
app.listen(port, (err) => {
if (err) {
console.log(`Error: ${err}`);
}
});

Resources