From da6f113d180648b0e27c00a772c5a2844e266e5e Mon Sep 17 00:00:00 2001 From: Joshua T Flowers Date: Sun, 15 Mar 2020 22:45:19 +0100 Subject: [PATCH] Onboarding: Update payments task flow (https://github.com/woocommerce/woocommerce-admin/pull/3782) * Add new payment method cards * Refactor payment methods * Track payment configuration and completion client-side * Record tasklist_payment_connect_method event in markConfigured * Record event on payment setup * Add recommended payment method ribbon * Return to payments task even when previously configured --- .../client/dashboard/task-list/index.js | 8 +- .../client/dashboard/task-list/style.scss | 138 ++-- .../task-list/tasks/payments/index.js | 662 ++++++------------ .../task-list/tasks/payments/klarna.js | 53 +- .../task-list/tasks/payments/payfast.js | 116 ++- .../task-list/tasks/payments/paypal.js | 141 ++-- .../task-list/tasks/payments/square.js | 96 ++- .../task-list/tasks/payments/stripe.js | 404 ++++++----- .../client/wc-api/onboarding/constants.js | 33 +- .../src/API/OnboardingPlugins.php | 12 +- .../src/Features/Onboarding.php | 24 +- 11 files changed, 812 insertions(+), 875 deletions(-) diff --git a/plugins/woocommerce-admin/client/dashboard/task-list/index.js b/plugins/woocommerce-admin/client/dashboard/task-list/index.js index 7994947dab8..7ebb989b48d 100644 --- a/plugins/woocommerce-admin/client/dashboard/task-list/index.js +++ b/plugins/woocommerce-admin/client/dashboard/task-list/index.js @@ -2,7 +2,7 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { Component, Fragment } from '@wordpress/element'; +import { Component, cloneElement, Fragment } from '@wordpress/element'; import { get } from 'lodash'; import { compose } from '@wordpress/compose'; import classNames from 'classnames'; @@ -334,7 +334,7 @@ class TaskDashboard extends Component { } render() { - const { inline } = this.props; + const { inline, query } = this.props; const { isCartModalOpen, isWelcomeModalOpen } = this.state; const currentTask = this.getCurrentTask(); const listTasks = this.getTasks().map( ( task ) => { @@ -362,7 +362,9 @@ class TaskDashboard extends Component {
{ currentTask ? ( - currentTask.container + cloneElement( currentTask.container, { + query, + } ) ) : ( .woocommerce-list__item-inner { + .woocommerce-card__body { flex-direction: column; } - .woocommerce-list__item-title { - border-top: 0; + .woocommerce-task-payment__recommended-ribbon { + display: none; } - .woocommerce-list__item .woocommerce-list__item-before { - margin-top: 0; + .woocommerce-task-payment__before { order: 1; + margin-right: auto; + margin-bottom: $gap-large; } - .woocommerce-task-payments__woocommerce-services-options { - margin-top: $gap-smaller; - border-top: 0; + .woocommerce-task-payment__after { + position: absolute; + right: $gap-larger; + top: $gap-large; + + .components-form-toggle { + margin-top: $gap-smallest; + } } - .woocommerce-task-payments__woocommerce-services-options .muriel-component { - margin-left: -$gap-larger; - } - - .woocommerce-list__item .woocommerce-list__item-after { - margin-left: 0; - order: 2; - align-self: flex-end; - margin-top: -$gap-larger; - } - - .woocommerce-list__item .woocommerce-list__item-text { + .woocommerce-task-payment__text { order: 3; margin-top: $gap; } + } +} - .woocommerce-task-payments__woocommerce-services-options .components-checkbox-control__label { - margin-left: -$gap-larger; - margin-top: -$gap-smaller; - } +.woocommerce-task-dashboard__container .woocommerce-task-payments .woocommerce-task-payments__actions { + text-align: center; + + button.components-button.is-primary { + margin: 0; + } + + button.components-button.is-link { + margin: 0; + height: auto; + color: $studio-gray-60; + font-weight: normal; } } diff --git a/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/index.js b/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/index.js index 6ccb5e3930c..f71905c92ee 100644 --- a/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/index.js +++ b/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/index.js @@ -1,27 +1,24 @@ /** * External dependencies */ -import { __ } from '@wordpress/i18n'; -import { Fragment, Component } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; +import { Fragment, cloneElement, Component } from '@wordpress/element'; import { compose } from '@wordpress/compose'; -import { get, filter, noop, keys, pickBy, difference } from 'lodash'; -import { Button, FormToggle, CheckboxControl } from '@wordpress/components'; +import { get, filter } from 'lodash'; +import { Button, FormToggle } from '@wordpress/components'; import { withDispatch } from '@wordpress/data'; /** * WooCommerce dependencies */ +import { Card, H } from '@woocommerce/components'; import { - Form, - Card, - Stepper, - TextControl, - List, -} from '@woocommerce/components'; -import { getHistory, getNewPath } from '@woocommerce/navigation'; + getHistory, + getNewPath, + updateQueryString, +} from '@woocommerce/navigation'; import { WC_ASSET_URL as wcAssetUrl, - getAdminLink, getSetting, } from '@woocommerce/wc-admin-settings'; @@ -35,6 +32,7 @@ import Plugins from '../steps/plugins'; import Stripe from './stripe'; import Square from './square'; import PayPal from './paypal'; +import { pluginNames } from 'wc-api/onboarding/constants'; import Klarna from './klarna'; import PayFast from './payfast'; @@ -42,64 +40,25 @@ class Payments extends Component { constructor() { super( ...arguments ); - this.chooseMethods = this.chooseMethods.bind( this ); - this.completeStep = this.completeStep.bind( this ); + this.recommendedMethod = 'stripe'; + this.completeTask = this.completeTask.bind( this ); this.markConfigured = this.markConfigured.bind( this ); - this.setMethodRequestPending = this.setMethodRequestPending.bind( - this - ); - this.completePluginInstall = this.completePluginInstall.bind( this ); - - const { methods, installed, configured } = this.props; - - let step = 'choose'; - let showIndividualConfigs = false; - - // Figure out which step to show initially if there are still steps to be configured, or redirect back to the task list. - if ( methods.length > 0 && configured.length > 0 ) { - step = difference( methods, configured )[ 0 ] || ''; - showIndividualConfigs = true; - const stepsLeft = difference( methods, configured ).length; - if ( stepsLeft === 0 ) { - this.state = { - step: 'done', - methodRequestPending: false, - }; - this.completeTask(); - return; - } - } else if ( installed === 1 && methods.length > 0 ) { - // Methods have been installed but not configured yet. - step = methods[ 0 ]; - showIndividualConfigs = true; - } - - this.state = { - step, - showIndividualConfigs, - methodRequestPending: false, - }; - } - - componentDidUpdate( prevProps ) { - const { methods, configured } = this.props; - if ( - prevProps.configured.length !== configured.length && - methods.length > 0 && - configured.length > 0 - ) { - const stepsLeft = difference( methods, configured ); - const nextStep = stepsLeft[ 0 ]; - /* eslint-disable react/no-did-update-set-state */ - this.setState( { - step: nextStep, - } ); - /* eslint-enable react/no-did-update-set-state */ - } + this.skipTask = this.skipTask.bind( this ); } completeTask() { - const { createNotice } = this.props; + const { configured, createNotice, options, updateOptions } = this.props; + + updateOptions( { + woocommerce_task_list_payments: { + ...options.woocommerce_task_list_payments, + completed: 1, + }, + } ); + + recordEvent( 'tasklist_payment_done', { + configured, + } ); createNotice( 'success', @@ -112,6 +71,23 @@ class Payments extends Component { getHistory().push( getNewPath( {}, '/', {} ) ); } + skipTask() { + const { options, updateOptions } = this.props; + + updateOptions( { + woocommerce_task_list_payments: { + ...options.woocommerce_task_list_payments, + completed: 1, + }, + } ); + + recordEvent( 'tasklist_payment_skip_task', { + options: this.getMethodOptions().map( ( method ) => method.key ), + } ); + + getHistory().push( getNewPath( {}, '/', {} ) ); + } + isStripeEnabled() { const { countryCode } = this.props; const stripeCountries = getSetting( 'onboarding', { @@ -120,116 +96,29 @@ class Payments extends Component { return stripeCountries.includes( countryCode ); } - getInitialValues() { - const stripeEmail = getSetting( 'onboarding', { userEmail: '' } ) - .userEmail; - const values = { - stripe: this.isStripeEnabled(), - paypal: false, - klarna_checkout: false, - klarna_payments: false, - square: false, - create_stripe: this.isStripeEnabled(), - stripe_email: ( this.isStripeEnabled() && stripeEmail ) || '', - payfast: false, - }; - return values; - } - - validate() { - const errors = {}; - return errors; - } - - completeStep() { - const { step } = this.state; - const steps = this.getSteps(); - const currentStepIndex = steps.findIndex( ( s ) => s.key === step ); - const nextStep = steps[ currentStepIndex + 1 ]; - - if ( nextStep ) { - this.setState( { step: nextStep.key } ); - } else { - getHistory().push( getNewPath( {}, '/', {} ) ); - } - } - - completePluginInstall() { - const { completed } = this.props; - this.props.updateOptions( { - woocommerce_task_list_payments: { - completed: completed || false, - installed: 1, - methods: this.getMethodsToConfigure(), - }, - } ); - - this.setState( { showIndividualConfigs: true }, function() { - this.completeStep(); - } ); - } - markConfigured( method ) { - const { options, methods, configured } = this.props; - configured.push( method ); - const stepsLeft = difference( methods, configured ); + const { options, configured, updateOptions } = this.props; - this.props.updateOptions( { + getHistory().push( getNewPath( { task: 'payments' }, '/', {} ) ); + + if ( configured.includes( method ) ) { + return; + } + + recordEvent( 'tasklist_payment_connect_method', { + payment_method: method, + } ); + + configured.push( method ); + updateOptions( { woocommerce_task_list_payments: { ...options.woocommerce_task_list_payments, configured, - completed: stepsLeft.length === 0 ? 1 : 0, }, } ); - - if ( stepsLeft.length === 0 ) { - this.completeTask(); - } - } - - setMethodRequestPending( status ) { - this.setState( { - methodRequestPending: status, - } ); - } - - // If Jetpack is connected and WCS is enabled, we will offer a streamlined option. - renderWooCommerceServicesStripeConnect() { - const { getInputProps, values } = this.formData; - if ( ! values.stripe ) { - return null; - } - - const { isJetpackConnected, activePlugins } = this.props; - if ( - ! isJetpackConnected || - ! activePlugins.includes( 'woocommerce-services' ) - ) { - return null; - } - - return ( -
- - - { values.create_stripe && ( - - ) } -
- ); } getMethodOptions() { - const { getInputProps } = this.formData; const { countryCode, profileItems } = this.props; const methods = [ @@ -246,12 +135,12 @@ class Payments extends Component { 'and one-touch checkout with Apple Pay.', 'woocommerce-admin' ) } - { this.renderWooCommerceServicesStripeConnect() }
), before: , - after: , visible: this.isStripeEnabled(), + plugins: [ 'woocommerce-gateway-stripe' ], + container: , }, { key: 'paypal', @@ -265,8 +154,9 @@ class Payments extends Component { ), before: , - after: , visible: true, + plugins: [ 'woocommerce-gateway-paypal-express-checkout' ], + container: , }, { key: 'klarna_checkout', @@ -281,8 +171,14 @@ class Payments extends Component { alt="" /> ), - after: , visible: [ 'SE', 'FI', 'NO', 'NL' ].includes( countryCode ), + plugins: [ 'klarna-checkout-for-woocommerce' ], + container: ( + + ), }, { key: 'klarna_payments', @@ -297,8 +193,14 @@ class Payments extends Component { alt="" /> ), - after: , visible: [ 'DK', 'DE', 'AT' ].includes( countryCode ), + plugins: [ 'klarna-payments-for-woocommerce' ], + container: ( + + ), }, { key: 'square', @@ -314,12 +216,13 @@ class Payments extends Component { alt="" /> ), - after: , visible: [ 'brick-mortar', 'brick-mortar-other' ].includes( profileItems.selling_venues ) && [ 'US', 'CA', 'JP', 'GB', 'AU' ].includes( countryCode ), + plugins: [ 'woocommerce-square' ], + container: , }, { key: 'payfast', @@ -344,279 +247,180 @@ class Payments extends Component { alt="PayFast logo" /> ), - after: , visible: [ 'ZA' ].includes( countryCode ), + plugins: [ 'woocommerce-payfast-gateway' ], + container: , }, ]; return filter( methods, ( method ) => method.visible ); } - getMethodsToConfigure() { - const { options } = this.props; - if ( - options && - options.woocommerce_task_list_payments && - options.woocommerce_task_list_payments.methods - ) { - return options.woocommerce_task_list_payments.methods; + getCurrentMethod() { + const { query } = this.props; + + if ( ! query.method ) { + return; } - const { values } = this.formData; - const methods = { - stripe: values.stripe, - paypal: values.paypal, - 'klarna-checkout': values.klarna_checkout, - 'klarna-payments': values.klarna_payments, - square: values.square, - payfast: values.payfast, - }; - return keys( pickBy( methods ) ); + const methods = this.getMethodOptions(); + + return methods.find( ( method ) => method.key === query.method ); } - getPluginsToInstall() { - const { values } = this.formData; - const pluginSlugs = { - 'woocommerce-gateway-stripe': values.stripe, - 'woocommerce-gateway-paypal-express-checkout': values.paypal, - 'klarna-checkout-for-woocommerce': values.klarna_checkout, - 'klarna-payments-for-woocommerce': values.klarna_payments, - 'woocommerce-square': values.square, - 'woocommerce-payfast-gateway': values.payfast, - }; - return keys( pickBy( pluginSlugs ) ); - } + getInstallStep() { + const currentMethod = this.getCurrentMethod(); - chooseMethods() { - const methodsDisplayed = this.getMethodOptions().map( - ( method ) => method.key + if ( ! currentMethod.plugins || ! currentMethod.plugins.length ) { + return; + } + + const { activePlugins } = this.props; + const pluginsToInstall = currentMethod.plugins.filter( + ( method ) => ! activePlugins.includes( method ) ); - const methodsChosen = this.getMethodsToConfigure(); - const { values } = this.formData; - const createAccount = values.create_stripe || false; + const pluginNamesString = currentMethod.plugins + .map( ( pluginSlug ) => pluginNames[ pluginSlug ] ) + .join( ' ' + __( 'and', 'woocommerce-admin' ) + ' ' ); - recordEvent( 'wcadmin_tasklist_payment_choose_method', { - payment_methods_displayed: methodsDisplayed, - payment_methods_chosen: methodsChosen, - create_stripe_account: createAccount, - } ); - - this.completeStep(); - } - - getSteps() { - const { values } = this.formData; - const isMethodSelected = - values.stripe || - values.paypal || - values.klarna_checkout || - values.klarna_payments || - values.square || - values.payfast; - - const { showIndividualConfigs } = this.state; - const { activePlugins, countryCode, isJetpackConnected } = this.props; - - const manualConfig = - isJetpackConnected && - activePlugins.includes( 'woocommerce-services' ) - ? false - : true; - - const methods = this.getMethodsToConfigure(); - - const steps = [ - { - key: 'choose', - label: __( 'Choose payment methods', 'woocommerce-admin' ), - description: __( - "Select which payment methods you'd like to use", - 'woocommerce-admin' - ), - content: ( - - - - - ), - visible: true, - }, - { - key: 'install', - label: __( 'Install selected methods', 'woocommerce-admin' ), - description: __( - 'Install plugins required to offer the selected payment methods', - 'woocommerce-admin' - ), - content: ! showIndividualConfigs && ( - { - this.completePluginInstall(); - recordEvent( 'tasklist_payment_install_method' ); - } } - autoInstall - pluginSlugs={ this.getPluginsToInstall() } - /> - ), - visible: true, - }, - { - key: 'configure', - label: __( 'Configure payment methods', 'woocommerce-admin' ), - description: __( - 'Set up your chosen payment methods', - 'woocommerce-admin' - ), - content: , - visible: ! showIndividualConfigs, - }, - { - key: 'stripe', - label: __( 'Enable Stripe', 'woocommerce-admin' ), - description: __( - 'Connect your store to your Stripe account', - 'woocommerce-admin' - ), - content: ( - - ), - visible: showIndividualConfigs && methods.includes( 'stripe' ), - }, - { - key: 'paypal', - label: __( 'Enable PayPal Checkout', 'woocommerce-admin' ), - description: __( - 'Connect your store to your PayPal account', - 'woocommerce-admin' - ), - content: ( - - ), - visible: showIndividualConfigs && methods.includes( 'paypal' ), - }, - { - key: 'square', - label: __( 'Enable Square', 'woocommerce-admin' ), - description: __( - 'Connect your store to your Square account', - 'woocommerce-admin' - ), - content: ( - - ), - visible: showIndividualConfigs && methods.includes( 'square' ), - }, - { - key: 'klarna-checkout', - label: __( 'Klarna', 'woocommerce-admin' ), - description: '', - content: ( - - ), - visible: - showIndividualConfigs && - methods.includes( 'klarna-checkout' ), - }, - { - key: 'klarna-payments', - label: __( 'Klarna', 'woocommerce-admin' ), - description: '', - content: ( - - ), - visible: - showIndividualConfigs && - methods.includes( 'klarna-payments' ), - }, - { - key: 'payfast', - label: __( 'Enable PayFast', 'woocommerce-admin' ), - description: __( - 'Connect your store to your PayFast account', - 'woocommerce-admin' - ), - content: ( - - ), - visible: showIndividualConfigs && methods.includes( 'payfast' ), - }, - ]; - - return filter( steps, ( step ) => step.visible ); + return { + key: 'install', + label: sprintf( + __( 'Install %s', 'woocommerce-admin' ), + pluginNamesString + ), + content: ( + { + recordEvent( 'tasklist_payment_install_method', { + plugins: currentMethod.plugins, + } ); + } } + autoInstall + pluginSlugs={ currentMethod.plugins } + /> + ), + isComplete: ! pluginsToInstall.length, + }; } render() { - const { step, methodRequestPending } = this.state; + const currentMethod = this.getCurrentMethod(); + const { configured, query } = this.props; + + if ( currentMethod ) { + return ( + + { cloneElement( currentMethod.container, { + query, + installStep: this.getInstallStep(), + } ) } + + ); + } + + const methods = this.getMethodOptions(); + return ( -
- { ( formData ) => { - this.formData = formData; +
+ { methods.map( ( method ) => { + const { + before, + container, + content, + key, + title, + visible, + } = method; + + if ( ! visible ) { + return null; + } + return ( -
- - - -
+ +
+ { key === this.recommendedMethod && + ! configured.includes( key ) && ( +
+ + { __( + 'Recommended', + 'woocommerce-admin' + ) } + +
+ ) } + { before } +
+
+ + { title } + +

+ { content } +

+
+
+ { container ? ( + + ) : ( + + ) } +
+
); - } } - + } ) } +
+ { configured.length === 0 ? ( + + ) : ( + + ) } +
+
); } } export default compose( withSelect( ( select ) => { - const { - getProfileItems, - isJetpackConnected, - getActivePlugins, - getOptions, - } = select( 'wc-api' ); + const { getProfileItems, getActivePlugins, getOptions } = select( + 'wc-api' + ); const options = getOptions( [ 'woocommerce_task_list_payments', @@ -626,38 +430,18 @@ export default compose( options.woocommerce_default_country ); - const methods = get( - options, - [ 'woocommerce_task_list_payments', 'methods' ], - [] - ); - const installed = get( - options, - [ 'woocommerce_task_list_payments', 'installed' ], - false - ); const configured = get( options, [ 'woocommerce_task_list_payments', 'configured' ], [] ); - const completed = get( - options, - [ 'woocommerce_task_list_payments', 'completed' ], - false - ); - return { countryCode, profileItems: getProfileItems(), activePlugins: getActivePlugins(), - isJetpackConnected: isJetpackConnected(), options, - methods, - installed, configured, - completed, }; } ), withDispatch( ( dispatch ) => { diff --git a/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/klarna.js b/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/klarna.js index d1c5ff17af8..3f31a30f531 100644 --- a/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/klarna.js +++ b/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/klarna.js @@ -10,12 +10,7 @@ import interpolateComponents from 'interpolate-components'; * WooCommerce dependencies */ import { ADMIN_URL as adminUrl } from '@woocommerce/wc-admin-settings'; -import { Link } from '@woocommerce/components'; - -/** - * Internal dependencies - */ -import { recordEvent } from 'lib/tracks'; +import { Link, Stepper } from '@woocommerce/components'; class Klarna extends Component { constructor( props ) { @@ -24,23 +19,20 @@ class Klarna extends Component { } continue() { + const { markConfigured, plugin } = this.props; + const slug = - this.props.plugin === 'checkout' - ? 'klarna-checkout' - : 'klarna-payments'; - recordEvent( 'tasklist_payment_connect_method', { - payment_method: slug, - } ); - this.props.markConfigured( slug ); + plugin === 'checkout' ? 'klarna-checkout' : 'klarna-payments'; + + markConfigured( slug ); } - render() { + renderConnectStep() { + const { plugin } = this.props; + const slug = - this.props.plugin === 'checkout' - ? 'klarna-checkout' - : 'klarna-payments'; - const section = - this.props.plugin === 'checkout' ? 'kco' : 'klarna_payments'; + plugin === 'checkout' ? 'klarna-checkout' : 'klarna-payments'; + const section = plugin === 'checkout' ? 'kco' : 'klarna_payments'; const link = ( ); } + + render() { + const { installStep } = this.props; + + return ( + + ); + } } export default Klarna; diff --git a/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/payfast.js b/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/payfast.js index 8dce2da9858..65d7aac89f6 100644 --- a/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/payfast.js +++ b/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/payfast.js @@ -11,12 +11,12 @@ import { withDispatch } from '@wordpress/data'; /** * WooCommerce dependencies */ -import { Form, Link, TextControl } from '@woocommerce/components'; +import { Form, Link, Stepper, TextControl } from '@woocommerce/components'; /** * Internal dependencies */ -import { recordEvent } from 'lib/tracks'; +import withSelect from 'wc-api/with-select'; class PayFast extends Component { getInitialConfigValues = () => { @@ -54,20 +54,39 @@ class PayFast extends Component { return errors; }; - updateSettings = async ( values ) => { + componentDidUpdate( prevProps ) { const { createNotice, - isSettingsError, - updateOptions, + isOptionsRequesting, + hasOptionsError, markConfigured, - setRequestPending, } = this.props; - setRequestPending( true ); + if ( prevProps.isOptionsRequesting && ! isOptionsRequesting ) { + if ( ! hasOptionsError ) { + markConfigured( 'payfast' ); + createNotice( + 'success', + __( 'PayFast connected successfully', 'woocommerce-admin' ) + ); + } else { + createNotice( + 'error', + __( + 'There was a problem saving your payment setings', + 'woocommerce-admin' + ) + ); + } + } + } + + updateSettings = ( values ) => { + const { updateOptions } = this.props; // Because the PayFast extension only works with the South African Rand // currency, force the store to use it while setting the PayFast settings - await updateOptions( { + updateOptions( { woocommerce_currency: 'ZAR', woocommerce_payfast_settings: { merchant_id: values.merchant_id, @@ -76,30 +95,10 @@ class PayFast extends Component { enabled: 'yes', }, } ); - - if ( ! isSettingsError ) { - recordEvent( 'tasklist_payment_connect_method', { - payment_method: 'payfast', - } ); - setRequestPending( false ); - markConfigured( 'payfast' ); - createNotice( - 'success', - __( 'PayFast connected successfully', 'woocommerce-admin' ) - ); - } else { - setRequestPending( false ); - createNotice( - 'error', - __( - 'There was a problem saving your payment setings', - 'woocommerce-admin' - ) - ); - } }; - render() { + renderConnectStep() { + const { isOptionsRequesting } = this.props; const helpText = interpolateComponents( { mixedString: __( 'Your API details can be obtained from your {{link}}PayFast account{{/link}}', @@ -149,16 +148,14 @@ class PayFast extends Component { required { ...getInputProps( 'pass_phrase' ) } /> - - +

{ helpText }

); @@ -166,9 +163,52 @@ class PayFast extends Component { ); } + + render() { + const { installStep, isOptionsRequesting } = this.props; + + return ( + + ); + } } export default compose( + withSelect( ( select ) => { + const { getOptionsError, isUpdateOptionsRequesting } = select( + 'wc-api' + ); + const isOptionsRequesting = Boolean( + isUpdateOptionsRequesting( [ + 'woocommerce_currency', + 'woocommerce_payfast_settings', + ] ) + ); + const hasOptionsError = getOptionsError( [ + 'woocommerce_currency', + 'woocommerce_payfast_settings', + ] ); + + return { + hasOptionsError, + isOptionsRequesting, + }; + } ), withDispatch( ( dispatch ) => { const { createNotice } = dispatch( 'core/notices' ); const { updateOptions } = dispatch( 'wc-api' ); diff --git a/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/paypal.js b/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/paypal.js index cef19c47c56..8c722608968 100644 --- a/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/paypal.js +++ b/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/paypal.js @@ -2,110 +2,84 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { Component, Fragment } from '@wordpress/element'; import apiFetch from '@wordpress/api-fetch'; import { Button } from '@wordpress/components'; -import { withDispatch } from '@wordpress/data'; +import { Component, Fragment } from '@wordpress/element'; import { compose } from '@wordpress/compose'; import interpolateComponents from 'interpolate-components'; +import { withDispatch } from '@wordpress/data'; /** * WooCommerce dependencies */ -import { WC_ADMIN_NAMESPACE } from 'wc-api/constants'; +import { Form, Link, Stepper, TextControl } from '@woocommerce/components'; import { getQuery } from '@woocommerce/navigation'; -import { Form, Link, TextControl } from '@woocommerce/components'; +import { WC_ADMIN_NAMESPACE } from 'wc-api/constants'; import withSelect from 'wc-api/with-select'; -import { recordEvent } from 'lib/tracks'; class PayPal extends Component { constructor( props ) { super( props ); this.state = { + autoConnectFailed: false, connectURL: '', - showManualConfiguration: props.manualConfig, + isPending: false, }; this.updateSettings = this.updateSettings.bind( this ); } componentDidMount() { - const { showManualConfiguration } = this.state; + const { autoConnectFailed } = this.state; + const { createNotice, markConfigured } = this.props; const query = getQuery(); // Handle redirect back from PayPal if ( query[ 'paypal-connect' ] ) { if ( query[ 'paypal-connect' ] === '1' ) { - recordEvent( 'tasklist_payment_connect_method', { - payment_method: 'paypal', - } ); - this.props.markConfigured( 'paypal' ); - this.props.createNotice( + createNotice( 'success', __( 'PayPal connected successfully.', 'woocommerce-admin' ) ); + markConfigured( 'paypal' ); return; } /* eslint-disable react/no-did-mount-set-state */ this.setState( { - showManualConfiguration: true, + autoConnectFailed: true, } ); /* eslint-enable react/no-did-mount-set-state */ return; } - if ( ! showManualConfiguration ) { + if ( ! autoConnectFailed ) { this.fetchOAuthConnectURL(); } } - componentDidUpdate( prevProps, prevState ) { - if ( - prevState.showManualConfiguration === true && - this.state.showManualConfiguration === false - ) { - this.fetchOAuthConnectURL(); - } - - if ( - prevProps.optionsIsRequesting === false && - this.props.optionsIsRequesting === true - ) { - this.props.setRequestPending( true ); - } - - if ( - prevProps.optionsIsRequesting === true && - this.props.optionsIsRequesting === false - ) { - this.props.setRequestPending( false ); - } - } - async fetchOAuthConnectURL() { - this.props.setRequestPending( true ); + this.setState( { isPending: true } ); try { const result = await apiFetch( { path: WC_ADMIN_NAMESPACE + '/onboarding/plugins/connect-paypal', method: 'POST', } ); if ( ! result || ! result.connectUrl ) { - this.props.setRequestPending( false ); this.setState( { - showManualConfiguration: true, + autoConnectFailed: true, } ); return; } - this.props.setRequestPending( false ); this.setState( { connectURL: result.connectUrl, + isPending: false, } ); } catch ( error ) { - this.props.setRequestPending( false ); this.setState( { - showManualConfiguration: true, + autoConnectFailed: true, + isPending: false, } ); } } @@ -123,14 +97,14 @@ class PayPal extends Component { const { createNotice, isSettingsError, + options, updateOptions, markConfigured, } = this.props; - this.props.setRequestPending( true ); await updateOptions( { woocommerce_ppec_paypal_settings: { - ...this.props.options.woocommerce_ppec_paypal_settings, + ...options.woocommerce_ppec_paypal_settings, api_username: values.api_username, api_password: values.api_password, enabled: 'yes', @@ -138,17 +112,12 @@ class PayPal extends Component { } ); if ( ! isSettingsError ) { - recordEvent( 'tasklist_payment_connect_method', { - payment_method: 'paypal', - } ); - this.props.setRequestPending( false ); - markConfigured( 'paypal' ); - this.props.createNotice( + createNotice( 'success', __( 'PayPal connected successfully.', 'woocommerce-admin' ) ); + markConfigured( 'paypal' ); } else { - this.props.setRequestPending( false ); createNotice( 'error', __( @@ -186,7 +155,7 @@ class PayPal extends Component { } renderManualConfig() { - const { optionsIsRequesting } = this.props; + const { isOptionsRequesting } = this.props; const link = ( { __( 'Proceed', 'woocommerce-admin' ) } - -

{ help }

); @@ -254,18 +215,50 @@ class PayPal extends Component { ); } + getConnectStep() { + const { autoConnectFailed, connectURL, isPending } = this.state; + const connectStep = { + key: 'connect', + label: __( 'Connect your PayPal account', 'woocommerce-admin' ), + }; + + if ( isPending ) { + return connectStep; + } + + if ( ! autoConnectFailed && connectURL ) { + return { + ...connectStep, + description: __( + 'A Paypal account is required to process payments. You will be redirected to the Paypal website to create the connection.', + 'woocommerce-admin' + ), + content: this.renderConnectButton(), + }; + } + + return { + ...connectStep, + description: __( + 'Connect your store to your PayPal account. Don’t have a PayPal account? Create one.', + 'woocommerce-admin' + ), + content: this.renderManualConfig(), + }; + } + render() { - const { connectURL, showManualConfiguration } = this.state; + const { installStep } = this.props; + const { isPending } = this.state; - if ( connectURL && ! showManualConfiguration ) { - return this.renderConnectButton(); - } - - if ( showManualConfiguration ) { - return this.renderManualConfig(); - } - - return null; + return ( + + ); } } @@ -277,13 +270,13 @@ export default compose( withSelect( ( select ) => { const { getOptions, isGetOptionsRequesting } = select( 'wc-api' ); const options = getOptions( [ 'woocommerce_ppec_paypal_settings' ] ); - const optionsIsRequesting = Boolean( + const isOptionsRequesting = Boolean( isGetOptionsRequesting( [ 'woocommerce_ppec_paypal_settings' ] ) ); return { options, - optionsIsRequesting, + isOptionsRequesting, }; } ), withDispatch( ( dispatch ) => { diff --git a/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/square.js b/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/square.js index e8d9a8e9e42..febcd35427e 100644 --- a/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/square.js +++ b/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/square.js @@ -5,68 +5,50 @@ import { __ } from '@wordpress/i18n'; import { Component, Fragment } from '@wordpress/element'; import apiFetch from '@wordpress/api-fetch'; import { Button } from '@wordpress/components'; -import { getQuery } from '@woocommerce/navigation'; import { withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/compose'; /** * WooCommerce dependencies */ +import { getQuery } from '@woocommerce/navigation'; import { WC_ADMIN_NAMESPACE } from 'wc-api/constants'; import withSelect from 'wc-api/with-select'; -import { recordEvent } from 'lib/tracks'; +import { Stepper } from '@woocommerce/components'; class Square extends Component { constructor( props ) { super( props ); this.state = { - showSkipButton: false, + isPending: false, }; this.connect = this.connect.bind( this ); } componentDidMount() { + const { createNotice, markConfigured } = this.props; const query = getQuery(); // Handle redirect back from Square if ( query[ 'square-connect' ] ) { if ( query[ 'square-connect' ] === '1' ) { - recordEvent( 'tasklist_payment_connect_method', { - payment_method: 'square', - } ); - this.props.markConfigured( 'square' ); - this.props.createNotice( + createNotice( 'success', __( 'Square connected successfully.', 'woocommerce-admin' ) ); + markConfigured( 'square' ); } } } - componentDidUpdate( prevProps ) { - if ( - prevProps.optionsIsRequesting === false && - this.props.optionsIsRequesting === true - ) { - this.props.setRequestPending( true ); - } - - if ( - prevProps.optionsIsRequesting === true && - this.props.optionsIsRequesting === false - ) { - this.props.setRequestPending( false ); - } - } - async connect() { - const { updateOptions } = this.props; - this.props.setRequestPending( true ); + const { createNotice, options, updateOptions } = this.props; + this.setState( { isPending: true } ); updateOptions( { woocommerce_stripe_settings: { - ...this.props.options.woocommerce_stripe_settings, + ...options.woocommerce_stripe_settings, enabled: 'yes', }, } ); @@ -83,39 +65,55 @@ class Square extends Component { } ); if ( ! result || ! result.connectUrl ) { - this.props.setRequestPending( false ); - this.setState( { showSkipButton: true } ); - this.props.createNotice( 'error', errorMessage ); + this.setState( { isPending: false } ); + createNotice( 'error', errorMessage ); return; } - this.props.setRequestPending( false ); + this.setState( { isPending: true } ); window.location = result.connectUrl; } catch ( error ) { - this.props.setRequestPending( false ); - this.setState( { showSkipButton: true } ); - this.props.createNotice( 'error', errorMessage ); + this.setState( { isPending: false } ); + createNotice( 'error', errorMessage ); } } render() { - const { showSkipButton } = this.state; + const { installStep } = this.props; + const { isPending } = this.state; return ( - - - { showSkipButton && ( - - ) } - + + + + ), + }, + ] } + /> ); } } diff --git a/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/stripe.js b/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/stripe.js index 973dbf76aa8..367e9073b13 100644 --- a/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/stripe.js +++ b/plugins/woocommerce-admin/client/dashboard/task-list/tasks/payments/stripe.js @@ -8,124 +8,149 @@ import apiFetch from '@wordpress/api-fetch'; import { withDispatch } from '@wordpress/data'; import interpolateComponents from 'interpolate-components'; import { Button, Modal } from '@wordpress/components'; -import { getQuery } from '@woocommerce/navigation'; import { get } from 'lodash'; /** * WooCommerce dependencies */ -import { Form, Link, TextControl } from '@woocommerce/components'; -import withSelect from 'wc-api/with-select'; +import { Form, Link, Stepper, TextControl } from '@woocommerce/components'; +import { getAdminLink } from '@woocommerce/wc-admin-settings'; +import { getQuery } from '@woocommerce/navigation'; import { WCS_NAMESPACE } from 'wc-api/constants'; -import { recordEvent } from 'lib/tracks'; +import withSelect from 'wc-api/with-select'; + +/** + * Internal dependencies + */ +import { getCountryCode } from 'dashboard/utils'; class Stripe extends Component { constructor( props ) { super( props ); this.state = { - errorMessage: '', - connectURL: '', - showConnectionButtons: - ! props.manualConfig && ! props.createAccount, - showManualConfiguration: props.manualConfig, + autoConnectFailed: false, + connectURL: null, + errorTitle: null, + errorMessage: null, + isPending: true, }; + this.autoCreateAccount = this.autoCreateAccount.bind( this ); this.updateSettings = this.updateSettings.bind( this ); } componentDidMount() { - const { createAccount, options } = this.props; - const { showConnectionButtons } = this.state; - + const { stripeSettings } = this.props; const query = getQuery(); // Handle redirect back from Stripe. if ( query[ 'stripe-connect' ] && query[ 'stripe-connect' ] === '1' ) { - const stripeSettings = get( - options, - [ 'woocommerce_stripe_settings' ], - [] - ); const isStripeConnected = stripeSettings.publishable_key && stripeSettings.secret_key; if ( isStripeConnected ) { - recordEvent( 'tasklist_payment_connect_method', { - payment_method: 'stripe', - } ); - this.props.markConfigured( 'stripe' ); - this.props.createNotice( - 'success', - __( 'Stripe connected successfully.', 'woocommerce-admin' ) - ); + this.completeMethod(); return; } /* eslint-disable react/no-did-mount-set-state */ this.setState( { - showConnectionButtons: false, - showManualConfiguration: true, + autoConnectFailed: true, } ); /* eslint-enable react/no-did-mount-set-state */ - return; } - if ( createAccount ) { - this.autoCreateAccount(); - } - - if ( showConnectionButtons ) { + if ( ! this.requiresManualConfig() ) { this.fetchOAuthConnectURL(); } } - componentDidUpdate( prevProps, prevState ) { - if ( - prevState.showConnectionButtons === false && - this.state.showConnectionButtons - ) { - this.fetchOAuthConnectURL(); + componentDidUpdate( prevProps ) { + const { + createNotice, + isOptionsRequesting, + hasOptionsError, + } = this.props; + + if ( prevProps.isOptionsRequesting && ! isOptionsRequesting ) { + if ( ! hasOptionsError ) { + this.completeMethod(); + } else { + createNotice( + 'error', + __( + 'There was a problem saving your payment setings', + 'woocommerce-admin' + ) + ); + } } } + requiresManualConfig() { + const { activePlugins, isJetpackConnected } = this.props; + const { autoConnectFailed } = this.state; + + return ( + ! isJetpackConnected || + ! activePlugins.includes( 'woocommerce-services' ) || + autoConnectFailed + ); + } + + completeMethod() { + const { createNotice, markConfigured } = this.props; + + this.setState( { isPending: false } ); + + createNotice( + 'success', + __( 'Stripe connected successfully.', 'woocommerce-admin' ) + ); + + markConfigured( 'stripe' ); + } + async fetchOAuthConnectURL() { - const { returnUrl } = this.props; try { - this.props.setRequestPending( true ); + this.setState( { isPending: true } ); const result = await apiFetch( { path: WCS_NAMESPACE + '/connect/stripe/oauth/init', method: 'POST', data: { - returnUrl, + returnUrl: getAdminLink( + 'admin.php?page=wc-admin&task=payments&method=stripe&stripe-connect=1' + ), }, } ); if ( ! result || ! result.oauthUrl ) { - this.props.setRequestPending( false ); this.setState( { - showConnectionButtons: false, - showManualConfiguration: true, + autoConnectFailed: true, + isPending: false, } ); return; } - this.props.setRequestPending( false ); this.setState( { connectURL: result.oauthUrl, + isPending: false, } ); } catch ( error ) { - this.props.setRequestPending( false ); - // Fallback to manual configuration if the OAuth URL cannot be grabbed. this.setState( { - showConnectionButtons: false, - showManualConfiguration: true, + autoConnectFailed: true, + isPending: false, } ); } } - async autoCreateAccount() { - const { email, countryCode } = this.props; + async autoCreateAccount( values ) { + const { countryCode } = this.props; + const { connectURL } = this.state; + const { email } = values; + try { - this.props.setRequestPending( true ); + this.setState( { isPending: true } ); + const result = await apiFetch( { path: WCS_NAMESPACE + '/connect/stripe/account', method: 'POST', @@ -136,29 +161,13 @@ class Stripe extends Component { } ); if ( result ) { - recordEvent( 'tasklist_payment_connect_method', { - payment_method: 'stripe', - } ); - this.props.setRequestPending( false ); - this.props.markConfigured( 'stripe' ); - this.props.createNotice( - 'success', - __( 'Stripe connected successfully.', 'woocommerce-admin' ) - ); + this.completeMethod(); return; } - } catch ( error ) { - this.props.setRequestPending( false ); - let errorTitle, errorMessage; - // This seems to be the best way to handle this. - // github.com/Automattic/woocommerce-services/blob/cfb6173deb3c72897ee1d35b8fdcf29c5a93dea2/woocommerce-services.php#L563-L570 - if ( - error.message.indexOf( - 'Account already exists for the provided email' - ) === -1 - ) { - errorTitle = __( 'Stripe', 'woocommerce-admin' ); - errorMessage = interpolateComponents( { + } catch { + if ( ! connectURL ) { + const errorTitle = __( 'Stripe', 'woocommerce-admin' ); + const errorMessage = interpolateComponents( { mixedString: sprintf( __( 'We tried to create a Stripe account automatically for {{strong}}%s{{/strong}}, but an error occured. Please try connecting manually to continue.', @@ -170,30 +179,17 @@ class Stripe extends Component { strong: , }, } ); - } else { - errorTitle = __( - 'You already have a Stripe account', - 'woocommerce-admin' - ); - errorMessage = interpolateComponents( { - mixedString: sprintf( - __( - 'We tried to create a Stripe account automatically for {{strong}}%s{{/strong}}, but one already exists. Please sign in and connect to continue.', - 'woocommerce-admin' - ), - email - ), - components: { - strong: , - }, - } ); - } - this.setState( { - showConnectionButtons: true, - errorTitle, - errorMessage, - } ); + this.setState( { + autoConnectFailed: true, + errorTitle, + errorMessage, + isPending: false, + } ); + } else { + // An account with that email may exist so send them to Stripe to connect via oAuth. + window.location = connectURL; + } } } @@ -203,7 +199,7 @@ class Stripe extends Component { - this.setState( { errorMessage: '', errorTitle: '' } ) + this.setState( { errorMessage: null, errorTitle: null } ) } className="woocommerce-task-payments__stripe-error-modal" > @@ -216,8 +212,8 @@ class Stripe extends Component { isDefault onClick={ () => this.setState( { - errorMessage: '', - errorTitle: '', + errorMessage: null, + errorTitle: null, } ) } > @@ -228,53 +224,53 @@ class Stripe extends Component { ); } - renderConnectButton() { - const { connectURL } = this.state; + renderAutoConnect() { + const { isPending } = this.state; + return ( - +
+ { ( { getInputProps, handleSubmit } ) => { + return ( +
+ + +
+ ); + } } +
); } - async updateSettings( values ) { - const { - createNotice, - isSettingsError, - updateOptions, - markConfigured, - } = this.props; + updateSettings( values ) { + const { updateOptions, stripeSettings } = this.props; - this.props.setRequestPending( true ); - await updateOptions( { + updateOptions( { woocommerce_stripe_settings: { - ...this.props.options.woocommerce_stripe_settings, + ...stripeSettings, publishable_key: values.publishable_key, secret_key: values.secret_key, enabled: 'yes', }, } ); - - if ( ! isSettingsError ) { - recordEvent( 'tasklist_payment_connect_method', { - payment_method: 'stripe', - } ); - this.props.setRequestPending( false ); - markConfigured( 'stripe' ); - this.props.createNotice( - 'success', - __( 'Stripe connected successfully.', 'woocommerce-admin' ) - ); - } else { - this.props.setRequestPending( false ); - createNotice( - 'error', - __( - 'There was a problem saving your payment settings.', - 'woocommerce-admin' - ) - ); - } } getInitialConfigValues() { @@ -284,7 +280,7 @@ class Stripe extends Component { }; } - validate( values ) { + validateManualConfig( values ) { const errors = {}; if ( ! values.publishable_key ) { @@ -303,16 +299,34 @@ class Stripe extends Component { return errors; } + validateAutoConnect( values ) { + const errors = {}; + + if ( ! values.email ) { + errors.email = __( 'Please enter your email', 'woocommerce-admin' ); + } + + return errors; + } + renderManualConfig() { + const { isOptionsRequesting } = this.props; const stripeHelp = interpolateComponents( { mixedString: __( - 'Your API details can be obtained from your {{link}}Stripe account{{/link}}', + 'Your API details can be obtained from your {{docsLink}}Stripe account{{/docsLink}}. Don’t have a Stripe account? {{registerLink}}Create one.{{/registerLink}}', 'woocommerce-admin' ), components: { - link: ( + docsLink: ( + ), + registerLink: ( + @@ -324,7 +338,7 @@ class Stripe extends Component {
{ ( { getInputProps, handleSubmit } ) => { return ( @@ -346,16 +360,12 @@ class Stripe extends Component { { ...getInputProps( 'secret_key' ) } /> - -

{ stripeHelp }

@@ -366,36 +376,98 @@ class Stripe extends Component { ); } - render() { - const { - errorMessage, - showConnectionButtons, - connectURL, - showManualConfiguration, - } = this.state; + getConnectStep() { + const { autoConnectFailed, connectURL, errorMessage } = this.state; + const connectStep = { + key: 'connect', + label: __( 'Connect your Stripe account', 'woocommerce-admin' ), + }; if ( errorMessage ) { - return this.renderErrorModal(); + return { + ...connectStep, + content: this.renderErrorModal(), + }; } - if ( showConnectionButtons && connectURL ) { - return this.renderConnectButton(); + if ( ! this.requiresManualConfig() ) { + // We may still be fetching the connect URL. + if ( ! autoConnectFailed && ! connectURL ) { + return connectStep; + } + + return { + ...connectStep, + description: __( + 'A Stripe account is required to process payments. We’ll create an account for you if you don’t have one already.', + 'woocommerce-admin' + ), + content: this.renderAutoConnect(), + }; } - if ( showManualConfiguration ) { - return this.renderManualConfig(); - } + return { + ...connectStep, + description: __( + 'Connect your store to your Stripe account. Don’t have a Stripe account? Create one.', + 'woocommerce-admin' + ), + content: this.renderManualConfig(), + }; + } - return null; + render() { + const { installStep, isOptionsRequesting } = this.props; + const { isPending } = this.state; + + return ( + + ); } } export default compose( withSelect( ( select ) => { - const { getOptions } = select( 'wc-api' ); - const options = getOptions( [ 'woocommerce_stripe_settings' ] ); - return { + const { + getActivePlugins, + getOptions, + getOptionsError, + isJetpackConnected, + isUpdateOptionsRequesting, + } = select( 'wc-api' ); + const options = getOptions( [ + 'woocommerce_stripe_settings', + 'woocommerce_default_country', + ] ); + const countryCode = getCountryCode( + options.woocommerce_default_country + ); + const stripeSettings = get( options, + [ 'woocommerce_stripe_settings' ], + [] + ); + const isOptionsRequesting = Boolean( + isUpdateOptionsRequesting( [ 'woocommerce_stripe_settings' ] ) + ); + const hasOptionsError = getOptionsError( [ + 'woocommerce_stripe_settings', + ] ); + + return { + activePlugins: getActivePlugins(), + countryCode, + hasOptionsError, + isJetpackConnected: isJetpackConnected(), + isOptionsRequesting, + stripeSettings, }; } ), withDispatch( ( dispatch ) => { diff --git a/plugins/woocommerce-admin/client/wc-api/onboarding/constants.js b/plugins/woocommerce-admin/client/wc-api/onboarding/constants.js index 1785e44d0c7..92b12ce120b 100644 --- a/plugins/woocommerce-admin/client/wc-api/onboarding/constants.js +++ b/plugins/woocommerce-admin/client/wc-api/onboarding/constants.js @@ -7,18 +7,39 @@ import { __ } from '@wordpress/i18n'; * Plugin slugs and names as key/value pairs. */ export const pluginNames = { - jetpack: __( 'Jetpack', 'woocommerce-admin' ), - 'woocommerce-services': __( 'WooCommerce Services', 'woocommerce-admin' ), - 'mailchimp-for-woocommerce': __( - 'Mailchimp for WooCommerce', - 'woocommerce-admin' - ), 'facebook-for-woocommerce': __( 'Facebook for WooCommerce', 'woocommerce-admin' ), + jetpack: __( 'Jetpack', 'woocommerce-admin' ), + 'klarna-checkout-for-woocommerce': __( + 'Klarna Checkout for WooCommerce', + 'woocommerce-admin' + ), + 'klarna-payments-for-woocommerce': __( + 'Klarna Payments for WooCommerce', + 'woocommerce-admin' + ), + 'mailchimp-for-woocommerce': __( + 'Mailchimp for WooCommerce', + 'woocommerce-admin' + ), + 'woocommerce-gateway-paypal-express-checkout': __( + 'WooCommerce PayPal', + 'woocommerce-admin' + ), + 'woocommerce-gateway-stripe': __( + 'WooCommerce Stripe', + 'woocommerce-admin' + ), + 'woocommerce-payfast-gateway': __( + 'WooCommerce PayFast', + 'woocommerce-admin' + ), + 'woocommerce-services': __( 'WooCommerce Services', 'woocommerce-admin' ), 'woocommerce-shipstation-integration': __( 'WooCommerce ShipStation Gateway', 'woocommerce-admin' ), + 'woocommerce-square': __( 'WooCommerce Square', 'woocommerce-admin' ), }; diff --git a/plugins/woocommerce-admin/src/API/OnboardingPlugins.php b/plugins/woocommerce-admin/src/API/OnboardingPlugins.php index 77976594283..a278ffa9a22 100644 --- a/plugins/woocommerce-admin/src/API/OnboardingPlugins.php +++ b/plugins/woocommerce-admin/src/API/OnboardingPlugins.php @@ -317,7 +317,7 @@ class OnboardingPlugins extends \WC_REST_Data_Controller { $redirect_url = apply_filters( 'woocommerce_admin_onboarding_jetpack_connect_redirect_url', esc_url_raw( $request['redirect_url'] ) ); $connect_url = \Jetpack::init()->build_connect_url( true, $redirect_url, 'woocommerce-onboarding' ); - $calypso_env = defined( 'WOOCOMMERCE_CALYPSO_ENVIRONMENT' ) && in_array( WOOCOMMERCE_CALYPSO_ENVIRONMENT, array( 'development', 'wpcalypso', 'horizon', 'stage' ) ) ? WOOCOMMERCE_CALYPSO_ENVIRONMENT : 'production'; + $calypso_env = defined( 'WOOCOMMERCE_CALYPSO_ENVIRONMENT' ) && in_array( WOOCOMMERCE_CALYPSO_ENVIRONMENT, array( 'development', 'wpcalypso', 'horizon', 'stage' ), true ) ? WOOCOMMERCE_CALYPSO_ENVIRONMENT : 'production'; $connect_url = add_query_arg( array( 'calypso_env' => $calypso_env ), $connect_url ); return( array( @@ -372,7 +372,7 @@ class OnboardingPlugins extends \WC_REST_Data_Controller { \WC_Helper_API::url( 'oauth/authorize' ) ); - if ( defined( 'WOOCOMMERCE_CALYPSO_ENVIRONMENT' ) && in_array( WOOCOMMERCE_CALYPSO_ENVIRONMENT, array( 'development', 'wpcalypso', 'horizon', 'stage' ) ) ) { + if ( defined( 'WOOCOMMERCE_CALYPSO_ENVIRONMENT' ) && in_array( WOOCOMMERCE_CALYPSO_ENVIRONMENT, array( 'development', 'wpcalypso', 'horizon', 'stage' ), true ) ) { $connect_url = add_query_arg( array( 'calypso_env' => WOOCOMMERCE_CALYPSO_ENVIRONMENT, @@ -463,12 +463,12 @@ class OnboardingPlugins extends \WC_REST_Data_Controller { 'env' => 'live', 'wc_ppec_ips_admin_nonce' => wp_create_nonce( 'wc_ppec_ips' ), ), - wc_admin_url( '&task=payments&paypal-connect-finish=1' ) + wc_admin_url( '&task=payments&method=paypal&paypal-connect-finish=1' ) ); // https://github.com/woocommerce/woocommerce-gateway-paypal-express-checkout/blob/b6df13ba035038aac5024d501e8099a37e13d6cf/includes/class-wc-gateway-ppec-ips-handler.php#L79-L93. $query_args = array( - 'redirect' => urlencode( $redirect_url ), + 'redirect' => rawurlencode( $redirect_url ), 'countryCode' => WC()->countries->get_base_country(), 'merchantId' => md5( site_url( '/' ) . time() ), ); @@ -491,9 +491,9 @@ class OnboardingPlugins extends \WC_REST_Data_Controller { $url = \WooCommerce\Square\Handlers\Connection::CONNECT_URL_PRODUCTION; - $redirect_url = wp_nonce_url( wc_admin_url( '&task=payments&square-connect-finish=1' ), 'wc_square_connected' ); + $redirect_url = wp_nonce_url( wc_admin_url( '&task=payments&method=square&square-connect-finish=1' ), 'wc_square_connected' ); $args = array( - 'redirect' => urlencode( urlencode( $redirect_url ) ), + 'redirect' => rawurlencode( rawurlencode( $redirect_url ) ), 'scopes' => implode( ',', array( diff --git a/plugins/woocommerce-admin/src/Features/Onboarding.php b/plugins/woocommerce-admin/src/Features/Onboarding.php index b904c9f1195..94e865c9742 100644 --- a/plugins/woocommerce-admin/src/Features/Onboarding.php +++ b/plugins/woocommerce-admin/src/Features/Onboarding.php @@ -655,7 +655,7 @@ class Onboarding { */ public function is_loading( $is_loading ) { $show_profiler = self::should_show_profiler(); - $is_dashboard = ! isset( $_GET['path'] ); // WPCS: csrf ok. + $is_dashboard = ! isset( $_GET['path'] ); // phpcs:ignore csrf ok. if ( ! $show_profiler || ! $is_dashboard ) { return $is_loading; @@ -675,7 +675,7 @@ class Onboarding { if ( substr( $location, -strlen( $settings_page ) ) === $settings_page ) { $settings_array = (array) get_option( 'woocommerce_ppec_paypal_settings', array() ); $connected = isset( $settings_array['api_username'] ) && isset( $settings_array['api_password'] ) ? true : false; - return wc_admin_url( '&task=payments&paypal-connect=' . $connected ); + return wc_admin_url( '&task=payments&method=paypal&paypal-connect=' . $connected ); } return $location; } @@ -686,7 +686,7 @@ class Onboarding { public function finish_paypal_connect() { if ( ! Loader::is_admin_page() || - ! isset( $_GET['paypal-connect-finish'] ) // WPCS: CSRF ok. + ! isset( $_GET['paypal-connect-finish'] ) // phpcs:ignore CSRF ok. ) { return; } @@ -711,7 +711,7 @@ class Onboarding { public function overwrite_square_redirect( $location, $status ) { $settings_page = 'page=wc-settings&tab=square'; if ( substr( $location, -strlen( $settings_page ) ) === $settings_page ) { - return wc_admin_url( '&task=payments&square-connect=1' ); + return wc_admin_url( '&task=payments&method=square&square-connect=1' ); } return $location; } @@ -722,7 +722,7 @@ class Onboarding { public function finish_square_connect() { if ( ! Loader::is_admin_page() || - ! isset( $_GET['square-connect-finish'] ) // WPCS: CSRF ok. + ! isset( $_GET['square-connect-finish'] ) // phpcs:ignore CSRF ok. ) { return; } @@ -873,9 +873,9 @@ class Onboarding { * Allows quick access to testing the calypso parts of onboarding. */ public static function calypso_tests() { - $calypso_env = defined( 'WOOCOMMERCE_CALYPSO_ENVIRONMENT' ) && in_array( WOOCOMMERCE_CALYPSO_ENVIRONMENT, array( 'development', 'wpcalypso', 'horizon', 'stage' ) ) ? WOOCOMMERCE_CALYPSO_ENVIRONMENT : 'production'; + $calypso_env = defined( 'WOOCOMMERCE_CALYPSO_ENVIRONMENT' ) && in_array( WOOCOMMERCE_CALYPSO_ENVIRONMENT, array( 'development', 'wpcalypso', 'horizon', 'stage' ), true ) ? WOOCOMMERCE_CALYPSO_ENVIRONMENT : 'production'; - if ( Loader::is_admin_page() && class_exists( 'Jetpack' ) && isset( $_GET['test_wc_jetpack_connect'] ) && 1 === absint( $_GET['test_wc_jetpack_connect'] ) ) { // WPCS: CSRF ok. + if ( Loader::is_admin_page() && class_exists( 'Jetpack' ) && isset( $_GET['test_wc_jetpack_connect'] ) && 1 === absint( $_GET['test_wc_jetpack_connect'] ) ) { // phpcs:ignore CSRF ok. $redirect_url = esc_url_raw( add_query_arg( array( @@ -892,7 +892,7 @@ class Onboarding { exit; } - if ( Loader::is_admin_page() && isset( $_GET['test_wc_helper_connect'] ) && 1 === absint( $_GET['test_wc_helper_connect'] ) ) { // WPCS: CSRF ok. + if ( Loader::is_admin_page() && isset( $_GET['test_wc_helper_connect'] ) && 1 === absint( $_GET['test_wc_helper_connect'] ) ) { // phpcs:ignore CSRF ok. include_once WC_ABSPATH . 'includes/admin/helper/class-wc-helper-api.php'; $redirect_uri = wc_admin_url( '&task=connect&wccom-connected=1' ); @@ -942,12 +942,12 @@ class Onboarding { public static function reset_profiler() { if ( ! Loader::is_admin_page() || - ! isset( $_GET['reset_profiler'] ) // WPCS: CSRF ok. + ! isset( $_GET['reset_profiler'] ) // phpcs:ignore CSRF ok. ) { return; } - $previous = 1 === absint( $_GET['reset_profiler'] ); + $previous = 1 === absint( $_GET['reset_profiler'] ); // phpcs:ignore CSRF ok. $new_value = ! $previous; wc_admin_record_tracks_event( @@ -978,12 +978,12 @@ class Onboarding { public static function reset_task_list() { if ( ! Loader::is_admin_page() || - ! isset( $_GET['reset_task_list'] ) // WPCS: CSRF ok. + ! isset( $_GET['reset_task_list'] ) // phpcs:ignore CSRF ok. ) { return; } - $new_value = 1 === absint( $_GET['reset_task_list'] ) ? 'no' : 'yes'; // WPCS: CSRF ok. + $new_value = 1 === absint( $_GET['reset_task_list'] ) ? 'no' : 'yes'; // phpcs:ignore CSRF ok. update_option( 'woocommerce_task_list_hidden', $new_value ); wp_safe_redirect( wc_admin_url() ); exit;