Skip to content

Disabling BNPLs when other official plugins are active #4492

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Jul 16, 2025
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e18b8a9
Disabling BNPLs when other official plugins are active
wjrosa Jul 14, 2025
fc3d51a
Simplifying logic
wjrosa Jul 15, 2025
e90b2d3
Changelog and readme entries
wjrosa Jul 15, 2025
19d0206
Unit tests
wjrosa Jul 15, 2025
de89343
Checking for specific official plugins instead of any
wjrosa Jul 15, 2025
82fcb80
Adding constants
wjrosa Jul 15, 2025
21f2457
Unit test
wjrosa Jul 15, 2025
d6f85d2
Merge branch 'develop' into update/disable-bnpls-when-other-plugins-a…
wjrosa Jul 15, 2025
569443d
Merge branch 'develop' into update/disable-bnpls-when-other-plugins-a…
wjrosa Jul 15, 2025
644c10a
Merge branch 'develop' into update/disable-bnpls-when-other-plugins-a…
wjrosa Jul 16, 2025
6465faf
Fix methods availability in the block checkout
wjrosa Jul 16, 2025
c2dfbc9
Fix methods availability in the block checkout
wjrosa Jul 16, 2025
c33624d
Keeping methods visible in the settings page
wjrosa Jul 16, 2025
7d76068
Fix methods availability in the shortcode checkout
wjrosa Jul 16, 2025
ce86f94
New pill to inform about the conflict
wjrosa Jul 16, 2025
3b5160d
Unit test
wjrosa Jul 16, 2025
4f94543
Disable checkbox when unavailable
wjrosa Jul 16, 2025
3760220
Merge branch 'develop' into update/disable-bnpls-when-other-plugins-a…
wjrosa Jul 16, 2025
4a26e67
Reverting unit tests removal
wjrosa Jul 16, 2025
b5fc2a5
Update index.js
wjrosa Jul 16, 2025
ed7c159
Update index.test.js
wjrosa Jul 16, 2025
9bcd50f
Merge branch 'develop' into update/disable-bnpls-when-other-plugins-a…
wjrosa Jul 16, 2025
7144812
Update index.js
wjrosa Jul 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
*** Changelog ***

= 9.7.0 - xxxx-xx-xx =
* Update - Removes BNPL payment methods (Klarna and Affirm) when other official plugins are active
* Fix - Moves the existing order lock functionality earlier in the order processing flow to prevent duplicate processing requests
* Add - Adds two new safety filters to the subscriptions detached debug tool: `wc_stripe_detached_subscriptions_maximum_time` and `wc_stripe_detached_subscriptions_maximum_count`
* Add - Show a notice when editing an active subscription that has no payment method attached
Expand Down
22 changes: 16 additions & 6 deletions client/blocks/upe/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import {
registerExpressPaymentMethod,
} from '@woocommerce/blocks-registry';
import {
PAYMENT_METHOD_AFFIRM,
PAYMENT_METHOD_AMAZON_PAY,
PAYMENT_METHOD_CARD,
PAYMENT_METHOD_GIROPAY,
PAYMENT_METHOD_KLARNA,
PAYMENT_METHOD_LINK,
} from '../../stripe-utils/constants';
import { updateTokenLabelsWhenLoaded } from './token-label-updater.js';
Expand Down Expand Up @@ -38,18 +40,26 @@ const api = new WCStripeAPI(
const paymentMethodsConfig =
getBlocksConfiguration()?.paymentMethodsConfig ?? {};

const methodsToFilter = [
PAYMENT_METHOD_AMAZON_PAY,
PAYMENT_METHOD_LINK,
PAYMENT_METHOD_GIROPAY, // Skip giropay as it was deprecated by Jun, 30th 2024.
];

// Register UPE Elements.
if ( getBlocksConfiguration()?.isOCEnabled ) {
registerPaymentMethod(
upeElement( PAYMENT_METHOD_CARD, api, paymentMethodsConfig.card )
);
} else {
const methodsToFilter = [
PAYMENT_METHOD_AMAZON_PAY,
PAYMENT_METHOD_LINK,
PAYMENT_METHOD_GIROPAY, // Skip giropay as it was deprecated by Jun, 30th 2024.
];

// Filter out some BNPLs when other official extensions are present.
if ( getBlocksConfiguration()?.hasAffirmGatewayPlugin ) {
methodsToFilter.push( PAYMENT_METHOD_AFFIRM );
}
if ( getBlocksConfiguration()?.hasKlarnaGatewayPlugin ) {
methodsToFilter.push( PAYMENT_METHOD_KLARNA );
}

Object.entries( paymentMethodsConfig )
.filter( ( [ method ] ) => ! methodsToFilter.includes( method ) )
.forEach( ( [ method, config ] ) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { screen, render } from '@testing-library/react';
import PaymentMethodUnavailableDueConflictPill from '..';
import { PAYMENT_METHOD_AFFIRM } from 'wcstripe/stripe-utils/constants';

describe( 'PaymentMethodUnavailableDueConflictPill', () => {
beforeEach( () => {
global.wc_stripe_settings_params = { has_affirm_gateway_plugin: false };
} );

it( 'should render the "Unavailable due conflict" text', () => {
global.wc_stripe_settings_params = { has_affirm_gateway_plugin: true };

render(
<PaymentMethodUnavailableDueConflictPill
id={ PAYMENT_METHOD_AFFIRM }
label="Affirm"
/>
);

expect(
screen.queryByText( 'Unavailable due conflict' )
).toBeInTheDocument();
} );

it( 'should not render when other extensions are not active', () => {
const { container } = render(
<PaymentMethodUnavailableDueConflictPill
id={ PAYMENT_METHOD_AFFIRM }
label="Affirm"
/>
);

expect( container.firstChild ).toBeNull();
} );
} );
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/* global wc_stripe_settings_params */
import { __, sprintf } from '@wordpress/i18n';
import React from 'react';
import styled from '@emotion/styled';
import interpolateComponents from 'interpolate-components';
import { Icon, info } from '@wordpress/icons';
import Popover from 'wcstripe/components/popover';
import {
PAYMENT_METHOD_AFFIRM,
PAYMENT_METHOD_KLARNA,
} from 'wcstripe/stripe-utils/constants';

const StyledPill = styled.span`
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
border: 1px solid #fcf9e8;
border-radius: 2px;
background-color: #fcf9e8;
color: #674600;
font-size: 12px;
font-weight: 400;
line-height: 16px;
width: fit-content;
`;

const StyledLink = styled.a`
&:focus,
&:visited {
box-shadow: none;
}
`;

const IconWrapper = styled.span`
height: 16px;
cursor: pointer;
`;

const AlertIcon = styled( Icon )`
fill: #674600;
`;

const IconComponent = ( { children, ...props } ) => (
<IconWrapper { ...props }>
<AlertIcon icon={ info } size="16" />
{ children }
</IconWrapper>
);

const PaymentMethodUnavailableDueConflictPill = ( { id, label } ) => {
if (
( id === PAYMENT_METHOD_AFFIRM &&
// eslint-disable-next-line camelcase
wc_stripe_settings_params.has_affirm_gateway_plugin ) ||
( id === PAYMENT_METHOD_KLARNA &&
// eslint-disable-next-line camelcase
wc_stripe_settings_params.has_klarna_gateway_plugin )
) {
return (
<StyledPill>
{ __(
'Unavailable due conflict',
'woocommerce-gateway-stripe'
) }
<Popover
BaseComponent={ IconComponent }
content={ interpolateComponents( {
mixedString: sprintf(
/* translators: $1: a payment method name */
__(
'%1$s is unavailable due to another official plugin being active.',
'woocommerce-gateway-stripe'
),
label
),
components: {
currencySettingsLink: (
<StyledLink
href="/wp-admin/admin.php?page=wc-settings&tab=general"
target="_blank"
rel="noreferrer"
onClick={ ( ev ) => {
// Stop propagation is necessary so it doesn't trigger the tooltip click event.
ev.stopPropagation();
} }
/>
),
},
} ) }
/>
</StyledPill>
);
}

return null;
};

export default PaymentMethodUnavailableDueConflictPill;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PaymentMethodMissingCurrencyPill from '../../components/payment-method-mi
import RecurringPaymentIcon from '../../components/recurring-payment-icon';
import PaymentMethodCapabilityStatusPill from 'wcstripe/components/payment-method-capability-status-pill';
import PaymentMethodDeprecationPill from 'wcstripe/components/payment-method-deprecation-pill';
import PaymentMethodUnavailableDueConflictPill from 'wcstripe/components/payment-method-unavailable-due-conflict-pill';

const Wrapper = styled.div`
display: flex;
Expand Down Expand Up @@ -69,6 +70,10 @@ const PaymentMethodDescription = ( {
id={ id }
label={ label }
/>
<PaymentMethodUnavailableDueConflictPill
id={ id }
label={ label }
/>
</>
) }
</LabelWrapper>
Expand Down
11 changes: 9 additions & 2 deletions client/settings/general-settings-section/payment-method.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
PAYMENT_METHOD_AFFIRM,
PAYMENT_METHOD_AFTERPAY_CLEARPAY,
PAYMENT_METHOD_CARD,
PAYMENT_METHOD_KLARNA,
} from 'wcstripe/stripe-utils/constants';
import PaymentMethodFeesPill from 'wcstripe/components/payment-method-fees-pill';
import { usePaymentMethodCurrencies } from 'utils/use-payment-method-currencies';
Expand Down Expand Up @@ -127,8 +128,14 @@ const PaymentMethod = ( { method, data } ) => {

const storeCurrency = window?.wcSettings?.currency?.code;
const isDisabled =
paymentMethodCurrencies.length &&
! paymentMethodCurrencies.includes( storeCurrency );
( paymentMethodCurrencies.length &&
! paymentMethodCurrencies.includes( storeCurrency ) ) ||
( PAYMENT_METHOD_AFFIRM === method &&
// eslint-disable-next-line camelcase
wc_stripe_settings_params.has_affirm_gateway_plugin ) ||
( PAYMENT_METHOD_KLARNA === method &&
// eslint-disable-next-line camelcase
wc_stripe_settings_params.has_klarna_gateway_plugin );

return (
<div key={ method }>
Expand Down
13 changes: 3 additions & 10 deletions includes/admin/class-wc-stripe-settings-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,15 +257,6 @@ public function admin_scripts( $hook_suffix ) {
// Show the BNPL promotional banner only if no BNPL payment methods are enabled.
&& ! array_intersect( WC_Stripe_Payment_Methods::BNPL_PAYMENT_METHODS, $enabled_payment_methods );

$has_other_bnpl_plugins_active = false;
$available_payment_gateways = WC()->payment_gateways->payment_gateways;
foreach ( $available_payment_gateways as $gateway ) {
if ( ( 'affirm' === $gateway->id || 'klarna_payments' === $gateway->id ) && 'yes' === $gateway->enabled ) {
$has_other_bnpl_plugins_active = true;
break;
}
}

$params = [
'time' => time(),
'i18n_out_of_sync' => $message,
Expand All @@ -287,7 +278,9 @@ public function admin_scripts( $hook_suffix ) {
'is_oc_available' => WC_Stripe_Feature_Flags::is_oc_available(),
'oauth_nonce' => wp_create_nonce( 'wc_stripe_get_oauth_urls' ),
'is_sepa_tokens_enabled' => 'yes' === $this->gateway->get_option( 'sepa_tokens_for_other_methods', 'no' ),
'has_other_bnpl_plugins' => $has_other_bnpl_plugins_active,
'has_affirm_gateway_plugin' => WC_Stripe_Helper::has_gateway_plugin_active( WC_Stripe_Helper::OFFICIAL_PLUGIN_ID_AFFIRM ),
'has_klarna_gateway_plugin' => WC_Stripe_Helper::has_gateway_plugin_active( WC_Stripe_Helper::OFFICIAL_PLUGIN_ID_KLARNA ),
'has_other_bnpl_plugins' => WC_Stripe_Helper::has_other_bnpl_plugins_active(),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making use of the new helper function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option here would be to reuse the return of both WC_Stripe_Helper::has_gateway_plugin_active. Not sure if it is worth it.

'is_payments_onboarding_task_completed' => $this->is_payments_onboarding_task_completed(),
];
wp_localize_script(
Expand Down
45 changes: 45 additions & 0 deletions includes/class-wc-stripe-helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ class WC_Stripe_Helper {
const META_NAME_STRIPE_CURRENCY = '_stripe_currency';
const PAYMENT_AWAITING_ACTION_META = '_stripe_payment_awaiting_action';

/**
* The identifier for the official Affirm gateway plugin.
*
* @var string
*/
const OFFICIAL_PLUGIN_ID_AFFIRM = 'affirm';

/**
* The identifier for the official Klarna gateway plugin.
*
* @var string
*/
const OFFICIAL_PLUGIN_ID_KLARNA = 'klarna_payments';

/**
* List of legacy Stripe gateways.
*
Expand Down Expand Up @@ -1851,4 +1865,35 @@ public static function get_refund_reason_description( $refund_reason_key ) {
return __( 'Unknown reason', 'woocommerce-gateway-stripe' );
}
}

/**
* Checks if there are other Buy Now Pay Later plugins active.
*
* @return bool
*/
public static function has_other_bnpl_plugins_active() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we need this logic in multiple places, I moved it to a new helper function.

$other_bnpl_gateway_ids = [ self::OFFICIAL_PLUGIN_ID_AFFIRM, self::OFFICIAL_PLUGIN_ID_KLARNA ];
foreach ( $other_bnpl_gateway_ids as $bnpl_gateway_id ) {
if ( self::has_gateway_plugin_active( $bnpl_gateway_id ) ) {
return true;
}
}
return false;
}

/**
* Checks if a given payment gateway plugin is active.
*
* @param string $plugin_id
* @return bool
*/
public static function has_gateway_plugin_active( $plugin_id ) {
$available_payment_gateways = WC()->payment_gateways->payment_gateways ?? [];
foreach ( $available_payment_gateways as $available_payment_gateway ) {
if ( $plugin_id === $available_payment_gateway->id && 'yes' === $available_payment_gateway->enabled ) {
return true;
}
}
return false;
}
}
11 changes: 1 addition & 10 deletions includes/notes/class-wc-stripe-bnpl-promotion-note.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,7 @@ public static function init( WC_Stripe_Payment_Gateway $gateway ) {
}
}

$has_other_bnpl_plugins_active = false;
$available_payment_gateways = WC()->payment_gateways->payment_gateways;
$other_bnpl_gateway_ids = [ 'affirm', 'klarna_payments' ];
foreach ( $available_payment_gateways as $available_payment_gateway ) {
if ( in_array( $available_payment_gateway->id, $other_bnpl_gateway_ids, true ) && 'yes' === $available_payment_gateway->enabled ) {
$has_other_bnpl_plugins_active = true;
break;
}
}
if ( $has_other_bnpl_plugins_active ) {
if ( WC_Stripe_Helper::has_other_bnpl_plugins_active() ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making use of the new helper function.

return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,10 @@ public function javascript_params() {
// Single Payment Element payment method parent configuration ID
$stripe_params['paymentMethodConfigurationParentId'] = WC_Stripe_Payment_Method_Configurations::get_parent_configuration_id();

// Checking for other BNPL extensions.
$stripe_params['hasAffirmGatewayPlugin'] = WC_Stripe_Helper::has_gateway_plugin_active( WC_Stripe_Helper::OFFICIAL_PLUGIN_ID_AFFIRM );
$stripe_params['hasKlarnaGatewayPlugin'] = WC_Stripe_Helper::has_gateway_plugin_active( WC_Stripe_Helper::OFFICIAL_PLUGIN_ID_KLARNA );

$cart_total = ( WC()->cart ? WC()->cart->get_total( '' ) : 0 );
$currency = get_woocommerce_currency();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,18 @@ public function is_available_for_account_country() {
public function requires_automatic_capture() {
return false;
}

/**
* Returns true if the UPE method is available.
*
* @inheritDoc
*/
public function is_available() {
// Affirm is only available if the official Affirm plugin is not active.
if ( WC_Stripe_Helper::has_gateway_plugin_active( WC_Stripe_Helper::OFFICIAL_PLUGIN_ID_AFFIRM ) ) {
return false;
}

return parent::is_available();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,18 @@ public function is_available_for_account_country() {
public function requires_automatic_capture() {
return false;
}

/**
* Returns true if the UPE method is available.
*
* @inheritDoc
*/
public function is_available() {
// Klarna is only available if the official Klarna plugin is not active.
if ( WC_Stripe_Helper::has_gateway_plugin_active( WC_Stripe_Helper::OFFICIAL_PLUGIN_ID_KLARNA ) ) {
return false;
}

return parent::is_available();
}
}
1 change: 1 addition & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o
== Changelog ==

= 9.7.0 - xxxx-xx-xx =
* Update - Removes BNPL payment methods (Klarna and Affirm) when other official plugins are active
* Fix - Moves the existing order lock functionality earlier in the order processing flow to prevent duplicate processing requests
* Add - Adds two new safety filters to the subscriptions detached debug tool: `wc_stripe_detached_subscriptions_maximum_time` and `wc_stripe_detached_subscriptions_maximum_count`
* Add - Show a notice when editing an active subscription that has no payment method attached
Expand Down
Loading
Loading