Node.js Paypal how to send other data alongside the trigger - node.js

I am attempting to send data alongside the code for paypal to the node.js client, but I am not sure where to send that data alongside it.
Here is the client side paypal code:
paypal.Buttons({
// Order is created on the server and the order id is returned
createOrder: (data, actions) => {
return fetch("/api/orders", {
method: "post",
// use the "body" param to optionally pass additional order information
// like product ids or amount
})
.then((response) => response.json())
.then((order) => order.id);
},
// Finalize the transaction on the server after payer approval
onApprove: (data, actions) => {
return fetch(`/api/orders/${data.orderID}/capture`, {
method: "post",
})
.then((response) => response.json())
.then((orderData) => {
// Successful capture! For dev/demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
const transaction = orderData.purchase_units[0].payments.captures[0];
alert(`Transaction ${transaction.status}: ${transaction.id}\n\nSee console for all available details`);
// When ready to go live, remove the alert and show a success message within this page. For example:
// const element = document.getElementById('paypal-button-container');
// element.innerHTML = '<h3>Thank you for your payment!</h3>';
// Or go to another URL: actions.redirect('thank_you.html');
});
}
}).render('#paypal-button-container');
Here is the server side paypal code:
app.post("/api/orders", async (req, res) => {
const order = await createOrder();
res.json(order);
});
// capture payment & store order information or fullfill order
app.post("/api/orders/:orderID/capture", async (req, res) => {
const { orderID } = req.params;
const captureData = await capturePayment(orderID);
// TODO: store payment information such as the transaction ID
res.json(captureData);
});
//////////////////////
// PayPal API helpers
//////////////////////
// use the orders api to create an order
async function createOrder() {
const accessToken = await generateAccessToken();
const url = `${base}/v2/checkout/orders`;
const response = await fetch(url, {
method: "post",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
intent: "CAPTURE",
purchase_units: [
{
amount: {
currency_code: "USD",
value: "100.00",
},
},
],
}),
});
const data = await response.json();
return data;
}
// use the orders api to capture payment for an order
async function capturePayment(orderId) {
const accessToken = await generateAccessToken();
const url = `${base}/v2/checkout/orders/${orderId}/capture`;
const response = await fetch(url, {
method: "post",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
});
const data = await response.json();
return data;
}
The issue is there is no req.body.stuff anywhere.
The goal I am trying to achieve, is is when the payment is made, I want to send the data of like the string "potato", Before the actual payment goes through on node. Because potato is a client side variable that will then when used in server retrieve the value of the amount to be paid in node.
Where/how do I put this so I can do this?
Thanks.

How to do it is literally explained in the comments of that client-side code:
return fetch("/api/orders", {
method: "post",
// use the "body" param to optionally pass additional order information
// like product ids or amount
})

Related

React direct to external NodeJS route and display data from processed body

i have this fetch request which send some body data to http://localhost:8000/report and then redirects to the same route on server to display data after processing :
const handleGo = async () => {
const employee = {
name: name.current.value,
month: month.current.value,
year: year.current.value,
};
await fetch("http://localhost:8000/report", {
method: "POST",
headers: {
"Content-type": "application/json",
},
body: JSON.stringify(employee),
});
window.location.replace("http://localhost:8000/report");
};
index.json server
const client = require("#jsreport/nodejs-client")("http://localhost:8001");
app.post("/report", (req, res, next) => {
employee.getEmpReport(req, res, next, client);
});
function getEmpReport
const getEmpReport = async (req, res, next, client) => {
const { name, month, year } = req.body; /// processing body data coming from react client
Employee.aggregate([
// doing some MongoDB join with conditions and returning results
]).exec(function (err, employee) {
const dataObject = {
/// setting some data from returned employee
};
client
.render({
template: { name: "/salary/salarySlipTemplate" },
data: dataObject,
options: { reports: { save: true } },
})
.then((response) => response.pipe(res))
.catch(next);
});
};
i want this route to process data and then display data after processing, but when window.location.replace("http://localhost:8000/report"); redirects to the route it says cannot get /report
i think i need a get route but then how can i recieve body data ? i want to be able to recieve the data from client and display it at the same time, how can i do that ?
i should send request from client to jsreport client and send data directly, server should only return the filtered employee:
import jsreport from "#jsreport/browser-client";
jsreport.serverUrl = "http://localhost:8001";
const handleGo = async () => {
const employee = {
name: name.current.value,
month: month.current.value,
year: year.current.value,
};
const response = await fetch("http://localhost:8000/report", {
method: "POST",
headers: {
"Content-type": "application/json",
},
body: JSON.stringify(employee),
});
const data = await response.json();
const report = await jsreport.render({
template: {
name: "/salary/salarySlipTemplate",
},
data: data,
});
report.openInWindow({ title: "salary slip" });
};

How to post a body correctly in Postman

I try to make a post request to an api but, api returns error. Although I thought about it for a long time, I could not find the cause of the error.
I think the error is due to not creating the body correctly because when I send empty array as items array the code works.
API DOCUMENTATION:
Here is my request:
module.exports.createOrder = (token, paymentId, items, callback) => {
const url = urlBase;
request(
{
url: url,
method: "POST",
json: true,
headers: {
"content-type": "application/json",
Authorization: `Bearer ${token}`,
},
body: {
secretKey: `${secretKey}`,
paymentId: paymentId,
items: items,
},
},
(error, response) => {
if (error) {
Sentry.captureException(error);
callback(errMessage, undefined);
} else {
const data = response.body;
callback(undefined, data);
}
}
);
};
Here is test values:
const testToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYyM2RmYjg4NDA4M2IwMDAyNDNjYzRhNyIsImlhdCI6MTY0ODIyOTI1NywiZXhwIjoxNjQ4MzE1NjU3fQ.9fgR3ei81vsHVMhSi8VwEyE2WMgFIMthm0PF9_zrqjw"
const paymentId = "pi_1Dq2f62eZvKYlo2Cy1moIb0G"
const variant = {
variations_values: {
color:'Black',
size:'42'
},
price:495,
product_id: "883360511078"
}
const productId = "82936941"
const quantity = 1
const item = {
variant:variant,
productId:productId,
quantity:quantity
}
let items = [item]
orderRequests.createOrder(testToken, paymentId, items, (err, data) => {
if(data){
console.log(data)
} else{
console.log(err)
}
})
I get internal server error as a response when I post these test values, but If I post an empty array as items, api does not return internal server error. What is the problem, any help?
Internal Server Error Response when I send an array with post body:
Expected request when I send an empty array with post body:

Firebase functions take MINUTES to write in Firestore

I'm building a mobile app and on some actions (like userCreate), I trigger some Firebase functions to perform an API call to a third service, and then write something in the Firestore database.
Everything works in theory, but in practice, the API calls are quite fast (even with cold start scenarios), but the database writes can take several MINUTES to complete (if they do at all, I suspect that sometimes it takes too long and times out).
Since the API call work just fine, and so does the DB write on some occasion, I suspect that this is simply due to very poor async management from me since I know about nothing in JS.
Here are two of many example functions which are concerned by this issue, just to showcase that it happens whereas I'm triggering functions onCreate or on HTTPS.
onCreate function
const functions = require("firebase-functions");
const axios = require('axios')
// The Firebase Admin SDK to access the Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
// Third party service credentials generation during onCreate request
exports.
buildCredentials = functions.auth.user().onCreate((user) => {
// Request to Third party to generate an Client Access Token
axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/oauth/token",
data: "client_id=xxx&client_secret=yyy&grant_type=client_credentials&scope=all:all",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
})
.then(function (response) {
//handle success
console.log('new user created: ')
console.log(user.id);
console.log(response.data);
// We write a new document in the users collection with the user ID and the Client Access Token
const db = admin.firestore();
const newUser = {
uid: user.uid,
clientAccessToken: response.data.access_token
};
db.collection('users').doc(user.uid).set(newUser)
.catch(function (error) {
//handle error
console.log(error);
});
})
.catch(function (response) {
//handle error
console.log(response);
});
})
HTTPS onCall function
exports.paymentRequest = functions.https.onCall(async (data, context) => {
const clientAccessToken = data.clientAccessToken;
const recipientIban = data.recipientIban;
const recipientName = data.recipientName;
const paymentDescription = data.paymentDescription;
const paymentReference = data.paymentReference;
const productPrice = parseInt(data.productPrice);
const uid = data.uid;
const jsonData = {
"destinations": [
{
"accountNumber": recipientIban,
"type": "iban"
}
],
"amount": productPrice,
"currency": "EUR",
"market": "FR",
"recipientName": recipientName,
"sourceMessage": paymentDescription,
"remittanceInformation": {
"type": "UNSTRUCTURED",
"value": paymentReference
},
"paymentScheme": "SEPA_INSTANT_CREDIT_TRANSFER"
};
(async function(){
response = await axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/pay",
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${clientAccessToken}`,},
data: jsonData,
})
// We write a the payment request ID in the user's document
const db = admin.firestore();
const paymentRequestID = response.data.id;
db.collection('users').doc(uid).set({
paymentRequestID: paymentRequestID
}, { merge: true })
.catch(function (error) {
//handle error
console.log(error);
});
console.log(response.data)
return response.data
})()
})
Am I on the right track thinking that this is an async problem?
Or is it a Firebase/Firestore issue?
Thanks
You are not returning the promises returned by the asynchronous methods (axios() and set()), potentially generating some "erratic" behavior of the Cloud Function.
As you will see in the three videos about "JavaScript Promises" from the official Firebase video series you MUST return a Promise or a value in a background triggered Cloud Function, to indicate to the platform that it has completed, and to avoid it is terminated before the asynchronous operations are done or it continues running after the work has been completed.
The following adaptations should do the trick (untested):
onCreate Function:
buildCredentials = functions.auth.user().onCreate((user) => {
// Request to Third party to generate an Client Access Token
return axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/oauth/token",
data: "client_id=xxx&client_secret=yyy&grant_type=client_credentials&scope=all:all",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
})
.then(function (response) {
//handle success
console.log('new user created: ')
console.log(user.id);
console.log(response.data);
// We write a new document in the users collection with the user ID and the Client Access Token
const db = admin.firestore();
const newUser = {
uid: user.uid,
clientAccessToken: response.data.access_token
};
return db.collection('users').doc(user.uid).set(newUser)
})
.catch(function (response) {
//handle error
console.log(response);
return null;
});
})
Callable Function:
exports.paymentRequest = functions.https.onCall(async (data, context) => {
try {
const clientAccessToken = data.clientAccessToken;
const recipientIban = data.recipientIban;
const recipientName = data.recipientName;
const paymentDescription = data.paymentDescription;
const paymentReference = data.paymentReference;
const productPrice = parseInt(data.productPrice);
const uid = data.uid;
const jsonData = {
// ...
};
const response = await axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/pay",
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${clientAccessToken}`,
},
data: jsonData,
})
// We write a the payment request ID in the user's document
const db = admin.firestore();
const paymentRequestID = response.data.id;
await db.collection('users').doc(uid).set({
paymentRequestID: paymentRequestID
}, { merge: true })
console.log(response.data)
return response.data
} catch (error) {
// See https://firebase.google.com/docs/functions/callable#handle_errors
}
})

NodeJS sending response before running the await methods

My NodeJS controller is sending the response before finish running the wait methods.
The response with the 'OK' message is immediately send back to the client (browser) when I run it. It sends the response before running all the methods.
try {
const ep = await new keysModel().getKeys(); // key is a list of objects
await ep.map( async (key) => {
// some operations with the keys here
// call a fetch to a third party API
await fetch(urlRequest, {
method: 'post',
body: JSON.stringify(myJSONObject),
headers: {
'Authorization': `Basic ${base64.encode(`${key.APIKey}:${key.PasswordKey}`)}`,
'Content-Type': 'application/json'
},
})
.then(async res => res.json())
.then(async json => {
// Here I have some operations with the json response data
// It includes some database queries
})
.catch(async err => {
// await method to send me an email
res.status(501).send('error');
});
});
res.status(202).send('OK');
} catch() {
// await method to send me an email
res.status(501).send('error');
}
Below is the route:
const express = require('express');
const router = express.Router();
router.get('/myPath', myController.testMethod);
I'm new with NodeJS. Trying to understand how it works.
Thanks
I think Promise.all should work, eg. use async await with array map :)
try {
const ep = await new keysModel().getKeys(); // key is a list of objects
await Promise.all(ep.map(async (key) => {
// some operations with the keys here
// call a fetch to a third party API
return fetch(urlRequest, {
method: 'post',
body: JSON.stringify(myJSONObject),
headers: {
'Authorization': `Basic ${base64.encode(`${key.APIKey}:${key.PasswordKey}`)}`,
'Content-Type': 'application/json'
},
})
.then(async res => res.json())
.then(async json => {
// Here I have some operations with the json response data
// It includes some database queries
})
.catch(async err => {
// await method to send me an email
res.status(501).send('error');
});
}));
res.status(202).send('OK');
} catch() {
// await method to send me an email
res.status(501).send('error');
}
By default, map is not async function, using async on map's call back would not be asynced.
you can use one of Promise.all, Promise.allSettled, Promise.any, Promise.race (depends on your needs, please read more about Promises here.
short example for your case, i will use Promise.all for the example.
try {
const ep = await new keysModel().getKeys(); // key is a list of objects
Promise.all(
ep.map((key) => {
// some operations with the keys here
// call a fetch to a third party API
fetch(urlRequest, {
method: 'post',
body: JSON.stringify(myJSONObject),
headers: {
'Authorization': `Basic
${base64.encode(`${key.APIKey}:${key.PasswordKey}`)}`,
'Content-Type': 'application/json'
},
})
.then(/* Handle array of responses */)
.catch(/* Handle errors */);
);
// proceed..
} catch() {
// await method to send me an email
res.status(501).send('error');
}

SurveyMonkey API Create a Survey using NodeJS

I created a small server using NodeJS/Express and I'm using node-fetch to interact with SurveyMonkeys API. I currently have two surveys on my account which I can view through their Postman collection. But when I try to use my own endpoints, it doesn't seem to work. The GET request to view all of the surveys returns a status code of "200" but responds with:
{
"size": 0,
"timeout": 0
}
The POST request to create a survey gives me a status code of "400" but returns the same response. Here is my code so far.
const router = require("express").Router();
const fetch = require("node-fetch");
const TOKEN = process.env.SM_ACCESS_TOKEN;
const BASEURL = process.env.SM_BASEURL;
const options = method => ({
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
method: method
}
});
/*
GET a list of surveys
*/
router.get("/", async (req, res) => {
try {
const surveys = await fetch(`${BASEURL}surveys`, options("GET"));
console.log(surveys);
if (surveys) {
return res.status(200).json(surveys);
}
} catch (err) {
console.log(err);
res.status(500).send({ message: "Server error", err });
}
});
router.post("/create-survey", (req, res) => {
const surveyData = req.body;
fetch(`${BASEURL}surveys`, {
method: "POST",
body: surveyData,
headers: {
Authorization: `bearer ${TOKEN}`,
"Content-Type": "application/json"
}
})
.then(data => {
return res.status(data.status).json(data);
})
.catch(err => console.log(err));
});
module.exports = router;
Additional information:
I am able to complete all of these actions using the POSTMAN collection provided by SurveyMonkey with my Access Token. BASEURL = "https://api.surveymonkey.com/v3/".
ServeyData = { "title": "Some Title" }
Resolved this issue by switching out of node-fetch and instead using axios. Could be the fetch vs xhr request I think.

Resources