braintree_gateway/forms/braintree.js
/*global braintree:true, paypal:true*/
( function ( $, mw ) {
/* Paypal iFrame comes with a zIndex of 100. Inorder to stack the autocomplete
menu above the Paypal button, we need to set the zIndex to be at least a step higher.
The mediawiki library for the menu item sets the autocomplete menu zIndex to 1 step higher
than the parents zIndex here: resources/lib/jquery.ui/jquery.ui.autocomplete.js::484
*/
$( '#employer' ).css( { position: 'relative', zIndex: 100 } );
var di = mw.donationInterface,
myDeviceData,
payment_method = $( '#payment_method' ).val();
$( '.submethods' ).before( '<div id="' + payment_method + '-button"></div>' );
function handleApiResult( result ) {
if ( result.isFailed ) {
document.location.replace( mw.config.get( 'DonationInterfaceFailUrl' ) );
} else if ( mw.monthlyConvert && mw.monthlyConvert.canShowModal() ) {
mw.monthlyConvert.init();
} else {
document.location.replace( mw.config.get( 'DonationInterfaceThankYouPage' ) );
}
}
// render error Message to client
function showClientSideErrorMessage( msg ) {
$( '.errorMsg' ).remove();
$( '#errorReference' ).html( '' );
$( '#topError' ).html( msg );
}
// myDeviceData will supply device data for non-recurring vault trxns
// see https://developer.paypal.com/braintree/docs/guides/paypal/vault#collecting-device-data
// https://developer.paypal.com/braintree/docs/guides/premium-fraud-management-tools/device-data-collection/javascript/v3/#collecting-device-data
function getDeviceData( clientInstance ) {
braintree.dataCollector.create( {
client: clientInstance
} ).then( function ( dataCollectorInstance ) {
// At this point, you should access the dataCollectorInstance.deviceData value and provide it
// to your server, e.g. by injecting it into your form as a hidden input
myDeviceData = dataCollectorInstance.deviceData;
} ).catch( function ( err ) {
showClientSideErrorMessage( 'Device data error' + err );
} );
}
// Create a client.
if ( payment_method === 'paypal' ) {
braintree.client.create( {
authorization: mw.config.get( 'clientToken' )
} ).then( function ( clientInstance ) {
getDeviceData( clientInstance );
// Create a PayPal Checkout component.
return braintree.paypalCheckout.create( {
client: clientInstance
} );
} ).then( function ( paypalCheckoutInstance ) {
return paypalCheckoutInstance.loadPayPalSDK( {
vault: true
} );
} ).then( function ( paypalCheckoutInstance ) {
return paypal.Buttons( {
fundingSource: paypal.FUNDING.PAYPAL,
createBillingAgreement: function () {
return paypalCheckoutInstance.createPayment( {
flow: 'vault' // Required
} );
},
onApprove: function ( data, actions ) {
return paypalCheckoutInstance.tokenizePayment( data ).then( function ( payload ) {
var sendData = {
payment_token: payload.nonce,
device_data: myDeviceData,
first_name: payload.details.firstName,
last_name: payload.details.lastName,
email: payload.details.email,
street_address: payload.details.shippingAddress
};
di.forms.callDonateApi(
handleApiResult, sendData, 'di_donate_braintree'
);
} );
},
onCancel: function ( data ) {
showClientSideErrorMessage( 'PayPal payment canceled' + JSON.stringify( data, 0, 2 ) );
},
onError: function ( err ) {
showClientSideErrorMessage( 'PayPal error' + err );
}
} ).render( '#paypal-button' );
} ).then( function () {
// The PayPal button will be rendered in an html element with the ID
// `paypal-button`. This function will be called when the PayPal button
// is set up and ready to be used
} ).catch( function ( err ) {
// Handle component creation error
showClientSideErrorMessage( 'component creation error: ' + err );
} );
} else if ( payment_method === 'venmo' ) {
var venmoButton = document.getElementById( 'venmo-button' );
braintree.client.create( {
authorization: mw.config.get( 'clientToken' )
} ).then( function ( clientInstance ) {
getDeviceData( clientInstance );
// Create a Venmo component.
return braintree.venmo.create( {
client: clientInstance,
allowDesktop: true,
mobileWebFallBack: true,
allowNewBrowserTab: false,
allowDesktopWebLogin: true, // force web login, QR code depreciate
paymentMethodUsage: $( '#recurring' ).val() === '1' ? 'multi_use' : 'single_use'
} );
} ).then( function ( venmoInstance ) {
// Verify browser support before proceeding.
if ( !venmoInstance.isBrowserSupported() ) {
showClientSideErrorMessage( 'Browser does not support Venmo' );
return;
}
function handleVenmoError( err ) {
if ( err.code === 'VENMO_CANCELED' ) {
showClientSideErrorMessage( 'App is not available or user aborted payment flow' );
} else if ( err.code === 'VENMO_APP_CANCELED' ) {
showClientSideErrorMessage( 'User canceled payment flow' );
} else {
showClientSideErrorMessage( err.message );
}
}
function handleVenmoSuccess( payload ) {
var sendData = {
payment_token: payload.nonce,
device_data: myDeviceData,
user_name: payload.details.username,
gateway_session_id: payload.details.paymentContextId
};
// payload.details.payerInfo is undefined for non-us sandbox account
if ( payload.details.payerInfo ) {
sendData.first_name = payload.details.payerInfo.firstName;
sendData.last_name = payload.details.payerInfo.lastName;
sendData.phone = payload.details.payerInfo.phoneNumber;
sendData.email = payload.details.payerInfo.email;
sendData.street_address = payload.details.payerInfo.shippingAddress;
sendData.customer_id = payload.details.payerInfo.externalId;
} else {
// todo:: either retokenize it or insert a email field for user to fill in, but let's wait for venmo's response
}
di.forms.callDonateApi(
handleApiResult, sendData, 'di_donate_braintree'
);
}
function displayVenmoButton() {
venmoButton.style.display = 'block';
venmoButton.addEventListener( 'click', function () {
venmoButton.disabled = true;
venmoInstance.tokenize().then( handleVenmoSuccess ).catch( handleVenmoError ).then( function () {
venmoButton.removeAttribute( 'disabled' );
} );
} );
}
displayVenmoButton();
} ).catch( function ( err ) {
showClientSideErrorMessage( 'Error creating Venmo:' + err );
} );
}
} )( jQuery, mediaWiki );