I'm trying to figure out how to get the transaction ID for a payment made using the PayPal SDK. Here is what I have in my JS:
paypal.Buttons({
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: parseFloat( window.my_config.amount ),
currency: 'USD',
},
description: window.my_config.description
}],
commit: true
});
},
onApprove: function(data, actions) {
console.dir({ data: data, actions:actions });
return actions.order.capture().then(function(details) {
console.log({ details: details });
// This function shows a transaction success message to your buyer.
alert('Transaction completed by ' + details.payer.name.given_name);
});
$('#AJAXloadingWrapper').show();
}
}).render('#paypalWrapper');
This is what my debug comes back with:
I can't see the transaction ID in there anywhere. It should be 7CA44490MT363621L (going by the transid in my PayPal account)
How does one go about getting that? In the old system I used to use:
curl https://api.paypal.com/v1/payments/payment/L8EM97CXSYBJ2 -H "Content-Type: application/json" -H "Authorization: Bearer tokenstring"
But that doesn't work. I've tried just changing it to v2:
curl https://api.paypal.com/v2/payments/payment/L8EM97CXSYBJ2 -H "Content-Type: application/json" -H "Authorization: Bearer xxxx"
...but all I get in response to that, is nothing (no error, but no output)
I have tried something based on:
https://developer.paypal.com/docs/checkout/reference/server-integration/get-transaction/#on-the-server
sub get_proper_trans_id {
my $token = encode_base64(qq|$config->{$config->{mode}}->{system_client_id}:$config->{$config->{mode}}->system_secret}|);
$token =~ s/\n//g;
my $trans_id = '50D311011N3466050';
print qq|curl https://$config->{$config->{mode}}->{transaction_endpoint}/v2/payments/payment/$trans_id -H "Content-Type: application/json" -H "Authorization: Bearer $token"\n|;
my $results = `curl https://$config->{$config->{mode}}->{transaction_endpoint}/v2/payments/payment/$trans_id -H "Content-Type: application/json" -H "Authorization: Bearer $token"`;
my $json_vals = decode_json($results);
use Data::Dumper;
print Dumper($json_vals);
}
Which doesn't output anything (no errors either)
UPDATE: As suggested, I'm trying to use purchase_units -> payments -> captures -> id from the outputted results. The issue is that this seems wrong / different to what I'm expecting. When I log into my PayPal, I get:
0NP90326GE709501L
Yet the value in the returned data comes up as:
891170207E054363D
Why are these differently? FWIW I'm looking at this in my personal PayPal account (not the business it was sent to). The trans ID's should be the same for both the merchant and customer though, surely?
UPDATE 2: So after all this, I've found a thread that says PayPal intentionally give the buyer and seller different transaction ID's. What a PITA. So the alternative is to pass in a custom invoice_id, which we can cross reference with. All good - apart from I can't seem to do that either!
I've tried:
return actions.order.create({
purchase_units: [{
amount: {
value: parseFloat( window.my_config.amount ),
currency: 'USD',
},
description: window.my_config.description,
}],
invoice_id: invoice_id,
commit: true
});
},
and also:
return actions.order.create({
purchase_units: [{
amount: {
value: parseFloat( window.my_config.amount ),
currency: 'USD',
},
description: window.my_config.description,
invoice_id: invoice_id
}],
commit: true
});
},
But while they go through the process of making the payment ok, it comes back with:
Error: Order could not be captured
..when trying to run return actions.order.capture().then(function(details) {
UPDATE 3:
I still can't get this to work. Is this how you pass a custom_id?
return actions.order.create({
purchase_units: [{
amount: {
value: parseFloat( window.my_config.amount ),
currency: 'USD',
custom_id: invoice_id
},
description: window.my_config.description
}],
commit: true
});
I've tried it there, and also as below commit: true, but it still doesn't work :( ("Order could not be captured" right after confirming payment)
It's in purchase_units -> payments -> captures -> id
See a sample response at https://developer.paypal.com/docs/api/orders/v2/#orders-capture-response
Once knowing the id, there's no reason you would need to do a separate GET request for this same data about it, but if you did the API call would be:
https://developer.paypal.com/docs/api/payments/v2/#captures_get
Note: This is the transaction id stored when you look at a paypal transaction in the paypal app. The following code shows you how to get the transaction ID using paypal javascript sdk. Go to the onApprove function and read the code:
<script>
paypal.Buttons({
// Sets up the transaction when a payment button is clicked
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: '1'
},
}],
application_context: {
shipping_preference: "NO_SHIPPING",
}
});
},
// 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));
//The transaction ID is below this comment in the alert
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>';
actions.redirect('https://wwwwebsite.com/att_thanks.php?id=1640035591');
});
}
}).render('#paypal-button-container');
</script>
Related
I have been struggling with this issue for some time now. Even created a support ticket at my Devs PayPal portal, but even the devs seem to not know what exactly is going wrong. Therefor, I'm asking it here, hoping someone can help me.
The issue: I have a website where people have to check out using iDEAL. To process these iDEAL payments, I use PayPal. Everything works great on desktop, but for some reason, the payments are not being captured when I use my iPhone to pay. I even checked it on different devices, but the issue remains.
What happens when I use my dekstop to pay: after selecting the desired bank, I get redirected to the payment page of my bank. I scan the QR code, make the payment, and get redirected to my own website where I store everything in the DB / show success message. The money has been taken out of my bank and added to my PayPal account. (the payment has been captured)
What happens when I use my phone to pay: after selecting the desired bank, I get redirected to the App to make the payment. Once done so, I get redirected to a 'Thank you' page from PayPal themselfs. The money has been taken out of my bank account, but NOT added to my PayPal account, instead it gone missing (and probably gets refunded by PayPal). - the payment has NOT been captured.
The above is whenever I'm not using the sandbox. However, I'm in the sandbox, the same thing happens: desktop payments are being captured, mobile payments are NOT being captured.
When I check the API calls in my PayPal Dev dashboard, I can see when I'm paying via mobile that the order has been created, but never captured. But the order is being created and captured when I use my desktop.
Any help would be appreciated.
This is the part of my code
// CREATE GLOBAL VARS
var invoice_id = '<?php echo $db_invoice_id; ?>';
function submit_database(transaction_id, transaction_amount, transaction_status) {
// DEBUG
console.log('---BELOW PAYPAL FUNCTION---');
console.log(invoice_id);
console.log(transaction_id);
console.log(transaction_amount);
console.log(transaction_status);
$.ajax({
type: "POST",
url: "/data/betalingen/herinnering/",
data: {invoice_id: invoice_id, transaction_id: transaction_id, transaction_amount: transaction_amount, transaction_status: transaction_status},
success: function(data,textStatus,jqXHR){ start_processingIDEALPayment(data,textStatus,jqXHR); }
});
function start_processingIDEALPayment(data,textStatus,jqXHR) {
// DEBUG
//alert(data);
console.log(data);
if (data == 'success') {
$('.access-purchase .actions').hide();
$('.access-purchase .content-inner .description').removeClass('alert-danger');
$('.access-purchase .content-inner .description').addClass('alert-success');
$('.access-purchase .content-inner .description').html('<i class="fa-solid fa-check"></i> Wij hebben je betaling succesvol ontvangen. De pagina wordt ieder moment ververst, waarna je toegang zult hebben tot de stream.');
setTimeout(function (){
//Reload page
location.reload();
}, 5000);
}
}
}
paypal.PaymentFields({
fundingSource: paypal.FUNDING.IDEAL,
fields: {
// PREFILL NAME FIELD
name: {
value: "<?pho echo $db_name; ?>",
},
}
})
.render("#ideal-fields");
paypal.Buttons({
fundingSource: paypal.FUNDING.IDEAL,
style: { label: "pay", },
createOrder: (data, actions) => {
return actions.order.create({
"purchase_units": [{
"amount": {
"currency_code": "EUR",
"value": "<?php echo $next_amount_to_pay; ?>",
"breakdown": {
"item_total": {
"currency_code": "EUR",
"value": "<?php echo $next_amount_to_pay; ?>"
}
}
},
"items": [
{
"name": "Betaling voor factuur: #<?php echo $db_invoice_id; ?>",
"unit_amount": {
"currency_code": "EUR",
"value": "<?php echo $next_amount_to_pay; ?>"
},
"quantity": "1"
},
],
}]
});
},
onApprove(data, actions) {
return actions.order.capture().then(function(orderData) {
// DEBUG
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
// CREATE VARS
var transaction = orderData.purchase_units[0].payments.captures[0];
var transaction_id = transaction.id;
var transaction_amount = transaction.amount.value;
var transaction_status = transaction.status;
// DEBUG
console.log(transaction_id);
console.log(transaction_amount);
console.log(transaction_status);
// SUBMIT TO DB
submit_database(transaction_id, transaction_amount, transaction_status);
// SHOW MESSAGE TO USER
$('.box.invoice-payment').hide(); // HIDE PAYMENT FORM
$('#openPaymentHandler').hide(); // HIDE PAYMENT FORM
$('#payment-callback').removeClass('alert-danger');
$('#payment-callback').addClass('alert-success');
$('#payment-callback').html('<i class="fa-solid fa-check"></i> Wij hebben je betaling in goede orde ontvangen.');
$('#payment-callback').show();
setTimeout(function (){
//Reload page
location.reload();
}, 3500);
});
},
onCancel(data, actions) {
// CANCELED
//DEBUG
console.log(`Order Canceled - ID: ${data.orderID}`);
var transaction_id = data.orderID;
var transaction_amount = '0.00';
var transaction_status = 'cancelled';
// SUBMIT TO DB
submit_database(transaction_id, transaction_amount, transaction_status);
// SHOW MESSAGE TO USER
$('#payment-callback').removeClass('alert-success');
$('#payment-callback').addClass('alert-danger');
$('#payment-callback').html('<i class="fa-regular fa-circle-xmark"></i> De betaling is afgebroken of mislukt.');
$('#payment-callback').show();
},
onError(err) {
// ERROR
// DEBUG
console.error(err);
if (err == 'Error: Detected popup close') {
var errorMsg = '<i class="fa-regular fa-circle-xmark"></i> De betaling is mislukt, omdat het betaalvenster is afgesloten.';
} else {
var errorMsg = '<i class="fa-regular fa-circle-xmark"></i> De betaling is afgebroken of mislukt.';
}
// SHOW MESSAGE TO USER
$('#payment-callback').removeClass('alert-success');
$('#payment-callback').addClass('alert-danger');
$('#payment-callback').html(errorMsg);
$('#payment-callback').show();
alert(err);
},
}).render("#ideal-btn");
I would like to:
Have the user log into paypal
Fetch the users information (billing&shipping addresses)
Then calculate the shipping fee
Place the payment
But, how can I receive the address and calculate the fee before I place the payment?
<script>
paypal.Button.render({
// Configure environment
env: '<?php echo $paypal->paypalEnv; ?>',
client: {
sandbox: '<?php echo $paypal->paypalClientID; ?>',
production: '<?php echo $paypal->paypalClientID; ?>'
},
// Customize button (optional)
locale: 'de_DE',
style: {
size: 'small',
color: 'gold',
shape: 'rect',
},
// Set up a payment
payment: function (data, actions) {
return actions.payment.create({
transactions: [{
amount: {
total: '10',
currency: 'EUR'
}
}]
});
},
// Execute the payment
onAuthorize: function (data, actions) {
return actions.payment.execute()
.then(function () {
window.location = "process.php?paymentID="+data.paymentID+"&token="+data.paymentToken+"&payerID="+data.payerID+"&pid=<?php echo $productID; ?>";
});
}
}, '#paypal-button');
</script>
Instead of "return actions.payment.execute()" calling the execute directly, I would like to fetch the address, then show the final basket with all the costs and then click on "pay now" - how can I do this?
Your sample is an older version of PayPal's checkout.js
Start by switching to the latest version of the PayPal Checkout: https://developer.paypal.com/docs/checkout/
Demo of client-side pattern: https://developer.paypal.com/demo/checkout/#/pattern/client
In the onApprove function, you can use the data object to get the address and do Javascript calculations about adding shipping before the capture() call with a modified total.
If you want to do the calculations on your server rather than in Javascript (more secure/reliable), use a server-side pattern:
https://developer.paypal.com/demo/checkout/#/pattern/server
thank you! mhhh okay but what has to be in the serverside part? i dont see any example of the actions to receive the buyers adress information before checkout and afterwards. ?
/demo/checkout/api/paypal/order/create
I use HostedFields for credit card and paypal web for paypal.
So inside of braintree.client.create i have braintree.hostedFields.create and braintree.paypalCheckout.create.
Code example of paypal:
braintree.paypalCheckout.create({
client: clientInstance
}, function (paypalCheckoutErr, paypalCheckoutInstance) {
if (paypalCheckoutErr) {
console.error('Error creating PayPal Checkout:', paypalCheckoutErr);
return;
}
paypal.Button.render({
env: 'sandbox',
commit: true,
buttonStyle: {
color: 'blue',
shape: 'rect',
size: 'medium'
},
payment: function () {
return paypalCheckoutInstance.createPayment({
flow: 'checkout',
locale: ''+langanalytics+'',
amount: '10.00',
currency: 'EUR'
});
},
onAuthorize: function (data, actions) {
return paypalCheckoutInstance.tokenizePayment(data, function (err, payload) {
document.getElementById("paynonce").value = payload.nonce;
document.getElementById("paymentform").submit();
});
},
onCancel: function (data) {
console.log('checkout.js payment cancelled', JSON.stringify(data, 0, 2));
},
onError: function (err) {
console.error('checkout.js error', err);
}
}, '#paypal-button').then(function () {
});
});
The problem is according to my client he want a single page checkout, where the amount get generated dynamically.
For example if someone change the country or payment method the price can change for some €. A click EventListener start the calculation and changes the total sum in real time.
My Problem:
I cant change the amount of my paypal intialization of this part:
return paypalCheckoutInstance.createPayment({
flow: 'checkout',
locale: ''+langanalytics+'',
amount: '10.00',
currency: 'EUR'
});
Is there any way to update the amount outsite of the function as soon as total sum changes (i have full control of this).
I already tried to intialize the whole braintree.client.create new on total sum change but i get errors from hosted fields a instance is already created and for paypal i recieve multiple buttons.
I want to accept payment in Multiple currencies & also autofill the Address/Name in PayPal form using Express Checkout method
I have tried going through various posts, paypal getting started, paypal community, but not able to find if it possible.
Currently, using Express Checkout I'm able to receive payment on changing the get parameter of the script.
<script src="https://www.paypal.com/sdk/js?client-id=valueOfClientID¤cy=GBP"> </script>
Is is possible to pass the currency & name/address details in below code?
<script>
paypal.Buttons({
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: '24'
}
}]
});
},
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('/paypal-transaction-complete', {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
orderID: data.orderID
})
});
});
}
}).render('#paypal-button-container');
</script>
Based on the currency dynamically inject the script to the page and pass the address object as mentioned in the link
Hello I am using the following setup
Created paypal merchant and customer sandbox accounts
Configured paypal REST API app
Added a webhook url to my server and have validated that it works using the webhook simulator
Used the Express Checkout javascript implementation found here
I am able to make successful payments when viewing the notifications in sandbox but no webhook is ever triggered???
Below is a sample of my javascript implementation that I have used, please not that it's embedded in a coldfusion script file hence the use hashtags.
`
var items = #paypalItems#;
// Render the PayPal button
paypal.Button.render({
env: '#application.config.paypal.bSandbox ? "sandbox" : "production"#', // sandbox | production
commit: true,
//style the button
style: {
label: 'pay'
},
// PayPal Client IDs - replace with your own
client: {
sandbox: '#application.config.paypal.sandbox_key#',
production: '#application.config.paypal.live_key#'
},
// Wait for the PayPal button to be clicked
payment: function(data, actions) {
// Make a client-side call to the REST api to create the payment
return actions.payment.create({
payment: {
transactions: [{
amount: {
total: #trim(numberFormat( application.oCart.getTotal(bDiscount=1,bShipping=1) , "99999999.99" ))#,
currency: "AUD",
details: {
subtotal: #trim(numberFormat( application.oCart.getTotal() - application.oCart.getAmountGST( amount=application.oCart.getTotal(bDiscount=1), addGST=false ), "99999999.99" ))#,
tax: #trim(numberFormat(application.oCart.getAmountGST( amount=application.oCart.getTotal(bDiscount=1), addGST=false ), "99999999.99" ))#,
shipping: #trim(numberFormat( application.oCart.oShipping.getCartShippingAmount(country=session.fcbshoppingCart.order.shippingCountry), "99999999.99" ))#
}
},
invoice_number: "#orderNumber#",
item_list: {
items: items,
shipping_address: {
recipient_name: "#session.fcbshoppingCart.customer.firstName# #session.fcbshoppingCart.customer.lastName#",
line1: "#session.fcbshoppingCart.order.shippingAddress1#",
line2: "#session.fcbshoppingCart.order.shippingAddress2#",
city: "#session.fcbshoppingCart.order.shippingSuburb#",
country_code: "#paypalCountryCode#",
postal_code: "#session.fcbshoppingCart.order.shippingPostCode#",
state: "#session.fcbshoppingCart.order.shippingState#"
}
}
}]
}
});
},
// Wait for the payment to be authorized by the customer
onAuthorize: function(data, actions) {
console.log( "Paypal Authorize:", data );
// Execute the payment
return actions.payment.execute().then(function(payment) {
console.log( "Paypal Response:", payment );
//payment has been accepted so we can now generate an order
$.ajax({
type: "get",
url: "/apps/paypal/createOrder.cfm",
data: {
transactionNumber: "#orderNumber#",
payPalPaymentId: data.paymentID
},
dataType: "json",
success: function( res ) {
console.log('edharry create order data', res);
if( res.BPAYMENTPROCEED ) {
$('##paypal-message').addClass("show success").text('Payment Successfully Complete!');
//lets redirect to the checkout success page.
window.location.href = window.location.origin + '/shop/checkout/confirmation?productOrder=' + res.PRODUCTORDER.OBJECTID;
} else {
//need to handle a failed transaction
$('##paypal-message').addClass("show failure").text('Payment did not complete on server!');
}
},
error: function() {
//lets show an error
$('##paypal-message').addClass("show failure").text('Payment did not complete on server!');
}
})
$('##paypal-message').addClass("show success").text('Payment Successfully Complete!');
});
},
onCancel: function(data) {
console.log('The payment was cancelled!');
}
}, '##paypal-button-container');`
This is an ongoing issue with Paypal. They are aware of this issue and are currently working to resolve this.