paypal-rest-sdk "MALFORMED_REQUEST_JSON" on execution - node.js

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;
}

Related

Trouble with return statement in Node Async Await

Trying to write a query for DynamoDB and learn promises etc. The console.log(resp.Items) returns the object that I am looking for so I think my query is formatted correctly. I get a status 200 back with an empty object.
I have read up for a few days and tried to implement various changes to the code by nothing is returning the object resp.Items. I am guessing the function is returning before the const is updated with the data but I am not sure why the console.log works.
const AWS = require('aws-sdk')
const dynamodb = new AWS.DynamoDB()
const getTechs = async () => {
try {
const resp = await dynamodb
.query({
ExpressionAttributeValues: {
':tech': { S: 'TECH#' },
},
KeyConditionExpression: 'PK = :tech',
TableName: process.env.TABLE_NAME,
ScanIndexForward: true,
})
.promise()
console.log(resp.Items)
if (!resp.Items) {
return {
error: 'No Techs in the DB',
}
}
return {
tech: resp.Items,
}
} catch (error) {
console.log('Error retrieving Tech List')
console.log(error)
return {
error: 'Could not retrieve Tech List',
}
}
}
handler func
const { makeHandler } = require('./utils')
const { getTechs } = require('../data')
// const { Tech } = require('../entities')
const inputSchema = {
type: 'object',
properties: {
pathParameters: {
type: 'object',
properties: {
tech: { type: 'string' },
},
required: ['tech'],
},
},
required: ['pathParameters'],
}
const handler = async (event) => {
const { techs, error } = await getTechs()
const statusCode = error ? 500 : 200
const body = error ? JSON.stringify({ error }) : JSON.stringify({ techs })
return {
statusCode,
body,
}
}
module.exports.handler = makeHandler({ handler })
executeTransactWrite func
const executeTransactWrite = async ({ tech, params }) => {
const transactionRequest = tech.transactWriteItems(params)
let cancellationReasons
transactionRequest.on('extractError', (response) => {
try {
cancellationReasons = JSON.parse(
response.httpResponse.body.toString()
).CancellationReasons
} catch (err) {
// suppress this just in case some types of errors aren't JSON parseable
console.error('Error extracting cancellation error', err)
}
})
return new Promise((resolve, reject) => {
transactionRequest.send((err, response) => {
if (err) {
err.cancellationReasons = cancellationReasons
return reject(err)
}
return resolve(response)
})
})
}
module.exports = {
executeTransactWrite,
makehandler func
const middy = require('middy')
const {
jsonBodyParser,
validator,
httpErrorHandler,
} = require('middy/middlewares')
const makeHandler = ({ handler, inputSchema }) =>
middy(handler)
.use(jsonBodyParser())
.use(validator({ inputSchema }))
.use(httpErrorHandler())
module.exports = { makeHandler }

Trying to get user email storage from google admin api

I am building a little script, that access the Google Reseller api that gets all the domains, then finds the users in those domains, to which it then looks at the emails for those users. That part all works.
The issue is that after I have gotten the users emails, I need to check the email storage so that I can then send an alert once the storage is nearly full.
I have searched the internet and tried various different methods, but seem to have hit a roadblock with the final step.
This is as far a I got with code
let privatekey = require("./spartan-concord-344213-d46691ffcd02.json");
let spreadsheetPriKey = require("./spreadsheet_service_account.json");
const {JWT} = require('google-auth-library');
// configure a JWT auth client
let jwtClient = new JWT({
email: privatekey.client_email,
key: privatekey.private_key,
subject: 'subject#email.com',
scopes:['https://www.googleapis.com/auth/apps.order',
'https://www.googleapis.com/auth/admin.reports.audit.readonly',
'https://www.googleapis.com/auth/admin.reports.usage.readonly',
'https://www.googleapis.com/auth/admin.directory.user',
'https://www.googleapis.com/auth/admin.directory.user.readonly',
],
});
let jwtSpreadsheetClient = new JWT({
email: spreadsheetPriKey.client_email,
key: spreadsheetPriKey.private_key,
scopes:['https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/drive','https://www.googleapis.com/auth/spreadsheets.readonly'],
});
const sites = []
//authenticate request
jwtSpreadsheetClient.authorize(async function (err, tokens) {
if (err) {
console.log(err);
return;
} else {
google.options({ auth: jwtSpreadsheetClient });
const client = google.sheets({ version: "v4" });
const supportEmail = await client.spreadsheets.values.get({ spreadsheetId: 'ID', range:'Sites!C2:C',});
let suppEmails = supportEmail.data.values;
suppEmails.forEach(element => {
if(element[0] != null){
sites.push(element[0]);
}
});
}
});
//authenticate request
jwtClient.authorize(async function (err, tokens) {
if (err) {
console.log(err);
return;
} else {
google.options({ auth: jwtClient });
const service = google.reseller({version: 'v1'});
const res = await service.customers.get({
'customerId': 'testDomain',
});
// console.log(res);
const services = google.admin({version: 'directory_v1'});
respo = await services.users.get({
userKey: 'test#email.com',
projection: 'FULL',
viewType: 'admin_view'
});
// console.log(respo);
const servicess = google.admin({version: 'reports_v1'});
// const response = await servicess.userUsageReport.get({
// userKey: 'all',
// date: '2022-10-11',
// parameters: 'gmail:is_gmail_enabled',
// customerId: 'customerId'
// });
const response = await servicess.activities.list({
userKey: 'all',
applicationName: 'drive',
maxResults: 10,
customerId: 'customerId'
});
console.log(response.data);
}
});
Any information is much appreciated :)
It appears you can get this information from the Reports API via userUsageReport.get with a delay.
Which provides the following useful Account Parameters with applicationName=accounts.
drive_used_quota_in_mb
gmail_used_quota_in_mb
gplus_photos_used_quota_in_mb
total_quota_in_mb
used_quota_in_mb
used_quota_in_percentage
Found here.
const { JWT } = require('google-auth-library');
const keys = require('./jwt.keys.json');
async function main() {
const client = new JWT({
email: keys.client_email,
key: keys.private_key,
scopes: [
'https://www.googleapis.com/auth/admin.reports.audit.readonly',
'https://www.googleapis.com/auth/admin.reports.usage.readonly'
],
});
const userKey = 'example#gmail.com';
const threeDaysAgo = new Date();
threeDaysAgo.setDate(new Date().getDate() - 3);
const date = datethreeDaysAgo.toISOString().split('T').slice(0, 1)[0]
const url = `https://admin.googleapis.com/admin/reports/v1/usage/users/${userKey}/dates/${date}?parameters=accounts:used_quota_in_percentage,accounts:total_quota_in_mb,accounts:used_quota_in_mb,accounts:drive_used_quota_in_mb,accounts:gmail_used_quota_in_mb,accounts:gplus_photos_used_quota_in_mb`;
const res = await client.request({ url });
console.log(res.data);
/*
{
"kind": "admin#reports#usageReports",
"etag": "\"iOcWZfmq3FoTN4bnM3qjxTbCKtrhwribSW4KdcAqWMQ/jldXIL3cW34P49N2Row7rqWkykQ\"",
"usageReports": [
{
"kind": "admin#reports#usageReport",
"date": "2022-10-17",
"etag": "\"iOcWZfmq3FoTN4bnM3qjxTbCKtrhwribSW4KdcAqWMQ/vroqtrwJo5ZJIWScePvwODh5ZGI\"",
"entity": {
"type": "USER",
"customerId": "FVdfugb32",
"userEmail": "example#gmail.com",
"profileId": "2350766532076257332587"
},
"parameters": [
{
"name": "accounts:used_quota_in_percentage",
"intValue": "0"
},
{
"name": "accounts:total_quota_in_mb",
"intValue": "-1"
},
{
"name": "accounts:used_quota_in_mb",
"intValue": "858"
},
{
"name": "accounts:drive_used_quota_in_mb",
"intValue": "461"
},
{
"name": "accounts:gmail_used_quota_in_mb",
"intValue": "397"
},
{
"name": "accounts:gplus_photos_used_quota_in_mb",
"intValue": "0"
}
]
}
]
}
*/
}
main().catch(console.error);

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?

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 not returning custom variable

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"));

Resources