* Add bacs method to fallback methods

* Register bacs plugin to override default setup

* Clean up prop usage

* Add changelog entry

* Fix imports for onboarding package

* Fix card divider

* Return early in updateSettings

* Move recommended gateway up to remote payments index

* Fix text domains
This commit is contained in:
Joshua T Flowers 2021-06-02 16:54:04 -04:00 committed by GitHub
parent 65de4bff00
commit b4c71ae644
10 changed files with 429 additions and 130 deletions

View File

@ -223,6 +223,10 @@
max-width: 70px;
}
}
.components-card__divider:last-child {
display: none;
}
}
.woocommerce-task-payment-method {

View File

@ -0,0 +1,178 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { Form, H, TextControl } from '@woocommerce/components';
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
import { registerPlugin } from '@wordpress/plugins';
import { useDispatch, useSelect } from '@wordpress/data';
import { WooRemotePayment } from '@woocommerce/onboarding';
const initialFormValues = {
account_name: '',
account_number: '',
bank_name: '',
sort_code: '',
iban: '',
bic: '',
};
const BacsPaymentGatewaySetup = () => {
const isUpdating = useSelect( ( select ) => {
return select( OPTIONS_STORE_NAME ).isOptionsUpdating();
} );
const { createNotice } = useDispatch( 'core/notices' );
const { updateOptions } = useDispatch( OPTIONS_STORE_NAME );
const validate = ( values ) => {
const errors = {};
if ( ! values.account_number && ! values.iban ) {
errors.account_number = errors.iban = __(
'Please enter an account number or IBAN',
'woocommerce-admin'
);
}
return errors;
};
const updateSettings = async ( values, markConfigured ) => {
const update = await updateOptions( {
woocommerce_bacs_settings: {
enabled: 'yes',
},
woocommerce_bacs_accounts: [ values ],
} );
if ( update.success ) {
markConfigured();
createNotice(
'success',
__(
'Direct bank transfer details added successfully',
'woocommerce-admin'
)
);
return;
}
createNotice(
'error',
__(
'There was a problem saving your payment settings',
'woocommerce-admin'
)
);
};
return (
<>
<WooRemotePayment id="bacs">
{ ( { markConfigured } ) => {
return (
<Form
initialValues={ initialFormValues }
onSubmit={ ( values ) =>
updateSettings( values, markConfigured )
}
validate={ validate }
>
{ ( { getInputProps, handleSubmit } ) => {
return (
<>
<H>
{ __(
'Add your bank details',
'woocommerce-admin'
) }
</H>
<p>
{ __(
'These details are required to receive payments via bank transfer',
'woocommerce-admin'
) }
</p>
<div className="woocommerce-task-payment-method__fields">
<TextControl
label={ __(
'Account name',
'woocommerce-admin'
) }
required
{ ...getInputProps(
'account_name'
) }
/>
<TextControl
label={ __(
'Account number',
'woocommerce-admin'
) }
required
{ ...getInputProps(
'account_number'
) }
/>
<TextControl
label={ __(
'Bank name',
'woocommerce-admin'
) }
required
{ ...getInputProps(
'bank_name'
) }
/>
<TextControl
label={ __(
'Sort code',
'woocommerce-admin'
) }
required
{ ...getInputProps(
'sort_code'
) }
/>
<TextControl
label={ __(
'IBAN',
'woocommerce-admin'
) }
required
{ ...getInputProps( 'iban' ) }
/>
<TextControl
label={ __(
'BIC / Swift',
'woocommerce-admin'
) }
required
{ ...getInputProps( 'bic' ) }
/>
</div>
<Button
isPrimary
isBusy={ isUpdating }
onClick={ handleSubmit }
>
{ __(
'Save',
'woocommerce-admin'
) }
</Button>
</>
);
} }
</Form>
);
} }
</WooRemotePayment>
</>
);
};
registerPlugin( 'wc-admin-payment-gateway-setup-bacs', {
render: BacsPaymentGatewaySetup,
} );

View File

@ -119,6 +119,7 @@ export const PaymentConnect = ( {
defaultSubmit: handleSubmit,
defaultFields: fields,
markConfigured: () => markConfigured( id ),
paymentGateway,
} }
id={ id }
/>

View File

@ -28,7 +28,7 @@ export const PaymentMethod = ( {
method,
recordConnectStartEvent,
} ) => {
const { key, plugins, title } = method;
const { key, plugins = [], title } = method;
const slot = useSlot( `woocommerce_remote_payment_${ key }` );
const hasFills = Boolean( slot?.fills?.length );
const [ isPluginLoaded, setIsPluginLoaded ] = useState( false );
@ -139,7 +139,7 @@ export const PaymentMethod = ( {
};
const stepperPending =
! installStep.isComplete ||
! installStep?.isComplete ||
isOptionUpdating ||
isPaymentGatewayResolving ||
! isPluginLoaded;
@ -149,7 +149,7 @@ export const PaymentMethod = ( {
<Stepper
isVertical
isPending={ stepperPending }
currentStep={ installStep.isComplete ? 'connect' : 'install' }
currentStep={ installStep?.isComplete ? 'connect' : 'install' }
steps={ [ installStep, connectStep ] }
{ ...props }
/>
@ -166,6 +166,8 @@ export const PaymentMethod = ( {
defaultStepper: DefaultStepper,
defaultInstallStep: installStep,
defaultConnectStep: connectStep,
markConfigured: () => markConfigured( key ),
paymentGateway,
} }
id={ key }
/>

View File

@ -1,121 +1,38 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { Fragment } from '@wordpress/element';
import {
Card,
CardBody,
CardMedia,
CardHeader,
CardDivider,
} from '@wordpress/components';
import { Text } from '@woocommerce/experimental';
import { recordEvent } from '@woocommerce/tracks';
import { RecommendedRibbon, SetupRequired } from '@woocommerce/onboarding';
import { Card, CardHeader } from '@wordpress/components';
/**
* Internal dependencies
*/
import { PaymentAction } from '../../../components/PaymentAction';
import { RecommendedPaymentGatewayListItem } from './RecommendedPaymentGatewayListItem';
import './RecommendedPaymentGatewayList.scss';
export const RecommendedPaymentGatewayList = ( {
recommendedMethod,
heading,
installedPaymentGateways,
recommendedPaymentGateway,
recommendedPaymentGateways,
markConfigured,
} ) => (
<Card>
<CardHeader as="h2">{ heading }</CardHeader>
{ recommendedPaymentGateways.map( ( method, index ) => {
const {
image,
content,
key,
plugins = [],
title,
is_visible: isVisible,
loading,
} = method;
if ( ! isVisible ) {
return null;
}
const installedPaymentGateway = installedPaymentGateways[ key ];
const {
enabled: isEnabled = false,
needs_setup: needsSetup = false,
required_settings_keys: requiredSettingsKeys = [],
settings_url: manageUrl,
} = installedPaymentGateway || {};
const isConfigured = ! needsSetup;
const hasSetup = Boolean(
plugins.length || requiredSettingsKeys.length
);
const isRecommended = key === recommendedMethod && ! isConfigured;
const showRecommendedRibbon = isRecommended;
const classes = classnames(
'woocommerce-task-payment',
'woocommerce-task-card',
! isConfigured && 'woocommerce-task-payment-not-configured',
'woocommerce-task-payment-' + key
);
return (
<Fragment key={ key }>
{ index !== 0 && <CardDivider /> }
<CardBody
style={ { paddingLeft: 0, marginBottom: 0 } }
className={ classes }
>
<CardMedia isBorderless>
<img src={ image } alt={ title } />
</CardMedia>
<div className="woocommerce-task-payment__description">
{ showRecommendedRibbon && <RecommendedRibbon /> }
<Text
as="h3"
className="woocommerce-task-payment__title"
>
{ title }
{ isEnabled && ! isConfigured && (
<SetupRequired />
) }
</Text>
<div className="woocommerce-task-payment__content">
{ content }
</div>
</div>
<div className="woocommerce-task-payment__footer">
<PaymentAction
manageUrl={ manageUrl }
methodKey={ key }
hasSetup={ hasSetup }
isConfigured={ isConfigured }
isEnabled={ isEnabled }
isRecommended={ isRecommended }
isLoading={ loading }
markConfigured={ markConfigured }
onSetup={ () =>
recordEvent( 'tasklist_payment_setup', {
options: recommendedPaymentGateways.map(
( option ) => option.key
),
selected: key,
} )
}
onSetupCallback={ method.onClick }
/>
</div>
</CardBody>
</Fragment>
);
} ) }
</Card>
);
} ) => {
return (
<Card>
<CardHeader as="h2">{ heading }</CardHeader>
{ Array.from( recommendedPaymentGateways.values() ).map(
( paymentGateway ) => {
const { key } = paymentGateway;
return (
<RecommendedPaymentGatewayListItem
key={ key }
markConfigured={ markConfigured }
paymentGateway={ paymentGateway }
isRecommended={ recommendedPaymentGateway === key }
recommendedPaymentGatewayKeys={ recommendedPaymentGateways.keys() }
/>
);
}
) }
</Card>
);
};

View File

@ -0,0 +1,110 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { Fragment } from '@wordpress/element';
import { CardBody, CardMedia, CardDivider } from '@wordpress/components';
import { PAYMENT_GATEWAYS_STORE_NAME } from '@woocommerce/data';
import { RecommendedRibbon, SetupRequired } from '@woocommerce/onboarding';
import { recordEvent } from '@woocommerce/tracks';
import { Text, useSlot } from '@woocommerce/experimental';
import { useSelect } from '@wordpress/data';
/**
* Internal dependencies
*/
import { PaymentAction } from '../../../components/PaymentAction';
import './RecommendedPaymentGatewayList.scss';
export const RecommendedPaymentGatewayListItem = ( {
isRecommended,
paymentGateway,
markConfigured,
recommendedPaymentGatewayKeys,
} ) => {
const {
image,
content,
key,
plugins = [],
title,
is_visible: isVisible,
loading,
} = paymentGateway;
const slot = useSlot( `woocommerce_remote_payment_${ key }` );
const installedPaymentGateway = useSelect( ( select ) => {
return (
select( PAYMENT_GATEWAYS_STORE_NAME ).getPaymentGateway( key ) || {}
);
} );
if ( ! isVisible ) {
return null;
}
const {
enabled: isEnabled = false,
needs_setup: needsSetup = false,
required_settings_keys: requiredSettingsKeys = [],
settings_url: manageUrl,
} = installedPaymentGateway;
const isConfigured = ! needsSetup;
const hasFills = Boolean( slot?.fills?.length );
const hasSetup = Boolean(
plugins.length || requiredSettingsKeys.length || hasFills
);
const showRecommendedRibbon = isRecommended && ! isConfigured;
const classes = classnames(
'woocommerce-task-payment',
'woocommerce-task-card',
! isConfigured && 'woocommerce-task-payment-not-configured',
'woocommerce-task-payment-' + key
);
return (
<Fragment key={ key }>
<CardBody
style={ { paddingLeft: 0, marginBottom: 0 } }
className={ classes }
>
<CardMedia isBorderless>
<img src={ image } alt={ title } />
</CardMedia>
<div className="woocommerce-task-payment__description">
{ showRecommendedRibbon && <RecommendedRibbon /> }
<Text as="h3" className="woocommerce-task-payment__title">
{ title }
{ isEnabled && ! isConfigured && <SetupRequired /> }
</Text>
<div className="woocommerce-task-payment__content">
{ content }
</div>
</div>
<div className="woocommerce-task-payment__footer">
<PaymentAction
manageUrl={ manageUrl }
methodKey={ key }
hasSetup={ hasSetup }
isConfigured={ isConfigured }
isEnabled={ isEnabled }
isRecommended={ isRecommended }
isLoading={ loading }
markConfigured={ markConfigured }
onSetup={ () =>
recordEvent( 'tasklist_payment_setup', {
options: recommendedPaymentGatewayKeys,
selected: key,
} )
}
/>
</div>
</CardBody>
<CardDivider />
</Fragment>
);
};

View File

@ -18,6 +18,7 @@ import { useMemo, useCallback } from '@wordpress/element';
import { RecommendedPaymentGatewayList } from './components/RecommendedPaymentGatewayList';
import { PaymentMethod } from './components/PaymentMethod';
import { WCPayMethodCard } from '../components/WCPayMethodCard';
import './components/BacsPaymentGatewaySetup';
const RECOMMENDED_GATEWAY_KEYS = [
'woocommerce_payments',
@ -73,16 +74,6 @@ export const RemotePayments = ( { query } ) => {
};
} );
const recommendedMethod = useMemo( () => {
for ( const key in RECOMMENDED_GATEWAY_KEYS ) {
const gateway = paymentGatewayRecommendations.get( key );
if ( gateway && gateway.visible ) {
return gateway;
}
}
return null;
}, [ paymentGatewayRecommendations ] );
const enablePaymentGateway = ( paymentGatewayKey ) => {
if ( ! paymentGatewayKey ) {
return;
@ -124,6 +115,16 @@ export const RemotePayments = ( { query } ) => {
} );
}, [] );
const recommendedPaymentGateway = useMemo( () => {
for ( const key in RECOMMENDED_GATEWAY_KEYS ) {
const gateway = paymentGatewayRecommendations.get( key );
if ( gateway && gateway.visible ) {
return gateway;
}
}
return null;
}, [ paymentGatewayRecommendations ] );
const currentPaymentGateway = useMemo( () => {
if (
! query.method ||
@ -176,23 +177,27 @@ export const RemotePayments = ( { query } ) => {
{ !! enabledPaymentGatewayRecommendations.size && (
<RecommendedPaymentGatewayList
recommendedMethod={ recommendedMethod }
heading={ __( 'Enabled payment methods', 'wc-admin' ) }
installedPaymentGateways={ installedPaymentGateways }
recommendedPaymentGateways={ Array.from(
enabledPaymentGatewayRecommendations.values()
heading={ __(
'Enabled payment methods',
'woocommerce-admin'
) }
recommendedPaymentGateway={ recommendedPaymentGateway }
recommendedPaymentGateways={
enabledPaymentGatewayRecommendations
}
/>
) }
{ !! additionalPaymentGatewayRecommendations.size && (
<RecommendedPaymentGatewayList
recommendedMethod={ recommendedMethod }
heading={ __( 'Additional payment methods', 'wc-admin' ) }
installedPaymentGateways={ installedPaymentGateways }
recommendedPaymentGateways={ Array.from(
additionalPaymentGatewayRecommendations.values()
heading={ __(
'Additional payment methods',
'woocommerce-admin'
) }
recommendedPaymentGateways={
additionalPaymentGatewayRecommendations
}
recommendedPaymentGateway={ recommendedPaymentGateway }
markConfigured={ markConfigured }
/>
) }

View File

@ -0,0 +1,72 @@
<svg
width="96"
height="32"
viewBox="0 0 96 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="32" height="32" rx="16" fill="#8E9196" />
<mask
id="bacs0"
mask-type="alpha"
maskUnits="userSpaceOnUse"
x="8"
y="8"
width="16"
height="16"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M8.875 12.25L16 8.5L23.125 12.25V13.75H8.875V12.25ZM16 10.195L19.9075 12.25H12.0925L16 10.195ZM10.75 15.25H12.25V20.5H10.75V15.25ZM15.25 20.5V15.25H16.75V20.5H15.25ZM23.125 23.5V22H8.875V23.5H23.125ZM19.75 15.25H21.25V20.5H19.75V15.25Z"
fill="white"
/>
</mask>
<g mask="url(#bacs0)">
<rect x="7" y="7" width="18" height="18" fill="white" />
</g>
<mask
id="bacs1"
mask-type="alpha"
maskUnits="userSpaceOnUse"
x="39"
y="10"
width="18"
height="12"
>
<path
d="M39 17L53.17 17L49.59 20.59L51 22L57 16L51 10L49.59 11.41L53.17 15L39 15L39 17Z"
fill="white"
/>
</mask>
<g mask="url(#bacs1)">
<rect
x="60"
y="28"
width="24"
height="24"
transform="rotate(-180 60 28)"
fill="#8E9196"
/>
</g>
<rect x="64" width="32" height="32" rx="16" fill="#8E9196" />
<mask
id="bacs2"
mask-type="alpha"
maskUnits="userSpaceOnUse"
x="72"
y="8"
width="16"
height="16"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M72.875 12.25L80 8.5L87.125 12.25V13.75H72.875V12.25ZM80 10.195L83.9075 12.25H76.0925L80 10.195ZM74.75 15.25H76.25V20.5H74.75V15.25ZM79.25 20.5V15.25H80.75V20.5H79.25ZM87.125 23.5V22H72.875V23.5H87.125ZM83.75 15.25H85.25V20.5H83.75V15.25Z"
fill="white"
/>
</mask>
<g mask="url(#bacs2)">
<rect x="71" y="7" width="18" height="18" fill="white" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -96,6 +96,7 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt
- Add: Progressive setup checklist copy and call to action buttons. #6956
- Add: Add Paystack as fallback gateway #7025
- Add: Add COD method to default payment gateway recommendations #7057
- Add: Add BACS as default fallback payment gateway #7073
- Add: A/B test of progressive checklist features. #7089
- Dev: Update package-lock to fix versioning of local packages. #6843
- Dev: Use rule processing for remote payment methods #6830

View File

@ -92,6 +92,15 @@ class DefaultPaymentGateways {
self::get_rules_for_cbd( false ),
),
),
array(
'key' => 'bacs',
'title' => __( 'Direct bank transfer', 'woocommerce-admin' ),
'content' => __( 'Take payments via bank transfer.', 'woocommerce-admin' ),
'image' => plugins_url( 'images/onboarding/bacs.svg', WC_ADMIN_PLUGIN_FILE ),
'is_visible' => array(
self::get_rules_for_cbd( false ),
),
),
);
}