Refactor payments to allow management of methods (https://github.com/woocommerce/woocommerce-admin/pull/6786)

* Add manage button for enabled and configured payments

* Enable methods only instead of toggling

* Update payment methods location

* Move ribbon to separate component

* Move setup container to separate component

* Move payment action to separate component

* Refactor payments to functional component

* Fix missing action props

* Fix broken onsetup callback

* Update payment method keys to match management pages

* Add changelog and testing instructions

* Revert key changes in favor of manageUrl

* Update e2e tests for payments
This commit is contained in:
Joshua T Flowers 2021-04-14 15:26:50 -04:00 committed by GitHub
parent 3745a6a74e
commit eb6186a29c
28 changed files with 439 additions and 389 deletions

View File

@ -11,6 +11,15 @@
5. Navigate to Homescreen. 5. Navigate to Homescreen.
6. Navigate back to previous Analytics Report. 6. Navigate back to previous Analytics Report.
7. Ensure that the time period is _still_ what you set on step 2. 7. Ensure that the time period is _still_ what you set on step 2.
### Refactor payments to allow management of methods #6786
1. Do not select "CBD industry" as a store industry during onboarding.
2. Make various payment methods visible by switching to different countries.
3. Attempt to set up various payment methods.
4. Make sure that after setup, a `Manage` link is shown that links to the payment method settings page.
5. Check that simple methods like, cash delivery or bank transfer initially have an `Enable` option.
### Fix varation bug with Products reports #6647
### Fix varation bug with Products reports #6647 ### Fix varation bug with Products reports #6647

View File

@ -36,7 +36,7 @@ const StoreAlerts = lazy( () =>
const WCPayUsageModal = lazy( () => const WCPayUsageModal = lazy( () =>
import( import(
/* webpackChunkName: "wcpay-usage-modal" */ '../task-list/tasks/payments/wcpay/wcpay-usage-modal' /* webpackChunkName: "wcpay-usage-modal" */ '../task-list/tasks/payments/methods/wcpay/wcpay-usage-modal'
) )
); );

View File

@ -22,7 +22,7 @@ import { AppIllustration } from '../app-illustration';
import './style.scss'; import './style.scss';
import { setAllPropsToValue } from '~/lib/collections'; import { setAllPropsToValue } from '~/lib/collections';
import { getCountryCode } from '~/dashboard/utils'; import { getCountryCode } from '~/dashboard/utils';
import { isWCPaySupported } from '~/task-list/tasks/payments/wcpay'; import { isWCPaySupported } from '~/task-list/tasks/payments/methods/wcpay';
const generatePluginDescriptionWithLink = ( const generatePluginDescriptionWithLink = (
description, description,

View File

@ -24,7 +24,7 @@ import Payments from './tasks/payments';
import { import {
installActivateAndConnectWcpay, installActivateAndConnectWcpay,
isWCPaySupported, isWCPaySupported,
} from './tasks/payments/wcpay'; } from './tasks/payments/methods/wcpay';
import { groupListOfObjectsBy } from '../lib/collections'; import { groupListOfObjectsBy } from '../lib/collections';
import { getLinkTypeAndHref } from '~/store-management-links'; import { getLinkTypeAndHref } from '~/store-management-links';

View File

@ -0,0 +1,98 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Button, Spinner } from '@wordpress/components';
import { getAdminLink } from '@woocommerce/wc-admin-settings';
import { updateQueryString } from '@woocommerce/navigation';
import { useState } from '@wordpress/element';
export const Action = ( {
hasSetup = false,
isConfigured = false,
isEnabled = false,
isLoading = false,
isRecommended = false,
manageUrl = null,
markConfigured,
methodKey,
onSetUp = () => {},
onSetupCallback,
} ) => {
const [ isBusy, setIsBusy ] = useState( false );
const classes = 'woocommerce-task-payment__action';
if ( isLoading ) {
return <Spinner />;
}
const handleClick = async () => {
onSetUp( methodKey );
if ( onSetupCallback ) {
setIsBusy( true );
await new Promise( onSetupCallback )
.then( () => {
setIsBusy( false );
} )
.catch( () => {
setIsBusy( false );
} );
return;
}
updateQueryString( {
method: methodKey,
} );
};
if ( hasSetup && ! isConfigured ) {
return (
<div>
<Button
className={ classes }
isPrimary={ isRecommended }
isSecondary={ ! isRecommended }
isBusy={ isBusy }
disabled={ isBusy }
onClick={ () => handleClick() }
>
{ __( 'Set up', 'woocommerce-admin' ) }
</Button>
</div>
);
}
if ( ( hasSetup && isConfigured ) || ( ! hasSetup && isEnabled ) ) {
if ( ! manageUrl ) {
return null;
}
return (
<div>
<Button
className={ classes }
isSecondary
href={ getAdminLink(
'admin.php?page=wc-settings&tab=checkout&section=' +
methodKey
) }
>
{ __( 'Manage', 'woocommerce-admin' ) }
</Button>
</div>
);
}
return (
<Button
className={ classes }
isSecondary
onClick={ () => markConfigured( methodKey ) }
>
{ __( 'Enable', 'woocommerce-admin' ) }
</Button>
);
};

View File

@ -1,41 +1,28 @@
/** /**
* External dependencies * External dependencies
*/ */
import { __, sprintf } from '@wordpress/i18n';
import classnames from 'classnames'; import classnames from 'classnames';
import { cloneElement, Component } from '@wordpress/element'; import { Card, CardBody, CardMedia, CardFooter } from '@wordpress/components';
import { compose } from '@wordpress/compose'; import { useDispatch, useSelect } from '@wordpress/data';
import { import { H } from '@woocommerce/components';
Button, import { getHistory, getNewPath } from '@woocommerce/navigation';
Card,
CardBody,
CardMedia,
CardFooter,
FormToggle,
Spinner,
} from '@wordpress/components';
import { withDispatch, withSelect } from '@wordpress/data';
import { H, Plugins } from '@woocommerce/components';
import {
getHistory,
getNewPath,
updateQueryString,
} from '@woocommerce/navigation';
import { import {
ONBOARDING_STORE_NAME, ONBOARDING_STORE_NAME,
OPTIONS_STORE_NAME, OPTIONS_STORE_NAME,
PLUGINS_STORE_NAME, PLUGINS_STORE_NAME,
pluginNames,
SETTINGS_STORE_NAME, SETTINGS_STORE_NAME,
} from '@woocommerce/data'; } from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks'; import { recordEvent } from '@woocommerce/tracks';
import { useMemo, useState } from '@wordpress/element';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { createNoticesFromResponse } from '../../../lib/notices'; import { Action } from './action';
import { getCountryCode } from '../../../dashboard/utils'; import { getCountryCode } from '../../../dashboard/utils';
import { getPaymentMethods } from './methods'; import { getPaymentMethods } from './methods';
import { RecommendedRibbon } from './recommended-ribbon';
import { Setup } from './setup';
export const setMethodEnabledOption = async ( export const setMethodEnabledOption = async (
optionName, optionName,
@ -57,331 +44,19 @@ export const setMethodEnabledOption = async (
} }
}; };
class Payments extends Component { export const Payments = ( { query } ) => {
constructor( props ) { const { createNotice } = useDispatch( 'core/notices' );
super( ...arguments );
const { methods } = props;
const enabledMethods = {};
methods.forEach(
( method ) => ( enabledMethods[ method.key ] = method.isEnabled )
);
this.state = {
busyMethod: null,
enabledMethods,
recommendedMethod: this.getRecommendedMethod(),
};
this.markConfigured = this.markConfigured.bind( this );
}
componentDidUpdate() {
const { recommendedMethod } = this.state;
const method = this.getRecommendedMethod();
if ( recommendedMethod !== method ) {
this.setState( {
recommendedMethod: method,
} );
}
}
getRecommendedMethod() {
const { methods } = this.props;
const recommendedMethod = methods.find(
( m ) =>
( m.key === 'wcpay' && m.visible ) ||
( m.key === 'mercadopago' && m.visible )
);
if ( ! recommendedMethod ) {
return 'stripe';
}
return recommendedMethod.key;
}
async markConfigured( methodName, queryParams = {} ) {
const { enabledMethods } = this.state;
const { methods } = this.props;
const method = methods.find( ( option ) => option.key === methodName );
if ( ! method ) {
throw `Method ${ methodName } not found in available methods list`;
}
this.setState( {
enabledMethods: {
...enabledMethods,
[ methodName ]: true,
},
} );
await setMethodEnabledOption( method.optionName, 'yes', this.props );
recordEvent( 'tasklist_payment_connect_method', {
payment_method: methodName,
} );
getHistory().push(
getNewPath( { ...queryParams, task: 'payments' }, '/', {} )
);
}
getCurrentMethod() {
const { methods, query } = this.props;
if ( ! query.method ) {
return;
}
const currentMethod = methods.find(
( method ) => method.key === query.method
);
if ( ! currentMethod ) {
throw `Current method ${ query.method } not found in available methods list`;
}
return currentMethod;
}
getInstallStep() {
const currentMethod = this.getCurrentMethod();
if ( ! currentMethod.plugins || ! currentMethod.plugins.length ) {
return;
}
const { activePlugins } = this.props;
const pluginsToInstall = currentMethod.plugins.filter(
( method ) => ! activePlugins.includes( method )
);
const pluginNamesString = currentMethod.plugins
.map( ( pluginSlug ) => pluginNames[ pluginSlug ] )
.join( ' ' + __( 'and', 'woocommerce-admin' ) + ' ' );
return {
key: 'install',
label: sprintf(
__( 'Install %s', 'woocommerce-admin' ),
pluginNamesString
),
content: (
<Plugins
onComplete={ ( plugins, response ) => {
createNoticesFromResponse( response );
recordEvent( 'tasklist_payment_install_method', {
plugins: currentMethod.plugins,
} );
} }
onError={ ( errors, response ) =>
createNoticesFromResponse( response )
}
autoInstall
pluginSlugs={ currentMethod.plugins }
/>
),
isComplete: ! pluginsToInstall.length,
};
}
async toggleMethod( key ) {
const { methods } = this.props;
const { enabledMethods } = this.state;
const method = methods.find( ( option ) => option.key === key );
if ( ! method ) {
throw `Method ${ key } not found in available methods list`;
}
enabledMethods[ key ] = ! enabledMethods[ key ];
this.setState( { enabledMethods } );
recordEvent( 'tasklist_payment_toggle', {
enabled: ! method.isEnabled,
payment_method: key,
} );
await setMethodEnabledOption(
method.optionName,
method.isEnabled ? 'no' : 'yes',
this.props
);
}
async handleClick( method ) {
const { methods } = this.props;
const { key, onClick } = method;
recordEvent( 'tasklist_payment_setup', {
options: methods.map( ( option ) => option.key ),
selected: key,
} );
if ( onClick ) {
this.setState( { busyMethod: key } );
await new Promise( onClick )
.then( () => {
this.setState( { busyMethod: null } );
} )
.catch( () => {
this.setState( { busyMethod: null } );
} );
return;
}
updateQueryString( {
method: key,
} );
}
getSetupButtons( method ) {
const { busyMethod, enabledMethods, recommendedMethod } = this.state;
const { container, isConfigured, key } = method;
if ( container && ! isConfigured ) {
return (
<div>
<Button
isPrimary={ key === recommendedMethod }
isSecondary={ key !== recommendedMethod }
isBusy={ busyMethod === key }
disabled={ busyMethod }
onClick={ () => this.handleClick( method ) }
>
{ __( 'Set up', 'woocommerce-admin' ) }
</Button>
</div>
);
}
return (
<FormToggle
checked={ enabledMethods[ key ] }
onChange={ () => this.toggleMethod( key ) }
onClick={ ( e ) => e.stopPropagation() }
/>
);
}
render() {
const currentMethod = this.getCurrentMethod();
const { recommendedMethod } = this.state;
const { methods, query } = this.props;
if ( currentMethod ) {
return (
<Card className="woocommerce-task-payment-method woocommerce-task-card">
<CardBody>
{ cloneElement( currentMethod.container, {
methodConfig: currentMethod,
query,
installStep: this.getInstallStep(),
markConfigured: this.markConfigured,
hasCbdIndustry: currentMethod.hasCbdIndustry,
} ) }
</CardBody>
</Card>
);
}
return (
<div className="woocommerce-task-payments">
{ methods.map( ( method ) => {
const {
before,
content,
isConfigured,
key,
title,
visible,
loading,
} = method;
if ( ! visible ) {
return null;
}
const classes = classnames(
'woocommerce-task-payment',
'woocommerce-task-card',
! isConfigured &&
'woocommerce-task-payment-not-configured',
'woocommerce-task-payment-' + key
);
const isRecommended =
key === recommendedMethod && ! isConfigured;
const showRecommendedRibbon =
isRecommended && key !== 'wcpay';
const showRecommendedPill =
isRecommended && key === 'wcpay';
const recommendedText =
key === 'mercadopago'
? __( 'Local Partner', 'woocommerce-admin' )
: __( 'Recommended', 'woocommerce-admin' );
return (
<Card key={ key } className={ classes }>
{ showRecommendedRibbon && (
<div className="woocommerce-task-payment__recommended-ribbon">
<span>{ recommendedText }</span>
</div>
) }
<CardMedia isBorderless>{ before }</CardMedia>
<CardBody>
<H className="woocommerce-task-payment__title">
{ title }
{ showRecommendedPill && (
<span className="woocommerce-task-payment__recommended-pill">
{ recommendedText }
</span>
) }
</H>
<div className="woocommerce-task-payment__content">
{ content }
</div>
</CardBody>
<CardFooter isBorderless>
{ loading ? (
<Spinner />
) : (
this.getSetupButtons( method )
) }
</CardFooter>
</Card>
);
} ) }
</div>
);
}
}
export default compose(
withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' );
const { const {
installAndActivatePlugins, installAndActivatePlugins,
invalidateResolutionForStoreSelector: invalidatePluginStoreSelector, invalidateResolutionForStoreSelector: invalidatePluginStoreSelector,
} = dispatch( PLUGINS_STORE_NAME ); } = useDispatch( PLUGINS_STORE_NAME );
const { updateOptions } = dispatch( OPTIONS_STORE_NAME ); const { updateOptions } = useDispatch( OPTIONS_STORE_NAME );
const { const {
invalidateResolution, invalidateResolution,
invalidateResolutionForStoreSelector, invalidateResolutionForStoreSelector,
} = dispatch( ONBOARDING_STORE_NAME ); } = useDispatch( ONBOARDING_STORE_NAME );
invalidateResolution( 'getProfileItems', [] );
invalidateResolution( 'getTasksStatus', [] ); const { methods, options } = useSelect( ( select ) => {
return {
clearTaskStatusCache: () => {
invalidateResolutionForStoreSelector( 'getTasksStatus' );
invalidatePluginStoreSelector( 'getPaypalOnboardingStatus' );
},
createNotice,
installAndActivatePlugins,
updateOptions,
};
} ),
withSelect( ( select, props ) => {
const { createNotice, installAndActivatePlugins } = props;
const { getProfileItems } = select( ONBOARDING_STORE_NAME ); const { getProfileItems } = select( ONBOARDING_STORE_NAME );
const { getOption } = select( OPTIONS_STORE_NAME ); const { getOption } = select( OPTIONS_STORE_NAME );
const { const {
@ -419,7 +94,7 @@ export default compose(
'woocommerce_woo-mercado-pago-basic_settings', 'woocommerce_woo-mercado-pago-basic_settings',
]; ];
const options = optionNames.reduce( ( result, name ) => { const retrievedOptions = optionNames.reduce( ( result, name ) => {
result[ name ] = getOption( name ); result[ name ] = getOption( name );
return result; return result;
}, {} ); }, {} );
@ -433,27 +108,174 @@ export default compose(
? getPaypalOnboardingStatus() ? getPaypalOnboardingStatus()
: null; : null;
const methods = getPaymentMethods( { return {
methods: getPaymentMethods( {
activePlugins, activePlugins,
countryCode, countryCode,
createNotice, createNotice,
installAndActivatePlugins, installAndActivatePlugins,
isJetpackConnected: isJetpackConnected(), isJetpackConnected: isJetpackConnected(),
onboardingStatus, onboardingStatus,
options, options: retrievedOptions,
profileItems, profileItems,
paypalOnboardingStatus, paypalOnboardingStatus,
loadingPaypalStatus: loadingPaypalStatus:
! hasFinishedResolution( 'getPaypalOnboardingStatus' ) && ! hasFinishedResolution( 'getPaypalOnboardingStatus' ) &&
! paypalOnboardingStatus, ! paypalOnboardingStatus,
} ),
options: retrievedOptions,
};
} ); } );
return { const [ enabledMethods, setEnabledMethods ] = useState(
countryCode, methods.reduce( ( acc, method ) => {
profileItems, acc[ method.key ] = method.isEnabled;
activePlugins, return acc;
options, }, {} )
methods, );
const recommendedMethod = useMemo( () => {
const method = methods.find(
( m ) =>
( m.key === 'wcpay' && m.visible ) ||
( m.key === 'mercadopago' && m.visible )
);
if ( ! method ) {
return 'stripe';
}
return method.key;
}, [ methods ] );
const markConfigured = async ( methodKey, queryParams = {} ) => {
const method = methods.find( ( option ) => option.key === methodKey );
if ( ! method ) {
throw `Method ${ methodKey } not found in available methods list`;
}
setEnabledMethods( {
...enabledMethods,
[ methodKey ]: true,
} );
const clearTaskStatusCache = () => {
invalidateResolution( 'getProfileItems', [] );
invalidateResolution( 'getTasksStatus', [] );
invalidateResolutionForStoreSelector( 'getTasksStatus' );
invalidatePluginStoreSelector( 'getPaypalOnboardingStatus' );
}; };
await setMethodEnabledOption( method.optionName, 'yes', {
clearTaskStatusCache,
updateOptions,
options,
} );
recordEvent( 'tasklist_payment_connect_method', {
payment_method: methodKey,
} );
getHistory().push(
getNewPath( { ...queryParams, task: 'payments' }, '/', {} )
);
};
const currentMethod = useMemo( () => {
if ( ! query.method ) {
return null;
}
const method = methods.find( ( m ) => m.key === query.method );
if ( ! method ) {
throw `Current method ${ query.method } not found in available methods list`;
}
return method;
}, [ query ] );
if ( currentMethod ) {
return (
<Setup method={ currentMethod } markConfigured={ markConfigured } />
);
}
return (
<div className="woocommerce-task-payments">
{ methods.map( ( method ) => {
const {
before,
container,
content,
isConfigured,
key,
manageUrl,
title,
visible,
loading,
onClick,
} = method;
if ( ! visible ) {
return null;
}
const classes = classnames(
'woocommerce-task-payment',
'woocommerce-task-card',
! isConfigured && 'woocommerce-task-payment-not-configured',
'woocommerce-task-payment-' + key
);
const isRecommended =
key === recommendedMethod && ! isConfigured;
return (
<Card key={ key } className={ classes }>
{ isRecommended && key !== 'wcpay' && (
<RecommendedRibbon methodKey={ key } />
) }
<CardMedia isBorderless>{ before }</CardMedia>
<CardBody>
<H className="woocommerce-task-payment__title">
{ title }
{ isRecommended && key === 'wcpay' && (
<RecommendedRibbon
isPill
methodKey={ key }
/>
) }
</H>
<div className="woocommerce-task-payment__content">
{ content }
</div>
</CardBody>
<CardFooter isBorderless>
<Action
manageUrl={ manageUrl }
methodKey={ key }
hasSetup={ !! container }
isConfigured={ isConfigured }
isEnabled={ enabledMethods[ key ] }
isRecommended={ isRecommended }
isLoading={ loading }
markConfigured={ markConfigured }
onSetup={ () =>
recordEvent( 'tasklist_payment_setup', {
options: methods.map(
( option ) => option.key
),
selected: key,
} ) } )
)( Payments ); }
onSetupCallback={ onClick }
/>
</CardFooter>
</Card>
);
} ) }
</div>
);
};
export default Payments;

View File

@ -14,12 +14,12 @@ import { Link } from '@woocommerce/components';
* Internal dependencies * Internal dependencies
*/ */
import Bacs from './bacs'; import Bacs from './bacs';
import BacsLogo from './images/bacs'; import BacsLogo from '../images/bacs';
import CodLogo from './images/cod'; import CodLogo from '../images/cod';
import WCPayLogo from './images/wcpay'; import WCPayLogo from '../images/wcpay';
import RazorpayLogo from './images/razorpay'; import RazorpayLogo from '../images/razorpay';
import { MollieLogo } from './images/mollie'; import { MollieLogo } from '../images/mollie';
import { PayUIndiaLogo } from './images/payu-india'; import { PayUIndiaLogo } from '../images/payu-india';
import Stripe from './stripe'; import Stripe from './stripe';
import Square from './square'; import Square from './square';
import { import {
@ -35,10 +35,16 @@ import EWay from './eway';
import Razorpay from './razorpay'; import Razorpay from './razorpay';
import { Mollie } from './mollie'; import { Mollie } from './mollie';
import { PayUIndia } from './payu-india'; import { PayUIndia } from './payu-india';
import { GenericPaymentStep } from './generic-payment-step'; import { GenericPaymentStep } from '../generic-payment-step';
const wcAdminAssetUrl = getSetting( 'wcAdminAssetUrl', '' ); const wcAdminAssetUrl = getSetting( 'wcAdminAssetUrl', '' );
const getPaymentsSettingsUrl = ( methodKey ) => {
return getAdminLink(
'admin.php?page=wc-settings&tab=checkout&section=' + methodKey
);
};
export function getPaymentMethods( { export function getPaymentMethods( {
activePlugins, activePlugins,
countryCode, countryCode,
@ -98,6 +104,7 @@ export function getPaymentMethods( {
options.woocommerce_stripe_settings && options.woocommerce_stripe_settings &&
options.woocommerce_stripe_settings.enabled === 'yes', options.woocommerce_stripe_settings.enabled === 'yes',
optionName: 'woocommerce_stripe_settings', optionName: 'woocommerce_stripe_settings',
manageUrl: getPaymentsSettingsUrl( 'stripe' ),
}, },
{ {
key: 'paystack', key: 'paystack',
@ -149,6 +156,7 @@ export function getPaymentMethods( {
}, },
}; };
}, },
manageUrl: getPaymentsSettingsUrl( 'paystack' ),
}, },
{ {
key: 'payfast', key: 'payfast',
@ -209,6 +217,7 @@ export function getPaymentMethods( {
}, },
}; };
}, },
manageUrl: getPaymentsSettingsUrl( 'stripe' ),
}, },
{ {
key: 'mercadopago', key: 'mercadopago',
@ -241,6 +250,7 @@ export function getPaymentMethods( {
options[ 'woocommerce_woo-mercado-pago-basic_settings' ] options[ 'woocommerce_woo-mercado-pago-basic_settings' ]
.enabled === 'yes', .enabled === 'yes',
optionName: 'woocommerce_woo-mercado-pago-basic_settings', optionName: 'woocommerce_woo-mercado-pago-basic_settings',
manageUrl: getPaymentsSettingsUrl( 'woo-mercado-pago-basic' ),
}, },
{ {
key: 'paypal', key: 'paypal',
@ -266,6 +276,7 @@ export function getPaymentMethods( {
loading: activePlugins.includes( PAYPAL_PLUGIN ) loading: activePlugins.includes( PAYPAL_PLUGIN )
? loadingPaypalStatus ? loadingPaypalStatus
: false, : false,
manageUrl: getPaymentsSettingsUrl( 'ppcp-gateway' ),
}, },
{ {
key: 'klarna_checkout', key: 'klarna_checkout',
@ -290,6 +301,7 @@ export function getPaymentMethods( {
options.woocommerce_kco_settings && options.woocommerce_kco_settings &&
options.woocommerce_kco_settings.enabled === 'yes', options.woocommerce_kco_settings.enabled === 'yes',
optionName: 'woocommerce_kco_settings', optionName: 'woocommerce_kco_settings',
manageUrl: getPaymentsSettingsUrl( 'kco' ),
}, },
{ {
key: 'klarna_payments', key: 'klarna_payments',
@ -325,6 +337,7 @@ export function getPaymentMethods( {
options.woocommerce_klarna_payments_settings && options.woocommerce_klarna_payments_settings &&
options.woocommerce_klarna_payments_settings.enabled === 'yes', options.woocommerce_klarna_payments_settings.enabled === 'yes',
optionName: 'woocommerce_klarna_payments_settings', optionName: 'woocommerce_klarna_payments_settings',
manageUrl: getPaymentsSettingsUrl( 'klarna_payments' ),
}, },
{ {
key: 'mollie', key: 'mollie',
@ -360,6 +373,7 @@ export function getPaymentMethods( {
options.woocommerce_mollie_payments_settings && options.woocommerce_mollie_payments_settings &&
options.woocommerce_mollie_payments_settings.enabled === 'yes', options.woocommerce_mollie_payments_settings.enabled === 'yes',
optionName: 'woocommerce_mollie_payments_settings', optionName: 'woocommerce_mollie_payments_settings',
manageUrl: getPaymentsSettingsUrl( 'mollie_wc_gateway_creditcard' ),
}, },
{ {
key: 'square', key: 'square',
@ -403,6 +417,7 @@ export function getPaymentMethods( {
'yes', 'yes',
optionName: 'woocommerce_square_credit_card_settings', optionName: 'woocommerce_square_credit_card_settings',
hasCbdIndustry, hasCbdIndustry,
manageUrl: getPaymentsSettingsUrl( 'square_credit_card' ),
}, },
{ {
key: 'eway', key: 'eway',
@ -432,6 +447,7 @@ export function getPaymentMethods( {
options.woocommerce_eway_settings && options.woocommerce_eway_settings &&
options.woocommerce_eway_settings.enabled === 'yes', options.woocommerce_eway_settings.enabled === 'yes',
optionName: 'woocommerce_eway_settings', optionName: 'woocommerce_eway_settings',
manageUrl: getPaymentsSettingsUrl( 'eway' ),
}, },
{ {
key: 'razorpay', key: 'razorpay',
@ -456,6 +472,7 @@ export function getPaymentMethods( {
options.woocommerce_razorpay_settings && options.woocommerce_razorpay_settings &&
options.woocommerce_razorpay_settings.enabled === 'yes', options.woocommerce_razorpay_settings.enabled === 'yes',
optionName: 'woocommerce_razorpay_settings', optionName: 'woocommerce_razorpay_settings',
manageUrl: getPaymentsSettingsUrl( 'razorpay' ),
}, },
{ {
key: 'payubiz', key: 'payubiz',
@ -475,6 +492,7 @@ export function getPaymentMethods( {
isConfigured: activePlugins.includes( 'payu-india' ), isConfigured: activePlugins.includes( 'payu-india' ),
isEnabled: enabledPaymentGateways.includes( 'payubiz' ), isEnabled: enabledPaymentGateways.includes( 'payubiz' ),
optionName: 'woocommerce_payubiz_settings', optionName: 'woocommerce_payubiz_settings',
manageUrl: getPaymentsSettingsUrl( 'payubiz' ),
}, },
{ {
key: 'cod', key: 'cod',
@ -489,6 +507,7 @@ export function getPaymentMethods( {
options.woocommerce_cod_settings && options.woocommerce_cod_settings &&
options.woocommerce_cod_settings.enabled === 'yes', options.woocommerce_cod_settings.enabled === 'yes',
optionName: 'woocommerce_cod_settings', optionName: 'woocommerce_cod_settings',
manageUrl: getPaymentsSettingsUrl( 'cod' ),
}, },
{ {
key: 'bacs', key: 'bacs',
@ -507,6 +526,7 @@ export function getPaymentMethods( {
options.woocommerce_bacs_settings && options.woocommerce_bacs_settings &&
options.woocommerce_bacs_settings.enabled === 'yes', options.woocommerce_bacs_settings.enabled === 'yes',
optionName: 'woocommerce_bacs_settings', optionName: 'woocommerce_bacs_settings',
manageUrl: getPaymentsSettingsUrl( 'bacs' ),
}, },
]; ];
@ -608,6 +628,7 @@ export function getPaymentMethods( {
options.woocommerce_woocommerce_payments_settings.enabled === options.woocommerce_woocommerce_payments_settings.enabled ===
'yes', 'yes',
optionName: 'woocommerce_woocommerce_payments_settings', optionName: 'woocommerce_woocommerce_payments_settings',
manageUrl: getPaymentsSettingsUrl( 'woocommerce_payments' ),
} ); } );
} }

View File

@ -12,7 +12,7 @@ import { SETTINGS_STORE_NAME } from '@woocommerce/data';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { getCountryCode } from '../../../dashboard/utils'; import { getCountryCode } from '~/dashboard/utils';
export const MERCADOPAGO_PLUGIN = 'woocommerce-mercadopago'; export const MERCADOPAGO_PLUGIN = 'woocommerce-mercadopago';

View File

@ -9,7 +9,7 @@ import { recordEvent } from '@woocommerce/tracks';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { createNoticesFromResponse } from '../../../../lib/notices'; import { createNoticesFromResponse } from '~/lib/notices';
export function installActivateAndConnectWcpay( export function installActivateAndConnectWcpay(
reject, reject,

View File

@ -10,7 +10,7 @@ import { Link } from '@woocommerce/components';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import UsageModal from '../../../../profile-wizard/steps/usage-modal'; import UsageModal from '~/profile-wizard/steps/usage-modal';
const WCPayUsageModal = () => { const WCPayUsageModal = () => {
const query = getQuery(); const query = getQuery();

View File

@ -0,0 +1,22 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
const localPartners = [ 'mercadopago' ];
export const RecommendedRibbon = ( { methodKey, isPill = false } ) => {
const classes = isPill
? 'woocommerce-task-payment__recommended-pill'
: 'woocommerce-task-payment__recommended-ribbon';
const text = localPartners.includes( methodKey )
? __( 'Local Partner', 'woocommerce-admin' )
: __( 'Recommended', 'woocommerce-admin' );
return (
<div className={ classes }>
<span>{ text }</span>
</div>
);
};

View File

@ -0,0 +1,81 @@
/**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { Card, CardBody } from '@wordpress/components';
import { cloneElement, useMemo } from '@wordpress/element';
import { Plugins } from '@woocommerce/components';
import { PLUGINS_STORE_NAME, pluginNames } from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import { useSelect } from '@wordpress/data';
/**
* Internal dependencies
*/
import { createNoticesFromResponse } from '~/lib/notices';
export const Setup = ( { method, markConfigured, query } ) => {
const { activePlugins } = useSelect( ( select ) => {
const { getActivePlugins } = select( PLUGINS_STORE_NAME );
return {
activePlugins: getActivePlugins(),
};
} );
const installStep = useMemo( () => {
if ( ! method.plugins || ! method.plugins.length ) {
return;
}
const pluginsToInstall = method.plugins.filter(
( m ) => ! activePlugins.includes( m )
);
const pluginNamesString = method.plugins
.map( ( pluginSlug ) => pluginNames[ pluginSlug ] )
.join( ' ' + __( 'and', 'woocommerce-admin' ) + ' ' );
return {
key: 'install',
label: sprintf(
/* translators: %s = one or more plugin names joined by "and" */
__( 'Install %s', 'woocommerce-admin' ),
pluginNamesString
),
content: (
<Plugins
onComplete={ ( plugins, response ) => {
createNoticesFromResponse( response );
recordEvent( 'tasklist_payment_install_method', {
plugins: method.plugins,
} );
} }
onError={ ( errors, response ) =>
createNoticesFromResponse( response )
}
autoInstall
pluginSlugs={ method.plugins }
/>
),
isComplete: ! pluginsToInstall.length,
};
}, [ activePlugins, method.plugins ] );
if ( ! method.container ) {
return null;
}
return (
<Card className="woocommerce-task-payment-method woocommerce-task-card">
<CardBody>
{ cloneElement( method.container, {
methodConfig: method,
query,
installStep,
markConfigured,
hasCbdIndustry: method.hasCbdIndustry,
} ) }
</CardBody>
</Card>
);
};

View File

@ -8,7 +8,7 @@ import apiFetch from '@wordpress/api-fetch';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { PayPal, PAYPAL_PLUGIN } from '../tasks/payments/paypal'; import { PayPal, PAYPAL_PLUGIN } from '../tasks/payments/methods/paypal';
import { getPaymentMethods } from '../tasks/payments/methods'; import { getPaymentMethods } from '../tasks/payments/methods';
import { setMethodEnabledOption } from '../../task-list/tasks/payments'; import { setMethodEnabledOption } from '../../task-list/tasks/payments';
import { GenericPaymentStep } from '../tasks/payments/generic-payment-step'; import { GenericPaymentStep } from '../tasks/payments/generic-payment-step';

View File

@ -80,6 +80,7 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt
- Fix: Retain persisted queries when navigating to Homescreen #6614 - Fix: Retain persisted queries when navigating to Homescreen #6614
- Update: Update choose niche note cta URL #6733 - Update: Update choose niche note cta URL #6733
- Fix: Update folded header style #6724 - Fix: Update folded header style #6724
- Tweak: Refactor payments to allow management of methods #6786
- Fix: Fix unreleated variations showing up in the Products reports #6647 - Fix: Fix unreleated variations showing up in the Products reports #6647
- Tweak: Add tracking data for the preview site btn #6623 - Tweak: Add tracking data for the preview site btn #6623
- Tweak: Update WC Payments copy on the task list #6734 - Tweak: Update WC Payments copy on the task list #6734

View File

@ -3,6 +3,7 @@
*/ */
import { waitForElementByText } from '../utils/actions'; import { waitForElementByText } from '../utils/actions';
import { BasePage } from './BasePage'; import { BasePage } from './BasePage';
import { getElementByText } from '../utils/actions';
type PaymentMethodWithSetupButton = type PaymentMethodWithSetupButton =
| 'wcpay' | 'wcpay'
@ -39,15 +40,10 @@ export class PaymentsSetup extends BasePage {
} }
async methodHasBeenSetup( method: PaymentMethod ) { async methodHasBeenSetup( method: PaymentMethod ) {
const toggle = this.getFormToggle( await getElementByText( 'button', 'Manage', `.woocommerce-task-payment-${ method }` )
`.woocommerce-task-payment-${ method }`
);
await toggle.isEnabled();
} }
async enableCashOnDelivery() { async enableCashOnDelivery() {
const toggle = this.getFormToggle( '.woocommerce-task-payment-cod' ); await this.clickButtonWithText( 'Enable' );
await toggle.switchOn();
} }
} }

View File

@ -53,7 +53,7 @@ describe( 'Payment setup task', () => {
await paymentsSetup.methodHasBeenSetup( 'bacs' ); await paymentsSetup.methodHasBeenSetup( 'bacs' );
} ); } );
it( 'Toggling cash on delivery enables the payment method', async () => { it( 'Enabling cash on delivery enables the payment method', async () => {
await paymentsSetup.enableCashOnDelivery(); await paymentsSetup.enableCashOnDelivery();
await paymentsSetup.methodHasBeenSetup( 'cod' ); await paymentsSetup.methodHasBeenSetup( 'cod' );
} ); } );