Skip to content

Commit

Permalink
Use statement_descriptor_suffix for card payments and fallback to S…
Browse files Browse the repository at this point in the history
…tripe account statement descriptions for all payments (#2833)

* Update Stripe statement description settings to include the date from the Stripe account

* Clear account cache on the settings page load

* Update the UPE gateway to make sure the statement descriptor uses the suffix for card payments

* Update the non-upe gateway to make sure the statement descriptor uses the suffix for card payments

* Add changelog entries

* Fix js tests

* Update js test to supply the statement descriptors via the account data

* Update php unit tests to no longer expect a statement descriptor

* Add missing "in"

* Add latin character suffix to statement descriptor preview

* Update js test to expect the suffix latin character insert
  • Loading branch information
james-allan authored Jan 24, 2024
1 parent ca36fae commit 8cdd33a
Show file tree
Hide file tree
Showing 12 changed files with 164 additions and 149 deletions.
4 changes: 4 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
*** Changelog ***

= 7.9.2 - 2024-xx-xx =
* Fix - Resolved an issue that could cause card payments to fail when providing a Bank statement description with the `statement_descriptor` parameter.
* Tweak - The Bank statement description settings in the Stripe plugin settings are no longer editable. The description is now automatically pulled from the Stripe account settings.

= 7.9.1 - 2024-01-16 =
* Fix - PHP fatal error when updating a user with saved tokens from the WP Dashboard.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import PaymentsAndTransactionsSection from '..';
import { useAccount } from 'wcstripe/data/account';
import {
Expand Down Expand Up @@ -34,110 +34,84 @@ describe( 'PaymentsAndTransactionsSection', () => {
jest.fn(),
] );
useSeparateCardForm.mockReturnValue( [ true, jest.fn() ] );
useAccountStatementDescriptor.mockReturnValue( [
'WOOTESTING, LTD',
jest.fn(),
] );
useShortAccountStatementDescriptor.mockReturnValue( [
'WOOTESTING',
jest.fn(),
] );
useAccount.mockReturnValue( {
data: {
account: {
settings: {
payments: { statement_descriptor: 'WOOTESTING, LTD' },
card_payments: {
statement_descriptor_prefix: 'WOOTEST',
},
},
},
},
} );

useGetSavingError.mockReturnValue( null );
useAccount.mockReturnValue( { data: {} } );
} );

it( 'displays the length of the bank statement input', () => {
const updateAccountStatementDescriptor = jest.fn();
useAccountStatementDescriptor.mockReturnValue( [
'WOOTESTING, LTD',
updateAccountStatementDescriptor,
] );
render( <PaymentsAndTransactionsSection /> );

// The default bank statement ("WOOTESTING, LTD") is 15 characters long.
expect( screen.getByText( '15 / 22' ) ).toBeInTheDocument();

fireEvent.change( screen.getByLabelText( 'Full bank statement' ), {
target: { value: 'New Statement Name' },
} );

expect( updateAccountStatementDescriptor ).toHaveBeenCalledWith(
'New Statement Name'
);
} );

it( 'shows the shortened bank statement input', () => {
useIsShortAccountStatementEnabled.mockReturnValue( [
true,
jest.fn(),
] );
const updateShortAccountStatementDescriptor = jest.fn();
useShortAccountStatementDescriptor.mockReturnValue( [
'WOOTEST',
updateShortAccountStatementDescriptor,
] );
render( <PaymentsAndTransactionsSection /> );

expect( screen.getByText( '7 / 10' ) ).toBeInTheDocument();
useAccount.mockReturnValue( {
data: {
account: {
settings: {
card_payments: {
statement_descriptor_prefix: 'WOOTEST',
},
},
},
},
} );

fireEvent.change(
screen.getByLabelText( 'Shortened customer bank statement' ),
{
target: { value: 'WOOTESTING' },
}
);
render( <PaymentsAndTransactionsSection /> );

expect( updateShortAccountStatementDescriptor ).toHaveBeenCalledWith(
'WOOTESTING'
);
// The default short bank statement ("WOOTEST") is 7 characters long.
expect( screen.getByText( '7 / 10' ) ).toBeInTheDocument();
} );

it( 'shows the full bank statement preview', () => {
const updateAccountStatementDescriptor = jest.fn();
const mockValue = 'WOOTESTING, LTD';
useAccountStatementDescriptor.mockReturnValue( [
mockValue,
updateAccountStatementDescriptor,
] );
render( <PaymentsAndTransactionsSection /> );

expect(
document.querySelector(
'.full-bank-statement .transaction-detail.description'
)
).toHaveTextContent( mockValue );
).toHaveTextContent( 'WOOTESTING, LTD' );
} );

it( 'shows the shortened customer bank statement preview when useIsShortAccountStatementEnabled is true', () => {
useIsShortAccountStatementEnabled.mockReturnValue( [
true,
jest.fn(),
] );
const updateShortAccountStatementDescriptor = jest.fn();
const mockValue = 'WOOTEST';
useShortAccountStatementDescriptor.mockReturnValue( [
mockValue,
updateShortAccountStatementDescriptor,
] );

render( <PaymentsAndTransactionsSection /> );

expect(
document.querySelector(
'.shortened-bank-statement .transaction-detail.description'
)
).toHaveTextContent( `${ mockValue }* #123456` );
).toHaveTextContent( 'WOOTEST* W #123456' );
} );

it( 'should not show the shortened customer bank statement preview when useIsShortAccountStatementEnabled is false', () => {
useIsShortAccountStatementEnabled.mockReturnValue( [
false,
jest.fn(),
] );
const updateShortAccountStatementDescriptor = jest.fn();
const mockValue = 'WOOTEST';
useShortAccountStatementDescriptor.mockReturnValue( [
mockValue,
updateShortAccountStatementDescriptor,
] );

render( <PaymentsAndTransactionsSection /> );

expect(
Expand Down Expand Up @@ -215,25 +189,4 @@ describe( 'PaymentsAndTransactionsSection', () => {
)
).toBeInTheDocument();
} );

it( "shows the account's statement descriptor placeholder", () => {
const mockValue = 'WOOTESTING, LTD';

useAccount.mockReturnValue( {
data: {
account: {
settings: { payments: { statement_descriptor: mockValue } },
},
},
} );
useIsShortAccountStatementEnabled.mockReturnValue( [
true,
jest.fn(),
] );
render( <PaymentsAndTransactionsSection /> );

expect(
screen.queryByText( 'Full bank statement' ).nextElementSibling
).toHaveAttribute( 'placeholder', mockValue );
} );
} );
79 changes: 49 additions & 30 deletions client/settings/payments-and-transactions-section/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { __ } from '@wordpress/i18n';
import React, { useContext } from 'react';
import { Card, CheckboxControl, TextControl } from '@wordpress/components';
import {
Card,
CheckboxControl,
TextControl,
ExternalLink,
} from '@wordpress/components';
import { Icon, help } from '@wordpress/icons';
import interpolateComponents from 'interpolate-components';
import CardBody from '../card-body';
import TextLengthHelpInputWrapper from './text-length-help-input-wrapper';
import StatementPreviewsWrapper from './statement-previews-wrapper';
Expand All @@ -12,9 +18,7 @@ import Tooltip from 'wcstripe/components/tooltip';
import {
useSavedCards,
useSeparateCardForm,
useAccountStatementDescriptor,
useIsShortAccountStatementEnabled,
useShortAccountStatementDescriptor,
useGetSavingError,
} from 'wcstripe/data';
import InlineNotice from 'wcstripe/components/inline-notice';
Expand All @@ -39,18 +43,10 @@ const PaymentsAndTransactionsSection = () => {
isSeparateCardFormEnabled,
setIsSeparateCardFormEnabled,
] = useSeparateCardForm();
const [
accountStatementDescriptor,
setAccountStatementDescriptor,
] = useAccountStatementDescriptor();
const [
isShortAccountStatementEnabled,
setIsShortAccountStatementEnabled,
] = useIsShortAccountStatementEnabled();
const [
shortAccountStatementDescriptor,
setShortAccountStatementDescriptor,
] = useShortAccountStatementDescriptor();

const { isUpeEnabled } = useContext( UpeToggleContext );

Expand All @@ -64,9 +60,19 @@ const PaymentsAndTransactionsSection = () => {
: __( 'All Payment Methods', 'woocommerce-gateway-stripe' );

const { data } = useAccount();
const statementDescriptorPlaceholder =
const stripeAccountStatementDescriptor =
data?.account?.settings?.payments?.statement_descriptor || '';

const stripeAccountShortStatementDescriptor =
data?.account?.settings?.card_payments?.statement_descriptor_prefix ||
'';

// Stripe requires the short statement descriptor suffix to have at least 1 latin character.
// To meet this requirement, we use the first character of the full statement descriptor.
const shortStatementDescriptorSuffix = stripeAccountShortStatementDescriptor.charAt(
0
);

return (
<Card className="transactions-and-payouts">
<CardBody>
Expand Down Expand Up @@ -122,23 +128,29 @@ const PaymentsAndTransactionsSection = () => {
</InlineNotice>
) }
<TextLengthHelpInputWrapper
textLength={ accountStatementDescriptor.length }
textLength={ stripeAccountStatementDescriptor.length }
maxLength={ 22 }
iconSlot={ <TooltipBankStatementHelp /> }
>
<TextControl
help={ __(
'Enter the name your customers will see on their transactions. Use a recognizable name – e.g. the legal entity name or website address – to avoid potential disputes and chargebacks.',
'woocommerce-gateway-stripe'
) }
help={ interpolateComponents( {
mixedString: __(
'You can change the description your customers will see on their bank statement in your {{settingsLink}}Stripe account settings{{/settingsLink}}. Set this to a recognizable name – e.g. the legal entity name or website address – to avoid potential disputes and chargebacks.',
'woocommerce-gateway-stripe'
),
components: {
settingsLink: (
<ExternalLink href="https://dashboard.stripe.com/settings/public" />
),
},
} ) }
label={ __(
'Full bank statement',
'woocommerce-gateway-stripe'
) }
value={ accountStatementDescriptor }
onChange={ setAccountStatementDescriptor }
placeholder={ statementDescriptorPlaceholder }
value={ stripeAccountStatementDescriptor }
maxLength={ 22 }
disabled={ true } // This field is read only. It is set in the Stripe account.
/>
</TextLengthHelpInputWrapper>

Expand Down Expand Up @@ -170,22 +182,29 @@ const PaymentsAndTransactionsSection = () => {
) }
<TextLengthHelpInputWrapper
textLength={
shortAccountStatementDescriptor.length
stripeAccountShortStatementDescriptor.length
}
maxLength={ 10 }
>
<TextControl
help={ __(
"We'll use the short version in combination with the customer order number.",
'woocommerce-gateway-stripe'
) }
help={ interpolateComponents( {
mixedString: __(
"We'll use the shortened descriptor in combination with the customer order number. You can change the shortened description in your {{settingsLink}}Stripe account settings{{/settingsLink}}.",
'woocommerce-gateway-stripe'
),
components: {
settingsLink: (
<ExternalLink href="https://dashboard.stripe.com/settings/public" />
),
},
} ) }
label={ __(
'Shortened customer bank statement',
'woocommerce-gateway-stripe'
) }
value={ shortAccountStatementDescriptor }
onChange={ setShortAccountStatementDescriptor }
value={ stripeAccountShortStatementDescriptor }
maxLength={ 10 }
disabled={ true } // This field is read only. It is set in the Stripe account.
/>
</TextLengthHelpInputWrapper>
</>
Expand All @@ -198,16 +217,16 @@ const PaymentsAndTransactionsSection = () => {
'Cards & Express Checkouts',
'woocommerce-gateway-stripe'
) }
text={ `${ shortAccountStatementDescriptor }* #123456` }
text={ `${ stripeAccountShortStatementDescriptor }* ${ shortStatementDescriptorSuffix } #123456` }
className="shortened-bank-statement"
/>
) }
<StatementPreview
icon="bank"
title={ translatedFullBankPreviewTitle }
text={
accountStatementDescriptor ||
statementDescriptorPlaceholder
stripeAccountStatementDescriptor ||
stripeAccountShortStatementDescriptor
}
className="full-bank-statement"
/>
Expand Down
27 changes: 8 additions & 19 deletions includes/abstracts/abstract-wc-stripe-payment-gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -429,13 +429,12 @@ public function get_stripe_return_url( $order = null, $id = null ) {
*/
public function generate_payment_request( $order, $prepared_payment_method ) {
$settings = get_option( 'woocommerce_stripe_settings', [] );
$statement_descriptor = ! empty( $settings['statement_descriptor'] ) ? str_replace( "'", '', $settings['statement_descriptor'] ) : '';
$short_statement_descriptor = ! empty( $settings['short_statement_descriptor'] ) ? str_replace( "'", '', $settings['short_statement_descriptor'] ) : '';
$is_short_statement_descriptor_enabled = ! empty( $settings['is_short_statement_descriptor_enabled'] ) && 'yes' === $settings['is_short_statement_descriptor_enabled'];
$capture = ! empty( $settings['capture'] ) && 'yes' === $settings['capture'] ? true : false;
$post_data = [];
$post_data['currency'] = strtolower( $order->get_currency() );
$post_data['amount'] = WC_Stripe_Helper::get_stripe_amount( $order->get_total(), $post_data['currency'] );

/* translators: 1) blog name 2) order number */
$post_data['description'] = sprintf( __( '%1$s - Order %2$s', 'woocommerce-gateway-stripe' ), wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), $order->get_order_number() );
$billing_email = $order->get_billing_email();
Expand All @@ -446,21 +445,11 @@ public function generate_payment_request( $order, $prepared_payment_method ) {
$post_data['receipt_email'] = $billing_email;
}

switch ( $order->get_payment_method() ) {
case 'stripe':
if ( $is_short_statement_descriptor_enabled && ! ( empty( $short_statement_descriptor ) && empty( $statement_descriptor ) ) ) {
$post_data['statement_descriptor'] = WC_Stripe_Helper::get_dynamic_statement_descriptor( $short_statement_descriptor, $order, $statement_descriptor );
} elseif ( ! empty( $statement_descriptor ) ) {
$post_data['statement_descriptor'] = WC_Stripe_Helper::clean_statement_descriptor( $statement_descriptor );
}

$post_data['capture'] = $capture ? 'true' : 'false';
break;
case 'stripe_sepa':
if ( ! empty( $statement_descriptor ) ) {
$post_data['statement_descriptor'] = WC_Stripe_Helper::clean_statement_descriptor( $statement_descriptor );
}
// other payment methods error if we try to add a statement descriptor in the request
if ( 'stripe' === $order->get_payment_method() ) {
$post_data['capture'] = $capture ? 'true' : 'false';
if ( $is_short_statement_descriptor_enabled ) {
$post_data['statement_descriptor_suffix'] = WC_Stripe_Helper::get_dynamic_statement_descriptor_suffix( $order );
}
}

if ( method_exists( $order, 'get_shipping_postcode' ) && ! empty( $order->get_shipping_postcode() ) ) {
Expand Down Expand Up @@ -1324,8 +1313,8 @@ public function generate_create_intent_request( $order, $prepared_source ) {
$request['customer'] = $prepared_source->customer;
}

if ( isset( $full_request['statement_descriptor'] ) ) {
$request['statement_descriptor'] = $full_request['statement_descriptor'];
if ( isset( $full_request['statement_descriptor_suffix'] ) ) {
$request['statement_descriptor_suffix'] = $full_request['statement_descriptor_suffix'];
}

if ( isset( $full_request['shipping'] ) ) {
Expand Down
Loading

0 comments on commit 8cdd33a

Please sign in to comment.