/** * This PaypalExpressCheckout global is included by wp_enqueue_script( 'jetpack-paypal-express-checkout' ); * It handles communication with Paypal Express checkout and public-api.wordpress.com for the purposes * of simple-payments module. */ /* global paypal */ /* exported PaypalExpressCheckout */ const PaypalExpressCheckout = { primaryCssClassName: 'jetpack-simple-payments', messageCssClassName: 'jetpack-simple-payments-purchase-message', wpRestAPIHost: 'https://public-api.wordpress.com', wpRestAPIVersion: '/wpcom/v2', getEnvironment: function () { if ( localStorage && localStorage.getItem && localStorage.getItem( 'simple-payments-env' ) === 'sandbox' ) { return 'sandbox'; } return 'production'; }, getCreatePaymentEndpoint: function ( blogId ) { return ( PaypalExpressCheckout.wpRestAPIHost + PaypalExpressCheckout.wpRestAPIVersion + '/sites/' + blogId + '/simple-payments/paypal/payment' ); }, getExecutePaymentEndpoint: function ( blogId, paymentId ) { return ( PaypalExpressCheckout.wpRestAPIHost + PaypalExpressCheckout.wpRestAPIVersion + '/sites/' + blogId + '/simple-payments/paypal/' + paymentId + '/execute' ); }, getNumberOfItems: function ( field, enableMultiple ) { if ( enableMultiple !== '1' ) { return 1; } const numberField = document.getElementById( field ); if ( ! numberField ) { return 1; } const number = Number( numberField.value ); if ( isNaN( number ) ) { return 1; } return number; }, /** * Get the DOM element-placeholder used to show message * about the transaction. If it doesn't exist then the function will create a new one. * * @param {string} domId - domId id of the payment button placeholder * @return {Element} the dom element to print the message */ getMessageContainer: function ( domId ) { return document.getElementById( domId + '-message-container' ); }, /** * Show a messange close to the Paypal button. * Use this function to give feedback to the user according * to the transaction result. * * @param {string} message - message to show * @param {string} domId - paypal-button element dom identifier * @param {boolean} [isError] - defines if it's a message error. Not TRUE as default. */ showMessage: function ( message, domId, isError ) { const domEl = PaypalExpressCheckout.getMessageContainer( domId ); // set css classes let cssClasses = PaypalExpressCheckout.messageCssClassName + ' show '; cssClasses += isError ? 'error' : 'success'; // show message 1s after PayPal popup is closed setTimeout( function () { domEl.innerHTML = message; domEl.setAttribute( 'class', cssClasses ); }, 1000 ); }, showError: function ( message, domId ) { PaypalExpressCheckout.showMessage( message, domId, true ); }, processErrorMessage: function ( errorResponse ) { const error = errorResponse ? errorResponse.responseJSON : null; const defaultMessage = 'There was an issue processing your payment.'; if ( ! error ) { return '
' + defaultMessage + '
'; } if ( error.additional_errors ) { const messages = []; error.additional_errors.forEach( function ( additionalError ) { if ( additionalError.message ) { messages.push( '' + additionalError.message.toString() + '
' ); } } ); return messages.join( '' ); } return '' + ( error.message || defaultMessage ) + '
'; }, processSuccessMessage: function ( successResponse ) { const message = successResponse.message; const defaultMessage = 'Thank you. Your purchase was successful!'; if ( ! message ) { return '' + defaultMessage + '
'; } return '' + message + '
'; }, cleanAndHideMessage: function ( domId ) { const domEl = PaypalExpressCheckout.getMessageContainer( domId ); domEl.setAttribute( 'class', PaypalExpressCheckout.messageCssClassName ); domEl.innerHTML = ''; }, renderButton: function ( blogId, buttonId, domId, enableMultiple ) { const env = PaypalExpressCheckout.getEnvironment(); if ( ! paypal ) { throw new Error( 'PayPal module is required by PaypalExpressCheckout' ); } const buttonDomId = domId + '_button'; paypal.Button.render( { env: env, commit: true, style: { label: 'pay', shape: 'rect', color: 'silver', size: 'responsive', fundingicons: true, }, payment: function () { PaypalExpressCheckout.cleanAndHideMessage( domId ); const payload = { number: PaypalExpressCheckout.getNumberOfItems( domId + '_number', enableMultiple ), buttonId: buttonId, env: env, }; return new paypal.Promise( function ( resolve, reject ) { // eslint-disable-next-line no-undef jQuery .post( PaypalExpressCheckout.getCreatePaymentEndpoint( blogId ), payload ) .done( function ( paymentResponse ) { if ( ! paymentResponse ) { PaypalExpressCheckout.showError( PaypalExpressCheckout.processErrorMessage(), domId ); return reject( new Error( 'server_error' ) ); } resolve( paymentResponse.id ); } ) .fail( function ( paymentError ) { const paymentErrorMessage = PaypalExpressCheckout.processErrorMessage( paymentError ); PaypalExpressCheckout.showError( paymentErrorMessage, domId ); const code = paymentError.responseJSON && paymentError.responseJSON.code ? paymentError.responseJSON.code : 'server_error'; reject( new Error( code ) ); } ); } ); }, onAuthorize: function ( onAuthData ) { const payload = { buttonId: buttonId, payerId: onAuthData.payerID, env: env, }; return new paypal.Promise( function ( resolve, reject ) { // eslint-disable-next-line no-undef jQuery .post( PaypalExpressCheckout.getExecutePaymentEndpoint( blogId, onAuthData.paymentID ), payload ) .done( function ( authResponse ) { if ( ! authResponse ) { PaypalExpressCheckout.showError( PaypalExpressCheckout.processErrorMessage(), domId ); return reject( new Error( 'server_error' ) ); } PaypalExpressCheckout.showMessage( PaypalExpressCheckout.processSuccessMessage( authResponse ), domId ); resolve(); } ) .fail( function ( authError ) { const authErrorMessage = PaypalExpressCheckout.processErrorMessage( authError ); PaypalExpressCheckout.showError( authErrorMessage, domId ); const code = authError.responseJSON && authError.responseJSON.code ? authError.responseJSON.code : 'server_error'; reject( new Error( code ) ); } ); } ); }, }, buttonDomId ); }, }; ; // listen for rlt authentication events and pass them to children of this document. ( function() { var currentToken; var parentOrigin; var iframeOrigins; var registeredIframes = []; var initializationListeners = []; var hasBeenInitialized = false; var RLT_KEY = 'jetpack:wpcomRLT'; // should we inject RLT into this iframe? function rltShouldAuthorizeIframe( frameOrigin ) { if ( ! Array.isArray( iframeOrigins ) ) { return false; } return iframeOrigins.includes( frameOrigin ); } function rltInvalidateWindowToken( token, target, origin ) { if ( target && typeof target.postMessage === 'function' ) { try { target.postMessage( JSON.stringify( { type: 'rltMessage', data: { event: 'invalidate', token: token, sourceOrigin: window.location.origin, }, } ), origin ); } catch ( err ) { return; } } } /** * PUBLIC METHODS */ window.rltInvalidateToken = function( token, sourceOrigin ) { // invalidate in current context if ( token === currentToken ) { currentToken = null; } // remove from localstorage, but only if in a top level window, not iframe try { if ( window.location === window.parent.location && window.localStorage ) { if ( window.localStorage.getItem(RLT_KEY) === token ) { window.localStorage.removeItem(RLT_KEY); } } } catch( e ) { console.info("localstorage access for invalidate denied - probably blocked third-party access", window.location.href); } // invalidate in registered iframes for ( const [ frameOrigin, frameWindow ] of registeredIframes ) { if ( frameOrigin !== sourceOrigin ) { rltInvalidateWindowToken( token, frameWindow, frameOrigin ); } } // invalidate in parent if ( parentOrigin && parentOrigin !== sourceOrigin && window.parent ) { rltInvalidateWindowToken( token, window.parent, parentOrigin ); } } window.rltInjectToken = function( token, target, origin ) { if ( target && typeof target.postMessage === 'function' ) { try { target.postMessage( JSON.stringify( { type: 'loginMessage', data: { event: 'login', success: true, type: 'rlt', token: token, sourceOrigin: window.location.origin, }, } ), origin ); } catch ( err ) { return; } } }; window.rltIsAuthenticated = function() { return !! currentToken; }; window.rltGetToken = function() { return currentToken; }; window.rltAddInitializationListener = function( listener ) { // if RLT is already initialized, call the listener immediately if ( hasBeenInitialized ) { listener( currentToken ); } else { initializationListeners.push( listener ); } }; // store the token in localStorage window.rltStoreToken = function( token ) { currentToken = token; try { if ( window.location === window.parent.location && window.localStorage ) { window.localStorage.setItem( RLT_KEY, token ); } } catch( e ) { console.info("localstorage access denied - probably blocked third-party access", window.location.href); } } window.rltInitialize = function( config ) { if ( ! config || typeof window.postMessage !== 'function' ) { return; } currentToken = config.token; iframeOrigins = config.iframeOrigins; parentOrigin = config.parentOrigin; // needed? // load token from localStorage if possible, but only in top level window try { if ( ! currentToken && window.location === window.parent.location && window.localStorage ) { currentToken = window.localStorage.getItem(RLT_KEY); } } catch( e ) { console.info("localstorage access denied - probably blocked third-party access", window.location.href); } // listen for RLT events from approved origins window.addEventListener( 'message', function( e ) { var message = e && e.data; if ( typeof message === 'string' ) { try { message = JSON.parse( message ); } catch ( err ) { return; } } var type = message && message.type; var data = message && message.data; if ( type === 'loginMessage' ) { if ( data && data.type === 'rlt' && data.token !== currentToken ) { // put into localStorage if running in top-level window (not iframe) rltStoreToken( data.token ); // send to registered iframes for ( const [ frameOrigin, frameWindow ] of registeredIframes ) { rltInjectToken( currentToken, frameWindow, frameOrigin ); } // send to the parent, unless the event was sent _by_ the parent if ( parentOrigin && parentOrigin !== data.sourceOrigin && window.parent ) { rltInjectToken( currentToken, window.parent, parentOrigin ); } } } if ( type === 'rltMessage' ) { if ( data && data.event === 'invalidate' && data.token === currentToken ) { rltInvalidateToken( data.token ); } if ( data && data.event === 'register' ) { if ( rltShouldAuthorizeIframe( e.origin ) ) { registeredIframes.push( [ e.origin, e.source ] ); if ( currentToken ) { rltInjectToken( currentToken, e.source, e.origin ); } } } } } ); initializationListeners.forEach( function( listener ) { listener( currentToken ); } ); initializationListeners = []; // inform the parent that we are ready to receive the RLT token window.parent.postMessage( { type: 'rltMessage', data: { event: 'register' }, }, '*' ); hasBeenInitialized = true; }; } )(); ;