Paypal not returning custom variable - node.js

I'm trying to complete a paypal transaction using paypal-rest-sdk, everything is set up and working, however, I need to get the clientId back from paypal in the success route in order to save it in my client_feature_payment model. I found that we can set a "custom" field where we can set anything and that'll be sent back by paypal but this feautre is available in classic paypal sdk only and not available in rest-sdk one.
Is there any workaround for this?
//Paypal objects and methods from rest-sdk:
client_page: {
args: {
clientId: {
type: GraphQLString
}
},
type: ClientType,
resolve: async (_, args) => {
if (args.clientId) {
let clientMongoId = fromGlobalId(args.clientId).id;
let client = await Client.queryOne("id")
.eq(clientMongoId)
.exec();
let clientName = client.name;
let clientSecret = client.secret;
let company = await Company.queryOne("id")
.eq(client.companyId)
.exec();
let companyName = company.name;
let service = await Service.queryOne("id")
.eq(client.serviceId)
.exec();
let serviceName = service.name;
let clientFeature = await ClientFeature.query("clientId")
.eq(clientMongoId)
.exec();
let totalFeatures = [];
let clientFeatureId = [];
for (let i = 0; i < clientFeature.length; i++) {
clientFeatureId.unshift(clientFeature[i].id);
let feature = await Feature.query("id")
.eq(clientFeature[i].featureId)
.exec();
let newFeature;
feature.map(
feature =>
(newFeature = [
feature.name,
feature.cost,
feature.trial,
feature.frequency
])
);
totalFeatures.unshift(newFeature);
}
let trial, freq;
let cost = [];
totalFeatures.map(item => {
if (item[2] && item[3]) {
trial = item[2];
freq = item[3];
}
cost.unshift(item[1]);
});
const finalCost = cost.reduce((accumulator, currentValue) => {
return accumulator + currentValue;
}, 0);
let paypalFreq;
let frequencyInterval;
var isoDate = new Date(Date.now() + 1 * 60 * 1000);
switch (freq) {
case "bi-weekly":
paypalFreq = "DAY";
frequencyInterval = "7";
break;
case "monthly":
paypalFreq = "MONTH";
frequencyInterval = "1";
break;
case "3 months":
paypalFreq = "MONTH";
frequencyInterval = "3";
break;
case "6 months":
paypalFreq = "MONTH";
frequencyInterval = "6";
break;
case "1 year":
paypalFreq = "YEAR";
frequencyInterval = "1";
break;
default:
break;
}
var billingPlanAttributes = {
description:
"Create Plan for Trial & Frequency based payment for features and services used by customer",
merchant_preferences: {
auto_bill_amount: "yes",
cancel_url: "http://localhost:3000/cancel",
initial_fail_amount_action: "continue",
max_fail_attempts: "1",
return_url: "http://localhost:3000/success",
setup_fee: {
currency: "USD",
value: "0"
}
},
name: "Client Services & Features Charge",
payment_definitions: [
{
amount: {
currency: "USD",
value: finalCost
},
cycles: "0",
frequency: paypalFreq,
frequency_interval: frequencyInterval,
name: "Regular 1",
type: "REGULAR"
},
{
amount: {
currency: "USD",
value: "0"
},
cycles: "1",
frequency: "DAY",
frequency_interval: trial,
name: "Trial 1",
type: "TRIAL"
}
],
type: "INFINITE"
};
var billingPlanUpdateAttributes = [
{
op: "replace",
path: "/",
value: {
state: "ACTIVE"
}
}
];
var billingAgreementAttr = {
name: "Fast Speed Agreement",
description: "Agreement for Fast Speed Plan",
start_date: isoDate,
plan: {
id: "P-0NJ10521L3680291SOAQIVTQ"
},
payer: {
payment_method: "paypal",
payer_info: {
payer_id: clientMongoId
}
},
shipping_address: {
line1: "StayBr111idge Suites",
line2: "Cro12ok Street",
city: "San Jose",
state: "CA",
postal_code: "95112",
country_code: "US"
}
};
// Create the billing plan
let billingPlan = await new Promise((resolve, reject) => {
paypal.billingPlan.create(
billingPlanAttributes,
(error, billingPlan) => {
if (error) {
throw error;
} else {
resolve(billingPlan);
}
}
);
});
// let billingPlan = await billingPlanPromise;
// Activate the plan by changing status to Active
let billingAgreementAttributes = await new Promise(
(resolve, reject) => {
paypal.billingPlan.update(
billingPlan.id,
billingPlanUpdateAttributes,
(error, response) => {
if (error) {
throw error;
} else {
billingAgreementAttr.plan.id = billingPlan.id;
resolve(billingAgreementAttr);
}
}
);
}
);
// Use activated billing plan to create agreement
let approval_url = await new Promise((resolve, reject) => {
paypal.billingAgreement.create(
billingAgreementAttributes,
(error, billingAgreement) => {
if (error) {
throw error;
} else {
for (
var index = 0;
index < billingAgreement.links.length;
index++
) {
if (billingAgreement.links[index].rel === "approval_url") {
var approval_url = billingAgreement.links[index].href;
let newApprovalUrl =
approval_url + `&custom=${clientFeatureId}`;
resolve(newApprovalUrl);
// See billing_agreements/execute.js to see example for executing agreement
// after you have payment token
}
}
}
}
);
});
let data = {
companyId: companyName,
serviceId: serviceName,
name: clientName,
secret: clientSecret,
features: totalFeatures,
endpoint: approval_url
};
return Object.assign(data);
}
}
},
The success route:
app.get("/success", (req, res) => {
console.log("This is response", res);
let paymentToken = req.query.token;
paypal.billingAgreement.execute(paymentToken, {}, function(
error,
billingAgreement
) {
if (error) {
throw error;
} else {
console.log("Billing agreement", billingAgreement);
let date = billingAgreement.start_date;
let amountString =
billingAgreement.plan.payment_definitions[1].amount.value;
let trial =
billingAgreement.plan.payment_definitions[0].frequency_interval;
let frequencyInterval =
billingAgreement.plan.payment_definitions[1].frequency_interval;
let frequency = billingAgreement.plan.payment_definitions[1].frequency;
let totalFrequency = frequencyInterval + " " + frequency;
let period = [trial, totalFrequency];
let amount = parseInt(amountString);
try {
Payment.create({
id: uuidv1(),
date: date,
amount: amount,
period: period
});
} catch (err) {
throw new Error(err);
}
res.render("index");
}
});
});

My implementation looks very different, but I can output any payment details. My payment.execute() looks like so:
const express = require("express");
const paypal = require("paypal-rest-sdk");
const app = express;
paypal.configure({
mode: "sandbox", //sandbox or live
client_id:
"...",
client_secret:
"..."
});
app.set("view engine", "ejs");
app.get("/", (req, res) => res.render("index"));
app.post("/pay", (req, res) => {
const create_payment_json = {
intent: "sale",
payer: {
payment_method: "paypal"
},
redirect_urls: {
return_url: "http://localhost:3000/success",
cancel_url: "http://localhost:3000/cancel"
},
transactions: [
{
item_list: {
items: [
{
name: "Item",
sku: "001",
price: "3.33",
currency: "USD",
quantity: 1
}
]
},
amount: {
currency: "USD",
total: "3.33"
},
description:
"Hope this helps."
}
]
};
paypal.payment.create(create_payment_json, function(error, payment) {
if (error) {
throw error;
} else {
// console.log("Create Payment Response");
// console.log(payment.id);
// res.send('test')
for (let i = 0; i < payment.links.length; i++) {
if (payment.links[i].rel === "approval_url") {
res.redirect(payment.links[i].href);
}
}
}
});
});
app.get("/success", (req, res) => {
const payerId = req.query.PayerID;
const paymentId = req.query.paymentId;
const execute_payment_json = {
payer_id: payerId,
transactions: [
{
amount: {
currency: "USD",
total: "3.33"
}
}
]
};
paypal.payment.execute(paymentId, execute_payment_json, function(
error,
payment
) {
if (error) {
console.log(error.response);
throw error;
} else {
console.log(JSON.stringify(payment));
}
});
});
app.get("/cancel", (req, res) => {
res.send("Cancelled");
});
app.listen(3000, () => console.log("Server Started"));

Related

How do I test a sequelize database updating function like this?

Hi I can I test a function like this? I cannot test it properly because it says that relation "facilities" does not exist, however I now it exists because I can add a new record.
I should I test this code:
export async function postMemberController(req, res) {
try {
if (req.decodedToken.user_id || req.decodedToken.id) {
const facility = await db.facility.findOne({
where: { id: req.body.facility_id },
raw: true,
});
if (!facility?.id) {
res.send(
"no facilities"
);
} else {
let member = await db.member.findOne({
where: { id_no: Number(req.body.id_no) },
raw: true,
});
const admin = await db.user.findOne({
where: { facility_id: req.body.facility_id },
raw: true,
});
const data = await db.sequelize.transaction(async t => {
if (member?.id_no) {
await db.member
.update(
{
...req.body,
facility_id: [...new Set([...member.facility_id, req.body.facility_id])],
},
{ where: { id_no: member.id_no } },
{ transaction: t }
)
.catch(e => console.log('error : creation MEMBER??? ', e));
const adminToken = await tokenCreator({ ...admin }, 3);
!req.body?.from &&
(await axios
.put(
`${process.env.CENTRAL_URL}/members`,
{
body: {
...req.body,
facility_id: [...new Set([...member.facility_id, req.body.facility_id])],
from: 'web',
},
where: { id: member.id },
},
{
headers: { authorization: 'Bearer ' + adminToken },
}
)
.catch(e => console.log('erorr ', e.response.data, e.config.url)));
} else {
const auth = adminFirebase.auth;
const firebaseToken = await auth.createCustomToken(generateUUID());
member = await db.member
.create(
{
...req.body,
firebase_token: firebaseToken,
facility_id: [req.body.facility_id],
creator: admin.user_id,
updater: admin.user_id,
},
{ transaction: t }
)
.catch(e => console.log('error : creation MEMBER??? ', e));
if (member) {
const card = await db.card.findOne({
where: { card_id: member.dataValues.card_id },
raw: true,
});
if (card) {
await db.card.update(
{ card_number: req.body.card_number },
{
where: {
card_id: member.dataValues ? member.dataValues.card_id : member.card_id,
},
},
{ transaction: t }
);
} else {
const newCard = await db.card.create(
{
card_number: req.body?.card_number,
card_id: member?.dataValues?.card_id,
creator: admin.user_id,
updater: admin.user_id,
facility_id: req.body.facility_id,
credits: 0,
},
{ transaction: t }
);
}
}
const adminToken = await tokenCreator({ ...admin }, 3);
!req.body?.from &&
(await axios
.post(
`${process.env.CENTRAL_URL}/members`,
{
...req.body,
id: member.dataValues.id,
card_id: member.dataValues ? member.dataValues.card_id : member.card_id,
facility_id: req.body.facility_id,
from: 'web',
},
{
headers: { authorization: 'Bearer ' + adminToken },
}
)
.catch(e => console.log('erorr ', e, e?.response?.data, e.config.url)));
!req.body?.from &&
(await axios
.put(
`${process.env.CENTRAL_URL}/cards`,
{
body: {
card_number: req.body.card_number.toString(),
facility_id: req.body.facility_id,
from: 'web',
},
where: {
card_id: member.dataValues ? member.dataValues.card_id : member.card_id,
},
},
{
headers: { authorization: 'Bearer ' + adminToken },
}
)
.catch(e => console.log('erorr ', e.response.data, e.config.url)));
}
return member;
});
delete data.dataValues?.password;
delete data?.password;
const memberToken = await tokenCreator({ ...data.dataValues }, 3);
return res
.status(200)
.json({ data, session_id: data.id_no || data.dataValues.id_no, token: memberToken });
}
} else {
return res.sendStatus(401);
}
} catch (error) {
res.status(500).send(error.message);
}
}
this is how I try to test it:
it('post member from web terminal', async () => {
try {
jest.mock('../db/models/index.js', () => {
const SequelizeMock = require('sequelize-mock');
const dbMock = new SequelizeMock();
const facility = dbMock.define('facilities', {
id: '6-30189',
})
const member = dbMock.define('members', {});
const card = dbMock.card('cards', {});
});
const req = {
decodedToken: { user_id: makeId() },
body: member,
};
const res = {
sendStatus(num) {},
status(num) {
return { json(payload) {}, send(payload) {} };
},
};
const request = await postMemberController(req, res);
delete member.from;
expect(request).toHaveBeenCalledWith({ ...member });
} catch (error) {
console.log('ERROR post member ', error);
throw new Error(error);
}
});
How do I write a test for this???? when I call postMemberController inside jest it seems that can't find the relations in the table, I tried to console.log db.facility object from jest test output, there is nothing, not even empty object or null or undefined. I don't understand why? How can I solve this??
Here is the object I am sending:
const card_number = 11111111111111;
const card_id = makeId();
const schedule = makeId();
const club = makeId();
const trainer = makeId();
const user = makeId();
const facility_id = '6-30189';
const member = {
from: 'central',
card_number: card_number,
id: makeId(),
first_name: 'Demo',
last_name: 'Demo',
id_no: card_number,
card_id,
home_phone: null,
urgent_phone: null,
email: null,
registered: true,
schedule,
club,
trainer,
birthplace: 'Gölbaşı/Ankara',
birthdate: new Date().toISOString(),
father_name: null,
mother_name: null,
gender: null,
profession: null,
address: null,
phone_number: null,
hes_code: null,
blood_type: null,
nationality: null,
profile_photo: null,
is_employee: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
firebase_token: null,
file: null,
session_ids: null,
facility_id,
following: null,
creator: user,
updater: user,
};

Stop node mailer scheduler after 3 days of subscribing to newsletter

I have a node.js application and I am using node mailer. When user subscribes to newsletter I send him newsletter every day at specific hour. How can I achieve that it will stop sending to that specific user after 3 days.
Code in MailServiceCron.ts:
export const CRON = () => {
scheduleJob("0 5 * * *", async () => {
try {
let UserList = await User.getUsersByDate();
UserList.forEach(async (user: IUserGet) => {
var content = fs.readFileSync("src/data/email.html");
var htmlbody = content.toString();
await fetch(
"api_url" +
process.env.USER_KEY,
{
method: "POST",
headers: { "Content-Type": "application/json" },
}
)
.then(async (res) => {
return [await res.json(), res.status];
})
.then(([data, status]) => {
console.log(data);
if (data.steviloDelovnihMest > 0) {
let transporter = nodemailer.createTransport({
host: "host",
port: 25,
secure: false, // true for 465, false for other ports
});
let info = transporter.sendMail({
from: "<no-reply#text.com>", // sender address
to: user.email, // list of receivers
subject: `test`, // Subject line// plain text body
html: htmlbody, // html body
});
}
})
.catch((error) => {
return console.log(error);
});
});
console.log("Sending mails");
} catch (e) {
console.log(e);
}
});
};
function deleteEmptyProps(obj: any): any {
Object.keys(obj).forEach((k) => {
if (
!obj[k] ||
obj[k] === undefined ||
(Array.isArray(obj[k]) && obj[k].length === 0)
) {
delete obj[k];
}
});
return obj;
}
export const deletingNonActiveCRON = () => {
scheduleJob("0 * * * *", async () => {
try {
let response = await User.deleteNonActive();
console.log(response);
} catch (e) {
console.log(e);
}
});
};
And in my separate file mail.ts i have this:
module.exports.deleteNonActive = async function () {
let date = new Date();
return await User.deleteMany({
$and: [
{ dateStart: { $lt: new Date(date.setHours(date.getHours() - 48)) } },
{ aktivnost: { $eq: false } },
],
});
};
My idea is that I need also some deleteExpired function, something like that?
module.exports.deleteExpired = async function () {
await User.updateMany(
{
$and: [
{ dateEnd: { $lt: new Date() } },
{ aktivnost: { $eq: true } },
],
},
{ $set: { aktivnost: false } }
);
};
Which I also call in MailServiceCron.ts file like deleteNonActive function?

Two if statements to make cmd differentiate between roles

I'm wondering if it is possible to use if statements this way so that the command gives separate messages depending on the users role. This is one attempt I've made but the second if statement is unreachable.
module.exports = {
name: "train",
description: "Train to earn some reputation!",
async execute(client, message, args, cmd, discord, profileData) {
const events1 = [
"sample event",
"sample event",
"sample event",
];
const events2 = [
"sample event",
"sample event",
"sample event",
];
const injuries = [
"sample injury",
"sample injury",
"sample injury",
];
const chosenEvent1 = events1.sort(() => Math.random() - Math.random()).slice(0, 1);
const chosenEvent2 = events2.sort(() => Math.random() - Math.random()).slice(0, 1);
const chosenInjury = injuries.sort(() => Math.random() - Math.random()).slice(0, 1);
const randomNumber1 = Math.floor(Math.random() * 29) + 5;
const randomNumber2 = Math.floor(Math.random() * 9) + 1;
if((!message.member.roles.cache.has('roleid#1'))); {
if(Math.floor(Math.random() * 3) === 0) {
await profileModel.findOneAndUpdate(
{
userID: message.author.id,
},
{
$inc: {
health: -randomNumber2,
},
}
);
return message.channel.send(`${chosenInjury} You lost ${randomNumber2} health and gained no reputation.`);
} else {
await profileModel.findOneAndUpdate(
{
userID: message.author.id,
},
{
$inc: {
reputation: randomNumber1,
},
}
);
return message.channel.send(`${chosenEvent1} You earned ${randomNumber1} reputation!`);
}
if((!message.member.roles.cache.has('roleid#2'))); {
return message.channel.send(`${chosenEvent2} You earned ${randomNumber1} reputation!`);
}
}}
};
So ideally if you have RoleID #1, you have a chance of injury or your reputation increases and you get a message with Event1 prompts. If you have RoleID #2, your reputation just increases and you get a message with Event2 prompts. I hope this is clear.
It seems the brackets are bit off and that is why the code is unreachable.
I have also attached an image of the locations where I made code changes. A key consideration is in javascript try to avoid placing semicolons right after an if statement
Please see the small bracket changes I made in your code example:
module.exports = {
name: 'train',
description: 'Train to earn some reputation!',
async execute(client, message, args, cmd, discord, profileData) {
const events1 = ['sample event', 'sample event', 'sample event'];
const events2 = ['sample event', 'sample event', 'sample event'];
const injuries = ['sample injury', 'sample injury', 'sample injury'];
const chosenEvent1 = events1.sort(() => Math.random() - Math.random()).slice(0, 1);
const chosenEvent2 = events2.sort(() => Math.random() - Math.random()).slice(0, 1);
const chosenInjury = injuries.sort(() => Math.random() - Math.random()).slice(0, 1);
const randomNumber1 = Math.floor(Math.random() * 29) + 5;
const randomNumber2 = Math.floor(Math.random() * 9) + 1;
if (!message.member.roles.cache.has('roleid#1')) {
if (Math.floor(Math.random() * 3) === 0) {
await profileModel.findOneAndUpdate(
{
userID: message.author.id,
},
{
$inc: {
health: -randomNumber2,
},
}
);
return message.channel.send(
`${chosenInjury} You lost ${randomNumber2} health and gained no reputation.`
);
} else {
await profileModel.findOneAndUpdate(
{
userID: message.author.id,
},
{
$inc: {
reputation: randomNumber1,
},
}
);
return message.channel.send(`${chosenEvent1} You earned ${randomNumber1} reputation!`);
}
} else if (!message.member.roles.cache.has('roleid#2')) {
return message.channel.send(`${chosenEvent2} You earned ${randomNumber1} reputation!`);
}
},
};

API Only sends 1 chunk of metadata when called

I have a problem with my API that sends metadata when called from my smart contract of website. Its NFT tokens and my database is postgres and API is node.js
The problem is when I mint 1 NFT metadata works perfect, but if I mint 2 or more it will only ever send 1 chunk of data? So only 1 NFT will mint properly and the rest with no data?
Do I need to set a loop function or delay? Does anyone have any experience with this?
Any help would be much appreciated.
Below is the code from the "controller" folder labeled "nft.js"
const models = require("../../models/index");
const path = require("path");
const fs = require("fs");
module.exports = {
create_nft: async (req, res, next) => {
try {
const dir = path.resolve(__dirname + `../../../data/traitsfinal.json`);
const readCards = fs.readFileSync(dir, "utf8");
const parsed = JSON.parse(readCards);
console.log("ya data ha final ??", parsed);
parsed.forEach(async (item) => {
// return res.json(item)
let newNft = await models.NFT.create({
name: item.Name,
description: item.Description,
background: item.Background,
body: item.Body,
mouth: item.Mouth,
eyes: item.Eyes,
head_gear: item.Head_Gear,
tokenId: item.tokenId,
image: item.imagesIPFS,
});
});
return res.json({
data: "nft created",
error: null,
success: true,
});
} catch (error) {
console.log("server error", error.message);
next(error);
}
},
get_nft: async (req, res, next) => {
try {
const { id } = req.params;
// console.log("id ?????????",id)
// console.log("type of ",typeof(id))
// const n=Number(id)
// console.log("type of ",typeof(id))
const nft = await models.NFT.findByPk(id);
if (!nft) {
throw new Error("Token ID invalid");
}
if (!nft.isMinted) {
throw new Error("Token not minted");
}
console.log(nft);
// }
const resObj = {
name: nft.name,
description: nft.description,
image: `https://gateway.pinata.cloud/ipfs/${nft.image}`,
attributes: [
{ trait_type: "background", value: `${nft.background}` },
{ trait_type: "body", value: `${nft.body}` },
{ trait_type: "mouth", value: `${nft.mouth}` },
{ trait_type: "eyes", value: `${nft.eyes}` },
{ trait_type: "tokenId", value: `${nft.tokenId}` },
{
display_type: "number",
trait_type: "Serial No.",
value: id,
max_value: 1000,
},
],
};
return res.json(resObj);
} catch (error) {
console.log("server error", error.message);
next(error);
}
},
get_nft_all: async (req, res, next) => {
try {
// console.log("id ?????????",id)
// console.log("type of ",typeof(id))
// const n=Number(id)
// console.log("type of ",typeof(id))
const nft = await models.NFT.findAndCountAll({
limit: 10
});
// console.log(nft);
if (!nft) {
throw new Error("Token ID invalid");
}
// if (nft.isMinted) {
// throw new Error("Token not minted");
// }
// console.log(nft);
// }
var resObjarr = [];
for (var i = 0; i < nft.rows.length; i++) {
resObj = {
name: nft.rows[i].name,
description: nft.rows[i].description,
image: `https://gateway.pinata.cloud/ipfs/${nft.rows[i].image}`,
attributes: [
{ trait_type: "background", value: `${nft.rows[i].background}` },
{ trait_type: "body", value: `${nft.rows[i].body}` },
{ trait_type: "mouth", value: `${nft.rows[i].mouth}` },
{ trait_type: "eyes", value: `${nft.rows[i].eyes}` },
{ trait_type: "tokenId", value: `${nft.rows[i].tokenId}` },
{
display_type: "number",
trait_type: "Serial No.",
value: nft.rows[i].id,
max_value: 1000,
},
],
};
resObjarr.push(resObj);
}
console.log(JSON.stringify(resObjarr))
return res.json(resObjarr);
} catch (error) {
console.log("server error", error.message);
next(error);
}
},
mint: async (req, res, next) => {
try {
const { id } = req.params;
const updated = await models.NFT.findByPk(id);
if (!updated) {
throw new Error("NFT ID invalid");
}
if (updated.isMinted) {
throw new Error("NFT Already minted");
}
updated.isMinted = true;
updated.save();
return res.json({
data: "Token minted successfully",
error: null,
success: true,
});
} catch (error) {
console.log("server error", error.message);
next(error);
}
},
};
Below is from the routes folder.
const router = require("express").Router();
const auth=require("../middleware/auth")
const {
create_nft,
get_nft,
get_nft_all,
mint
} = require("../controller/nft");
router.post(
"/create",
create_nft
);
router.get(
"/metadata/:id",
get_nft
);
router.get(
"/metadata",
get_nft_all
);
router.put(
"/mint/:id",
mint
);
module.exports = router;
Looking your code,you may having some kind of asyncrhonous issue in this part:
parsed.forEach(async (item) => {
// return res.json(item)
let newNft = await models.NFT.create({
name: item.Name,
description: item.Description,
background: item.Background,
body: item.Body,
mouth: item.Mouth,
eyes: item.Eyes,
head_gear: item.Head_Gear,
tokenId: item.tokenId,
image: item.imagesIPFS,
});
});
Because .forEach is a function to be used in synchronous context and NFT.create returns a promise (that is async). So things happens out of order.
So one approach is to process the data first and then perform a batch operation using Promise.all.
const data = parsed.map(item => {
return models.NFT.create({
name: item.Name,
description: item.Description,
background: item.Background,
body: item.Body,
mouth: item.Mouth,
eyes: item.Eyes,
head_gear: item.Head_Gear,
tokenId: item.tokenId,
image: item.imagesIPFS,
})
})
const results = await Promise.all(data)
The main difference here is Promise.all resolves the N promises NFT.create in an async context in paralell. But if you are careful about the number of concurrent metadata that data may be too big to process in parallel, then you can use an async iteration provided by bluebird's Promise.map library.
const Promise = require('bluebird')
const data = await Promise.map(parsed, item => {
return models.NFT.create({
name: item.Name,
description: item.Description,
background: item.Background,
body: item.Body,
mouth: item.Mouth,
eyes: item.Eyes,
head_gear: item.Head_Gear,
tokenId: item.tokenId,
image: item.imagesIPFS,
})
})
return data

paypal-rest-sdk "MALFORMED_REQUEST_JSON" on execution

I'm trying to implement PayPal payments on my website, I can create a payment once the user requests and I send him the redirect approval URL, after clients pay I call the execution on my backend with the payment ID and buyer ID, but I receive the error below
{"response":{"name":"VALIDATION_ERROR","message":"Invalid request - see details","debug_id":"4f3a6da7e0c7d","details":[{"location":"body","issue":"MALFORMED_REQUEST_JSON"}],"links":[],"httpStatusCode":400},"httpStatusCode":400}
I have been trying everything for a few hours, I tried to copy the create payment JSON from anywhere including PayPal API example and still, nothing works.
Also, I wanna redirect the client to success page just after transaction is approved (execute), it has to be handled by the front?
Code
const paypal = require('paypal-rest-sdk');
const dbService = require('../../services/mongodb-service');
const { ObjectId } = require('mongodb');
const getProducts = async () => {
const products = await dbService.getCollection('products');
return await products.find({}).toArray();
}
paypal.configure({
'mode': 'sandbox',
'client_id': 'id',
'client_secret': 'secret'
});
const createPayment = async (productId) => {
const products = await dbService.getCollection('products');
const product = await products.findOne({ '_id': ObjectId(productId) })
if (!product) return Promise.reject('Product not found');
const payment = {
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"transactions": [{
"amount": {
"currency": "USD",
"total": product.price,
},
"description": product.description,
"payment_options": {
"allowed_payment_method": "IMMEDIATE_PAY"
},
"item_list": {
"items": [{
"name": product.name,
"description": product.description,
"quantity": 1,
"price": product.price,
"tax": 0,
"sku": product._id,
"currency": "USD"
}]
}
}],
"redirect_urls": {
"return_url": "http://localhost:3000/purchase-success",
"cancel_url": "http://localhost:3000/purchase-error"
}
}
const transaction = await _createPay(payment);
const redirect = transaction.links.find(link => link.method === 'REDIRECT');
return redirect;
}
const _createPay = (payment) => {
return new Promise((resolve, reject) => {
paypal.payment.create(payment, (err, payment) => err ? reject(err) : resolve(payment));
});
}
const executePayment = async (paymentId, payerId) => {
try {
const execute = await _executePay(paymentId, payerId);
console.log(execute);
return execute;
} catch (err) { console.log(JSON.stringify(err)) }
}
const _executePay = (paymentId, payerId) => {
return new Promise((resolve, reject) => {
console.log(paymentId, payerId);
paypal.payment.execute(paymentId, payerId, (error, payment) => {
return error ? reject(error) : resolve(JSON.stringify(payment));
})
})
}
module.exports = {
createPayment,
executePayment,
getProducts
}
should be
const _executePay = (paymentId, payerId) => {
return new Promise((resolve, reject) => {
console.log(paymentId, payerId);
var payerIdObj = { payer_id: payerId };
paypal.payment.execute(paymentId, payerIdObj, (error, payment) => {
return error ? reject(error) : resolve(JSON.stringify(payment));
})
})
}
the doc
I was able to resolve it by doing a post request, code:
const _executePay = async (paymentId, payerId) => {
const response = await axios.post(`https://api.sandbox.paypal.com/v1/payments/payment/${paymentId}/execute`, { 'payer_id': payerId }, {
auth: {
username: CLIENT_ID,
password: CLIENT_SECRET
}
})
return response;
}

Resources