Paypal Smart Button – Delay firing "onApproval" causes issues - javascript

Using the Smart Payment buttons, there is a 3-4 second delay after the payment pop-up window closes. It takes 3-4 seconds after the close of the transaction window to fire the onApproval event which gets the transaction ID needed to process an order.
This causes trouble as the buyer could close the window in the meantime (as nothing seems to happen) and the event is never received, thus the order not processed (although paid for).
Here is the code:
paypal.Buttons({
createOrder: function(data,actions) {
// do some stuff
return fetch('/createOrder', {
method: 'post',
headers: {
'content-type': 'application/json'
}
}).then(function(res) {
return res.json();
}).then(function(data) {
return data.orderID;
});
},
// onApprove will be fired 3-4 second AFTER the popup of transaction closes
onApprove: function(data, actions) {
return fetch('captureOrder', {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
orderID:data.orderID
})
}).then(function(res) {
return res.json();
}).then(function(details) {
});
}
}).render(selector);
Is there any way to have the popup close AFTER the even is fired? Otherwise the only work-around would be to make an overlay with a spinner (or something similar) that will disappear once the onApproval is received. But that is cumbersome. The pop-up really shouldn't close before the event is fired.

I haven't observed that long of a delay myself, and it shouldn't cause issues as the buyer should wait for their confirmation in any case, but well it is as it is.
You can use the onClick method to trigger a please wait / spinner or whatever if you feel it's necessary, and nuke it within your onApprove's fetch (and also an onError and onCancel function). But you're overcomplicating things.

use this
// Finalize the transaction after payer approval
onApprove: function(data, actions) {
return actions.order.capture().then(function(orderData) {
// Successful capture! For dev/demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
var 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:
// var element = document.getElementById('paypal-button-container');
// element.innerHTML = '';
// element.innerHTML = '<h3>Thank you for your payment!</h3>';
// Or go to another URL: actions.redirect('thank_you.html');
});
}

Related

How to reload page after using fetch

Was wondering why my code below isnt working. Basically I am fetch data from my url to delete something. on delete, it should refresh. however it doesnt seem to let me do that. But what it does is delete the post if i manually refresh the page.
Works when I use Ajax method though which I don't know why.
Fetch method
const deleteBTN = document.querySelectorAll('.deleteBtn');
const update = document.querySelectorAll('.postedNote');
console.log(deleteBTN);
for (let btn of deleteBTN) {
btn.addEventListener('click', (e) => {
console.log("Delete from front end");
console.log(btn[btn])
let id = e.target.dataset.btn;
fetch('http://localhost:3000/api/notes' + '/' + id, {
method: "DELETE",
}).then(() => {
Location: reload()
})
})
}
Ajax method
$(".deleteBtn").click((e) => {
$.ajax({
type: "DELETE",
url: `http://localhost:3000/api/notes/${e.target.dataset.btn}`,
success: function () {
console.log("delete success");
},
}).done(
setTimeout(() => {
window.location.reload();
}, 500)
);
});
We can reload by using window.location.reload as already doing on ajax success.
Kindly find below as code snippet
const deleteBTN = document.querySelectorAll('.deleteBtn');
const update = document.querySelectorAll('.postedNote');
console.log(deleteBTN);
for (let btn of deleteBTN) {
btn.addEventListener('click', (e) => {
console.log("Delete from front end");
console.log(btn[btn])
let id = e.target.dataset.btn;
fetch('http://localhost:3000/api/notes' + '/' + id, {
method: "DELETE",
}).then(() => {
window.location.reload();
})
})
}
Also, a few considerations below
The use of Ajax is defeated here as we are reloading the page.
Either
we should perform some DOM manipulation to delete the deleted post
from the page
Or,
in case we are using React, we can bind posts to state and then
delete the deleted post on API success call, so that component is
re-rendered and we do not need to refresh the page.
I think you should'nt try to reload the page when removing one of the elements,
instead I would have checked the response status and manually update the DOM to make the deleted element disappear if response says it was removed successfully.

How to properly add headers, paypal tutorial?

Im doing the paypal tutorial server side integration.
With PHP.
I'm having a problem.
I want to test funding failures,the last part
https://developer.paypal.com/docs/business/checkout/server-side-api-calls/handle-funding-failures/
I think I add the mock header in the right place but is doing nothing it keeps saying Transaction completed .
Whats the proper way to put it?
Paypal says:
In the call to Capture payment for order, include the PayPal-Mock-Response header with the mock_application_codes value set to INSTRUMENT_DECLINED. Sample code: -H "PayPal-Mock-Response: {"mock_application_codes" : "INSTRUMENT_DECLINED"}"
<script>
// Render the PayPal button into #paypal-button-container
paypal.Buttons({
// Call your server to set up the transaction
createOrder: function() {
return fetch('examplecreateorder.php', {
method: 'post',
headers: {
'content-type': 'application/json'
}
}).then(function(res) {
return res.json();
}).then(function(data) {
return data.id;
});
},
// Call your server to finalize the transaction
onApprove: function(data) {
return fetch('examplecaptureorder.php', {
method: 'post',
headers: {
'content-type': 'application/json',
'PayPal-Mock-Response': {
'mock_application_codes': 'INSTRUMENT_DECLINED'
}
},
body: JSON.stringify({
orderID: data.orderID
})
}
).then(function(res) {
return res.json().catch(error => console.error('Error:', error));
}).then(function(orderData) {
// Three cases to handle:
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
// (2) Other non-recoverable errors -> Show a failure message
// (3) Successful transaction -> Show confirmation or thank you
// This example reads a v2/checkout/orders capture response, propagated from the server
// You could use a different API or structure for your 'orderData'
var errorDetail = Array.isArray(orderData.details) && orderData.details[0];
if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
return actions.restart(); // Recoverable state, per:
// https://developer.paypal.com/docs/checkout/integration-features/funding-failure/
}
if (errorDetail) {
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
}
// Show a success message
alert('Transaction completed by + orderData.payer.name.given_name);
})
}
}).render('#paypal-button-container');
</script>
In the headers, the value for PayPal-Mock-Response should just be a string and not an object:
...
headers: {
'content-type': 'application/json',
'PayPal-Mock-Response': '{"mock_application_codes" : "INSTRUMENT_DECLINED" }'
},
...
Note the difference between what was being specified in OPs code as an object literal:
'PayPal-Mock-Response': {
'mock_application_codes': 'INSTRUMENT_DECLINED'
}
And the working code treating the value of PayPal-Mock-Response as a string:
'PayPal-Mock-Response': '{"mock_application_codes" : "INSTRUMENT_DECLINED" }'

How to re-render PayPal Smart Buttons with different values?

I'm using PayPal Smart button on my web site, and after each payment is made the user is able to make another new payment with PayPal smart button. The problem is that once I've created my PayPal button I'm unable to set the new createOrder in it with new orderID.
So in my website I have the following function which init the PayPal button if there is yet a pending order or it's called when a new order is made and I get a new orderID.
function initPayPal(orderID, publicKey) {
var paymentID = $("#paypal-button-container").data("payment-id")
var PAYPAL_SCRIPT =
"https://www.paypal.com/sdk/js?client-id=" + publicKey + "&currency=EUR&intent=capture&commit=false&vault=true";
var script = document.createElement("script");
script.setAttribute("src", PAYPAL_SCRIPT);
script.onload = function () {
paypal
.Buttons({
style: {
shape: "rect",
color: "gold",
layout: "horizontal",
label: "paypal",
tagline: false,
height: 52,
},
createOrder: async function () {
$(".body-payments").hide();
$(".loader-payments").show();
const res = await fetch(
"/api/payment/paypal/" + paymentID + "/" + orderID,
{
method: "post",
headers: {
"content-type": "application/json",
},
credentials: "include",
}
);
const data = await res.json();
return data.id;
},
onApprove: async function (data) {
const res = await fetch(
"/api/payment/paypal/capture/" + paymentID + "/" + data.orderID,
{
method: "post",
headers: {
"content-type": "application/json",
},
credentials: "include",
}
);
if (localStorage.STBOrder) {
localStorage.removeItem("STBOrder");
}
$("#modalPayments").modal("hide");
$("#modalSuccess").modal("show");
$(".body-payments").show();
$(".loader-payments").hide();
},
onCancel: function (data) {
$(".body-payments").show();
$(".loader-payments").hide();
},
})
.render("#paypal-button-container");
};
document.head.appendChild(script);
}
Client id is set dynamically as each user on its own page will get the payment on its own account. The issue here is that the button is initiated the first time all works fine, but if I dismiss the order and I call this function again it will create another button instead of setting new values to existing (already rendered) one.
There should be no need to init the button more than once.
Initialize the button one time (on page load).
Then, inside createOrder, ensure paymentID and orderID reference non-local variables or are replaced with function calls that will return the correct value at the time you want them to, e.g. some kind of getOrderID() of yours, and $("#paypal-button-container").data("payment-id") , in place of your existing local variables
In the unlikely event that you really did need to render the buttons more than once, you could save a reference to myButtons = paypal.Buttons({..}) before calling .render() --- and then, later myButtons.close() before saving a new reference to and rendering a new buttons object. But again, there should be no reason to do that here.

PayPal Smart Button Orders API Ignoring notify_url in purchase_units object

We are sending an order using the PayPal Smart Button with their latest JS SDK (February 2019). We have set the notify_url in the purchase_units object in the createOrder function as specified in the Orders API Documentation.
The transaction is still taking the IPN url from the account rather than the one we have provided upon creating the transaction.
We have tried different keys as suggested on stackoverflow such as 'NOTIFYURL', 'NOTIFY_URL', 'notifyurl' and 'notify_url'. None of which worked.
We have tried to remove the IPN from the account settings but this was not possible (notify_url should always override this anyway according to documentation).
paypal.Buttons({
createOrder: function (data, actions) {
return actions.order.create({
intent: "CAPTURE",
purchase_units: [{
amount: {
value: '#Model.Total.ToString("F")',
},
NOTIFYURL: "#notifyUrl"
}]
});
},
onApprove: function (data, actions) {
return actions.order.capture().then(function (details) {
return fetch('/umbraco/surface/PayPalPayment/process', {
method: 'post',
redirect: 'follow',
body: JSON.stringify({
OrderID: data.orderID,
PayerID: data.payerID,
}),
headers: {
'content-type': 'application/json'
}
}).then(function (response) {
response.json().then(function (data) {
window.location.href = data.redirect;
})
});
}).catch(function (error) {
window.location.href = '/umbraco/surface/PaymentFailed/PaymentFailed/?error=' + error;
});
}
}).render('#paypal-button-container');
Note: variables are added via Razor Syntax, I have confirmed that these values are being set correctly in Fiddler. Post is below but the IPN Url has been redacted.
{"intent":"CAPTURE","purchase_units":[{"amount":{"value":"0.01","currency_code":"GBP"},"NOTIFYURL":"https://<REDACTED>/umbraco/surface/paypalipn/receive"}],"application_context":{}}
We should be seeing the notify URL being set but when checking the message ID in the IPN history it is trying to use the IPN url found on the account rather than the notify_url that was provided.
Notify URL is not used with the REST APIs. They don't use IPN. They use Webhooks. You will need to register webhooks in your PayPal app and setup a listener for those hooks accordingly.

CORS error when trying to post message

I have an AngularJS Application I am trying to post a message through. I am successfully able to log the user in, get the access token, and I have ensured I have my domain in the JavaScript Origins within Yammer.
Whenever I try to post a message, however, I get the following error:
The strange thing is when it does the preflight it seems OK but as the error states I can't figure out why it isn't coming back in the CORS header as I have it registered within the Yammer Client area.
Here is the code for posting:
$scope.YammerPost = function (Yammer) {
var _token = Yammer.access_token.token;
var config = {
headers: {
'Authorization': 'Bearer ' + _token
}
};
$http.post('https://api.yammer.com/api/v1/messages.json', { body: 'blah blah', group_id: XXXXXXX }, config);
}
I call that scope variable in the view via a button click.
Here is the logic I use to sign the user in:
function checkYammerLogin() {
$scope.Yammer = {};
yam.getLoginStatus(
function (response) {
if (response.authResponse) {
$scope.Yammer = response;
console.dir(response); //print user information to the console
}
else {
yam.platform.login(function (response) {
if (response.authResponse) {
$scope.Yammer = response;
console.dir(response);
}
});
}
}
);
}
I ended up finding the issue.
For some odd reason, every time I would try to use an $http post it would include an Auth token from AD (app using Azure AD for authentication).
I ended up using jQuery inside of my Angular scope function on the button click and it works as I can control the headers for the request.
$.ajax({
url: 'https://api.yammer.com/api/v1/messages.json',
type: 'post',
data: {
body: 'this is a test from jQuery using AngularJS',
group_id: <group_id>
},
headers: {
'Authorization': _token
},
dataType: 'json',
success: function (data) {
console.info(data);
}
});
Fixed the issue and I can now post.
If anyone sees any issues with this practice please let me know, still a little new to angular

Categories

Resources