PayPal allows adding a delivery address street without a number.
As far as I know, it is not possible to check this behavior during the order process before the payment is finished.
The order data is available in the onApprove function scope.
But that's too late, because the payment is finished then?
Is there a way to add address validation?
<script src="https://www.paypal.com/sdk/js?client-id=abc123&disable-funding=credit,card,sepa,giropay¤cy=EUR"></script>
<script>
paypal.Buttons({
createOrder: function(data, actions) {
return fetch('/paypal/order/create/', {
method: 'post'
}).then(function(res) {
return res.json();
}).then(function(orderData) {
if (orderData.error)
{
return actions.reject();
}
return orderData.id;
});
},
onShippingChange: function(data, actions) {
if (data.shipping_address.country_code !== 'DE')
{
return actions.reject();
}
return actions.resolve();
},
onApprove: function(data, actions) {
return fetch('/paypal/order/' + data.orderID + '/capture/', {
method: 'post'
}).then(function(res)
{
return res.json();
}).then(function(orderData) {
let re = /[0-9]/;
// test
if (re.exec(orderData.payer.address.address_line_1) === null)
{
// cancle payment on this place is to late, because payment is finished
}
var errorDetail = Array.isArray(orderData.details) && orderData.details[0];
if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
return actions.restart();
}
if (errorDetail)
{
return;
}
return fetch('/paypal/finish-order/' + data.orderID + '/',{
headers: {
'Content-Type' : 'application/json',
},
body: JSON.stringify(orderData),
method: 'post'
}).then(function(result){
let dat = result.json();
return dat;
}).then(function(data)
{
if (data.error)
{
actions.reject();
}
if (data.destination)
{
actions.redirect(data.destination)
}
});
});
},
funding: {
allowed: [ paypal.FUNDING.CARD ],
disallowed: [ paypal.FUNDING.CREDIT ]
},
style: {
size: 'medium',
height: 40,
label: 'checkout'
}
}).render('#ppbut');
</script>
When onApprove is called, the payment is not finished. The order still needs to be captured.
Before capture, you can show an order review step if desired (for the customer to verify a new total, for example, or select from available shipping methods). This step can be done by updating the DOM dynamically (a redirect can also work but is best avoided).
If the address is invalid, show an error message accordingly and give an option to restart; do not proceed with any capture. You do not need to void the order (there is no method), simply forget about that order id and never capture it.
You can patch the order to a new total as needed (automatically, or based on selections in the review step you add).
Finally, proceed with the capture as normal and show success.
If you do add a user review step after approval, you should also change the last button at PayPal to say "Continue" instead of the default "Pay Now" to match your behavior. This is only a text change, the behavior only changes based on what you do in onApprove, which must correspond. To make the text change, add an application_context object to the top level (not inside purchase_units) of your order creation request, in which you specify a user_action parameter with the value CONTINUE.
Related
I'm making an investment fundraising page for a small business and I'm trying to create a custom PayPal button where the amount to give is set by a slider. The reason for this is that the business doesn't want to take loans less than a certain amount, but they don't want to restrict people to a limited number of choices (most PayPal buttons give people limited options for donation amounts). They want to be able to choose any amount from $500-$38,000. The idea is that they'll select this amount with a slider, then click a button that takes them to a payment screen for that amount. The code is on my GitHub at https://github.com/cbarboza2011/funkychickenfoodtruck. The slider and paypal button on are on donate.html
I've looked into the PayPal Docs, but I'm really new to backend stuff and so I'm not sure how to implement the information provided. I think I need to somehow access the HTML variable amount but I'm not sure how to do so. Currently, the slider has no effect on the resulting paypal page; users are taken to a page showing "$0.01". I want the slider to set the amount so that when a user clicks the paypal button, a payment page with their chosen amount comes up instead of a page showing "$0.01".
NOTE: The donate.html page is only currently styled for mobile, so view in devtools mobile view on Chrome if possible, otherwise everything will be enormous in a regular desktop window. It will still work, but it's huge.
$('.slider').slider({
max: 38000,
min: 500,
step: 100,
value: 500,
});
function getLoanAmount() {
var loanAmount = $('.loanamount');
var $selection = $( ".slider" ).slider( "value" );
$(loanAmount).text($selection);
}
getLoanAmount();
$('.slider').on('slide', function() {
getLoanAmount();
});
$(".slider").on( "slidestop", function() {
var $amount = $('.loanamount').text();
$( ".slider" ).slider( "option", "value", $amount );
$('a.paypalbutton').attr('href', 'https://www.paypal.com/donate?hosted_button_id=LR2YL2LXSYSTQ&amount=' + $amount);
});
$('button.submitloan').on('click', function() {
$('.loan-setter').slideUp();
$('#smart-button-container').css('display', 'block');
});
<div class="slider"></div>
<!-- PayPal Widget -->
<a href="https://www.paypal.com/donate?hosted_button_id=LR2YL2LXSYSTQ" class="paypalbutton">
<img src="https://www.paypalobjects.com/webstatic/en_US/i/btn/png/btn_buynow_107x26.png" alt="Buy Now" />
</a>
UPDATE: I've tried modifying the PayPal JS, but I don't know how to check if it works?
function initPayPalButton() {
paypal.Buttons({
style: {
shape: 'pill',
color: 'white',
layout: 'vertical',
label: 'paypal',
},
createOrder: function(data, actions) {
var $amount = $('.loanamount').text();
$('.dollaramount').text($amount);
return actions.order.create({
purchase_units: [{"amount":{"currency_code":"USD","value": $amount}}]
});
},
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
alert('Transaction completed by ' + details.payer.name.given_name + '!');
});
},
onError: function(err) {
console.log(err);
}
}).render('#paypal-button-container');
}
initPayPalButton();
For a classic HTML-only button that redirects:
Use an unhosted button (no hosted_button_id), of type Donate or Buy Now.
Go to https://www.paypal.com/buttons , sign in, start creating your button, but before creating it ensure that in Step 2 you uncheck the option to Save the button at PayPal, so it is unhosted.
Once you generate your code, click above it to remove the code protection.
Now you have code that can be copied and edited, or you can use the 'Email' tab if you prefer a URL. The input parameter to add/modify is "amount", as documented here.
For a better JS in-context payment experience, use a smart checkout button; here is an example implementation: https://developer.paypal.com/demo/checkout/#/pattern/client
I got the new amount from your slider from copying the $selection var of your getLoanAmount function. Its originally a var called AmountNumeral because its a number value. Then $amount uses toString() just to make it a text string so that you can pass it into your paypal URL as the amount you want to show up. The console logs can just show how it formats it from numeral to string if that helps in seeing the difference
The amount on paypal still shows up as $0.00 initially. I read that if your paypal donate button is "hosted" then its impossible to change the amount value or any preset numbers as a safety precaution. Not sure if your paypal button was setup as "hosted" or "unhosted" but I would check that out maybe.
I haven't worked with PayPal before I just quickly skimmed through some answers about it and their website. But this should fix your updated slider value at least.
Pre-populate Donation Amount on Website
function getLoanAmount() {
var loanAmount = $('.loanamount');
var $selection = $( ".slider" ).slider( "value" );
$(loanAmount).text($selection);
console.log(($selection));
console.log($(loanAmount).text($selection));
}
getLoanAmount();
$('.slider').on('slide', function() {
getLoanAmount();
});
$(".slider").on( "slidestop", function() {
var $amountNumeral = $( ".slider" ).slider( "value" );
console.log($amountNumeral);
var $amount = $amountNumeral.toString();
console.log('$amount');
$( ".slider" ).slider( "option", "value", $amount );
$('a.paypalbutton').attr('href', 'https://www.paypal.com/donate?hosted_button_id=LR2YL2LXSYSTQ¤cy_code=USD&amount=10.00');
});
I'm trying to add a popup that will be shown on a page when the user starts to populate data in the form and then he decided to go somewhere else in the app.
This popup will show this message: 'Do you want to leave this page and save your changes?'
three buttons are available: Stay, Leave and Save before leaving.
I'm new to ionic logic and I couldn't figure out how to do this.
I started by adding a button in the page that shows the popup (Still don't know how to trigger the event when the user clicks on any link of the sidebar for example). when the user clicks on that button the popup is shown with the three buttons.
The problem is that I don't know how to implement the handlers of these buttons.
This is what I have in the ts file :
leaveOrStayModal() {
let e = event || window.event;
e.stopPropagation();
this.alertMixin.presentAlert(
'Do you want to leave this site?\n',
"You haven't saved your changes!",
'Stay',
'Leave',
'Save',
null,
() => {
console.log('leave handler')
// this.navCtrl.push() Here I don't know how to get the exact link clicked from the sidebar ? to go to
},
() => {
console.log('Save handler')
//here I want to save the form ?
}
)
}
the popup code:
presentAlert(title: string, message: string, btnOneText: string, btnTowText: string, btnThreeText: string,
btnOneHandler?: () => void, btnTowHandler?: () => void, btnThreeHandler?: () => void,
present: boolean = true) {
let confirm = this.alertCtrl.create({
title: title,
message: message,
buttons: [
{
text: btnOneText,
handler: () => {
if (btnOneHandler) {
btnOneHandler();
}
}
},
{
text: btnTowText,
handler: () => {
if (btnTowHandler) {
btnTowHandler();
}
}
},
{
text: btnThreeText,
handler: () => {
if (btnThreeHandler) {
btnThreeHandler();
}
}
}
]
});
if (present) {
confirm.present().then();
}
return confirm;
}
And this is the button that shows the popup (to be removed )
<button ion-button icon-left item-right type="button" (click)="leaveOrStayModal()"> Click to show modal </button>
You should leverage life cycle hook ionViewCanLeave for that. Some basic documentation here: https://ionicframework.com/docs/api/navigation/NavController/
For your context I just drafted the way I would do it (its a bit dirty):
userCanLeave = false;
ionViewCanLeave() {
// here you can use other vars to see if there are reasons we want to keep user in this page:
if (!this.userCanLeave) {
return new Promise((resolve, reject) => {
let alert = this.alertCtrl.create({
title: 'Are you sure?',
message: 'The form data may be lost',
buttons: [
{
text: 'Stay',
role: 'cancel',
handler: () => {
console.log('User stayed');
this.userCanLeave = false;
reject();
}
},
{
text: 'Leave',
handler: () => {
console.log('User leaves');
this.userCanLeave = true;
resolve();
}
},
{
text: 'Save',
handler: () => {
console.log('User saved data');
// do saving logic
this.userCanLeave = true;
resolve();
}
}
]
});
alert.present();
});
} else { return true }
}
userCanLeave - here is just example of a var that defines if the page has the state where we would not want a user to leave "freely".
then we use promise to ensure that user can not leave without answering dialogue options, we wait for their answers to define whether life cycle hook gets true/false flag to proceed.
Please note also that this life cycle hook only "kicks in" when a view (page) gets off the stack (pops) if you would push in a new view - it won't guard that. But in this case new pushed in page won't destroy user's data in the form anyway and user can safely return to it once you dismiss that newly pushed in page.
Hope this helps.
I have a link that opens a dialog modal asking for a date. When they click submit, javascript takes the form data, generates an ajax call, and returns the response. This works no problem. However if I immediately click the same link again and submit a new date in the form, I get the results from my first ajax POST.
Essentially, subsequent ajax calls are using the original POST data and nothing new. Code has alerts for troubleshooting. Im assuming im setting some var thats not getting reset, but thought this event handler was canceled with the "off", then re-added immediately after and the vars would be in scope to that function alone.
<script>
//Modal submit
$(document).off('click', '#SubmitAllChecks');
$(document).on('click', '#SubmitAllChecks', function(e) {
e.preventDefault();
var form = $('#AllChecks');
var url = form.attr('action');
var method = form.attr('method');
var data = form.serializeArray();
$.each(data, function(k,v) {
alert(v.name + ' : ' + v.value);
});
$.ajax({
url: url,
type: method,
data: data,
dataType: 'json',
beforeSend: function() {
//add load indicator
window.erpui.startload();
},
success: function(data) {
alert('xhr complete');
$.each(data, function() {
alert(this.value + ' data');
if (this.value == 'error' && this.msg != '') {
window.erpui.endload();
window.erpui.notify.error(this.msg);
window.erpui.notify.commit();
}
else if (this.value == 'success') {
window.erpui.endload();
window.erpui.notify.success(this.msg);
window.erpui.notify.commit();
$('.ui-dialog').remove();
//window.location.href="{% url all_checks %}";
//window.location.reload();
}
});
},
error: function() {
alert('error');
//remove load indicator
window.erpui.endload();
}
});
});
</script>
So I'm trying to get the select2 plugin to work with a Backbone.js / CakePHP app. The idea is that this select2 holds email addresses for contacting people as tasks become completed, but the form is editable. What I want to do is (1) load / display all the already saved email addresses for the task being edited, and (2) I want to still have the select2 perform AJAX searches to list recognized emails.
I keep having this issue where I can either show initial data, OR have the AJAX search feature.
My current code for my select2 box is a Backbone.View, and it looks like:
define([
'backbone',
'jquery',
'jquery.select2'
],
function(Backbone, $, select2) {
var notificationSelector = Backbone.View.extend({
notifications: undefined,
events: {
'change' : 'select2ContactsChanged'
},
initialize: function(attrs) {
this.collection.on('add remove reset', this.render(), this);
this.select2ContactsChanged();
},
render: function() {
var contacts = ["abc#def.com", "joe#banana.com"];
$('.notification-selector').attr('value', contacts);
if(this.select2Control == undefined)
{
// Do Search() + query here
this.select2Control = this.$el.select2({
width: '200px',
placeholder: '#email',
tags: [],
minimumInputLength: 3,
// initSelection: function(element, callback) {
// return $.ajax({
// type: "GET",
// url: "/notifications/fetch/",
// dataType: 'json',
// data: { id: (element.val()) },
// success: function(data) {
// }
// }).done(function(data) {
// console.log(data);
// });
// },
});
}
else
{
// Do Search() + query here
this.select2Control = this.$el.select2({
width: '200px',
placeholder: '#email',
tags: [],
minimumInputLength: 3,
ajax: {
url: '/notifications/search/',
dataType: 'json',
data: function(term, page) {
return {
SearchTerm: term
};
},
results: function(data, page) {
return {
results: data
};
}
}
});
}
},
select2ContactsChanged: function() {
var contacts = this.select2Control.val().split(',');
this.collection.reset(contacts);
}
});
return notificationSelector;
});
I read a response by the creator of Select2 to someone else (https://github.com/ivaynberg/select2/issues/392) in which he says to use a 'custom query' to achieve what seems to be what I want. I'm having trouble finding relevant examples or making enough sense of the docs to figure out what he means.
Can anyone spot what I'm doing wrong / missing?
Thanks for your time!
EDIT
I forgot to mention -- the DOM element this is attached to is <input type="hidden" multiple="true" class="notification-selector select2-result-selectable"></input>
Ok, I finally figured out the solution.
I was misunderstanding $.ajax() -- I did not really think about it actually being an asynchronous call. My code to check for the data being returned from the call was running before the AJAX actually finished, so I was always getting undefined.
I assigned a variable to the AJAX call, and set "async: false", and it worked perfectly.
fetchSetNotifications: function() {
var addresses = $.ajax({
method: 'GET',
dataType: 'json',
context: $('#notifications'),
url: '/Notifications/fetch/',
async: false,
alert(addresses);
}
The jqXHR object I get in 'addresses' then contains the response data I want in the "responseText" attribute.
I am using form editing but I only want the ADD form to appear if the results from dataURL are correct. I can hide it, but I really don't want it at all on condition. Plus, the hide() only works after the alert is cleared
$("#schedule").jqGrid('editGridRow', "new", {
url: './ar_schedule_update.cgi?',
editData: {visitor:visitor},
beforeInitData: function() {
$('#schedule').setColProp('archiveid',{editable: true,hidden:false, edittype: 'select',
editoptions: {dataUrl: './ar_archiveid_edit_options.cgi?system=' + selected_system,
buildSelect: function(data) {
if (data.match(/^ERROR/)) {
$('#editmodschedule').hide(); //Makes it disappear ok after alert cleared
alert(data);
return false;
}
return data;
}
}
});
},
beforeShowForm: function(formid) {
//NEED TO EVALUATE CONDITION HERE? AND BAIL IF ERROR
},
onClose: function() {...............
Thanks in advance,
Mike