With PayPal JS SDK, how to conditionally 'run' the onApprove part? - paypal

I have implemented a sandbox environment with PayPal JS SDK and the corresponding PHP SDK.
In JavaScript I use:
paypal.Buttons({
createOrder: function(data, actions) {
return fetch('../checkout-sdk/samples/CaptureIntentExamples/CreateOrder.php', {
method: 'post',
headers: {
'content-type': 'application/json'
}
}).then(function(resp) {
respjs = resp.json();
return respjs;
}).then(function(orderData) {
order_id = orderData.id;
return order_id;
});
},
onApprove: function(data, actions) {
return fetch('../checkout-sdk/samples/CaptureIntentExamples/CaptureOrder.php?order_id='+data.orderID, {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
orderID: data.orderID
})
}).then(function(appr_res) {
apprjs = appr_res.json();
return apprjs;
}).then(function(orderData) {
var errorDetail = Array.isArray(orderData.details) && orderData.details[0];
if (errorDetail) {
if (errorDetail.issue === 'INSTRUMENT_DECLINED') {
return actions.restart(); // Recoverable state, per:
// https://developer.paypal.com/docs/checkout/integration-features/funding-failure/
} else {
var msg = 'Sorry, your transaction could not be processed.';
if (errorDetail.description) msg += '\n\n' + errorDetail.description;
if (orderData.debug_id) msg += ' (' + orderData.debug_id + ')';
return alert(msg); // Show a failure message
}
}
// Successful capture! For demo purposes:
console.log('Capture result', JSON.stringify(orderData, null, 2));
})
.catch(function(error) {
console.error("There was a problem with the approval fetch: ", error);
});
},
onError: function(err) {
alert(err);
}
}).render('#paypal-button-container');
When the function createOrder fails, the PHP-script CreateOrder.php should return a message to the javascript SDK so it's possible to inform the customer.
On failing, the onApprove does not needed to 'run'.
How can I change the code to conditionally let 'run' the onApprove function?
I tried something like below on JSLint:
paypal.Buttons({
createOrder: function(data, actions) {
return fetch(url, {
method: "post"
}).then(function(res) {
return res.json();
}).then(function(orderData) {
return orderData.id;
});
},
if (orderData !== false) { // error: Expected ')' and instead saw '!=='.
onApprove: function(data, actions) {
return fetch(url2 + data.orderID, {
method: "post"
}).then(function(res) {
return res.json();
}).then(function(orderData) {
//
});
},
}
}).render("#paypal-button-container");
but JSLint reported: Expected ')' and instead saw '!=='.
How can I conditionally implement an if-then condition there?

onApprove is already conditional on the create being successful and the order being successfully approved by the customer. It will not be called otherwise. You do not need to do anything.

Related

Hosting a Forge Autodesk viewer on Github

I've an issue with the Forge viewer I'm developping : Im' trying to host it using Github-page, but it doesn't seem to work correctly.
The issue is on the File tree : when I load the viewer page from the Github pages, the file tree seems stuck on "Loading...". However, it correctly loads when I load the page from localhost.
The code of the File tree :
$(document).ready(function () {
prepareAppBucketTree();
$('#refreshBuckets').click(function () {
$('#appBuckets').jstree(true).refresh();
});
$('#createNewBucket').click(function () {
createNewBucket();
});
$('#createBucketModal').on('shown.bs.modal', function () {
$("#newBucketKey").focus();
})
$('#hiddenUploadField').change(function () {
var node = $('#appBuckets').jstree(true).get_selected(true)[0];
var _this = this;
if (_this.files.length == 0) return;
var file = _this.files[0];
switch (node.type) {
case 'bucket':
var formData = new FormData();
formData.append('fileToUpload', file);
formData.append('bucketKey', node.id);
$.ajax({
url: '/api/forge/oss/objects',
data: formData,
processData: false,
contentType: false,
type: 'POST',
success: function (data) {
$('#appBuckets').jstree(true).refresh_node(node);
_this.value = '';
}
});
break;
}
});
});
function createNewBucket() {
var bucketKey = $('#newBucketKey').val();
var policyKey = $('#newBucketPolicyKey').val();
console.log(bucketKey)
jQuery.post({
url: '/api/forge/oss/buckets',
contentType: 'application/json',
data: JSON.stringify({ 'bucketKey': bucketKey, 'policyKey': policyKey }),
success: function (res) {
$('#appBuckets').jstree(true).refresh();
$('#createBucketModal').modal('toggle');
},
error: function (err) {
if (err.status == 409)
alert('Bucket already exists - 409: Duplicated')
console.log(err);
}
});
}
function prepareAppBucketTree() {
$('#appBuckets').jstree({
'core': {
'themes': { "icons": true },
'data': {
"url": '/api/forge/oss/buckets',
"dataType": "json",
'multiple': false,
"data": function (node) {
return { "id": node.id };
}
}
},
'types': {
'default': {
'icon': 'glyphicon glyphicon-question-sign'
},
'#': {
'icon': 'glyphicon glyphicon-cloud'
},
'bucket': {
'icon': 'glyphicon glyphicon-folder-open'
},
'object': {
'icon': 'glyphicon glyphicon-file'
}
},
"plugins": ["types", "state", "sort", "contextmenu"],
contextmenu: { items: autodeskCustomMenu }
}).on('loaded.jstree', function () {
$('#appBuckets').jstree('open_all');
}).bind("activate_node.jstree", function (evt, data) {
if (data != null && data.node != null && data.node.type == 'object') {
// $("#MyViewerDiv").empty();
var urn = data.node.id;
getForgeToken(function (access_token) {
jQuery.ajax({
url: 'https://developer.api.autodesk.com/modelderivative/v2/designdata/' + urn + '/manifest',
headers: { 'Authorization': 'Bearer ' + access_token },
success: function (res) {
if (res.status === 'success') callByUrn('urn:'+urn);
else $("#MyViewerDiv").html('The translation job still running: ' + res.progress + '. Please try again in a moment.');
},
error: function (err) {
var msgButton = 'This file is not translated yet! ' +
'<button class="btn btn-xs btn-info" onclick="translateObject()"><span class="glyphicon glyphicon-eye-open"></span> ' +
'Start translation</button>'
$("#MyViewerDiv").html(msgButton);
}
});
})
}
});
}
function autodeskCustomMenu(autodeskNode) {
var items;
switch (autodeskNode.type) {
case "bucket":
items = {
uploadFile: {
label: "Upload file",
action: function () {
uploadFile();
},
icon: 'glyphicon glyphicon-cloud-upload'
}
};
break;
case "object":
items = {
translateFile: {
label: "Translate",
action: function () {
var treeNode = $('#appBuckets').jstree(true).get_selected(true)[0];
translateObject(treeNode);
},
icon: 'glyphicon glyphicon-eye-open'
}
};
break;
}
return items;
}
function uploadFile() {
$('#hiddenUploadField').click();
}
function translateObject(node) {
$("#MyViewerDiv").empty();
if (node == null) node = $('#appBuckets').jstree(true).get_selected(true)[0];
var bucketKey = node.parents[0];
var objectKey = node.id;
jQuery.post({
url: '/api/forge/modelderivative/jobs',
contentType: 'application/json',
data: JSON.stringify({ 'bucketKey': bucketKey, 'objectName': objectKey }),
success: function (res) {
$("#MyViewerDiv").html('Translation started! Please try again in a moment.');
},
});
}
Please note that Github Pages are used for serving static pages without any special server-side logic. Your Forge application requires a server to talk to as well, for example, to obtain a list of buckets for the tree view (by making a request to /api/forge/oss/buckets).
You could potentially host your application's server-side logic on something like Heroku, and then have your static HTML/CSS/JavaScript page on Github talk to that server (for example, https://my-forge-app.herokuapp.com/api/forge/oss/buckets). Just be careful about CORS.

Can't get paypal smart button to accept php value

I am trying to create a paypal smart button in my php script. I am unable to get values from my php script to the javascript code in the json object. I tried echoing the php directly into the javascript but that didn't work. I tried using the Jquery but that didn't work. Can anyone help me? here is my code:
I tried using Jquery and directly echoing my php into the json object but neither of them worked.
var valueNumber = $("#grandTotal").html();
valueNumberArray = valueNumber.split(">")
valueNumberArray[1] = "'"+valueNumberArray[1]+"'";
alert(valueNumberArray[1]);
paypal.Buttons({
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: valueNumberArray[1]
}
}]
});//insert JSON above this line
},
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
alert('Transaction completed by ' + details.payer.name.given_name);
// Call your server to save the transaction
return fetch('/BurgerJoint/GetOrder.php', {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
orderID: data.orderID
})
});
});
}
}).render('#paypal-button-container');
Please save your file with .php extension. You can assign php value into js variable and pass it to PayPal. I have attached sample code below: I am passing amount value from php variable.`<script>
var total_price= '<?php echo $total_price; ?>';
paypal.Buttons({
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: total_price
}
}]
});//insert JSON above this line
},
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
alert('Transaction completed by ' + details.payer.name.given_name);
// Call your server to save the transaction
return fetch('/BurgerJoint/GetOrder.php', {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
orderID: data.orderID
})
});
});
}
}).render('#paypal-button-container');
</script>`

Mongoose Library find / findOne Method is not returning value when the search dont match with the document

I am facing one issue with Mongoose. When I use find or findOne method and there is no matching results, then callback function is not returning null / err and hung the process. Using Mongoss 5.1.5 , MongoDB V3.4.2. Please advise
module.exports.validateappsubscripition = function (userid, appkey, cb) {
//console.error(userid + ' ' + appkey)
var userobj_id = mongoose.Types.ObjectId(userid);
appsubscripitions.model.findOne({'subscribersuserid': userobj_id , 'appkey'
:appkey }, function(err,doc){
console.error('test2');
if(doc ){
cb(null, doc );
}
else{
cb(null, null );
}
} );
}
Calling Block : Trying to validate the key from req header. I am trying to call the function validateappsubscripition from below block.
module.exports.sendEmail = function (req, res, next) {
let appkey;
let userid;
if (req.headers.appkey) {
appkey = req.headers.appkey;
console.error(appkey);
}
else {
appkey = '';
}
if(req.user._id){
userid = req.user._id ;
console.error(userid);
}
if (!req.body.fromEmail || !req.body.toEmail || !req.body.subject || !req.body.content) {
res.json({ success: false, msg: 'Please pass all the required parameters' });
next();
}
appsubcripitions.validateappsubscripition(userid, appkey, function (err, doc) {
console.error('test2');
if (err) {
res.json({ success: false, msg: 'Unauthorized. App Key is misssing on the header or App key is not valid' });
next();
}
else if (doc ) {
this.getSMTP('smtp.gmail.com', 'arbalu#gmail.com', function (err, userInfo) {
if (err) {
res.json({ success: false, msg: err.message });
next();
}
if (userInfo) {
//userInfo = user;
var send = require('gmail-send')({
user: userInfo.user,
pass: userInfo.pass,
from: req.body.fromEmail,
to: req.body.toEmail,
subject: req.body.subject, // Override value set as default
text: req.body.content
});
send({ // Overriding default parameters
// to: req.toEmail,
// subject: req.subject, // Override value set as default
// text: req.content
// files: [filepath],
}, function (err, response) {
//console.log('* [example 1.1] send() callback returned: err:', err, '; res:', res);
if (err) {
res.json({ success: false, msg: err.message });
next();
}
else {
res.json({ success: true, msg: response });
next();
}
});
}
})
}
else {
res.json({ success: false, msg: 'Some issue on sending email.Please contact the support.' });
next();
}
});
}

Paypal SDK - paymentId vs invoice_number vs orderId

I find it a little hard to understand what is the difference between the paymentId invoice_number and orderId.
This is a demo code in nodejs where I mark the fields.
Also is there a way to get the invoice_number of the item that the user want to pay for before the execuation of the payment.
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const app = express();
const paypal = require('paypal-rest-sdk');
app.use(bodyParser.urlencoded());
app.use(cors())
app.use(bodyParser.json());
paypal.configure({
'mode': 'sandbox',
'client_id':
'client_secret':
});
//Is being called from client
app.post('/pay', (req, res, next) => {
createOrder(req.body.transactionId, paymentUrl => {
res.send({paymentUrl});
});
})
//Is called from paypal after user confirmed the payment
app.get('/process', (req, res, next) => {
//Can I get here the invoice_number in order to understand what the payment for before charging ?
var paymentId = req.query.paymentId;
var payerId = {
'payer_id': req.query.PayerID
};
var order;
paypal.payment.execute(paymentId, payerId, function (error, payment) {
if (error) {
console.error(JSON.stringify(error));
} else {
if (payment.state === 'approved' &&
payment.transactions &&
payment.transactions[0].related_resources &&
payment.transactions[0].related_resources[0].order) {
orderId = payment.transactions[0].related_resources[0].order.id;
//What is the difference between the invoice_number payment.id and orderId
res.redirect('http://localhost:4200/order/payment/success/' + orderId);
} else {
res.redirect('http://localhost:4200/order/payment/failure/' + orderId);
}
}
});
});
//Should be called only from paypal
app.post('/error', (req, res, next) => {
console.log("Error from paypal");
})
app.listen(3300, function () {
console.log('Example app listening on port 3000!')
})
function createOrder(data, callback) {
let invoice_number = Math.round(Math.random() * 1000000);
var payReq = JSON.stringify({
intent: 'order',
payer: {
payment_method: 'paypal'
},
redirect_urls: {
return_url: 'http://localhost:3300/process',
cancel_url: 'http://localhost:4200/order/payment/failure'
},
transactions: [{
amount: {
total: '30.03',
currency: 'USD'
},
description: 'This is the payment transaction description.',
invoice_number: invoice_number, //should be the transactionId
payment_options: {
allowed_payment_method: 'INSTANT_FUNDING_SOURCE'
}
}]
});
paypal.payment.create(payReq, function (error, payment) {
var links = {};
if (error) {
console.error(JSON.stringify(error));
} else {
// Capture HATEOAS links
payment.links.forEach(function (linkObj) {
links[linkObj.rel] = {
href: linkObj.href,
method: linkObj.method
};
})
if (links.hasOwnProperty('approval_url')) {
callback(links.approval_url.href);
} else {
console.error('no redirect URI present');
}
}
});
}

Paypal Checkout - don't ask for delivery address for non-members?

I've just started playing with this module:
https://github.com/paypal/paypal-checkout
I'm trying to work out how to can turn off the shipping address for clients. I know in order versions you could do &NOSHIPPING=1 in the URL, but I can't find anything about the API 4 version. My code is:
paypal.Button.render({
// Pass the client ids to use to create your transaction on sandbox and production environments
locale: 'fr_FR',
//env: 'production',
env: 'sandbox',
client: {
sandbox: "...",
production: "..."
},
// Pass the payment details for your transaction
// See https://developer.paypal.com/docs/api/payments/#payment_create for the expected json parameters
payment: function() {
return paypal.rest.payment.create(this.props.env, this.props.client, {
transactions: [
{
amount: {
total: window.my_config.grand_total,
currency: 'EUR',
details: {
"subtotal": window.my_config.price,
"tax": window.my_config.vat_amount
}
},
}
]
});
},
// Display a "Pay Now" button rather than a "Continue" button
commit: true,
// Pass a function to be called when the customer completes the payment
onAuthorize: function(data, actions) {
return actions.payment.execute().then(function() {
console.log('The payment was completed!');
console.log(data, actions)
if (error === 'INSTRUMENT_DECLINED') {
actions.restart();
}
});
},
// Pass a function to be called when the customer cancels the payment
onCancel: function(data) {
console.log('The payment was cancelled!');
},
style: {
shape: 'rect',
size: "medium"
}
}, '#paypalContainerEl');
Use "shipping_preference: 'NO_SHIPPING'."
createOrder: function(data, actions) {
$('#paypalmsg').html('<b>' + 'WAITING ON AUTHORIZATION TO RETURN...' + '</b>');
$('#chkoutmsg').hide()
return actions.order.create({
purchase_units: [{
description: 'GnG Order',
amount: {
value: cartTotal
}
}],
application_context: {
shipping_preference: 'NO_SHIPPING'
}
});
},
You need to pass the no_shipping option under experience in the payment function, like so:
return actions.payment.create(
{
payment:
{
transactions: [
{
amount:
{
total: "10",
currency: 'EUR'
}
}]
},
experience:
{
input_fields:
{
no_shipping: 1
}
}
});
In the docs, here and here. A quick note though, guests will still be asked for their billing address, even though their shipping address will no longer be asked.
For those of you integrating via PayPal REST API in PHP, to set the no_shipping attribute:
apiContext = $this->apiContext;
$payer = new \PayPal\Api\Payer();
$payer->setPaymentMethod('paypal');
$inputFields = new \PayPal\Api\InputFields();
$inputFields->setNoShipping(1); //<-- NO SHIPPING!!!!!!!!!!
$webProfile = new \PayPal\Api\WebProfile();
$webProfile->setName($uid); // <-- UNIQUE NAME FOR THE TRANSACTION
$webProfile->setInputFields($inputFields);
$createProfileResponse = $webProfile->create($apiContext);
$webProfile = \PayPal\Api\WebProfile::get($createProfileResponse->getId(), $apiContext);
$amount = new \PayPal\Api\Amount();
$amount->setCurrency('EUR')
->setTotal($this->deposit_eur);
$transaction = new \PayPal\Api\Transaction();
$transaction->setAmount($amount);
$redirectUrls = new \PayPal\Api\RedirectUrls();
$redirectUrls->setReturnUrl($this->return_url)
->setCancelUrl($this->cancel_url);
$payment = new \PayPal\Api\Payment();
$payment->setIntent('sale')
->setPayer($payer)
->setRedirectUrls($redirectUrls)
->setTransactions(array($transaction))
->setExperienceProfileId($webProfile->getId()); //<-- SET EXPERIENCE PROFILE
try{
$payment->create($apiContext);
} catch (\Exception $ex) {
debug($ex);
exit;
}
$approvalUrl = $payment->getApprovalLink();
For the unlucky lads integrating this via PayPal REST API, using C#, this is a bit trickier.
You create a WebProfile as in the Paypal Repo Example.
var experienceProfile = new WebProfile()
{
name = Guid.NewGuid().ToString(), // required field
input_fields = new InputFields()
{
no_shipping = 1
}
};
var experienceId = experienceProfile .Create(_apiContext).id;
new Payment
{
intent = "sale",
payer = new Payer
{
payment_method = "paypal"
},
transactions = new List<Transaction>
{
// ...
},
redirect_urls = new RedirectUrls
{
return_url = "..",
cancel_url = ".."
},
experience_profile_id = experienceId
};
If anyone runs into this problem with the v2 API REST approach, setting application_context with shipping_preference on create order in the body did work for me.
https://developer.paypal.com/docs/api/orders/v2/#definition-experience_context_base
const response = await fetch(url, {
method: 'post',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
intent: 'CAPTURE',
application_context: {
shipping_preference: 'NO_SHIPPING',
},
purchase_units: []
})
For the new API you need to set the parameter no_shipping=1
https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/