I am trying to implement paypal advanced credit and debit cards payments using javascript sdk on client side. I am using the following example as reference https://developer.paypal.com/docs/business/checkout/advanced-card-payments/.
I have created the order, got links as response, how to approve the order using the approve url link as the user is using credit card details for payment without paypal login before performing order capture.
I know that it is not best approach for this implementation. firstly i want to try out this example as given in paypal docs.i will implement it in server side later to avoid security vulnerabilities.
<html>
<head>
<meta charset="utf-8"/>
<!-- Optimal rendering on mobile devices. -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Optimal Internet Explorer compatibility -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- Sample CSS styles for demo purposes. You can override these styles to match your web page's branding. -->
<link rel="stylesheet" type="text/css" href="https://www.paypalobjects.com/webstatic/en_US/developer/docs/css/cardfields.css"/>
</head>
<body>
<!-- JavaScript SDK -->
<script src="https://www.paypal.com/sdk/js?components=buttons,hosted-fields&client-id=abc"
data-client-token="xyz">
<div align="center"> or </div>
<!-- Advanced credit and debit card payments form -->
<div class="card_container">
<form id="card-form">
<label for="card-number">Card Number</label><div id="card-number" class="card_field"></div>
<div>
<label for="expiration-date">Expiration Date</label>
<div id="expiration-date" class="card_field"></div>
</div>
<div>
<label for="cvv">CVV</label><div id="cvv" class="card_field"></div>
</div>
<label for="card-holder-name">Name on Card</label>
<input type="text" id="card-holder-name" name="card-holder-name" autocomplete="off" placeholder="card holder name"/>
<button value="submit" id="submit" class="btn">Pay</button>
</form>
</div>
<!-- Implementation -->
<script>
let orderId;
// Displays PayPal buttons
paypal.Buttons({
style: {
layout: 'horizontal'
},
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: "1.00"
}
}]
});
},
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
console.log(details)
window.location.href = '/success.html';
});
}
}).render("#paypal-button-container");
// If this returns false or the card fields aren't visible, see Step #1.
if (paypal.HostedFields.isEligible()) {
// Renders card fields
paypal.HostedFields.render({
// Call your server to set up the transaction
createOrder: function () {
return fetch('https://api-m.sandbox.paypal.com/v2/checkout/orders', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer wert'
}
}).then(function(res) {
return res.json();
}).then(function(orderData) {
console.log(orderData)
orderId = orderData.id;
fetch('https://api-m.sandbox.paypal.com/v2/checkout/orders/' + orderId + '/capture/', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer AGeU7jOKBXFPa0Fe_e9Xv3g',
'PayPal-Request-Id': '7b92603e-77ed-4896-8e78-5dea2050476a'
},
"application_context": {
"return_url": "https://google.com"
}
}).then(function(res) {
console.log(res)
return res.json();
})
// return orderId;
});
},
onApprove: function(data) {
console.log(data)
return fetch('/my-server/capture-paypal-transaction', {
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
orderID: data.orderID
})
}).then(function(res) {
return res.json();
}).then(function(details) {
alert('Transaction funds captured from ' + details.payer_given_name);
})
},
styles: {
'.valid': {
'color': 'green'
},
'.invalid': {
'color': 'red'
}
},
fields: {
number: {
selector: "#card-number",
placeholder: "4111 1111 1111 1111"
},
cvv: {
selector: "#cvv",
placeholder: "123"
},
expirationDate: {
selector: "#expiration-date",
placeholder: "MM/YY"
}
}
}).then(function (cardFields) {
document.querySelector("#card-form").addEventListener('submit', (event) => {
event.preventDefault();
cardFields.submit({
// Cardholder's first and last name
cardholderName: document.getElementById('card-holder-name').value,
// Billing Address
// billingAddress: {
// // Street address, line 1
// streetAddress: document.getElementById('card-billing-address-street').value,
// // Street address, line 2 (Ex: Unit, Apartment, etc.)
// extendedAddress: document.getElementById('card-billing-address-unit').value,
// // State
// region: document.getElementById('card-billing-address-state').value,
// // City
// locality: document.getElementById('card-billing-address-city').value,
// // Postal Code
// postalCode: document.getElementById('card-billing-address-zip').value,
// // Country Code
// countryCodeAlpha2: document.getElementById('card-billing-address-country').value
// }
}).then(function (data) {
console.log(orderId)
console.log(data);
fetch('https://api-m.sandbox.paypal.com/v2/checkout/orders/' + orderId + '/capture/', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer AjOKBXFPa0Fe_e9Xv3g'
},
"application_context": {
"return_url": "https://google.com"
}
}).then(function(res) {
console.log(res)
return res.json();
}).then(function (orderData) {
console.log(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 or redirect
alert('Transaction completed!');
})
}).catch(function (err) {
alert('Payment could not be captured! ' + JSON.stringify(err))
});
});
});
} else {
// Hides card fields if the merchant isn't eligible
document.querySelector("#card-form").style = 'display: none';
}
</script>
</body>
</html>
after executing above snippet, getting this as response
{name: "UNPROCESSABLE_ENTITY", details: [{issue: "ORDER_NOT_APPROVED",…}],…} debug_id: "a63fbc3806995" details: [{issue: "ORDER_NOT_APPROVED",…}] 0: {issue: "ORDER_NOT_APPROVED",…} links: [{href: "https://developer.paypal.com/docs/api/orders/v2/#error-ORDER_NOT_APPROVED",…}] 0: {href: "https://developer.paypal.com/docs/api/orders/v2/#error-ORDER_NOT_APPROVED",…} message: "The requested action could not be performed, semantically incorrect, or failed business validation." name: "UNPROCESSABLE_ENTITY