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");
Related
With the below code, along with the PayPal button a "PayPal Credit" button is shown. But I want to display Debit or Credit card black button instead of that PayPal Credit button. Please help me to find out the solution. I don't want to change anything in script tag, is there any change to be made?
<form class="form-horizontal">
<div class="form-group">
<!-- PayPal Shortcut Checkout -->
<div id="paypalCheckoutContainer"></div>
</div>
</form>
//Javascript
<script src="https://www.paypalobjects.com/api/checkout.js"></script>
<script type="text/javascript">
paypal.Button.render({
// Set your environment
env: '<?= PAYPAL_ENVIRONMENT ?>',
// Set style of buttons
style: {
layout: 'vertical', // horizontal | vertical
size: 'medium', // medium | large | responsive
shape: 'pill', // pill | rect
color: 'gold', // gold | blue | silver | black,
fundingicons: false, // true | false,
tagline: false, // true | false,
},
// Set allowed funding sources
funding: {
allowed: [
paypal.FUNDING.CARD,
paypal.FUNDING.CREDIT
],
disallowed: [ ]
},
// Show the buyer a 'Pay Now' button in the checkout flow
commit: false,
// payment() is called to start the payment flow when a button is clicked
payment: function() {
const postData = {
'original': JSON.parse('<?= json_encode($orderDetails) ?>'),
'update': null,
'flow': 'shortcut'
};
return request.post(
'<?= $rootPath.URL['services']['orders']['create'] ?>',
postData
).then(function(returnObject) {
return returnObject.data.id;
});
},
// onAuthorize() is called when the buyer approves the payment
onAuthorize: function(data) {
const postData = {
key: "order_id",
value: data.orderID
};
submitForm('<?= $baseUrl.URL['redirect']['orders']['return_url'] ?>?flow=shortcut', postData);
},
// onCancel() is called when the buyer cancels payment authorization
onCancel: function(data) {
let url = "<?= $baseUrl ?>pages/orders/error?type=error",
postData = {
key: "error",
value: data
};
submitForm(url, postData);
},
// onError() is called when there is an error in this Checkout.js script
onError: function (error) {
let url = "<?= $baseUrl ?>pages/orders/error?type=error",
postData = {
key: "error",
value: error
};
submitForm(url, postData);
}
}, '#paypalCheckoutContainer');
</script>
You are using the old "v4" checkout.js (old demo site). Upgrade to a current PayPal Checkout integration (which uses the "v5" JS SDK), and the black button will be available.
Since you appear to be using a backend for initial payment/order creation and later execution/capture , this front-end approval code has the best sample to work from: https://developer.paypal.com/demo/checkout/#/pattern/server
Since you ask about disabling the PayPal Credit button, for v5 the simplest way is to add &disable-funding=credit on the SDK src line
Is it possible to sell some non-physical product and get its payment directly into your personal paypal account? Has anyone did something like this?
It is certainly possible, and actually very easy. What kind of integration are you looking for? Maybe one of the buttons at http://www.paypal.com/buttons would be a good start for you.
Here is a sample of a VERY Simple checkout Script using REST API (Client Side Only Code)
First add this line to "head" of your HTML page:
{
<script src="https://www.paypal.com/sdk/js?client-
id=YOUR USER ID GOES HEREg&disable-funding=credit,card"></script>
}
Then add this to where you want your PayPal Button(s) to be on your HTML Page:
'<div style="width:30%" id="paypal-button-container"><p style="font-size:small">
<b>Checkout using your PayPal Acount <br>
or if you do not have a PayPal Account, you can pay with your Debit or Credit
Card (option at bottom of Pop-up Window).</b></p></div>
<script>
// Render the PayPal button into #paypal-button-container
paypal.Buttons({
onError: function (err) {
alert('An Error Occured, returning you to the Form. Please Check that you
are not submitting a Zero Amount and have Filled out the Form');
},
style: {
layout: 'horizontal',
color: 'gold',
shape: 'pill',
label: 'checkout',
size: 'responsive',
tagline: 'true',
},
// Set up the transaction
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
description: 'GnG Order',
amount: {
value: cartTotal
}
}]
});
},
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
// Show a success message to the buyer
alert('Transaction completed by ' + details.payer.name.given_name +
'!');
});
if (details.error === 'INSTRUMENT_DECLINED') {
return actions.restart();
};
}
}).render('#paypal-button-container');
</script>'
That should do it for you. Be sure to login to the Developer Panel to set up your API credentials.
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.
I'm working with .Net, trying to implement multiple buttons.
I'm getting an answer from PayPal (payment id, payer-id, etc.), but everything is client-side. How can I check the payment on server-side?
Do I need to implement all this code for each button?
<script>
paypal.Button.render({
env: 'production', // Optional: specify 'sandbox' environment
client: {
sandbox: 'xxxxxxxxx',
production: 'xxxxxxxxx'
},
payment: function() {
var env = this.props.env;
var client = this.props.client;
return paypal.rest.payment.create(env, client, {
transactions: [
{
amount: { total: '1.00', currency: 'USD' }
}
]
});
},
commit: true, // Optional: show a 'Pay Now' button in the checkout flow
onAuthorize: function(data, actions) {
// Optional: display a confirmation page here
return actions.payment.execute().then(function() {
// Show a success page to the buyer
});
}
}, '#paypal-button');
</script>
To get the information on the server side, you can pass data.paymentID to your server side and use it to make a REST call to paypal: https://developer.paypal.com/docs/integration/direct/express-checkout/integration-jsv4/advanced-payments-api/show-payment-details/
To render multiple buttons you do need to call paypal.Button.render() multiple times, but if you need to you can do this in a for loop, or something.
I'm trying to integrate Paypal Express Checkout into simple Shopping Cart. There are different ways to do that. Paypal recommends to choose between Basic or Advanced integration and version 4.0 of checkout.js (with REST API). So far so good.
I created Paypal App in my Paypal account to get credentials and start testing it.
The test was OK, but there are some misunderstandings here.
Checkout.js send the amount ( 1.00 ) and currency ( EUR ) to the Paypal servers via REST API (along with my credentials). And if the payment is finished OK - callback function onAuthorize is triggered and there are two parameters with response (data and actions). Well, here I call my own AJAX function to write transaction response data in my database. BUT... I get here only PaymentID and PayerID of the paid transaction?!! And if I want to search later into web interface of paypal.com - there is no such thing as PaymentID. There is only TransactionID ??? How to get other transaction details in the response in onAutorize callback function? How can I get TransactionID here to write down in my database? May be here I have to call Paypal API, or have to implement Paypal IPN (instant payment notification )? BUT how to call IPN API, if I don't have TransactionID :)
<div style="width: 906px; text-align: right; height: 100px;
margin-top: 50px;">
<div id="paypal-button"></div>
</div>
<script src="https://www.paypalobjects.com/api/checkout.js"></script>
<script>
paypal.Button.render({
env: 'production', // Optional: specify 'sandbox' environment
style: {
size: 'medium',
shape: 'rect'
},
client: {
sandbox: 'xxx-my-credentials-xxx',
production: 'xxx-my-credentials-xxx'
},
payment: function() {
var env = this.props.env;
var client = this.props.client;
return paypal.rest.payment.create(env, client, {
transactions: [
{
amount: { total: '1.00', currency: 'EUR' }
}
]
});
},
commit: true, // Optional: show a 'Pay Now' button in the checkout flow
onAuthorize: function(data, actions) {
// Optional: display a confirmation page here
var EXECUTE_PAYMENT_URL = 'payment-process.php';
paypal.request.post(EXECUTE_PAYMENT_URL, { paymentID: data.paymentID, payerID: data.payerID, transactionID: data.transactionID, data: data }) .then(function(data) { }) .catch(function(err) { });
return actions.payment.execute().then(function() {
// Show a success page to the buyer
});
}
}, '#paypal-button');
</script>
To read the information from the transaction you need to call and save data JSON in database
return actions.payment.execute().then(function() {
actions.payment.get().then(function(data) {
if(data.state === 'approved'){
console.log(data);
var transactionId = data.id;
alert("Transaction ID: "+ transactionId + " \n State: " +data.state);
}else{
console.log(data);
}
});
});