Skip to content

Commit

Permalink
Merge branch 'develop' into fix-qit-security
Browse files Browse the repository at this point in the history
  • Loading branch information
faisal-alvi authored Jul 10, 2024
2 parents c212c4e + fe2938d commit ccf791a
Show file tree
Hide file tree
Showing 14 changed files with 90 additions and 102 deletions.
2 changes: 2 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*** Changelog ***
= 8.5.0 - 2024-xx-xx =
* Tweak - Remove Giropay from the list of payment methods (for all versions) due deprecation.
* Tweak - Additional visual improvement for the webhook configuration notice.
* Add - Allow changing display order of payment methods in the new checkout experience.
* Add - Update the payment method associated with a subscription to a PaymentMethod when it's using a Stripe Source that was migrated to PaymentMethods.
Expand All @@ -14,6 +15,7 @@
* Fix - Ensure subscriptions purchased with iDEAL or Bancontact are correctly set to SEPA debit prior to processing the intitial payment.
* Tweak - Stripe API version updated to support 2024-06-20.
* Fix - Ensure SEPA tokens are attached to customers in the legacy checkout experience when the payment method is saved. This addresses subscription recurring payment "off-session" errors with SEPA.
* Fix - Prevent saved SEPA Sources from being displayed as available payment methods when the Updated checkout experience is enabled.

= 8.4.0 - 2024-06-13 =
* Tweak - Resets the list of payment methods when any Stripe key is updated.
Expand Down
1 change: 1 addition & 0 deletions client/blocks/upe/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const api = new WCStripeAPI(
const upeMethods = getPaymentMethodsConstants();
Object.entries( getBlocksConfiguration()?.paymentMethodsConfig )
.filter( ( [ upeName ] ) => upeName !== 'link' )
.filter( ( [ upeName ] ) => upeName !== 'giropay' ) // Skip giropay as it was deprecated by Jun, 30th 2024.
.forEach( ( [ upeName, upeConfig ] ) => {
let iconName = upeName;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe( 'GeneralSettingsSection', () => {
global.wcSettings = { currency: { code: 'EUR' } };
useGetCapabilities.mockReturnValue( {
card_payments: 'active',
giropay_payments: 'active',
alipay_payments: 'active',
} );
useManualCapture.mockReturnValue( [ false ] );
useGetAvailablePaymentMethodIds.mockReturnValue( [ 'card', 'link' ] );
Expand Down Expand Up @@ -132,17 +132,11 @@ describe( 'GeneralSettingsSection', () => {
it( 'should allow to enable a payment method when UPE is enabled', () => {
useGetAvailablePaymentMethodIds.mockReturnValue( [
'card',
'giropay',
'sofort',
'alipay',
'sepa_debit',
] );
useGetOrderedPaymentMethodIds.mockReturnValue( {
orderedPaymentMethodIds: [
'card',
'giropay',
'sofort',
'sepa_debit',
],
orderedPaymentMethodIds: [ 'card', 'alipay', 'sepa_debit' ],
setOrderedPaymentMethodIds: jest.fn(),
saveOrderedPaymentMethodIds: jest.fn(),
} );
Expand All @@ -158,26 +152,25 @@ describe( 'GeneralSettingsSection', () => {
</UpeToggleContext.Provider>
);

const giropayCheckbox = screen.getByRole( 'checkbox', {
name: /giropay/,
const alipayCheckbox = screen.getByRole( 'checkbox', {
name: /Alipay/,
} );

expect( updateEnabledMethodsMock ).not.toHaveBeenCalled();
expect( giropayCheckbox ).not.toBeChecked();
expect( alipayCheckbox ).not.toBeChecked();

userEvent.click( giropayCheckbox );
userEvent.click( alipayCheckbox );

expect( updateEnabledMethodsMock ).toHaveBeenCalledWith( [
'card',
'giropay',
'alipay',
] );
} );

it( 'should allow to enable a payment method when UPE is disabled', () => {
useGetAvailablePaymentMethodIds.mockReturnValue( [
'card',
'giropay',
'sofort',
'alipay',
'sepa_debit',
] );
const updateEnabledMethodsMock = jest.fn();
Expand All @@ -186,12 +179,7 @@ describe( 'GeneralSettingsSection', () => {
updateEnabledMethodsMock,
] );
useGetOrderedPaymentMethodIds.mockReturnValue( {
orderedPaymentMethodIds: [
'card',
'giropay',
'sofort',
'sepa_debit',
],
orderedPaymentMethodIds: [ 'card', 'alipay', 'sepa_debit' ],
setOrderedPaymentMethodIds: jest.fn(),
saveOrderedPaymentMethodIds: jest.fn(),
} );
Expand All @@ -202,26 +190,25 @@ describe( 'GeneralSettingsSection', () => {
</UpeToggleContext.Provider>
);

const giropayCheckbox = screen.getByRole( 'checkbox', {
name: /giropay/,
const alipayCheckbox = screen.getByRole( 'checkbox', {
name: /Alipay/,
} );

expect( updateEnabledMethodsMock ).not.toHaveBeenCalled();
expect( giropayCheckbox ).not.toBeChecked();
expect( alipayCheckbox ).not.toBeChecked();

userEvent.click( giropayCheckbox );
userEvent.click( alipayCheckbox );

expect( updateEnabledMethodsMock ).toHaveBeenCalledWith( [
'card',
'giropay',
'alipay',
] );
} );

it( 'should show modal to disable a payment method', () => {
useGetAvailablePaymentMethodIds.mockReturnValue( [
'card',
'giropay',
'sofort',
'alipay',
'sepa_debit',
] );
const updateEnabledMethodsMock = jest.fn();
Expand Down Expand Up @@ -259,8 +246,7 @@ describe( 'GeneralSettingsSection', () => {
it( 'should not allow to disable a payment method when canceled via modal', () => {
useGetAvailablePaymentMethodIds.mockReturnValue( [
'card',
'giropay',
'sofort',
'alipay',
'sepa_debit',
] );
const updateEnabledMethodsMock = jest.fn();
Expand Down Expand Up @@ -291,8 +277,7 @@ describe( 'GeneralSettingsSection', () => {
it( 'should allow to disable a payment method when confirmed via modal', () => {
useGetAvailablePaymentMethodIds.mockReturnValue( [
'card',
'giropay',
'sofort',
'alipay',
'sepa_debit',
] );
const updateEnabledMethodsMock = jest.fn();
Expand Down Expand Up @@ -322,10 +307,7 @@ describe( 'GeneralSettingsSection', () => {

it( 'does not display the payment method checkbox when currency is not supprted', () => {
global.wcSettings = { currency: { code: 'USD' } };
useGetAvailablePaymentMethodIds.mockReturnValue( [
'card',
'giropay',
] );
useGetAvailablePaymentMethodIds.mockReturnValue( [ 'card', 'alipay' ] );
render(
<UpeToggleContext.Provider value={ { isUpeEnabled: true } }>
<GeneralSettingsSection />
Expand All @@ -345,10 +327,7 @@ describe( 'GeneralSettingsSection', () => {
} );

it( 'does not display the payment method checkbox when manual capture is enabled', () => {
useGetAvailablePaymentMethodIds.mockReturnValue( [
'card',
'giropay',
] );
useGetAvailablePaymentMethodIds.mockReturnValue( [ 'card', 'alipay' ] );
useManualCapture.mockReturnValue( [ true ] );
render(
<UpeToggleContext.Provider value={ { isUpeEnabled: true } }>
Expand All @@ -363,7 +342,7 @@ describe( 'GeneralSettingsSection', () => {
).toBeInTheDocument();
expect(
screen.queryByRole( 'checkbox', {
name: 'giropay',
name: 'Alipay',
} )
).not.toBeInTheDocument();
} );
Expand All @@ -379,17 +358,17 @@ describe( 'GeneralSettingsSection', () => {
name: 'Card',
description: 'Pay with Card',
},
giropay: {
name: 'Giropay',
description: 'Pay with Giropay',
alipay: {
name: 'Alipay',
description: 'Pay with Alipay',
},
},
isCustomizing: false,
customizePaymentMethod: customizePaymentMethodMock,
} );
useGetAvailablePaymentMethodIds.mockReturnValue( [ 'giropay' ] );
useGetAvailablePaymentMethodIds.mockReturnValue( [ 'alipay' ] );
useGetOrderedPaymentMethodIds.mockReturnValue( {
orderedPaymentMethodIds: [ 'giropay' ],
orderedPaymentMethodIds: [ 'alipay' ],
setOrderedPaymentMethodIds: jest.fn(),
saveOrderedPaymentMethodIds: jest.fn(),
} );
Expand All @@ -402,7 +381,7 @@ describe( 'GeneralSettingsSection', () => {

expect(
screen.queryByRole( 'checkbox', {
name: 'giropay',
name: 'Alipay',
} )
).toBeInTheDocument();
expect(
Expand All @@ -418,9 +397,9 @@ describe( 'GeneralSettingsSection', () => {
);

// Expect the customization section to be open
expect( screen.getByLabelText( 'Name' ) ).toHaveValue( 'Giropay' );
expect( screen.getByLabelText( 'Name' ) ).toHaveValue( 'Alipay' );
expect( screen.getByLabelText( 'Description' ) ).toHaveValue(
'Pay with Giropay'
'Pay with Alipay'
);
expect(
screen.queryByRole( 'button', {
Expand Down Expand Up @@ -455,9 +434,9 @@ describe( 'GeneralSettingsSection', () => {
} );

it( 'should display customization section in the payment method when UPE is enabled', () => {
useGetAvailablePaymentMethodIds.mockReturnValue( [ 'giropay' ] );
useGetAvailablePaymentMethodIds.mockReturnValue( [ 'alipay' ] );
useGetOrderedPaymentMethodIds.mockReturnValue( {
orderedPaymentMethodIds: [ 'giropay' ],
orderedPaymentMethodIds: [ 'alipay' ],
setOrderedPaymentMethodIds: jest.fn(),
saveOrderedPaymentMethodIds: jest.fn(),
} );
Expand All @@ -470,7 +449,7 @@ describe( 'GeneralSettingsSection', () => {

expect(
screen.queryByRole( 'checkbox', {
name: 'giropay',
name: 'Alipay',
} )
).toBeInTheDocument();
expect(
Expand All @@ -481,12 +460,9 @@ describe( 'GeneralSettingsSection', () => {
} );

it( 'displays the payment method checkbox when manual capture is disabled', () => {
useGetAvailablePaymentMethodIds.mockReturnValue( [
'card',
'giropay',
] );
useGetAvailablePaymentMethodIds.mockReturnValue( [ 'card', 'alipay' ] );
useGetOrderedPaymentMethodIds.mockReturnValue( {
orderedPaymentMethodIds: [ 'card', 'giropay' ],
orderedPaymentMethodIds: [ 'card', 'alipay' ],
setOrderedPaymentMethodIds: jest.fn(),
saveOrderedPaymentMethodIds: jest.fn(),
} );
Expand All @@ -504,16 +480,13 @@ describe( 'GeneralSettingsSection', () => {
).toBeInTheDocument();
expect(
screen.queryByRole( 'checkbox', {
name: 'giropay',
name: 'Alipay',
} )
).toBeInTheDocument();
} );

it( 'should not render payment methods that are not part of the account capabilities', () => {
useGetAvailablePaymentMethodIds.mockReturnValue( [
'card',
'giropay',
] );
useGetAvailablePaymentMethodIds.mockReturnValue( [ 'card', 'alipay' ] );
useGetCapabilities.mockReturnValue( {
card_payments: 'active',
} );
Expand All @@ -526,7 +499,7 @@ describe( 'GeneralSettingsSection', () => {

expect(
screen.queryByRole( 'checkbox', {
name: 'giropay',
name: 'Alipay',
} )
).not.toBeInTheDocument();
} );
Expand All @@ -537,7 +510,7 @@ describe( 'GeneralSettingsSection', () => {
} );
useGetAvailablePaymentMethodIds.mockReturnValue( [
'card',
'giropay',
'alipay',
'sepa_debit',
'sofort',
'eps',
Expand Down
10 changes: 10 additions & 0 deletions client/settings/general-settings-section/payment-methods-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,11 @@ const GeneralSettingsSection = ( {
onReorder={ onReorder }
>
{ availablePaymentMethods.map( ( method ) => {
// Skip giropay as it was deprecated by Jun, 30th 2024.
if ( method === 'giropay' ) {
return null;
}

const {
Icon,
label,
Expand Down Expand Up @@ -286,6 +291,11 @@ const GeneralSettingsSection = ( {
) : (
<List>
{ availablePaymentMethods.map( ( method ) => {
// Skip giropay as it was deprecated by Jun, 30th 2024.
if ( method === 'giropay' ) {
return null;
}

const {
Icon,
label,
Expand Down
17 changes: 13 additions & 4 deletions includes/class-wc-stripe-payment-tokens.php
Original file line number Diff line number Diff line change
Expand Up @@ -262,15 +262,19 @@ public function woocommerce_get_customer_upe_payment_tokens( $tokens, $user_id,
}

try {
$stored_tokens = [];

$stored_tokens = [];
$deprecated_tokens = [];

foreach ( $tokens as $token ) {
if ( in_array( $token->get_gateway_id(), self::UPE_REUSABLE_GATEWAYS_BY_PAYMENT_METHOD, true ) ) {

// APM tokens from before Split PE was in place that will get removed.
if ( 'stripe' === $token->get_gateway_id() && 'sepa' === $token->get_type() ) {
// Remove the following deprecated tokens:
// - APM tokens from before Split PE was in place.
// - Tokens using the sources API. Payments using these will fail with the PaymentMethods API.
if (
( 'stripe' === $token->get_gateway_id() && 'sepa' === $token->get_type() ) ||
str_starts_with( $token->get_token(), 'src_' )
) {
$deprecated_tokens[ $token->get_token() ] = $token;
continue;
}
Expand Down Expand Up @@ -310,8 +314,13 @@ public function woocommerce_get_customer_upe_payment_tokens( $tokens, $user_id,
// Retrieve the real APM behind SEPA PaymentMethods.
$payment_method_type = $this->get_original_payment_method_type( $payment_method );

// Create a new token when:
// - The payment method doesn't have an associated token in WooCommerce.
// - The payment method is not a source.
// - The payment method belongs to the gateway ID being retrieved or the gateway ID is empty (meaning we're looking for all payment methods).
if (
! isset( $stored_tokens[ $payment_method->id ] ) &&
! str_starts_with( $payment_method->id, 'src_' ) &&
( $this->is_valid_payment_method_type_for_gateway( $payment_method_type, $gateway_id ) || empty( $gateway_id ) )
) {
$token = $this->add_token_to_user( $payment_method, $customer );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ private function set_subscription_updated_payment_method( WC_Subscription $subsc
$source_id = $subscription->get_meta( self::SOURCE_ID_META_KEY );

// Bail out if the subscription is already using a pm_.
if ( 0 === strpos( $source_id, 'src_' ) ) {
if ( 0 !== strpos( $source_id, 'src_' ) ) {
throw new \Exception( sprintf( 'The subscription is not using a Stripe Source for renewals.', $subscription->get_id() ) );
}

Expand Down
6 changes: 1 addition & 5 deletions includes/payment-methods/class-wc-gateway-stripe-giropay.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,7 @@ public function get_supported_currency() {
* @return bool
*/
public function is_available() {
if ( ! in_array( get_woocommerce_currency(), $this->get_supported_currency() ) ) {
return false;
}

return parent::is_available();
return false;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class WC_Stripe_UPE_Payment_Gateway extends WC_Gateway_Stripe {
const UPE_AVAILABLE_METHODS = [
WC_Stripe_UPE_Payment_Method_CC::class,
WC_Stripe_UPE_Payment_Method_Alipay::class,
WC_Stripe_UPE_Payment_Method_Giropay::class,
WC_Stripe_UPE_Payment_Method_Klarna::class,
WC_Stripe_UPE_Payment_Method_Affirm::class,
WC_Stripe_UPE_Payment_Method_Afterpay_Clearpay::class,
Expand Down
Loading

0 comments on commit ccf791a

Please sign in to comment.