/* global ppcp_onboarding */ /** * External dependencies */ import { __ } from '@wordpress/i18n'; import apiFetch from '@wordpress/api-fetch'; import { Button } from '@wordpress/components'; import { Component, Fragment, useEffect } from '@wordpress/element'; import { compose } from '@wordpress/compose'; import interpolateComponents from 'interpolate-components'; import { withDispatch, withSelect } from '@wordpress/data'; import { isEmail } from '@wordpress/url'; import { Form, Link, TextControl, Stepper } from '@woocommerce/components'; import { getQuery } from '@woocommerce/navigation'; import { PLUGINS_STORE_NAME, OPTIONS_STORE_NAME } from '@woocommerce/data'; const PAYPAL_PLUGIN = 'woocommerce-paypal-payments'; const WC_PAYPAL_NAMESPACE = '/wc-paypal/v1'; /** * Loads the onboarding script file into the dom on the fly. * * @param {string} url of the onboarding js file. * @param {Object} data required for the onboarding script, labeled as PayPalCommerceGatewayOnboarding * @param {Function} onLoad callback for when the script is loaded. */ function loadOnboardingScript( url, data, onLoad ) { try { // eslint-disable-next-line camelcase if ( ppcp_onboarding ) { onLoad(); } } catch ( e ) { const script = document.createElement( 'script' ); script.src = url; document.body.append( script ); // Callback after scripts have loaded. script.onload = function () { onLoad(); }; window.PayPalCommerceGatewayOnboarding = data; } } function PaypalConnectButton( { connectUrl } ) { useEffect( () => { // eslint-disable-next-line camelcase if ( ppcp_onboarding ) { // Makes sure the onboarding is hooked up to the Connect button rendered. ppcp_onboarding.reload(); } }, [] ); return ( { __( 'Connect', 'woocommerce-admin' ) } ); } class PayPal extends Component { constructor( props ) { super( props ); this.state = { autoConnectFailed: false, connectURL: '', }; this.enablePaypalPlugin = this.enablePaypalPlugin.bind( this ); this.setCredentials = this.setCredentials.bind( this ); this.validate = this.validate.bind( this ); } componentDidMount() { const { createNotice } = this.props; const query = getQuery(); // Handle redirect back from PayPal if ( query.onboarding ) { if ( query.onboarding === 'complete' && ! query[ 'ppcp-onboarding-error' ] ) { this.enablePaypalPlugin(); return; } if ( query[ 'ppcp-onboarding-error' ] ) { /* eslint-disable react/no-did-mount-set-state */ this.setState( { autoConnectFailed: true, } ); createNotice( 'error', __( 'There was a problem saving your payment settings through the onboarding, please fill the fields in manually.', 'woocommerce-admin' ) ); } return; } this.fetchOAuthConnectURLAndOnboardingSetup(); } componentDidUpdate( prevProps ) { const { activePlugins } = this.props; if ( ! prevProps.activePlugins.includes( PAYPAL_PLUGIN ) && activePlugins.includes( PAYPAL_PLUGIN ) ) { this.fetchOAuthConnectURLAndOnboardingSetup(); } } async fetchOAuthConnectURLAndOnboardingSetup() { const { activePlugins, createNotice } = this.props; if ( ! activePlugins.includes( PAYPAL_PLUGIN ) ) { return; } this.setState( { isPending: true } ); try { const result = await apiFetch( { path: WC_PAYPAL_NAMESPACE + '/onboarding/get-params', method: 'POST', data: { environment: 'production', returnUrlArgs: { ppcpobw: '1', }, }, } ); if ( ! result || ! result.signupLink ) { this.setState( { autoConnectFailed: true, isPending: false, } ); return; } loadOnboardingScript( result.scriptURL, result.scriptData, () => { this.setState( { connectURL: result.signupLink, isPending: false, } ); } ); } catch ( error ) { if ( error && error.data && error.data.status === 500 ) { createNotice( 'error', __( 'There was a problem with the Paypal onboarding setup, please fill the fields in manually.', 'woocommerce-admin' ) ); } this.setState( { autoConnectFailed: true, isPending: false, } ); } } async enablePaypalPlugin( skipPpcpSettingsUpdate ) { const { createNotice, updateOptions, markConfigured, options, } = this.props; const updatedOptions = { 'woocommerce_ppcp-gateway_settings': { enabled: 'yes', }, }; if ( ! skipPpcpSettingsUpdate ) { updatedOptions[ 'woocommerce-ppcp-settings' ] = { ...options, enabled: true, }; } const update = await updateOptions( updatedOptions ); if ( update.success ) { createNotice( 'success', __( 'PayPal connected successfully.', 'woocommerce-admin' ) ); markConfigured( 'paypal' ); } else { createNotice( 'error', __( 'There was a problem saving your payment settings.', 'woocommerce-admin' ) ); } } async setCredentials( values ) { const { createNotice } = this.props; try { const result = await apiFetch( { path: WC_PAYPAL_NAMESPACE + '/onboarding/set-credentials', method: 'POST', data: { environment: 'production', ...values, }, } ); if ( result && result.data ) { createNotice( 'error', __( 'There was a problem updating the credentials.', 'woocommerce-admin' ) ); } else { await this.enablePaypalPlugin( true ); } } catch ( error ) { if ( error && error.data && error.data.status === 404 ) { await this.updateManualSettings( values ); } } } async updateManualSettings( values ) { const { createNotice, options, updateOptions, markConfigured, } = this.props; const productionValues = Object.keys( values ).reduce( ( vals, key ) => { const prodKey = key + '_production'; return { ...vals, [ prodKey ]: values[ key ], }; }, {} ); /** * merchant data can be the same across sandbox and production, that's why we set it as * standalone as well. */ const optionValues = { ...options, enabled: true, sandbox_on: false, merchant_email: values.merchant_email, merchant_id: values.merchant_id, ...productionValues, }; const update = await updateOptions( { 'woocommerce-ppcp-settings': optionValues, 'woocommerce_ppcp-gateway_settings': { enabled: 'yes', }, } ); if ( update.success ) { createNotice( 'success', __( 'PayPal connected successfully.', 'woocommerce-admin' ) ); markConfigured( 'paypal' ); } else { createNotice( 'error', __( 'There was a problem saving your payment settings.', 'woocommerce-admin' ) ); } } getInitialConfigValues() { const { options } = this.props; return [ 'merchant_email', 'merchant_id', 'client_id', 'client_secret', ].reduce( ( initialVals, key ) => { return { ...initialVals, [ key ]: options && options[ key + '_production' ] ? options[ key + '_production' ] : '', }; }, {} ); } validate( values ) { const errors = {}; if ( ! values.merchant_email ) { errors.merchant_email = __( 'Please enter your Merchant email', 'woocommerce-admin' ); } if ( ! isEmail( values.merchant_email ) ) { errors.merchant_email = __( 'Please enter a valid email address', 'woocommerce-admin' ); } if ( ! values.merchant_id ) { errors.merchant_id = __( 'Please enter your Merchant Id', 'woocommerce-admin' ); } if ( ! values.client_id ) { errors.client_id = __( 'Please enter your Client Id', 'woocommerce-admin' ); } if ( ! values.client_secret ) { errors.client_secret = __( 'Please enter your Client Secret', 'woocommerce-admin' ); } return errors; } renderManualConfig() { const { isOptionsUpdating } = this.props; const stripeHelp = interpolateComponents( { mixedString: __( 'Your API details can be obtained from your {{docsLink}}Paypal developer account{{/docsLink}}, and your Merchant Id from your {{merchantLink}}Paypal Business account{{/merchantLink}}. Don’t have a Paypal account? {{registerLink}}Create one.{{/registerLink}}', 'woocommerce-admin' ), components: { docsLink: ( ), merchantLink: ( ), registerLink: ( ), }, } ); return (
); } renderConnectFields() { const { autoConnectFailed, connectURL } = this.state; if ( ! autoConnectFailed && connectURL ) { return ( <>{ __( 'You will be redirected to the PayPal website to create the connection.', 'woocommerce-admin' ) }
> ); } if ( autoConnectFailed ) { return this.renderManualConfig(); } } getConnectStep() { const { isRequestingOptions } = this.props; return { key: 'connect', label: __( 'Connect your PayPal account', 'woocommerce-admin' ), description: __( 'A PayPal account is required to process payments. Connect your store to your PayPal account.', 'woocommerce-admin' ), content: isRequestingOptions ? null : this.renderConnectFields(), }; } render() { const { installStep, isRequestingOptions, isOptionsUpdating, } = this.props; const { isPending } = this.state; return (