Merge branch 'trunk' into PCP-4038-check-the-i-18-n-translations-in-react-app

This commit is contained in:
carmenmaymo 2025-02-19 10:48:20 +01:00
commit 3fdc251483
No known key found for this signature in database
GPG key ID: 6023F686B0F3102E
100 changed files with 1740 additions and 909 deletions

View file

@ -80,12 +80,11 @@ use WooCommerce\PayPalCommerce\ApiClient\Repository\PayeeRepository;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\ConnectBearer;
use WooCommerce\PayPalCommerce\WcGateway\Helper\EnvironmentConfig;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
return array(
'api.host' => static function( ContainerInterface $container ) : string {
$environment = $container->get( 'onboarding.environment' );
$environment = $container->get( 'settings.environment' );
assert( $environment instanceof Environment );
if ( $environment->is_sandbox() ) {

View file

@ -42,7 +42,7 @@ return array(
assert( $display_manager instanceof DisplayManager );
// Domain registration.
$env = $container->get( 'onboarding.environment' );
$env = $container->get( 'settings.environment' );
assert( $env instanceof Environment );
$domain_registration_url = 'https://www.paypal.com/uccservicing/apm/applepay';

View file

@ -20,7 +20,6 @@ use WooCommerce\PayPalCommerce\Applepay\Helper\ApmApplies;
use WooCommerce\PayPalCommerce\Applepay\Helper\AvailabilityNotice;
use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
return array(
@ -260,15 +259,15 @@ return array(
},
'applepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string {
$state = $container->get( 'onboarding.state' );
if ( $state->current_state() < State::STATE_ONBOARDED ) {
$is_connected = $container->get( 'settings.flag.is-connected' );
if ( ! $is_connected ) {
return '';
}
$product_status = $container->get( 'applepay.apple-product-status' );
assert( $product_status instanceof AppleProductStatus );
$environment = $container->get( 'onboarding.environment' );
$environment = $container->get( 'settings.environment' );
assert( $environment instanceof Environment );
$enabled = $product_status->is_active();

View file

@ -368,7 +368,7 @@ class ApplepayModule implements ServiceModule, ExtendingModule, ExecutableModule
if ( ! $button->is_enabled() ) {
return;
}
$env = $c->get( 'onboarding.environment' );
$env = $c->get( 'settings.environment' );
assert( $env instanceof Environment );
$is_sandobx = $env->current_environment_is( Environment::SANDBOX );
$this->load_domain_association_file( $is_sandobx );

View file

@ -89,6 +89,7 @@ class AppleProductStatus extends ProductStatus {
}
}
// Settings used as a cache; `settings->set` is compatible with new UI.
if ( $has_capability ) {
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
} else {

View file

@ -36,7 +36,7 @@ return array(
fn(): SmartButtonInterface => $container->get( 'button.smart-button' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'wcgateway.configuration.dcc' ),
$container->get( 'onboarding.environment' ),
$container->get( 'settings.environment' ),
$container->get( 'wcgateway.url' ),
$container->get( 'axo.payment_method_selected_map' ),
$container->get( 'axo.supported-country-card-type-matrix' )

View file

@ -10,7 +10,6 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Axo;
use WooCommerce\PayPalCommerce\Axo\Helper\NoticeRenderer;
use WooCommerce\PayPalCommerce\Axo\Helper\PropertiesDictionary;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager;

View file

@ -66,7 +66,7 @@ return array(
$container->get( 'ppcp.asset-version' ),
$container->get( 'session.handler' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'onboarding.environment' ),
$container->get( 'settings.environment' ),
$container->get( 'axo.insights' ),
$container->get( 'wcgateway.settings.status' ),
$container->get( 'api.shop.currency.getter' ),
@ -89,7 +89,7 @@ return array(
$container->get( 'api.factory.purchase-unit' ),
$container->get( 'api.factory.shipping-preference' ),
$container->get( 'wcgateway.transaction-url-provider' ),
$container->get( 'onboarding.environment' ),
$container->get( 'settings.environment' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},

View file

@ -36,7 +36,6 @@ use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler;
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration;
@ -49,7 +48,7 @@ return array(
return $client_id;
}
$env = $container->get( 'onboarding.environment' );
$env = $container->get( 'settings.environment' );
/**
* The environment.
*
@ -125,8 +124,8 @@ return array(
}
}
$state = $container->get( 'onboarding.state' );
if ( $state->current_state() !== State::STATE_ONBOARDED ) {
$is_connected = $container->get( 'settings.flag.is-connected' );
if ( ! $is_connected ) {
return new DisabledSmartButton();
}
@ -142,7 +141,7 @@ return array(
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
$messages_apply = $container->get( 'button.helper.messages-apply' );
$environment = $container->get( 'onboarding.environment' );
$environment = $container->get( 'settings.environment' );
$payment_token_repository = $container->get( 'vaulting.repository.payment-token' );
return new SmartButton(
$container->get( 'button.url' ),
@ -241,11 +240,11 @@ return array(
);
},
'button.helper.early-order-handler' => static function ( ContainerInterface $container ) : EarlyOrderHandler {
$state = $container->get( 'onboarding.state' );
$order_processor = $container->get( 'wcgateway.order-processor' );
$session_handler = $container->get( 'session.handler' );
return new EarlyOrderHandler( $state, $order_processor, $session_handler );
return new EarlyOrderHandler(
$container->get( 'settings.flag.is-connected' ),
$container->get( 'wcgateway.order-processor' ),
$container->get( 'session.handler' )
);
},
'button.endpoint.approve-order' => static function ( ContainerInterface $container ): ApproveOrderEndpoint {
$request_data = $container->get( 'button.request-data' );

View file

@ -272,8 +272,9 @@ class ApproveOrderEndpoint implements EndpointInterface {
* @return void
*/
protected function toggle_final_review_enabled_setting(): void {
// TODO new-ux: This flag must also be updated in the new settings.
$final_review_enabled_setting = $this->settings->has( 'blocks_final_review_enabled' ) && $this->settings->get( 'blocks_final_review_enabled' );
$final_review_enabled_setting ? $this->settings->set( 'blocks_final_review_enabled', false ) : $this->settings->set( 'blocks_final_review_enabled', true );
$this->settings->set( 'blocks_final_review_enabled', ! $final_review_enabled_setting );
$this->settings->persist();
}
}

View file

@ -11,8 +11,6 @@ namespace WooCommerce\PayPalCommerce\Button\Helper;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
@ -23,11 +21,11 @@ use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
class EarlyOrderHandler {
/**
* The State.
* Whether the merchant is connected to PayPal (onboarding completed).
*
* @var State
* @var bool
*/
private $state;
private bool $is_connected;
/**
* The Order Processor.
@ -46,17 +44,17 @@ class EarlyOrderHandler {
/**
* EarlyOrderHandler constructor.
*
* @param State $state The State.
* @param bool $is_connected Whether onboarding was completed.
* @param OrderProcessor $order_processor The Order Processor.
* @param SessionHandler $session_handler The Session Handler.
*/
public function __construct(
State $state,
bool $is_connected,
OrderProcessor $order_processor,
SessionHandler $session_handler
) {
$this->state = $state;
$this->is_connected = $is_connected;
$this->order_processor = $order_processor;
$this->session_handler = $session_handler;
}
@ -67,7 +65,7 @@ class EarlyOrderHandler {
* @return bool
*/
public function should_create_early_order(): bool {
return $this->state->current_state() === State::STATE_ONBOARDED;
return $this->is_connected;
}
//phpcs:disable WordPress.Security.NonceVerification.Recommended

View file

@ -30,13 +30,17 @@ class SettingsTabMapHelper {
*/
public function map(): array {
return array(
'disable_cards' => 'disabled_cards',
'brand_name' => 'brand_name',
'soft_descriptor' => 'soft_descriptor',
'payee_preferred' => 'instant_payments_only',
'subtotal_mismatch_behavior' => 'subtotal_adjustment',
'landing_page' => 'landing_page',
'smart_button_language' => 'button_language',
'disable_cards' => 'disabled_cards',
'brand_name' => 'brand_name',
'soft_descriptor' => 'soft_descriptor',
'payee_preferred' => 'instant_payments_only',
'subtotal_mismatch_behavior' => 'subtotal_adjustment',
'landing_page' => 'landing_page',
'smart_button_language' => 'button_language',
'prefix' => 'invoice_prefix',
'intent' => '',
'vault_enabled_dcc' => 'save_card_details',
'blocks_final_review_enabled' => 'enable_pay_now',
);
}
@ -57,6 +61,12 @@ class SettingsTabMapHelper {
case 'landing_page':
return $this->mapped_landing_page_value( $settings_model );
case 'intent':
return $this->mapped_intent_value( $settings_model );
case 'blocks_final_review_enabled':
return $this->mapped_pay_now_value( $settings_model );
default:
return $settings_model[ $new_key ] ?? null;
}
@ -98,4 +108,37 @@ class SettingsTabMapHelper {
: ApplicationContext::LANDING_PAGE_NO_PREFERENCE
);
}
/**
* Retrieves the mapped value for the order intent from the new settings.
*
* @param array<string, scalar|array> $settings_model The new settings model data as an array.
* @return 'AUTHORIZE'|'CAPTURE'|null The mapped 'intent' setting value.
*/
protected function mapped_intent_value( array $settings_model ): ?string {
$authorize_only = $settings_model['authorize_only'] ?? null;
$capture_virtual_orders = $settings_model['capture_virtual_orders'] ?? null;
if ( is_null( $authorize_only ) && is_null( $capture_virtual_orders ) ) {
return null;
}
return $authorize_only ? 'AUTHORIZE' : 'CAPTURE';
}
/**
* Retrieves the mapped value for the "Pay Now Experience" from the new settings.
*
* @param array<string, scalar|array> $settings_model The new settings model data as an array.
* @return bool|null The mapped 'Pay Now Experience' setting value.
*/
protected function mapped_pay_now_value( array $settings_model ): ?bool {
$enable_pay_now = $settings_model['enable_pay_now'] ?? null;
if ( is_null( $enable_pay_now ) ) {
return null;
}
return ! $enable_pay_now;
}
}

View file

@ -19,7 +19,6 @@ use WooCommerce\PayPalCommerce\Googlepay\Helper\ApmApplies;
use WooCommerce\PayPalCommerce\Googlepay\Helper\ApmProductStatus;
use WooCommerce\PayPalCommerce\Googlepay\Helper\AvailabilityNotice;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
return array(
@ -174,7 +173,7 @@ return array(
$container->get( 'session.handler' ),
$container->get( 'wc-subscriptions.helper' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'onboarding.environment' ),
$container->get( 'settings.environment' ),
$container->get( 'wcgateway.settings.status' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
@ -221,15 +220,15 @@ return array(
},
'googlepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string {
$state = $container->get( 'onboarding.state' );
if ( $state->current_state() < State::STATE_ONBOARDED ) {
$is_connected = $container->get( 'settings.flag.is-connected' );
if ( ! $is_connected ) {
return '';
}
$product_status = $container->get( 'googlepay.helpers.apm-product-status' );
assert( $product_status instanceof ApmProductStatus );
$environment = $container->get( 'onboarding.environment' );
$environment = $container->get( 'settings.environment' );
assert( $environment instanceof Environment );
$enabled = $product_status->is_active();

View file

@ -89,6 +89,7 @@ class ApmProductStatus extends ProductStatus {
}
}
// Settings used as a cache; `settings->set` is compatible with new UI.
if ( $has_capability ) {
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
} else {

View file

@ -11,7 +11,6 @@ namespace WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods;
use WC_Order;
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
@ -56,8 +55,9 @@ class LocalAlternativePaymentMethodsModule implements ServiceModule, ExtendingMo
if ( ! self::should_add_local_apm_gateways( $c ) ) {
return $methods;
}
$onboarding_state = $c->get( 'onboarding.state' );
if ( $onboarding_state->current_state() === State::STATE_START ) {
$is_connected = $c->get( 'settings.flag.is-connected' );
if ( ! $is_connected ) {
return $methods;
}

View file

@ -71,6 +71,7 @@ class LocalApmProductStatus extends ProductStatus {
}
}
// Settings used as a cache; `settings->set` is compatible with new UI.
if ( $has_capability ) {
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
} else {

View file

@ -18,10 +18,11 @@ use WooCommerce\PayPalCommerce\Onboarding\Endpoint\UpdateSignupLinksEndpoint;
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingSendOnlyNoticeRenderer;
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
return array(
'api.paypal-host' => function( ContainerInterface $container ) : string {
$environment = $container->get( 'onboarding.environment' );
$environment = $container->get( 'settings.environment' );
/**
* The current environment.
*
@ -34,7 +35,7 @@ return array(
},
'api.paypal-website-url' => function( ContainerInterface $container ) : string {
$environment = $container->get( 'onboarding.environment' );
$environment = $container->get( 'settings.environment' );
assert( $environment instanceof Environment );
if ( $environment->current_environment_is( Environment::SANDBOX ) ) {
return $container->get( 'api.paypal-website-url-sandbox' );
@ -56,9 +57,16 @@ return array(
return $state->current_state() >= State::STATE_ONBOARDED;
},
'onboarding.environment' => function( ContainerInterface $container ) : Environment {
'settings.flag.is-sandbox' => static function ( ContainerInterface $container ) : bool {
$settings = $container->get( 'wcgateway.settings' );
return new Environment( $settings );
assert( $settings instanceof Settings );
return $settings->has( 'sandbox_on' ) && $settings->get( 'sandbox_on' );
},
'settings.environment' => function ( ContainerInterface $container ) : Environment {
return new Environment(
$container->get( 'settings.flag.is-sandbox' )
);
},
'onboarding.assets' => function( ContainerInterface $container ) : OnboardingAssets {
@ -68,7 +76,7 @@ return array(
$container->get( 'onboarding.url' ),
$container->get( 'ppcp.asset-version' ),
$state,
$container->get( 'onboarding.environment' ),
$container->get( 'settings.environment' ),
$login_seller_endpoint,
$container->get( 'wcgateway.current-ppcp-settings-page-id' )
);

View file

@ -133,9 +133,11 @@ class OnboardingRESTController {
* @return array
*/
public function get_status( $request ) {
$environment = $this->container->get( 'onboarding.environment' );
$environment = $this->container->get( 'settings.environment' );
$state = $this->container->get( 'onboarding.state' );
// Legacy onboarding module; using `State::STATE_ONBOARDED` checks is valid here.
return array(
'environment' => $environment->current_environment(),
'onboarded' => ( $state->current_state() >= State::STATE_ONBOARDED ),

View file

@ -95,6 +95,7 @@ class SaveConfig {
* @param array $config The configurator config.
*/
public function save_config( array $config ): void {
// TODO new-ux: We should convert this to a new AbstractDataModel class in the settings folder!
$this->settings->set( 'pay_later_enable_styling_per_messaging_location', true );
$this->settings->set( 'pay_later_messaging_enabled', true );

View file

@ -365,7 +365,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
function( $subscription ) use ( $c ) {
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
if ( $subscription_id ) {
$environment = $c->get( 'onboarding.environment' );
$environment = $c->get( 'settings.environment' );
$host = $environment->current_environment_is( Environment::SANDBOX ) ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com';
?>
<tr>
@ -476,7 +476,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
return;
}
$environment = $c->get( 'onboarding.environment' );
$environment = $c->get( 'settings.environment' );
echo '<div class="options_group subscription_pricing show_if_subscription hidden">';
$this->render_paypal_subscription_fields( $product, $environment );
echo '</div>';
@ -507,7 +507,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
return;
}
$environment = $c->get( 'onboarding.environment' );
$environment = $c->get( 'settings.environment' );
$this->render_paypal_subscription_fields( $product, $environment );
}

View file

@ -24,6 +24,10 @@
padding-top: var(--block-separator-gap, 32px);
border-top: var(--block-separator-size, 1px) solid var(--block-separator-color);
}
&.ppcp--pull-right {
float: right;
}
}
.ppcp-r-settings-block {

View file

@ -70,16 +70,33 @@ $width_gap: 24px;
margin: 0;
}
+ .ppcp-r-settings-card {
margin-top: $card-vertical-gap;
padding-top: $card-vertical-gap;
border-top: 1px solid $color-gray-200;
}
.ppcp--card-actions {
opacity: 0.5;
transition: opacity 0.3s;
&:hover {
opacity: 1;
&.ppcp--dimmed {
opacity: 0.5;
&:hover {
opacity: 1;
}
}
.components-button.is-tertiary:first-child {
padding-left: 0;
.components-button.is-tertiary {
transition: color 0.3s, background 0.3s;
&:first-child {
padding-left: 0;
}
svg {
margin-right: 4px;
}
}
}
}

View file

@ -13,14 +13,3 @@
padding-bottom: 36px;
}
}
.ppcp-r-settings {
> * {
margin-bottom: $card-vertical-gap;
}
> *:not(:last-child) {
padding-bottom: $card-vertical-gap;
border-bottom: 1px solid $color-gray-200;
}
}

View file

@ -227,11 +227,6 @@
}
// Payment Methods
.ppcp-r-payment-methods {
display: flex;
flex-direction: column;
gap: 48px;
}
.ppcp-highlight {
animation: ppcp-highlight-fade 2s cubic-bezier(0.4, 0, 0.2, 1);

View file

@ -13,7 +13,7 @@
#configurator-eligibleContainer.css-4nclxm.e1vy3g880 {
width: 100%;
max-width: 100%;
padding: 48px 0px 48px 48px;
padding: 16px 0px 16px 16px;
#configurator-controlPanelContainer.css-5urmrq.e1vy3g880 {
width: 374px;
@ -25,6 +25,7 @@
.css-7xkxom, .css-8tvj6u {
height: auto;
width: 1.2rem;
}
.css-10nkerk.ej6n7t60 {
@ -37,14 +38,19 @@
}
.css-1vc34jy-handler {
height: 1.6rem;
width: 1.6rem;
height: 1.7em;
width: 1.5rem;
}
.css-8vwtr6-state {
height: 1.6rem;
height: 1.4rem;
width: 3rem;
}
}
.css-1s8clkf.etu8a6w2 {
width: 374px;
}
}
&__subheader, #configurator-controlPanelSubHeader {
@ -68,6 +74,7 @@
.css-rok10q, .css-dfgbdq-text_body_strong {
margin-top: 0;
margin-bottom: 0;
}
&__publish-button {
@ -110,4 +117,30 @@
width: 100%;
}
}
.css-n4cwz8 {
margin-top: 20px;
}
.css-1ce6bcu-container {
width: 3rem;
height: 1.8rem;
}
#configurator-previewSectionSubHeaderText {
margin-right: 10px;
}
.css-zcyvrz.ej6n7t60 {
margin-bottom: 5px;
.css-3xbhoy-svg-size_md-icon {
width: 1.5rem;
height: 1.5rem;
}
.css-7i5kpm-icon-button_base-size_xl-size_sm-secondary {
padding: 0.5rem;
}
}
}

View file

@ -3,8 +3,8 @@
--block-separator-gap: 24px;
--block-header-gap: 18px;
--panel-width: 422px;
--sticky-offset-top: 92px; // 32px admin-bar + 60px TopNavigation height
--preview-height-reduction: 236px; // 32px admin-bar + 60px TopNavigation height + 48px TopNavigation margin + 48px TabList height + 48px TabList margin
--sticky-offset-top: 132px; // 32px admin-bar + 100px TopNavigation height
--preview-height-reduction: 276px; // 32px admin-bar + 100px TopNavigation height + 48px TopNavigation margin + 48px TabList height + 48px TabList margin
display: flex;
border: 1px solid var(--color-separators);

View file

@ -11,8 +11,8 @@ import { getQuery } from '../utils/navigation';
const SettingsApp = () => {
const { isReady: onboardingIsReady, completed: onboardingCompleted } =
OnboardingHooks.useSteps();
const { isReady: merchantIsReady } = CommonHooks.useStore();
const {
isReady: merchantIsReady,
merchant: { isSendOnlyCountry },
} = CommonHooks.useMerchantInfo();

View file

@ -1,7 +1,13 @@
import { ToggleControl } from '@wordpress/components';
import { Action, Description } from '../Elements';
const ControlToggleButton = ( { label, description, value, onChange } ) => (
const ControlToggleButton = ( {
label,
description,
value,
onChange,
disabled = false,
} ) => (
<Action>
<ToggleControl
className="ppcp--control-toggle"
@ -12,6 +18,7 @@ const ControlToggleButton = ( { label, description, value, onChange } ) => (
help={
description ? <Description>{ description }</Description> : null
}
disabled={ disabled }
/>
</Action>
);

View file

@ -0,0 +1,11 @@
import classNames from 'classnames';
const CardActions = ( { isDimmed = false, children } ) => {
const className = classNames( 'ppcp--card-actions', {
'ppcp--dimmed': isDimmed,
} );
return <div className={ className }>{ children }</div>;
};
export default CardActions;

View file

@ -3,6 +3,7 @@
*/
export { default as Action } from './Action';
export { default as CardActions } from './CardActions';
export { default as Content } from './Content';
export { default as ContentWrapper } from './ContentWrapper';
export { default as Description } from './Description';

View file

@ -11,7 +11,7 @@ const PricingDescription = () => {
return null;
}
const lastDate = 'October 25th, 2024'; // TODO -- needs to be the last plugin update date.
const lastDate = 'February 1st, 2025'; // TODO -- needs to be the last plugin update date.
const countryLinks = learnMoreLinks[ storeCountry ] || learnMoreLinks.US;
const label = sprintf(

View file

@ -76,7 +76,17 @@ const StepProducts = () => {
'woocommerce-paypal-payments'
),
isDisabled: isCasualSeller,
contents: <DetailsSubscriptions showNotice={ isCasualSeller } />,
contents: (
/*
* Note: The link should be only displayed if the subscriptions plugin is not installed.
* But when the plugin is not active, this option is completely hidden;
* This means: In the current configuration, we never show the link.
*/
<DetailsSubscriptions
showLink={ false }
showNotice={ isCasualSeller }
/>
),
},
];
return (
@ -117,14 +127,19 @@ const DetailsPhysical = () => (
</ul>
);
const DetailsSubscriptions = ( { showNotice } ) => (
const DetailsSubscriptions = ( { showLink, showNotice } ) => (
<>
<a
target="__blank"
href="https://woocommerce.com/document/woocommerce-paypal-payments/#subscriptions-faq"
>
{ __( 'WooCommerce Subscriptions', 'woocommerce-paypal-payments' ) }
</a>
{ showLink && (
<a
target="__blank"
href="https://woocommerce.com/document/woocommerce-paypal-payments/#subscriptions-faq"
>
{ __(
'WooCommerce Subscriptions',
'woocommerce-paypal-payments'
) }
</a>
) }
{ showNotice && (
<p>
{ __(

View file

@ -3,7 +3,7 @@ import { __ } from '@wordpress/i18n';
import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
import TopNavigation from '../../../ReusableComponents/TopNavigation';
import { useSaveSettings } from '../../../../hooks/useSaveSettings';
import { useStoreManager } from '../../../../hooks/useStoreManager';
import { CommonHooks } from '../../../../data';
import TabBar from '../../../ReusableComponents/TabBar';
import classNames from 'classnames';
@ -20,7 +20,7 @@ const SettingsNavigation = ( {
activePanel,
setActivePanel,
} ) => {
const { persistAll } = useSaveSettings();
const { persistAll } = useStoreManager();
const title = __( 'PayPal Payments', 'woocommerce-paypal-payments' );

View file

@ -1,4 +1,5 @@
import { __ } from '@wordpress/i18n';
import { useEffect } from 'react';
import { ControlToggleButton } from '../../../../../ReusableComponents/Controls';
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
@ -12,6 +13,12 @@ const OrderIntent = () => {
setCaptureVirtualOnlyOrders,
} = SettingsHooks.useSettings();
useEffect( () => {
if ( ! authorizeOnly && captureVirtualOnlyOrders ) {
setCaptureVirtualOnlyOrders( false );
}
}, [ authorizeOnly ] );
return (
<SettingsBlock
title={ __( 'Order Intent', 'woocommerce-paypal-payments' ) }
@ -34,6 +41,7 @@ const OrderIntent = () => {
) }
onChange={ setCaptureVirtualOnlyOrders }
value={ captureVirtualOnlyOrders }
disabled={ ! authorizeOnly }
/>
</SettingsBlock>
);

View file

@ -7,6 +7,7 @@ import ConnectionStatusBadge from './Parts/ConnectionStatusBadge';
import DisconnectButton from './Parts/DisconnectButton';
import SettingsBlock from '../../../../ReusableComponents/SettingsBlock';
import { ControlStaticValue } from '../../../../ReusableComponents/Controls';
import { CardActions } from '../../../../ReusableComponents/Elements';
const ConnectionStatus = () => {
const merchant = CommonHooks.useMerchant();
@ -21,18 +22,20 @@ const ConnectionStatus = () => {
title={ __( 'Connection status', 'woocommerce-paypal-payments' ) }
description={ <ConnectionDescription /> }
>
<SettingsBlock>
<SettingsBlock className="ppcp--pull-right">
<ControlStaticValue
value={
<ConnectionStatusBadge
isActive={ merchant.isConnected }
isSandbox={ merchant.isSandbox }
isBusinessSeller={ merchant.isBusinessSeller }
/>
}
/>
</SettingsBlock>
<SettingsBlock
title={ __( 'Merchant ID', 'woocommerce-paypal-payments' ) }
className="ppcp--no-gap"
>
<ControlStaticValue value={ merchant.id } />
</SettingsBlock>
@ -59,9 +62,9 @@ const ConnectionDescription = () => {
'Your PayPal account connection details.',
'woocommerce-paypal-payments'
) }
<div className="ppcp--card-actions">
<CardActions isDimmed={ true }>
<DisconnectButton />
</div>
</CardActions>
</>
);
};

View file

@ -4,7 +4,6 @@ import {
Content,
ContentWrapper,
} from '../../../../ReusableComponents/Elements';
import ConnectionDetails from './Blocks/ConnectionDetails';
import Troubleshooting from './Blocks/Troubleshooting';
import PaypalSettings from './Blocks/PaypalSettings';
import OtherSettings from './Blocks/OtherSettings';
@ -29,12 +28,12 @@ const ExpertSettings = () => {
contentContainer={ false }
>
<ContentWrapper>
<Content>
{ /*<Content>
<ConnectionDetails
updateFormValue={ updateFormValue }
settings={ settings }
/>
</Content>
</Content>*/ }
<Content>
<Troubleshooting

View file

@ -5,11 +5,19 @@ import TitleBadge, {
TITLE_BADGE_POSITIVE,
} from '../../../../../ReusableComponents/TitleBadge';
const ConnectionStatusBadge = ( { isActive, isSandbox } ) => {
const ConnectionStatusBadge = ( { isActive, isSandbox, isBusinessSeller } ) => {
if ( isActive ) {
const label = isSandbox
? __( 'Sandbox Mode', 'woocommerce-paypal-payments' )
: __( 'Active', 'woocommerce-paypal-payments' );
let label;
if ( isBusinessSeller ) {
label = isSandbox
? __( 'Business | Sandbox', 'woocommerce-paypal-payments' )
: __( 'Business | Live', 'woocommerce-paypal-payments' );
} else {
label = isSandbox
? __( 'Sandbox', 'woocommerce-paypal-payments' )
: __( 'Active', 'woocommerce-paypal-payments' );
}
return <TitleBadge type={ TITLE_BADGE_POSITIVE } text={ label } />;
}

View file

@ -9,14 +9,19 @@ import {
TodoSettingsBlock,
FeatureSettingsBlock,
} from '../../../ReusableComponents/SettingsBlocks';
import { Content, ContentWrapper } from '../../../ReusableComponents/Elements';
import {
Content,
ContentWrapper,
CardActions,
} from '../../../ReusableComponents/Elements';
import SettingsCard from '../../../ReusableComponents/SettingsCard';
import { TITLE_BADGE_POSITIVE } from '../../../ReusableComponents/TitleBadge';
import { useTodos } from '../../../../data/todos/hooks';
import { useMerchantInfo } from '../../../../data/common/hooks';
import { STORE_NAME as COMMON_STORE_NAME } from '../../../../data/common';
import { STORE_NAME as TODOS_STORE_NAME } from '../../../../data/todos';
import { CommonHooks, TodosHooks } from '../../../../data';
import {
CommonStoreName,
TodosStoreName,
CommonHooks,
TodosHooks,
} from '../../../../data';
import { getFeatures } from '../Components/Overview/features-config';
@ -27,8 +32,8 @@ import {
import SpinnerOverlay from '../../../ReusableComponents/SpinnerOverlay';
const TabOverview = () => {
const { isReady: areTodosReady } = TodosHooks.useTodos();
const { isReady: merchantIsReady } = CommonHooks.useMerchantInfo();
const { isReady: areTodosReady } = TodosHooks.useStore();
const { isReady: merchantIsReady } = CommonHooks.useStore();
if ( ! areTodosReady || ! merchantIsReady ) {
return <SpinnerOverlay asModal={ true } />;
@ -47,11 +52,12 @@ export default TabOverview;
const OverviewTodos = () => {
const [ isResetting, setIsResetting ] = useState( false );
const { todos, isReady: areTodosReady, dismissTodo } = useTodos();
const { todos, dismissTodo } = TodosHooks.useTodos();
const { isReady: areTodosReady } = TodosHooks.useStore();
const { setActiveModal, setActiveHighlight } =
useDispatch( COMMON_STORE_NAME );
useDispatch( CommonStoreName );
const { resetDismissedTodos, setDismissedTodos } =
useDispatch( TODOS_STORE_NAME );
useDispatch( TodosStoreName );
const { createSuccessNotice } = useDispatch( noticesStore );
const showTodos = areTodosReady && todos.length > 0;
@ -90,19 +96,24 @@ const OverviewTodos = () => {
'woocommerce-paypal-payments'
) }
</p>
<Button
variant="tertiary"
onClick={ resetHandler }
disabled={ isResetting }
>
<Icon icon={ reusableBlock } size={ 18 } />
{ isResetting
? __( 'Restoring…', 'woocommerce-paypal-payments' )
: __(
'Restore dismissed Things To Do',
'woocommerce-paypal-payments'
) }
</Button>
<CardActions>
<Button
variant="tertiary"
onClick={ resetHandler }
disabled={ isResetting }
>
<Icon icon={ reusableBlock } size={ 18 } />
{ isResetting
? __(
'Restoring…',
'woocommerce-paypal-payments'
)
: __(
'Restore dismissed Things To Do',
'woocommerce-paypal-payments'
) }
</Button>
</CardActions>
</>
}
>
@ -118,9 +129,10 @@ const OverviewTodos = () => {
const OverviewFeatures = () => {
const [ isRefreshing, setIsRefreshing ] = useState( false );
const { merchant, features: merchantFeatures } = useMerchantInfo();
const { merchant, features: merchantFeatures } =
CommonHooks.useMerchantInfo();
const { refreshFeatureStatuses, setActiveModal } =
useDispatch( COMMON_STORE_NAME );
useDispatch( CommonStoreName );
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );
@ -281,14 +293,16 @@ const OverviewFeatureDescription = ( { refreshHandler, isRefreshing } ) => {
'woocommerce-paypal-payments'
) }
</p>
<Button
variant="tertiary"
onClick={ refreshHandler }
disabled={ isRefreshing }
>
<Icon icon={ reusableBlock } size={ 18 } />
{ buttonLabel }
</Button>
<CardActions>
<Button
variant="tertiary"
onClick={ refreshHandler }
disabled={ isRefreshing }
>
<Icon icon={ reusableBlock } size={ 18 } />
{ buttonLabel }
</Button>
</CardActions>
</>
);
};

View file

@ -82,3 +82,16 @@ export function persist() {
} );
};
}
/**
* Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values.
*
* @return {Function} The thunk function.
*/
export function refresh() {
return ( { dispatch, select } ) => {
dispatch.invalidateResolutionForStore();
select.persistentData();
};
}

View file

@ -38,10 +38,16 @@ const useStoreData = () => {
};
export const useStore = () => {
const { dispatch, useTransient } = useStoreData();
const { select, dispatch, useTransient } = useStoreData();
const { persist, refresh } = dispatch;
const [ isReady ] = useTransient( 'isReady' );
return { persist: dispatch.persist, isReady };
// Load persistent data from REST if not done yet.
if ( ! isReady ) {
select.persistentData();
}
return { persist, refresh, isReady };
};
// TODO: Replace with real hook.

View file

@ -27,6 +27,19 @@ export function persist() {
};
}
/**
* Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values.
*
* @return {Function} The thunk function.
*/
export function refresh() {
return ( { dispatch, select } ) => {
dispatch.invalidateResolutionForStore();
select.persistentData();
};
}
/**
* Side effect. Fetches the ISU-login URL for a sandbox account.
*

View file

@ -13,8 +13,32 @@ import { useCallback, useEffect, useMemo, useState } from '@wordpress/element';
import { createHooksForStore } from '../utils';
import { STORE_NAME } from './constants';
const useHooks = () => {
/**
* Single source of truth for access Redux details.
*
* This hook returns a stable API to access actions, selectors and special hooks to generate
* getter- and setters for transient or persistent properties.
*
* @return {{select, dispatch, useTransient, usePersistent}} Store data API.
*/
const useStoreData = () => {
const select = useSelect( ( selectors ) => selectors( STORE_NAME ), [] );
const dispatch = useDispatch( STORE_NAME );
const { useTransient, usePersistent } = createHooksForStore( STORE_NAME );
return useMemo(
() => ( {
select,
dispatch,
useTransient,
usePersistent,
} ),
[ select, dispatch, useTransient, usePersistent ]
);
};
const useHooks = () => {
const { useTransient, usePersistent, dispatch, select } = useStoreData();
const {
persist,
sandboxOnboardingUrl,
@ -23,10 +47,9 @@ const useHooks = () => {
authenticateWithOAuth,
startWebhookSimulation,
checkWebhookSimulationState,
} = useDispatch( STORE_NAME );
} = dispatch;
// Transient accessors.
const [ isReady ] = useTransient( 'isReady' );
const [ activeModal, setActiveModal ] = useTransient( 'activeModal' );
const [ activeHighlight, setActiveHighlight ] =
useTransient( 'activeHighlight' );
@ -38,18 +61,9 @@ const useHooks = () => {
);
// Read-only properties.
const wooSettings = useSelect(
( select ) => select( STORE_NAME ).wooSettings(),
[]
);
const features = useSelect(
( select ) => select( STORE_NAME ).features(),
[]
);
const webhooks = useSelect(
( select ) => select( STORE_NAME ).webhooks(),
[]
);
const wooSettings = select.wooSettings();
const features = select.features();
const webhooks = select.webhooks();
const savePersistent = async ( setter, value ) => {
setter( value );
@ -57,7 +71,6 @@ const useHooks = () => {
};
return {
isReady,
activeModal,
setActiveModal,
activeHighlight,
@ -82,6 +95,19 @@ const useHooks = () => {
};
};
export const useStore = () => {
const { select, dispatch, useTransient } = useStoreData();
const { persist, refresh } = dispatch;
const [ isReady ] = useTransient( 'isReady' );
// Load persistent data from REST if not done yet.
if ( ! isReady ) {
select.persistentData();
}
return { persist, refresh, isReady };
};
export const useSandbox = () => {
const { isSandboxMode, setSandboxMode, sandboxOnboardingUrl } = useHooks();
@ -139,7 +165,7 @@ export const useWebhooks = () => {
};
export const useMerchantInfo = () => {
const { isReady, features } = useHooks();
const { features } = useHooks();
const merchant = useMerchant();
const { refreshMerchantData, setMerchant } = useDispatch( STORE_NAME );
@ -164,7 +190,6 @@ export const useMerchantInfo = () => {
}, [ refreshMerchantData, setMerchant ] );
return {
isReady,
merchant, // Merchant details
features, // Eligible merchant features
verifyLoginStatus, // Callback
@ -204,7 +229,9 @@ export const useActiveHighlight = () => {
return { activeHighlight, setActiveHighlight };
};
// -- Not using the `useHooks()` data provider --
/*
* Busy state management hooks
*/
export const useBusyState = () => {
const { startActivity, stopActivity } = useDispatch( STORE_NAME );

View file

@ -18,10 +18,15 @@ export const addDebugTools = ( context, modules ) => {
if ( ! context.debug ) { return }
*/
const describe = ( fnName, fnInfo ) => {
// eslint-disable-next-line no-console
console.log( `\n%c${ fnName }:`, 'font-weight:bold', fnInfo, '\n\n' );
};
const debugApi = ( window.ppcpDebugger = window.ppcpDebugger || {} );
// Dump the current state of all our Redux stores.
debugApi.dumpStore = async () => {
debugApi.dumpStore = async ( cbFilter = null ) => {
/* eslint-disable no-console */
if ( ! console?.groupCollapsed ) {
console.error( 'console.groupCollapsed is not supported.' );
@ -34,11 +39,19 @@ export const addDebugTools = ( context, modules ) => {
console.group( `[STORE] ${ storeSelector }` );
const dumpStore = ( selector ) => {
const contents = wp.data.select( storeName )[ selector ]();
let contents = wp.data.select( storeName )[ selector ]();
console.groupCollapsed( `.${ selector }()` );
console.table( contents );
console.groupEnd();
if ( cbFilter ) {
contents = cbFilter( contents, selector, storeName );
if ( undefined !== contents && null !== contents ) {
console.log( `.${ selector }() [filtered]`, contents );
}
} else {
console.groupCollapsed( `.${ selector }()` );
console.table( contents );
console.groupEnd();
}
};
Object.keys( module.selectors ).forEach( dumpStore );
@ -51,45 +64,89 @@ export const addDebugTools = ( context, modules ) => {
// Reset all Redux stores to their initial state.
debugApi.resetStore = () => {
const stores = [];
const { isConnected } = wp.data.select( CommonStoreName ).merchant();
if ( isConnected ) {
// Make sure the Onboarding wizard is "completed".
const onboarding = wp.data.dispatch( OnboardingStoreName );
onboarding.setPersistent( 'completed', true );
onboarding.persist();
describe(
'resetStore',
'Reset all Redux stores to their DEFAULT state, without changing any server-side data. The default state is defined in the JS code.'
);
// Reset all stores, except for the onboarding store.
stores.push( CommonStoreName );
stores.push( PaymentStoreName );
stores.push( SettingsStoreName );
stores.push( StylingStoreName );
stores.push( TodosStoreName );
} else {
// Only reset the common & onboarding stores to restart the onboarding wizard.
stores.push( CommonStoreName );
const { completed } = wp.data
.select( OnboardingStoreName )
.persistentData();
// Reset all stores, except for the onboarding store.
stores.push( CommonStoreName );
stores.push( PaymentStoreName );
stores.push( SettingsStoreName );
stores.push( StylingStoreName );
stores.push( TodosStoreName );
// Only reset the onboarding store when the wizard is not completed.
if ( ! completed ) {
stores.push( OnboardingStoreName );
}
stores.forEach( ( storeName ) => {
const store = wp.data.dispatch( storeName );
// eslint-disable-next-line no-console
console.log( `Reset store: ${ storeName }...` );
try {
store.reset();
store.persist();
// eslint-disable-next-line no-console
console.log( `Done: Store '${ storeName }' reset` );
} catch ( error ) {
console.error( ' ... Reset failed, skipping this store' );
console.error(
`Failed: Could not reset store '${ storeName }'`
);
}
} );
// eslint-disable-next-line no-console
console.log( '---- Complete ----\n\n' );
};
debugApi.refreshStore = () => {
const stores = [];
describe(
'refreshStore',
'Refreshes all Redux details with details provided by the server. This has a similar effect as reloading the page without saving'
);
stores.push( CommonStoreName );
stores.push( PaymentStoreName );
stores.push( SettingsStoreName );
stores.push( StylingStoreName );
stores.push( TodosStoreName );
stores.push( OnboardingStoreName );
stores.forEach( ( storeName ) => {
const store = wp.data.dispatch( storeName );
try {
store.refresh();
// eslint-disable-next-line no-console
console.log(
`Done: Store '${ storeName }' refreshed from REST`
);
} catch ( error ) {
console.error(
`Failed: Could not refresh store '${ storeName }' from REST`
);
}
} );
// eslint-disable-next-line no-console
console.log( '---- Complete ----\n\n' );
};
// Disconnect the merchant and display the onboarding wizard.
debugApi.disconnect = () => {
const common = wp.data.dispatch( CommonStoreName );
describe();
common.disconnectMerchant();
// eslint-disable-next-line no-console
@ -102,6 +159,11 @@ export const addDebugTools = ( context, modules ) => {
debugApi.onboardingMode = ( state ) => {
const onboarding = wp.data.dispatch( OnboardingStoreName );
describe(
'onboardingMode',
'Toggle between onboarding wizard and the settings screen.'
);
onboarding.setPersistent( 'completed', ! state );
onboarding.persist();
};

View file

@ -87,3 +87,16 @@ export function persist() {
}
};
}
/**
* Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values.
*
* @return {Function} The thunk function.
*/
export function refresh() {
return ( { dispatch, select } ) => {
dispatch.invalidateResolutionForStore();
select.persistentData();
};
}

View file

@ -82,3 +82,16 @@ export function persist() {
} );
};
}
/**
* Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values.
*
* @return {Function} The thunk function.
*/
export function refresh() {
return ( { dispatch, select } ) => {
dispatch.invalidateResolutionForStore();
select.persistentData();
};
}

View file

@ -94,3 +94,16 @@ export function persist() {
} );
};
}
/**
* Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values.
*
* @return {Function} The thunk function.
*/
export function refresh() {
return ( { dispatch, select } ) => {
dispatch.invalidateResolutionForStore();
select.persistentData();
};
}

View file

@ -7,22 +7,58 @@
* @file
*/
import { useDispatch } from '@wordpress/data';
import { useDispatch, useSelect } from '@wordpress/data';
import { STORE_NAME } from './constants';
import { createHooksForStore } from '../utils';
import { useMemo } from '@wordpress/element';
const useHooks = () => {
/**
* Single source of truth for access Redux details.
*
* This hook returns a stable API to access actions, selectors and special hooks to generate
* getter- and setters for transient or persistent properties.
*
* @return {{select, dispatch, useTransient, usePersistent}} Store data API.
*/
const useStoreData = () => {
const select = useSelect( ( selectors ) => selectors( STORE_NAME ), [] );
const dispatch = useDispatch( STORE_NAME );
const { useTransient, usePersistent } = createHooksForStore( STORE_NAME );
const { persist, setPersistent, changePaymentSettings } =
useDispatch( STORE_NAME );
// Read-only flags and derived state.
// Nothing here yet.
return useMemo(
() => ( {
select,
dispatch,
useTransient,
usePersistent,
} ),
[ select, dispatch, useTransient, usePersistent ]
);
};
// Transient accessors.
export const useStore = () => {
const { select, useTransient, dispatch } = useStoreData();
const { persist, refresh, setPersistent, changePaymentSettings } = dispatch;
const [ isReady ] = useTransient( 'isReady' );
// Load persistent data from REST if not done yet.
if ( ! isReady ) {
select.persistentData();
}
return {
persist,
refresh,
setPersistent,
changePaymentSettings,
isReady,
};
};
export const usePaymentMethods = () => {
const { usePersistent } = useStoreData();
// PayPal checkout.
const [ paypal ] = usePersistent( 'ppcp-gateway' );
const [ venmo ] = usePersistent( 'venmo' );
@ -47,79 +83,6 @@ const useHooks = () => {
const [ pui ] = usePersistent( 'ppcp-pay-upon-invoice-gateway' );
const [ oxxo ] = usePersistent( 'ppcp-oxxo-gateway' );
// Custom modal data.
const [ paypalShowLogo ] = usePersistent( 'paypalShowLogo' );
const [ threeDSecure ] = usePersistent( 'threeDSecure' );
const [ fastlaneCardholderName ] = usePersistent(
'fastlaneCardholderName'
);
const [ fastlaneDisplayWatermark ] = usePersistent(
'fastlaneDisplayWatermark'
);
return {
persist,
isReady,
setPersistent,
changePaymentSettings,
paypal,
venmo,
payLater,
creditCard,
advancedCreditCard,
fastlane,
applePay,
googlePay,
bancontact,
blik,
eps,
ideal,
mybank,
p24,
trustly,
multibanco,
pui,
oxxo,
paypalShowLogo,
threeDSecure,
fastlaneCardholderName,
fastlaneDisplayWatermark,
};
};
export const useStore = () => {
const { persist, isReady, setPersistent, changePaymentSettings } =
useHooks();
return { persist, isReady, setPersistent, changePaymentSettings };
};
export const usePaymentMethods = () => {
const {
// PayPal Checkout.
paypal,
venmo,
payLater,
creditCard,
// Online card payments.
advancedCreditCard,
fastlane,
applePay,
googlePay,
// Local APMs.
bancontact,
blik,
eps,
ideal,
mybank,
p24,
trustly,
multibanco,
pui,
oxxo,
} = useHooks();
const payPalCheckout = [ paypal, venmo, payLater, creditCard ];
const onlineCardPayments = [
advancedCreditCard,
@ -169,12 +132,16 @@ export const usePaymentMethods = () => {
};
export const usePaymentMethodsModal = () => {
const {
paypalShowLogo,
threeDSecure,
fastlaneCardholderName,
fastlaneDisplayWatermark,
} = useHooks();
const { usePersistent } = useStoreData();
const [ paypalShowLogo ] = usePersistent( 'paypalShowLogo' );
const [ threeDSecure ] = usePersistent( 'threeDSecure' );
const [ fastlaneCardholderName ] = usePersistent(
'fastlaneCardholderName'
);
const [ fastlaneDisplayWatermark ] = usePersistent(
'fastlaneDisplayWatermark'
);
return {
paypalShowLogo,

View file

@ -19,6 +19,7 @@ const defaultTransient = Object.freeze( {
// Persistent: Values that are loaded from the DB.
const defaultPersistent = Object.freeze( {
// Payment methods.
'ppcp-gateway': {},
venmo: {},
'pay-later': {},
@ -37,6 +38,8 @@ const defaultPersistent = Object.freeze( {
'ppcp-multibanco': {},
'ppcp-pay-upon-invoice-gateway': {},
'ppcp-oxxo-gateway': {},
// Custom payment method properties.
paypalShowLogo: false,
threeDSecure: 'no-3d-secure',
fastlaneCardholderName: false,

View file

@ -84,3 +84,16 @@ export function persist() {
} );
};
}
/**
* Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values.
*
* @return {Function} The thunk function.
*/
export function refresh() {
return ( { dispatch, select } ) => {
dispatch.invalidateResolutionForStore();
select.persistentData();
};
}

View file

@ -6,17 +6,38 @@
*
* @file
*/
import { useDispatch } from '@wordpress/data';
import { useDispatch, useSelect } from '@wordpress/data';
import { STORE_NAME } from './constants';
import { createHooksForStore } from '../utils';
import { useMemo } from '@wordpress/element';
/**
* Single source of truth for access Redux details.
*
* This hook returns a stable API to access actions, selectors and special hooks to generate
* getter- and setters for transient or persistent properties.
*
* @return {{select, dispatch, useTransient, usePersistent}} Store data API.
*/
const useStoreData = () => {
const select = useSelect( ( selectors ) => selectors( STORE_NAME ), [] );
const dispatch = useDispatch( STORE_NAME );
const { useTransient, usePersistent } = createHooksForStore( STORE_NAME );
return useMemo(
() => ( {
select,
dispatch,
useTransient,
usePersistent,
} ),
[ select, dispatch, useTransient, usePersistent ]
);
};
const useHooks = () => {
const { useTransient, usePersistent } = createHooksForStore( STORE_NAME );
const { persist } = useDispatch( STORE_NAME );
// Read-only flags and derived state.
const [ isReady ] = useTransient( 'isReady' );
const { usePersistent } = useStoreData();
// Persistent accessors.
const [ invoicePrefix, setInvoicePrefix ] =
@ -47,8 +68,6 @@ const useHooks = () => {
usePersistent( 'disabledCards' );
return {
persist,
isReady,
invoicePrefix,
setInvoicePrefix,
authorizeOnly,
@ -79,8 +98,16 @@ const useHooks = () => {
};
export const useStore = () => {
const { persist, isReady } = useHooks();
return { persist, isReady };
const { select, dispatch, useTransient } = useStoreData();
const { persist, refresh } = dispatch;
const [ isReady ] = useTransient( 'isReady' );
// Load persistent data from REST if not done yet.
if ( ! isReady ) {
select.persistentData();
}
return { persist, refresh, isReady };
};
export const useSettings = () => {

View file

@ -82,3 +82,16 @@ export function persist() {
} );
};
}
/**
* Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values.
*
* @return {Function} The thunk function.
*/
export function refresh() {
return ( { dispatch, select } ) => {
dispatch.invalidateResolutionForStore();
select.persistentData();
};
}

View file

@ -7,7 +7,7 @@
* @file
*/
import { useCallback } from '@wordpress/element';
import { useCallback, useMemo } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import { createHooksForStore } from '../utils';
@ -20,13 +20,37 @@ import {
STYLING_PAYMENT_METHODS,
STYLING_SHAPES,
} from './configuration';
import { persistentData } from './selectors';
/**
* Single source of truth for access Redux details.
*
* This hook returns a stable API to access actions, selectors and special hooks to generate
* getter- and setters for transient or persistent properties.
*
* @return {{select, dispatch, useTransient, usePersistent}} Store data API.
*/
const useStoreData = () => {
const select = useSelect( ( selectors ) => selectors( STORE_NAME ), [] );
const dispatch = useDispatch( STORE_NAME );
const { useTransient, usePersistent } = createHooksForStore( STORE_NAME );
return useMemo(
() => ( {
select,
dispatch,
useTransient,
usePersistent,
} ),
[ select, dispatch, useTransient, usePersistent ]
);
};
const useHooks = () => {
const { useTransient } = createHooksForStore( STORE_NAME );
const { persist, setPersistent } = useDispatch( STORE_NAME );
const { useTransient, dispatch } = useStoreData();
const { setPersistent } = dispatch;
// Transient accessors.
const [ isReady ] = useTransient( 'isReady' );
const [ location, setLocation ] = useTransient( 'location' );
// Persistent accessors.
@ -61,8 +85,6 @@ const useHooks = () => {
);
return {
persist,
isReady,
location,
setLocation,
getLocationProp,
@ -71,8 +93,16 @@ const useHooks = () => {
};
export const useStore = () => {
const { persist, isReady } = useHooks();
return { persist, isReady };
const { select, dispatch, useTransient } = useStoreData();
const { persist, refresh } = dispatch;
const [ isReady ] = useTransient( 'isReady' );
// Load persistent data from REST if not done yet.
if ( ! isReady ) {
select.persistentData();
}
return { persist, refresh, isReady };
};
export const useStylingLocation = () => {

View file

@ -5,6 +5,12 @@
*/
export default {
/**
* Resets the store state to its initial values.
* Used when needing to clear all store data.
*/
RESET: 'ppcp/todos/RESET',
// Transient data
SET_TRANSIENT: 'ppcp/todos/SET_TRANSIENT',
SET_COMPLETED_TODOS: 'ppcp/todos/SET_COMPLETED_TODOS',

View file

@ -17,11 +17,47 @@ import {
REST_RESET_DISMISSED_TODOS_PATH,
} from './constants';
export const setIsReady = ( isReady ) => ( {
type: ACTION_TYPES.SET_TRANSIENT,
payload: { isReady },
/**
* Special. Resets all values in the store to initial defaults.
*
* @return {Object} The action.
*/
export const reset = () => ( {
type: ACTION_TYPES.RESET,
} );
/**
* Generic transient-data updater.
*
* @param {string} prop Name of the property to update.
* @param {any} value The new value of the property.
* @return {Object} The action.
*/
export const setTransient = ( prop, value ) => ( {
type: ACTION_TYPES.SET_TRANSIENT,
payload: { [ prop ]: value },
} );
/**
* Generic persistent-data updater.
*
* @param {string} prop Name of the property to update.
* @param {any} value The new value of the property.
* @return {Object} The action.
*/
export const setPersistent = ( prop, value ) => ( {
type: ACTION_TYPES.SET_PERSISTENT,
payload: { [ prop ]: value },
} );
/**
* Transient. Marks the store as "ready", i.e., fully initialized.
*
* @param {boolean} isReady Whether the store is ready
* @return {Object} The action.
*/
export const setIsReady = ( isReady ) => setTransient( 'isReady', isReady );
export const setTodos = ( todos ) => ( {
type: ACTION_TYPES.SET_TODOS,
payload: todos,
@ -39,6 +75,7 @@ export const setCompletedTodos = ( completedTodos ) => ( {
// Thunks
// TODO: Possibly, this should be a resolver?
export function fetchTodos() {
return async () => {
const response = await apiFetch( { path: REST_PATH } );
@ -46,9 +83,14 @@ export function fetchTodos() {
};
}
/**
* Thunk action creator. Triggers the persistence of store data to the server.
*
* @return {Function} The thunk function.
*/
export function persist() {
return async ( { select } ) => {
return await apiFetch( {
await apiFetch( {
path: REST_PERSIST_PATH,
method: 'POST',
data: select.persistentData(),
@ -56,6 +98,19 @@ export function persist() {
};
}
/**
* Thunk action creator. Forces a data refresh from the REST API, replacing the current Redux values.
*
* @return {Function} The thunk function.
*/
export function refresh() {
return ( { dispatch, select } ) => {
dispatch.invalidateResolutionForStore();
select.persistentData();
};
}
export function resetDismissedTodos() {
return async ( { dispatch } ) => {
try {

View file

@ -10,31 +10,40 @@
import { useSelect, useDispatch } from '@wordpress/data';
import { STORE_NAME } from './constants';
import { createHooksForStore } from '../utils';
import { useMemo } from '@wordpress/element';
const ensureArray = ( value ) => {
if ( ! value ) {
return [];
}
return Array.isArray( value ) ? value : Object.values( value );
/**
* Single source of truth for access Redux details.
*
* This hook returns a stable API to access actions, selectors and special hooks to generate
* getter- and setters for transient or persistent properties.
*
* @return {{select, dispatch, useTransient, usePersistent}} Store data API.
*/
const useStoreData = () => {
const select = useSelect( ( selectors ) => selectors( STORE_NAME ), [] );
const dispatch = useDispatch( STORE_NAME );
const { useTransient, usePersistent } = createHooksForStore( STORE_NAME );
return useMemo(
() => ( {
select,
dispatch,
useTransient,
usePersistent,
} ),
[ select, dispatch, useTransient, usePersistent ]
);
};
const useHooks = () => {
const { useTransient } = createHooksForStore( STORE_NAME );
const { fetchTodos, setDismissedTodos, setCompletedTodos, persist } =
useDispatch( STORE_NAME );
// Read-only flags and derived state.
const [ isReady ] = useTransient( 'isReady' );
const { dispatch, select } = useStoreData();
const { fetchTodos, setDismissedTodos, setCompletedTodos } = dispatch;
// Get todos data from store
const { todos, dismissedTodos, completedTodos } = useSelect( ( select ) => {
const store = select( STORE_NAME );
return {
todos: ensureArray( store.getTodos() ),
dismissedTodos: ensureArray( store.getDismissedTodos() ),
completedTodos: ensureArray( store.getCompletedTodos() ),
};
}, [] );
const todos = select.getTodos();
const dismissedTodos = select.getDismissedTodos();
const completedTodos = select.getCompletedTodos();
const dismissedSet = new Set( dismissedTodos );
@ -62,8 +71,6 @@ const useHooks = () => {
);
return {
persist,
isReady,
todos: filteredTodos,
dismissedTodos,
completedTodos,
@ -74,14 +81,21 @@ const useHooks = () => {
};
export const useStore = () => {
const { persist, isReady } = useHooks();
return { persist, isReady };
const { select, dispatch, useTransient } = useStoreData();
const { persist, refresh } = dispatch;
const [ isReady ] = useTransient( 'isReady' );
// Load persistent data from REST if not done yet.
if ( ! isReady ) {
select.getTodos();
}
return { persist, refresh, isReady };
};
export const useTodos = () => {
const { todos, fetchTodos, dismissTodo, setTodoCompleted, isReady } =
useHooks();
return { todos, fetchTodos, dismissTodo, setTodoCompleted, isReady };
const { todos, fetchTodos, dismissTodo, setTodoCompleted } = useHooks();
return { todos, fetchTodos, dismissTodo, setTodoCompleted };
};
export const useDismissedTodos = () => {

View file

@ -52,6 +52,21 @@ const reducer = createReducer( defaultTransient, defaultPersistent, {
[ ACTION_TYPES.SET_TRANSIENT ]: ( state, payload ) =>
changeTransient( state, payload ),
/**
* Resets state to defaults while maintaining initialization status
*
* @param {Object} state Current state
* @return {Object} Reset state
*/
[ ACTION_TYPES.RESET ]: ( state ) => {
const cleanState = changeTransient(
changePersistent( state, defaultPersistent ),
defaultTransient
);
cleanState.isReady = true; // Keep initialization flag
return cleanState;
},
/**
* Updates todos list
*
@ -99,6 +114,7 @@ const reducer = createReducer( defaultTransient, defaultPersistent, {
},
/**
* TODO: This is not used anywhere. Remove "SET_TODOS" and use this resolver instead.
* Initializes persistent state with data from the server
*
* @param {Object} state Current state

View file

@ -11,7 +11,17 @@ const EMPTY_OBJ = Object.freeze( {} );
const EMPTY_ARR = Object.freeze( [] );
const getState = ( state ) => state || EMPTY_OBJ;
const getArray = ( value ) => {
if ( Array.isArray( value ) ) {
return value;
}
if ( value ) {
return Object.values( value );
}
return EMPTY_ARR;
};
// TODO: Implement a persistentData resolver!
export const persistentData = ( state ) => {
return getState( state ).data || EMPTY_OBJ;
};
@ -23,15 +33,15 @@ export const transientData = ( state ) => {
export const getTodos = ( state ) => {
const todos = state?.todos || persistentData( state ).todos;
return todos || EMPTY_ARR;
return getArray( todos );
};
export const getDismissedTodos = ( state ) => {
const dismissed =
state?.dismissedTodos || persistentData( state ).dismissedTodos;
return dismissed || EMPTY_ARR;
return getArray( dismissed );
};
export const getCompletedTodos = ( state ) => {
return state?.completedTodos || EMPTY_ARR; // Only look at root state, not persistent data
return getArray( state?.completedTodos ); // Only look at root state, not persistent data
};

View file

@ -4,6 +4,7 @@ import { useState, useEffect, useCallback, useRef } from '@wordpress/element';
import { store as noticesStore } from '@wordpress/notices';
import { CommonHooks, OnboardingHooks } from '../data';
import { useStoreManager } from './useStoreManager';
const PAYPAL_PARTNER_SDK_URL =
'https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js';
@ -30,7 +31,7 @@ export const useHandleOnboardingButton = ( isSandbox ) => {
const { sandboxOnboardingUrl } = CommonHooks.useSandbox();
const { productionOnboardingUrl } = CommonHooks.useProduction();
const products = OnboardingHooks.useDetermineProducts();
const { withActivity, startActivity } = CommonHooks.useBusyState();
const { startActivity } = CommonHooks.useBusyState();
const { authenticateWithOAuth } = CommonHooks.useAuthentication();
const [ onboardingUrl, setOnboardingUrl ] = useState( '' );
const [ scriptLoaded, setScriptLoaded ] = useState( false );
@ -134,7 +135,7 @@ export const useHandleOnboardingButton = ( isSandbox ) => {
// Ensure the onComplete handler is not removed by a PayPal init script.
timerRef.current = setInterval( addHandler, 250 );
},
[ authenticateWithOAuth, withActivity ]
[ authenticateWithOAuth, startActivity ]
);
const removeCompleteHandler = useCallback( () => {
@ -161,6 +162,7 @@ const useConnectionBase = () => {
useDispatch( noticesStore );
const { verifyLoginStatus } = CommonHooks.useMerchantInfo();
const { withActivity } = CommonHooks.useBusyState();
const { refreshAll } = useStoreManager();
return {
handleFailed: ( res, genericMessage ) => {
@ -178,6 +180,7 @@ const useConnectionBase = () => {
if ( loginSuccessful ) {
createSuccessNotice( MESSAGES.CONNECTED );
await setCompleted( true );
refreshAll();
} else {
createErrorNotice( MESSAGES.LOGIN_FAILED );
}

View file

@ -1,74 +0,0 @@
import { useCallback, useMemo } from '@wordpress/element';
import {
CommonHooks,
PayLaterMessagingHooks,
PaymentHooks,
SettingsHooks,
StylingHooks,
TodosHooks,
} from '../data';
export const useSaveSettings = () => {
const { withActivity } = CommonHooks.useBusyState();
const { persist: persistPayment } = PaymentHooks.useStore();
const { persist: persistSettings } = SettingsHooks.useStore();
const { persist: persistStyling } = StylingHooks.useStore();
const { persist: persistTodos } = TodosHooks.useStore();
const { persist: persistPayLaterMessaging } =
PayLaterMessagingHooks.useStore();
const persistActions = useMemo(
() => [
{
key: 'persist-methods',
message: 'Save payment methods',
action: persistPayment,
},
{
key: 'persist-settings',
message: 'Save the settings',
action: persistSettings,
},
{
key: 'persist-styling',
message: 'Save styling details',
action: persistStyling,
},
{
key: 'persist-todos',
message: 'Save todos state',
action: persistTodos,
},
{
key: 'persist-pay-later-messaging',
message: 'Save pay later messaging details',
action: persistPayLaterMessaging,
},
],
[
persistPayLaterMessaging,
persistPayment,
persistSettings,
persistStyling,
persistTodos,
]
);
const persistAll = useCallback( () => {
/**
* Executes onSave on TabPayLaterMessaging component.
*
* Todo: find a better way for this, because it's highly unreliable
* (it only works when the user is still on the "Pay Later Messaging" tab)
*/
document.getElementById( 'configurator-publishButton' )?.click();
persistActions.forEach( ( { key, message, action } ) => {
withActivity( key, message, action );
} );
}, [ persistActions, withActivity ] );
return { persistAll };
};

View file

@ -0,0 +1,76 @@
import { useCallback, useMemo } from '@wordpress/element';
import {
CommonHooks,
PayLaterMessagingHooks,
PaymentHooks,
SettingsHooks,
StylingHooks,
TodosHooks,
} from '../data';
export const useStoreManager = () => {
const { withActivity } = CommonHooks.useBusyState();
const paymentStore = PaymentHooks.useStore();
const settingsStore = SettingsHooks.useStore();
const stylingStore = StylingHooks.useStore();
const todosStore = TodosHooks.useStore();
const payLaterStore = PayLaterMessagingHooks.useStore();
const storeActions = useMemo(
() => [
{
key: 'methods',
message: 'Process payment methods',
store: paymentStore,
},
{
key: 'settings',
message: 'Process the settings',
store: settingsStore,
},
{
key: 'styling',
message: 'Process styling details',
store: stylingStore,
},
{
key: 'todos',
message: 'Process todos state',
store: todosStore,
},
{
key: 'pay-later-messaging',
message: 'Process pay later messaging details',
store: payLaterStore,
},
],
[ payLaterStore, paymentStore, settingsStore, stylingStore, todosStore ]
);
const persistAll = useCallback( () => {
/**
* Executes onSave on TabPayLaterMessaging component.
*
* Todo: find a better way for this, because it's highly unreliable
* (it only works when the user is still on the "Pay Later Messaging" tab)
*/
document.getElementById( 'configurator-publishButton' )?.click();
storeActions.forEach( ( { key, message, store } ) => {
withActivity( `persist-${ key }`, message, store.persist );
} );
}, [ storeActions, withActivity ] );
const refreshAll = useCallback( () => {
storeActions.forEach( ( { key, message, store } ) => {
withActivity( `refresh-${ key }`, message, store.refresh );
} );
}, [ storeActions, withActivity ] );
return {
persistAll,
refreshAll,
};
};

View file

@ -20,13 +20,11 @@ use WooCommerce\PayPalCommerce\Settings\Data\TodosModel;
use WooCommerce\PayPalCommerce\Settings\Data\Definition\TodosDefinition;
use WooCommerce\PayPalCommerce\Settings\Endpoint\AuthenticationRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\CommonRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\CompleteOnClickEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\LoginLinkRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\OnboardingRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\PayLaterMessagingEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\PaymentRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\RefreshFeatureStatusEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\ResetDismissedTodosEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\WebhookSettingsEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\SettingsRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\StylingRestEndpoint;
@ -36,6 +34,7 @@ use WooCommerce\PayPalCommerce\Settings\Service\AuthenticationManager;
use WooCommerce\PayPalCommerce\Settings\Service\ConnectionUrlGenerator;
use WooCommerce\PayPalCommerce\Settings\Service\OnboardingUrlManager;
use WooCommerce\PayPalCommerce\Settings\Service\TodosEligibilityService;
use WooCommerce\PayPalCommerce\Settings\Service\TodosSortingAndFilteringService;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\Settings\Service\DataSanitizer;
use WooCommerce\PayPalCommerce\Settings\Service\SettingsDataManager;
@ -43,6 +42,8 @@ use WooCommerce\PayPalCommerce\Settings\Data\Definition\PaymentMethodsDefinition
use WooCommerce\PayPalCommerce\PayLaterConfigurator\Factory\ConfigFactory;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\PayLaterConfigurator\Endpoint\SaveConfig;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
use WooCommerce\PayPalCommerce\WcGateway\Helper\ConnectionState;
return array(
'settings.url' => static function ( ContainerInterface $container ) : string {
@ -116,13 +117,51 @@ return array(
);
},
/**
* Checks if valid merchant connection details are stored in the DB.
* Merchant connection details, which includes the connection status
* (onboarding/connected) and connection-aware environment checks.
* This is the preferred solution to check environment and connection state.
*/
'settings.flag.is-connected' => static function ( ContainerInterface $container ) : bool {
'settings.connection-state' => static function ( ContainerInterface $container ) : ConnectionState {
$data = $container->get( 'settings.data.general' );
assert( $data instanceof GeneralSettings );
return $data->is_merchant_connected();
$is_connected = $data->is_merchant_connected();
$environment = new Environment( $data->is_sandbox_merchant() );
return new ConnectionState( $is_connected, $environment );
},
'settings.environment' => static function ( ContainerInterface $container ) : Environment {
// We should remove this service in favor of directly using `settings.connection-state`.
$state = $container->get( 'settings.connection-state' );
assert( $state instanceof ConnectionState );
return $state->get_environment();
},
/**
* Checks if valid merchant connection details are stored in the DB.
*/
'settings.flag.is-connected' => static function ( ContainerInterface $container ) : bool {
/*
* This service only resolves the connection status once per request.
* We should remove this service in favor of directly using `settings.connection-state`.
*/
$state = $container->get( 'settings.connection-state' );
assert( $state instanceof ConnectionState );
return $state->is_connected();
},
/**
* Checks if the merchant is connected to a sandbox environment.
*/
'settings.flag.is-sandbox' => static function ( ContainerInterface $container ) : bool {
/*
* This service only resolves the sandbox flag once per request.
* We should remove this service in favor of directly using `settings.connection-state`.
*/
$state = $container->get( 'settings.connection-state' );
assert( $state instanceof ConnectionState );
return $state->is_sandbox();
},
'settings.rest.onboarding' => static function ( ContainerInterface $container ) : OnboardingRestEndpoint {
return new OnboardingRestEndpoint( $container->get( 'settings.data.onboarding' ) );
@ -268,6 +307,7 @@ return array(
$container->get( 'api.env.paypal-host' ),
$container->get( 'api.env.endpoint.login-seller' ),
$container->get( 'api.repository.partner-referrals-data' ),
$container->get( 'settings.connection-state' ),
$container->get( 'woocommerce.logger.woocommerce' ),
);
},
@ -298,7 +338,8 @@ return array(
return new TodosRestEndpoint(
$container->get( 'settings.data.todos' ),
$container->get( 'settings.data.definition.todos' ),
$container->get( 'settings.rest.settings' )
$container->get( 'settings.rest.settings' ),
$container->get( 'settings.service.todos_sorting' )
);
},
'settings.data.todos' => static function ( ContainerInterface $container ) : TodosModel {
@ -307,7 +348,8 @@ return array(
'settings.data.definition.todos' => static function ( ContainerInterface $container ) : TodosDefinition {
return new TodosDefinition(
$container->get( 'settings.service.todos_eligibilities' ),
$container->get( 'settings.data.general' )
$container->get( 'settings.data.general' ),
$container->get( 'wc-subscriptions.helper' )
);
},
'settings.data.definition.methods' => static function ( ContainerInterface $container ) : PaymentMethodsDefinition {
@ -315,17 +357,10 @@ return array(
$container->get( 'settings.data.payment' ),
);
},
'settings.service.todos_eligibilities' => static function ( ContainerInterface $container ) : TodosEligibilityService {
$features = apply_filters(
'woocommerce_paypal_payments_rest_common_merchant_features',
array()
);
$payment_endpoint = $container->get( 'settings.rest.payment' );
$settings = $payment_endpoint->get_details()->get_data();
'settings.service.pay_later_status' => static function ( ContainerInterface $container ) : array {
$pay_later_endpoint = $container->get( 'settings.rest.pay_later_messaging' );
$pay_later_settings = $pay_later_endpoint->get_details()->get_data();
$pay_later_statuses = array(
'cart' => $pay_later_settings['data']['cart']['status'] === 'enabled',
'checkout' => $pay_later_settings['data']['checkout']['status'] === 'enabled',
@ -336,16 +371,41 @@ return array(
$pay_later_settings['data']['custom_placement'][0]['status'] === 'enabled',
);
// Settings status.
$gateways = array(
$is_pay_later_messaging_enabled_for_any_location = ! array_filter( $pay_later_statuses );
return array(
'statuses' => $pay_later_statuses,
'is_enabled_for_any_location' => $is_pay_later_messaging_enabled_for_any_location,
);
},
'settings.service.button_locations' => static function ( ContainerInterface $container ) : array {
$styling_endpoint = $container->get( 'settings.rest.styling' );
$styling_data = $styling_endpoint->get_details()->get_data()['data'];
return array(
'cart_enabled' => $styling_data['cart']->enabled ?? false,
'block_checkout_enabled' => $styling_data['expressCheckout']->enabled ?? false,
'product_enabled' => $styling_data['product']->enabled ?? false,
);
},
'settings.service.gateways_status' => static function ( ContainerInterface $container ) : array {
$payment_endpoint = $container->get( 'settings.rest.payment' );
$settings = $payment_endpoint->get_details()->get_data();
return array(
'apple_pay' => $settings['data']['ppcp-applepay']['enabled'] ?? false,
'google_pay' => $settings['data']['ppcp-googlepay']['enabled'] ?? false,
'axo' => $settings['data']['ppcp-axo-gateway']['enabled'] ?? false,
'card-button' => $settings['data']['ppcp-card-button-gateway']['enabled'] ?? false,
);
},
'settings.service.merchant_capabilities' => static function ( ContainerInterface $container ) : array {
$features = apply_filters(
'woocommerce_paypal_payments_rest_common_merchant_features',
array()
);
// Merchant eligibility.
$capabilities = array(
return array(
'apple_pay' => $features['apple_pay']['enabled'] ?? false,
'google_pay' => $features['google_pay']['enabled'] ?? false,
'acdc' => $features['advanced_credit_and_debit_cards']['enabled'] ?? false,
@ -353,25 +413,67 @@ return array(
'apm' => $features['alternative_payment_methods']['enabled'] ?? false,
'paylater' => $features['pay_later_messaging']['enabled'] ?? false,
);
},
$is_pay_later_messaging_enabled_for_any_location = ! array_filter( $pay_later_statuses );
'settings.service.todos_eligibilities' => static function ( ContainerInterface $container ) : TodosEligibilityService {
$pay_later_service = $container->get( 'settings.service.pay_later_status' );
$pay_later_statuses = $pay_later_service['statuses'];
$is_pay_later_messaging_enabled_for_any_location = $pay_later_service['is_enabled_for_any_location'];
$button_locations = $container->get( 'settings.service.button_locations' );
$gateways = $container->get( 'settings.service.gateways_status' );
$capabilities = $container->get( 'settings.service.merchant_capabilities' );
/**
* Initializes TodosEligibilityService with eligibility conditions for various PayPal features.
* Each parameter determines whether a specific feature should be shown in the Things To Do list.
*
* Logic relies on two main factors:
* 1. $capabilities - Whether the merchant is eligible for specific features on their PayPal account.
* 2. $gateways, $pay_later_statuses, $button_locations - Plugin settings (enabled/disabled status).
*
* @param bool $is_fastlane_eligible - Show if merchant is eligible (ACDC) but hasn't enabled Fastlane gateway.
* @param bool $is_card_payment_eligible - Show if merchant is eligible (ACDC) but hasn't enabled card button gateway.
* @param bool $is_pay_later_messaging_eligible - Show if Pay Later messaging is enabled for at least one location.
* @param bool $is_pay_later_messaging_product_eligible - Show if Pay Later is not enabled anywhere and specifically not on product page.
* @param bool $is_pay_later_messaging_cart_eligible - Show if Pay Later is not enabled anywhere and specifically not on cart.
* @param bool $is_pay_later_messaging_checkout_eligible - Show if Pay Later is not enabled anywhere and specifically not on checkout.
* @param bool $is_subscription_eligible - Show if WooCommerce Subscriptions plugin is active but merchant is not eligible for PayPal Vaulting.
* @param bool $is_paypal_buttons_cart_eligible - Show if PayPal buttons are not enabled on cart page.
* @param bool $is_paypal_buttons_block_checkout_eligible - Show if PayPal buttons are not enabled on blocks checkout.
* @param bool $is_paypal_buttons_product_eligible - Show if PayPal buttons are not enabled on product page.
* @param bool $is_apple_pay_domain_eligible - Show if merchant has Apple Pay capability on PayPal account.
* @param bool $is_digital_wallet_eligible - Show if merchant is eligible (ACDC) but doesn't have both wallet types on PayPal.
* @param bool $is_apple_pay_eligible - Show if merchant is eligible (ACDC) but doesn't have Apple Pay on PayPal.
* @param bool $is_google_pay_eligible - Show if merchant is eligible (ACDC) but doesn't have Google Pay on PayPal.
* @param bool $is_enable_apple_pay_eligible - Show if merchant has Apple Pay capability but hasn't enabled the gateway.
* @param bool $is_enable_google_pay_eligible - Show if merchant has Google Pay capability but hasn't enabled the gateway.
*/
return new TodosEligibilityService(
$capabilities['acdc'] && ! $gateways['axo'], // Enable Fastlane.
$capabilities['acdc'] && ! $gateways['card-button'], // Enable Credit and Debit Cards on your checkout.
$is_pay_later_messaging_enabled_for_any_location, // Enable Pay Later messaging.
! $is_pay_later_messaging_enabled_for_any_location && ! $pay_later_statuses['product'], // Add Pay Later messaging (Product page).
! $is_pay_later_messaging_enabled_for_any_location && ! $pay_later_statuses['cart'], // Add Pay Later messaging (Cart).
! $is_pay_later_messaging_enabled_for_any_location && ! $pay_later_statuses['checkout'], // Add Pay Later messaging (Checkout).
true, // Configure a PayPal Subscription.
true, // Add PayPal buttons.
true, // Register Domain for Apple Pay.
$capabilities['acdc'] && ! ( $capabilities['apple_pay'] && $capabilities['google_pay'] ), // Add digital wallets to your account.
$capabilities['acdc'] && ! $capabilities['apple_pay'], // Add Apple Pay to your account.
$capabilities['acdc'] && ! $capabilities['google_pay'], // Add Google Pay to your account.
true, // Configure a PayPal Subscription.
$capabilities['apple_pay'] && ! $gateways['apple_pay'], // Enable Apple Pay.
$capabilities['google_pay'] && ! $gateways['google_pay'], // Enable Google Pay.
$capabilities['acdc'] && ! $gateways['axo'], // Enable Fastlane.
$capabilities['acdc'] && ! $gateways['card-button'], // Enable Credit and Debit Cards on your checkout.
$is_pay_later_messaging_enabled_for_any_location, // Enable Pay Later messaging.
! $is_pay_later_messaging_enabled_for_any_location && ! $pay_later_statuses['product'], // Add Pay Later messaging (Product page).
! $is_pay_later_messaging_enabled_for_any_location && ! $pay_later_statuses['cart'], // Add Pay Later messaging (Cart).
! $is_pay_later_messaging_enabled_for_any_location && ! $pay_later_statuses['checkout'], // Add Pay Later messaging (Checkout).
$container->has( 'save-payment-methods.eligible' ) &&
! $container->get( 'save-payment-methods.eligible' ) &&
$container->has( 'wc-subscriptions.helper' ) &&
$container->get( 'wc-subscriptions.helper' )->plugin_is_active(), // Configure a PayPal Subscription.
! $button_locations['cart_enabled'], // Add PayPal buttons to cart.
! $button_locations['block_checkout_enabled'], // Add PayPal buttons to block checkout.
! $button_locations['product_enabled'], // Add PayPal buttons to product.
$capabilities['apple_pay'], // Register Domain for Apple Pay.
$capabilities['acdc'] && ! ( $capabilities['apple_pay'] && $capabilities['google_pay'] ), // Add digital wallets to your account.
$capabilities['acdc'] && ! $capabilities['apple_pay'], // Add Apple Pay to your account.
$capabilities['acdc'] && ! $capabilities['google_pay'], // Add Google Pay to your account.
$capabilities['apple_pay'] && ! $gateways['apple_pay'], // Enable Apple Pay.
$capabilities['google_pay'] && ! $gateways['google_pay'],
);
},
'settings.service.todos_sorting' => static function ( ContainerInterface $container ) : TodosSortingAndFilteringService {
return new TodosSortingAndFilteringService(
$container->get( 'settings.data.todos' )
);
},
);

View file

@ -1,6 +1,6 @@
<?php
/**
* PayPal Commerce Todos Definitions
* Todos Definitions
*
* @package WooCommerce\PayPalCommerce\Settings\Data\Definition
*/
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Settings\Data\Definition;
use WooCommerce\PayPalCommerce\Settings\Service\TodosEligibilityService;
use WooCommerce\PayPalCommerce\Settings\Data\GeneralSettings;
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
/**
* Class TodosDefinition
@ -34,18 +35,28 @@ class TodosDefinition {
*/
protected GeneralSettings $settings;
/**
* The subscription helper.
*
* @var SubscriptionHelper
*/
protected SubscriptionHelper $subscription_helper;
/**
* Constructor.
*
* @param TodosEligibilityService $eligibilities The todos eligibility service.
* @param GeneralSettings $settings The general settings service.
* @param SubscriptionHelper $subscription_helper The subscription helper.
*/
public function __construct(
TodosEligibilityService $eligibilities,
GeneralSettings $settings
GeneralSettings $settings,
SubscriptionHelper $subscription_helper
) {
$this->eligibilities = $eligibilities;
$this->settings = $settings;
$this->eligibilities = $eligibilities;
$this->settings = $settings;
$this->subscription_helper = $subscription_helper;
}
/**
@ -126,15 +137,38 @@ class TodosDefinition {
'description' => __( 'Connect a subscriptions-type product from WooCommerce with PayPal', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['configure_paypal_subscription'],
'action' => array(
'type' => 'external',
'url' => admin_url( 'edit.php?post_type=product&product_type=subscription' ),
'type' => 'external',
'url' => $this->subscription_helper->has_subscription_products()
? admin_url( 'edit.php?post_type=product&product_type=subscription' ) // If subscription products exist, go to the subscriptions products archive page.
: admin_url( 'post-new.php?post_type=product' ), // If there are no subscriptions products, go to create one.
'completeOnClick' => true,
),
'priority' => 5,
),
'add_paypal_buttons' => array(
'title' => __( 'Add PayPal buttons', 'woocommerce-paypal-payments' ),
'description' => __( 'Allow customers to check out quickly and securely from the <x> page. Customers save time and get through checkout in fewer clicks.', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['add_paypal_buttons'],
'add_paypal_buttons_cart' => array(
'title' => __( 'Add PayPal buttons to the Cart page', 'woocommerce-paypal-payments' ),
'description' => __( 'Allow customers to check out quickly and securely from the Cart page. Customers save time and get through checkout in fewer clicks.', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['add_paypal_buttons_cart'],
'action' => array(
'type' => 'tab',
'tab' => 'styling',
),
'priority' => 6,
),
'add_paypal_buttons_block_checkout' => array(
'title' => __( 'Add PayPal buttons to the Express Checkout page', 'woocommerce-paypal-payments' ),
'description' => __( 'Allow customers to check out quickly and securely from the Express Checkout page. Customers save time and get through checkout in fewer clicks.', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['add_paypal_buttons_block_checkout'],
'action' => array(
'type' => 'tab',
'tab' => 'styling',
),
'priority' => 6,
),
'add_paypal_buttons_product' => array(
'title' => __( 'Add PayPal buttons to the Product page', 'woocommerce-paypal-payments' ),
'description' => __( 'Allow customers to check out quickly and securely from the Product page. Customers save time and get through checkout in fewer clicks.', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['add_paypal_buttons_product'],
'action' => array(
'type' => 'tab',
'tab' => 'styling',

View file

@ -17,6 +17,7 @@ use WP_REST_Response;
use WP_REST_Request;
use WooCommerce\PayPalCommerce\Settings\Data\TodosModel;
use WooCommerce\PayPalCommerce\Settings\Data\Definition\TodosDefinition;
use WooCommerce\PayPalCommerce\Settings\Service\TodosSortingAndFilteringService;
/**
* REST controller for the "Things To Do" items in the Overview tab.
@ -33,17 +34,6 @@ class TodosRestEndpoint extends RestEndpoint {
*/
protected $rest_base = 'todos';
/**
* Pay Later messaging todo IDs in priority order.
*
* @var array
*/
private const PAY_LATER_IDS = array(
'add_pay_later_messaging_product_page',
'add_pay_later_messaging_cart',
'add_pay_later_messaging_checkout',
);
/**
* The todos model instance.
*
@ -65,21 +55,31 @@ class TodosRestEndpoint extends RestEndpoint {
*/
protected SettingsRestEndpoint $settings;
/**
* The todos sorting service.
*
* @var TodosSortingAndFilteringService
*/
protected TodosSortingAndFilteringService $sorting_service;
/**
* TodosRestEndpoint constructor.
*
* @param TodosModel $todos The todos model instance.
* @param TodosDefinition $todos_definition The todos definition instance.
* @param SettingsRestEndpoint $settings The settings endpoint instance.
* @param TodosModel $todos The todos model instance.
* @param TodosDefinition $todos_definition The todos definition instance.
* @param SettingsRestEndpoint $settings The settings endpoint instance.
* @param TodosSortingAndFilteringService $sorting_service The todos sorting service.
*/
public function __construct(
TodosModel $todos,
TodosDefinition $todos_definition,
SettingsRestEndpoint $settings
SettingsRestEndpoint $settings,
TodosSortingAndFilteringService $sorting_service
) {
$this->todos = $todos;
$this->todos_definition = $todos_definition;
$this->settings = $settings;
$this->sorting_service = $sorting_service;
}
/**
@ -157,8 +157,7 @@ class TodosRestEndpoint extends RestEndpoint {
}
}
$sorted_todos = $this->sort_todos_by_priority( $todos );
$filtered_todos = $this->filter_pay_later_todos( $sorted_todos );
$filtered_todos = $this->sorting_service->apply_all_priority_filters( $todos );
return $this->return_success(
array(
@ -245,67 +244,4 @@ class TodosRestEndpoint extends RestEndpoint {
);
}
}
/**
* Filters Pay Later messaging todos to show only the highest priority eligible todo.
*
* @param array $todos The array of todos to filter.
* @return array Filtered todos with only one Pay Later messaging todo.
*/
private function filter_pay_later_todos( array $todos ): array {
$pay_later_todos = array_filter(
$todos,
function( $todo ) {
return in_array( $todo['id'], self::PAY_LATER_IDS, true );
}
);
$other_todos = array_filter(
$todos,
function( $todo ) {
return ! in_array( $todo['id'], self::PAY_LATER_IDS, true );
}
);
// Find the highest priority Pay Later todo that's eligible.
$priority_pay_later_todo = null;
foreach ( self::PAY_LATER_IDS as $pay_later_id ) {
$matching_todo = current(
array_filter(
$pay_later_todos,
function( $todo ) use ( $pay_later_id ) {
return $todo['id'] === $pay_later_id;
}
)
);
if ( $matching_todo ) {
$priority_pay_later_todo = $matching_todo;
break;
}
}
return $priority_pay_later_todo
? array_merge( $other_todos, array( $priority_pay_later_todo ) )
: $other_todos;
}
/**
* Sorts todos by their priority value.
*
* @param array $todos Array of todos to sort.
* @return array Sorted array of todos.
*/
private function sort_todos_by_priority( array $todos ): array {
usort(
$todos,
function( $a, $b ) {
$priority_a = $a['priority'] ?? 999;
$priority_b = $b['priority'] ?? 999;
return $priority_a <=> $priority_b;
}
);
return $todos;
}
}

View file

@ -24,6 +24,7 @@ use WooCommerce\WooCommerce\Logging\Logger\NullLogger;
use WooCommerce\PayPalCommerce\Settings\DTO\MerchantConnectionDTO;
use WooCommerce\PayPalCommerce\Webhooks\WebhookRegistrar;
use WooCommerce\PayPalCommerce\Settings\Enum\SellerTypeEnum;
use WooCommerce\PayPalCommerce\WcGateway\Helper\ConnectionState;
/**
* Class that manages the connection to PayPal.
@ -64,27 +65,37 @@ class AuthenticationManager {
*/
private PartnerReferralsData $referrals_data;
/**
* The connection state manager.
*
* @var ConnectionState
*/
private ConnectionState $connection_state;
/**
* Constructor.
*
* @param GeneralSettings $common_settings Data model that stores the connection details.
* @param EnvironmentConfig $connection_host API host for direct authentication.
* @param EnvironmentConfig $login_endpoint API handler to fetch merchant credentials.
* @param PartnerReferralsData $referrals_data Partner referrals data.
* @param ?LoggerInterface $logger Logging instance.
* @param GeneralSettings $common_settings Data model that stores the connection details.
* @param EnvironmentConfig $connection_host API host for direct authentication.
* @param EnvironmentConfig $login_endpoint API handler to fetch merchant credentials.
* @param PartnerReferralsData $referrals_data Partner referrals data.
* @param ConnectionState $connection_state Connection state manager.
* @param ?LoggerInterface $logger Logging instance.
*/
public function __construct(
GeneralSettings $common_settings,
EnvironmentConfig $connection_host,
EnvironmentConfig $login_endpoint,
PartnerReferralsData $referrals_data,
ConnectionState $connection_state,
?LoggerInterface $logger = null
) {
$this->common_settings = $common_settings;
$this->connection_host = $connection_host;
$this->login_endpoint = $login_endpoint;
$this->referrals_data = $referrals_data;
$this->logger = $logger ?: new NullLogger();
$this->common_settings = $common_settings;
$this->connection_host = $connection_host;
$this->login_endpoint = $login_endpoint;
$this->referrals_data = $referrals_data;
$this->connection_state = $connection_state;
$this->logger = $logger ?: new NullLogger();
}
/**
@ -112,6 +123,9 @@ class AuthenticationManager {
$this->common_settings->reset_merchant_data();
$this->common_settings->save();
// Update the connection status and clear the environment flags.
$this->connection_state->disconnect();
/**
* Broadcast, that the plugin disconnected from PayPal. This allows other
* modules to clean up merchant-related details, such as eligibility flags.
@ -420,6 +434,9 @@ class AuthenticationManager {
if ( $this->common_settings->is_merchant_connected() ) {
$this->logger->info( 'Merchant successfully connected to PayPal' );
// Update the connection status and set the environment flags.
$this->connection_state->connect( $connection->is_sandbox );
/**
* Request to flush caches before authenticating the merchant, to
* ensure the new merchant does not use stale data from previous

View file

@ -211,7 +211,8 @@ class SettingsDataManager {
$this->payment_methods->toggle_method_state( $method['id'], false );
}
// Always enable Venmo and Pay Later.
// Always enable PayPal, Venmo and Pay Later.
$this->payment_methods->toggle_method_state( PayPalGateway::ID, true );
$this->payment_methods->toggle_method_state( 'venmo', true );
$this->payment_methods->toggle_method_state( 'pay-later', true );

View file

@ -1,9 +1,9 @@
<?php
/**
* PayPal Commerce eligibility service for WooCommerce.
* Eligibility service for Todos.
*
* This file contains the TodosEligibilityService class which manages eligibility checks
* for various PayPal Commerce features including Fastlane, card payments, Pay Later messaging,
* for various features including Fastlane, card payments, Pay Later messaging,
* subscriptions, Apple Pay, Google Pay, and other digital wallet features.
*
* @package WooCommerce\PayPalCommerce\Settings\Service
@ -66,6 +66,27 @@ class TodosEligibilityService {
*/
private bool $is_subscription_eligible;
/**
* Whether PayPal buttons for cart are eligible.
*
* @var bool
*/
private bool $is_paypal_buttons_cart_eligible;
/**
* Whether PayPal buttons for block checkout are eligible.
*
* @var bool
*/
private bool $is_paypal_buttons_block_checkout_eligible;
/**
* Whether PayPal buttons for product page are eligible.
*
* @var bool
*/
private bool $is_paypal_buttons_product_eligible;
/**
* Whether Apple Pay domain registration is eligible.
*
@ -94,20 +115,6 @@ class TodosEligibilityService {
*/
private bool $is_google_pay_eligible;
/**
* Whether PayPal buttons are eligible.
*
* @var bool
*/
private bool $is_paypal_buttons_eligible;
/**
* Whether PayPal subscription configuration is eligible.
*
* @var bool
*/
private bool $is_add_subscription_eligible;
/**
* Whether enabling Apple Pay is eligible.
*
@ -132,12 +139,13 @@ class TodosEligibilityService {
* @param bool $is_pay_later_messaging_cart_eligible Whether Pay Later messaging for cart is eligible.
* @param bool $is_pay_later_messaging_checkout_eligible Whether Pay Later messaging for checkout is eligible.
* @param bool $is_subscription_eligible Whether subscriptions are eligible.
* @param bool $is_paypal_buttons_eligible Whether PayPal buttons are eligible.
* @param bool $is_paypal_buttons_cart_eligible Whether PayPal buttons for cart are eligible.
* @param bool $is_paypal_buttons_block_checkout_eligible Whether PayPal buttons for block checkout are eligible.
* @param bool $is_paypal_buttons_product_eligible Whether PayPal buttons for product page are eligible.
* @param bool $is_apple_pay_domain_eligible Whether Apple Pay domain registration is eligible.
* @param bool $is_digital_wallet_eligible Whether digital wallet features are eligible.
* @param bool $is_apple_pay_eligible Whether Apple Pay is eligible.
* @param bool $is_google_pay_eligible Whether Google Pay is eligible.
* @param bool $is_add_subscription_eligible Whether PayPal subscription configuration is eligible.
* @param bool $is_enable_apple_pay_eligible Whether enabling Apple Pay is eligible.
* @param bool $is_enable_google_pay_eligible Whether enabling Google Pay is eligible.
*/
@ -149,30 +157,32 @@ class TodosEligibilityService {
bool $is_pay_later_messaging_cart_eligible,
bool $is_pay_later_messaging_checkout_eligible,
bool $is_subscription_eligible,
bool $is_paypal_buttons_eligible,
bool $is_paypal_buttons_cart_eligible,
bool $is_paypal_buttons_block_checkout_eligible,
bool $is_paypal_buttons_product_eligible,
bool $is_apple_pay_domain_eligible,
bool $is_digital_wallet_eligible,
bool $is_apple_pay_eligible,
bool $is_google_pay_eligible,
bool $is_add_subscription_eligible,
bool $is_enable_apple_pay_eligible,
bool $is_enable_google_pay_eligible
) {
$this->is_fastlane_eligible = $is_fastlane_eligible;
$this->is_card_payment_eligible = $is_card_payment_eligible;
$this->is_pay_later_messaging_eligible = $is_pay_later_messaging_eligible;
$this->is_pay_later_messaging_product_eligible = $is_pay_later_messaging_product_eligible;
$this->is_pay_later_messaging_cart_eligible = $is_pay_later_messaging_cart_eligible;
$this->is_pay_later_messaging_checkout_eligible = $is_pay_later_messaging_checkout_eligible;
$this->is_subscription_eligible = $is_subscription_eligible;
$this->is_paypal_buttons_eligible = $is_paypal_buttons_eligible;
$this->is_apple_pay_domain_eligible = $is_apple_pay_domain_eligible;
$this->is_digital_wallet_eligible = $is_digital_wallet_eligible;
$this->is_apple_pay_eligible = $is_apple_pay_eligible;
$this->is_google_pay_eligible = $is_google_pay_eligible;
$this->is_add_subscription_eligible = $is_add_subscription_eligible;
$this->is_enable_apple_pay_eligible = $is_enable_apple_pay_eligible;
$this->is_enable_google_pay_eligible = $is_enable_google_pay_eligible;
$this->is_fastlane_eligible = $is_fastlane_eligible;
$this->is_card_payment_eligible = $is_card_payment_eligible;
$this->is_pay_later_messaging_eligible = $is_pay_later_messaging_eligible;
$this->is_pay_later_messaging_product_eligible = $is_pay_later_messaging_product_eligible;
$this->is_pay_later_messaging_cart_eligible = $is_pay_later_messaging_cart_eligible;
$this->is_pay_later_messaging_checkout_eligible = $is_pay_later_messaging_checkout_eligible;
$this->is_subscription_eligible = $is_subscription_eligible;
$this->is_paypal_buttons_cart_eligible = $is_paypal_buttons_cart_eligible;
$this->is_paypal_buttons_block_checkout_eligible = $is_paypal_buttons_block_checkout_eligible;
$this->is_paypal_buttons_product_eligible = $is_paypal_buttons_product_eligible;
$this->is_apple_pay_domain_eligible = $is_apple_pay_domain_eligible;
$this->is_digital_wallet_eligible = $is_digital_wallet_eligible;
$this->is_apple_pay_eligible = $is_apple_pay_eligible;
$this->is_google_pay_eligible = $is_google_pay_eligible;
$this->is_enable_apple_pay_eligible = $is_enable_apple_pay_eligible;
$this->is_enable_google_pay_eligible = $is_enable_google_pay_eligible;
}
/**
@ -189,7 +199,9 @@ class TodosEligibilityService {
'add_pay_later_messaging_cart' => fn() => $this->is_pay_later_messaging_cart_eligible,
'add_pay_later_messaging_checkout' => fn() => $this->is_pay_later_messaging_checkout_eligible,
'configure_paypal_subscription' => fn() => $this->is_subscription_eligible,
'add_paypal_buttons' => fn() => $this->is_paypal_buttons_eligible,
'add_paypal_buttons_cart' => fn() => $this->is_paypal_buttons_cart_eligible,
'add_paypal_buttons_block_checkout' => fn() => $this->is_paypal_buttons_block_checkout_eligible,
'add_paypal_buttons_product' => fn() => $this->is_paypal_buttons_product_eligible,
'register_domain_apple_pay' => fn() => $this->is_apple_pay_domain_eligible,
'add_digital_wallets' => fn() => $this->is_digital_wallet_eligible,
'add_apple_pay' => fn() => $this->is_apple_pay_eligible,

View file

@ -0,0 +1,182 @@
<?php
/**
* Service for sorting and filtering todo items.
*
* @package WooCommerce\PayPalCommerce\Settings\Service
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Settings\Service;
use WooCommerce\PayPalCommerce\Settings\Data\TodosModel;
/**
* Service class that provides todo sorting and filtering functionality.
*/
class TodosSortingAndFilteringService {
/**
* Pay Later messaging todo IDs in priority order.
*
* @var array
*/
private const PAY_LATER_IDS = array(
'add_pay_later_messaging_product_page',
'add_pay_later_messaging_cart',
'add_pay_later_messaging_checkout',
);
/**
* Button placement todo IDs in priority order.
*
* @var array
*/
private const BUTTON_PLACEMENT_IDS = array(
'add_paypal_buttons_cart',
'add_paypal_buttons_block_checkout',
'add_paypal_buttons_product',
);
/**
* The TodosModel instance.
*
* @var TodosModel
*/
private TodosModel $todos_model;
/**
* Constructor.
*
* @param TodosModel $todos_model The TodosModel instance.
*/
public function __construct( TodosModel $todos_model ) {
$this->todos_model = $todos_model;
}
/**
* Returns Pay Later messaging todo IDs in priority order.
*
* @return array Pay Later messaging todo IDs.
*/
public function get_pay_later_ids(): array {
return self::PAY_LATER_IDS;
}
/**
* Returns Button Placement todo IDs in priority order.
*
* @return array Button Placement todo IDs.
*/
public function get_button_placement_ids(): array {
return self::BUTTON_PLACEMENT_IDS;
}
/**
* Sorts todos by their priority value.
*
* @param array $todos Array of todos to sort.
* @return array Sorted array of todos.
*/
public function sort_todos_by_priority( array $todos ): array {
usort(
$todos,
function( $a, $b ) {
$priority_a = $a['priority'] ?? 999;
$priority_b = $b['priority'] ?? 999;
return $priority_a <=> $priority_b;
}
);
return $todos;
}
/**
* Filters a group of todos to show only the highest priority one.
* Takes into account dismissed todos.
*
* @param array $todos The array of todos to filter.
* @param array $group_ids Array of todo IDs in priority order.
* @return array Filtered todos with only one todo from the specified group.
*/
public function filter_highest_priority_todo( array $todos, array $group_ids ): array {
$dismissed_todos = $this->todos_model->get_dismissed_todos();
$group_todos = array_filter(
$todos,
function( $todo ) use ( $group_ids ) {
return in_array( $todo['id'], $group_ids, true );
}
);
$other_todos = array_filter(
$todos,
function( $todo ) use ( $group_ids ) {
return ! in_array( $todo['id'], $group_ids, true );
}
);
// Find the highest priority todo from the group that's eligible AND not dismissed.
$priority_todo = null;
foreach ( $group_ids as $todo_id ) {
// Skip if this todo ID is dismissed.
if ( in_array( $todo_id, $dismissed_todos, true ) ) {
continue;
}
$matching_todo = current(
array_filter(
$group_todos,
function( $todo ) use ( $todo_id ) {
return $todo['id'] === $todo_id;
}
)
);
if ( $matching_todo ) {
$priority_todo = $matching_todo;
break;
}
}
return $priority_todo
? array_merge( $other_todos, array( $priority_todo ) )
: $other_todos;
}
/**
* Filter pay later todos to show only the highest priority eligible one.
*
* @param array $todos The array of todos to filter.
* @return array Filtered todos.
*/
public function filter_pay_later_todos( array $todos ): array {
return $this->filter_highest_priority_todo( $todos, self::PAY_LATER_IDS );
}
/**
* Filter button placement todos to show only the highest priority eligible one.
*
* @param array $todos The array of todos to filter.
* @return array Filtered todos.
*/
public function filter_button_placement_todos( array $todos ): array {
return $this->filter_highest_priority_todo( $todos, self::BUTTON_PLACEMENT_IDS );
}
/**
* Apply all priority filters to the todos list.
*
* This method applies sorting and all priority filtering in the correct order.
*
* @param array $todos The original todos array.
* @return array Fully filtered and sorted todos.
*/
public function apply_all_priority_filters( array $todos ): array {
$sorted_todos = $this->sort_todos_by_priority( $todos );
$filtered_todos = $this->filter_pay_later_todos( $sorted_todos );
$filtered_todos = $this->filter_button_placement_todos( $filtered_todos );
return $filtered_todos;
}
}

View file

@ -21,7 +21,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
use WooCommerce\PayPalCommerce\Compat\PPEC\PPECHelper;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Webhooks\WebhookEventStorage;
/**
@ -58,8 +57,8 @@ class StatusReportModule implements ServiceModule, ExtendingModule, ExecutableMo
$subscriptions_mode_settings = $c->get( 'wcgateway.settings.fields.subscriptions_mode' ) ?: array();
/* @var State $state The state. */
$state = $c->get( 'onboarding.state' );
/* @var bool $is_connected Whether onboarding is complete. */
$is_connected = $c->get( 'settings.flag.is-connected' );
/* @var Bearer $bearer The bearer. */
$bearer = $c->get( 'api.bearer' );
@ -92,7 +91,7 @@ class StatusReportModule implements ServiceModule, ExtendingModule, ExecutableMo
'exported_label' => 'Onboarded',
'description' => esc_html__( 'Whether PayPal account is correctly configured or not.', 'woocommerce-paypal-payments' ),
'value' => $this->bool_to_html(
$this->onboarded( $bearer, $state )
$this->onboarded( $bearer, $is_connected )
),
),
array(
@ -230,19 +229,18 @@ class StatusReportModule implements ServiceModule, ExtendingModule, ExecutableMo
/**
* It returns the current onboarding status.
*
* @param Bearer $bearer The bearer.
* @param State $state The state.
* @param Bearer $bearer The bearer.
* @param bool $is_connected Whether onboarding is complete.
* @return bool
*/
private function onboarded( Bearer $bearer, State $state ): bool {
private function onboarded( Bearer $bearer, bool $is_connected ): bool {
try {
$token = $bearer->bearer();
} catch ( RuntimeException $exception ) {
return false;
}
$current_state = $state->current_state();
return $token->is_valid() && $current_state === $state::STATE_ONBOARDED;
return $is_connected && $token->is_valid();
}
/**

View file

@ -37,7 +37,7 @@ return array(
$container->get( 'api.factory.payer' ),
$container->get( 'api.factory.shipping-preference' ),
$container->get( 'api.endpoint.order' ),
$container->get( 'onboarding.environment' ),
$container->get( 'settings.environment' ),
$container->get( 'wcgateway.processor.authorized-payments' ),
$container->get( 'wcgateway.settings' )
);

View file

@ -26,7 +26,7 @@ return array(
return $settings->has( 'merchant_id' ) ? (string) $settings->get( 'merchant_id' ) : '';
},
'api.partner_merchant_id' => static function ( string $previous, ContainerInterface $container ): string {
$environment = $container->get( 'onboarding.environment' );
$environment = $container->get( 'settings.environment' );
/**
* The environment.

View file

@ -87,35 +87,21 @@ use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration;
return array(
'wcgateway.paypal-gateway' => static function ( ContainerInterface $container ): PayPalGateway {
$order_processor = $container->get( 'wcgateway.order-processor' );
$settings_renderer = $container->get( 'wcgateway.settings.render' );
$funding_source_renderer = $container->get( 'wcgateway.funding-source.renderer' );
$settings = $container->get( 'wcgateway.settings' );
$session_handler = $container->get( 'session.handler' );
$refund_processor = $container->get( 'wcgateway.processor.refunds' );
$state = $container->get( 'onboarding.state' );
$transaction_url_provider = $container->get( 'wcgateway.transaction-url-provider' );
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
$page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' );
$payment_token_repository = $container->get( 'vaulting.repository.payment-token' );
$environment = $container->get( 'onboarding.environment' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
$api_shop_country = $container->get( 'api.shop.country' );
return new PayPalGateway(
$settings_renderer,
$funding_source_renderer,
$order_processor,
$settings,
$session_handler,
$refund_processor,
$state,
$transaction_url_provider,
$subscription_helper,
$page_id,
$environment,
$payment_token_repository,
$logger,
$api_shop_country,
$container->get( 'wcgateway.settings.render' ),
$container->get( 'wcgateway.funding-source.renderer' ),
$container->get( 'wcgateway.order-processor' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'session.handler' ),
$container->get( 'wcgateway.processor.refunds' ),
$container->get( 'settings.flag.is-connected' ),
$container->get( 'wcgateway.transaction-url-provider' ),
$container->get( 'wc-subscriptions.helper' ),
$container->get( 'wcgateway.current-ppcp-settings-page-id' ),
$container->get( 'settings.environment' ),
$container->get( 'vaulting.repository.payment-token' ),
$container->get( 'woocommerce.logger.woocommerce' ),
$container->get( 'api.shop.country' ),
$container->get( 'api.endpoint.order' ),
$container->get( 'api.factory.paypal-checkout-url' ),
$container->get( 'wcgateway.place-order-button-text' ),
@ -127,42 +113,26 @@ return array(
);
},
'wcgateway.credit-card-gateway' => static function ( ContainerInterface $container ): CreditCardGateway {
$order_processor = $container->get( 'wcgateway.order-processor' );
$settings_renderer = $container->get( 'wcgateway.settings.render' );
$settings = $container->get( 'wcgateway.settings' );
$dcc_configuration = $container->get( 'wcgateway.configuration.dcc' );
$module_url = $container->get( 'wcgateway.url' );
$session_handler = $container->get( 'session.handler' );
$refund_processor = $container->get( 'wcgateway.processor.refunds' );
$state = $container->get( 'onboarding.state' );
$transaction_url_provider = $container->get( 'wcgateway.transaction-url-provider' );
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
$payments_endpoint = $container->get( 'api.endpoint.payments' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
$vaulted_credit_card_handler = $container->get( 'vaulting.credit-card-handler' );
$icons = $container->get( 'wcgateway.credit-card-icons' );
return new CreditCardGateway(
$settings_renderer,
$order_processor,
$settings,
$dcc_configuration,
$icons,
$module_url,
$session_handler,
$refund_processor,
$state,
$transaction_url_provider,
$subscription_helper,
$payments_endpoint,
$vaulted_credit_card_handler,
$container->get( 'onboarding.environment' ),
$container->get( 'wcgateway.settings.render' ),
$container->get( 'wcgateway.order-processor' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'wcgateway.configuration.dcc' ),
$container->get( 'wcgateway.credit-card-icons' ),
$container->get( 'wcgateway.url' ),
$container->get( 'session.handler' ),
$container->get( 'wcgateway.processor.refunds' ),
$container->get( 'wcgateway.transaction-url-provider' ),
$container->get( 'wc-subscriptions.helper' ),
$container->get( 'api.endpoint.payments' ),
$container->get( 'vaulting.credit-card-handler' ),
$container->get( 'settings.environment' ),
$container->get( 'api.endpoint.order' ),
$container->get( 'wcgateway.endpoint.capture-card-payment' ),
$container->get( 'api.prefix' ),
$container->get( 'api.endpoint.payment-tokens' ),
$container->get( 'vaulting.wc-payment-tokens' ),
$logger
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'wcgateway.credit-card-labels' => static function ( ContainerInterface $container ) : array {
@ -234,11 +204,11 @@ return array(
$container->get( 'wcgateway.settings' ),
$container->get( 'session.handler' ),
$container->get( 'wcgateway.processor.refunds' ),
$container->get( 'onboarding.state' ),
$container->get( 'settings.flag.is-connected' ),
$container->get( 'wcgateway.transaction-url-provider' ),
$container->get( 'wc-subscriptions.helper' ),
$container->get( 'wcgateway.settings.allow_card_button_gateway.default' ),
$container->get( 'onboarding.environment' ),
$container->get( 'settings.environment' ),
$container->get( 'vaulting.repository.payment-token' ),
$container->get( 'woocommerce.logger.woocommerce' ),
$container->get( 'api.factory.paypal-checkout-url' ),
@ -321,10 +291,9 @@ return array(
$section = isset( $_GET['section'] ) ? sanitize_text_field( wp_unslash( $_GET['section'] ) ) : '';
$ppcp_tab = isset( $_GET[ SectionsRenderer::KEY ] ) ? sanitize_text_field( wp_unslash( $_GET[ SectionsRenderer::KEY ] ) ) : '';
$state = $container->get( 'onboarding.state' );
assert( $state instanceof State );
$is_connected = $container->get( 'settings.flag.is-connected' );
if ( ! $ppcp_tab && PayPalGateway::ID === $section && $state->current_state() !== State::STATE_ONBOARDED ) {
if ( ! $ppcp_tab && PayPalGateway::ID === $section && ! $is_connected ) {
return Settings::CONNECTION_TAB_ID;
}
@ -343,29 +312,25 @@ return array(
}
),
'wcgateway.notice.connect' => static function ( ContainerInterface $container ): ConnectAdminNotice {
$state = $container->get( 'onboarding.state' );
$settings = $container->get( 'wcgateway.settings' );
$is_current_country_send_only = $container->get( 'wcgateway.is-send-only-country' );
return new ConnectAdminNotice( $state, $settings, $is_current_country_send_only );
return new ConnectAdminNotice(
$container->get( 'settings.flag.is-connected' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'wcgateway.is-send-only-country' )
);
},
'wcgateway.notice.currency-unsupported' => static function ( ContainerInterface $container ): UnsupportedCurrencyAdminNotice {
$state = $container->get( 'onboarding.state' );
$shop_currency = $container->get( 'api.shop.currency.getter' );
$supported_currencies = $container->get( 'api.supported-currencies' );
$is_wc_gateways_list_page = $container->get( 'wcgateway.is-wc-gateways-list-page' );
$is_ppcp_settings_page = $container->get( 'wcgateway.is-ppcp-settings-page' );
return new UnsupportedCurrencyAdminNotice(
$state,
$shop_currency,
$supported_currencies,
$is_wc_gateways_list_page,
$is_ppcp_settings_page
$container->get( 'settings.flag.is-connected' ),
$container->get( 'api.shop.currency.getter' ),
$container->get( 'api.supported-currencies' ),
$container->get( 'wcgateway.is-wc-gateways-list-page' ),
$container->get( 'wcgateway.is-ppcp-settings-page' )
);
},
'wcgateway.notice.dcc-without-paypal' => static function ( ContainerInterface $container ): GatewayWithoutPayPalAdminNotice {
return new GatewayWithoutPayPalAdminNotice(
CreditCardGateway::ID,
$container->get( 'onboarding.state' ),
$container->get( 'settings.flag.is-connected' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'wcgateway.is-wc-payments-page' ),
$container->get( 'wcgateway.is-ppcp-settings-page' )
@ -374,7 +339,7 @@ return array(
'wcgateway.notice.card-button-without-paypal' => static function ( ContainerInterface $container ): GatewayWithoutPayPalAdminNotice {
return new GatewayWithoutPayPalAdminNotice(
CardButtonGateway::ID,
$container->get( 'onboarding.state' ),
$container->get( 'settings.flag.is-connected' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'wcgateway.is-wc-payments-page' ),
$container->get( 'wcgateway.is-ppcp-settings-page' ),
@ -477,14 +442,12 @@ return array(
return in_array( $store_country, $send_only_countries, true );
},
'wcgateway.notice.send-only-country' => static function ( ContainerInterface $container ) {
$onboarding_state = $container->get( 'onboarding.state' );
assert( $onboarding_state instanceof State );
return new SendOnlyCountryNotice(
$container->get( 'wcgateway.send-only-message' ),
$container->get( 'wcgateway.is-send-only-country' ),
$container->get( 'wcgateway.is-ppcp-settings-page' ),
$container->get( 'wcgateway.is-wc-gateways-list-page' ),
$onboarding_state->current_state()
$container->get( 'settings.flag.is-connected' )
);
},
@ -527,7 +490,7 @@ return array(
'wcgateway.settings.sections-renderer' => static function ( ContainerInterface $container ): SectionsRenderer {
return new SectionsRenderer(
$container->get( 'wcgateway.current-ppcp-settings-page-id' ),
$container->get( 'onboarding.state' ),
$container->get( 'settings.flag.is-connected' ),
$container->get( 'wcgateway.helper.dcc-product-status' ),
$container->get( 'api.helpers.dccapplies' ),
$container->get( 'button.helper.messages-apply' ),
@ -545,57 +508,36 @@ return array(
return new SettingsStatus( $settings );
},
'wcgateway.settings.render' => static function ( ContainerInterface $container ): SettingsRenderer {
$settings = $container->get( 'wcgateway.settings' );
$state = $container->get( 'onboarding.state' );
$fields = $container->get( 'wcgateway.settings.fields' );
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
$messages_apply = $container->get( 'button.helper.messages-apply' );
$dcc_product_status = $container->get( 'wcgateway.helper.dcc-product-status' );
$settings_status = $container->get( 'wcgateway.settings.status' );
$page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' );
$api_shop_country = $container->get( 'api.shop.country' );
return new SettingsRenderer(
$settings,
$state,
$fields,
$dcc_applies,
$messages_apply,
$dcc_product_status,
$settings_status,
$page_id,
$api_shop_country
$container->get( 'wcgateway.settings' ),
$container->get( 'onboarding.state' ), // Correct.
$container->get( 'wcgateway.settings.fields' ),
$container->get( 'api.helpers.dccapplies' ),
$container->get( 'button.helper.messages-apply' ),
$container->get( 'wcgateway.helper.dcc-product-status' ),
$container->get( 'wcgateway.settings.status' ),
$container->get( 'wcgateway.current-ppcp-settings-page-id' ),
$container->get( 'api.shop.country' )
);
},
'wcgateway.settings.listener' => static function ( ContainerInterface $container ): SettingsListener {
$settings = $container->get( 'wcgateway.settings' );
$fields = $container->get( 'wcgateway.settings.fields' );
$webhook_registrar = $container->get( 'webhook.registrar' );
$state = $container->get( 'onboarding.state' );
$cache = $container->get( 'api.paypal-bearer-cache' );
$bearer = $container->get( 'api.bearer' );
$page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' );
$signup_link_cache = $container->get( 'onboarding.signup-link-cache' );
$signup_link_ids = $container->get( 'onboarding.signup-link-ids' );
$pui_status_cache = $container->get( 'pui.status-cache' );
$dcc_status_cache = $container->get( 'dcc.status-cache' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
return new SettingsListener(
$settings,
$fields,
$webhook_registrar,
$cache,
$state,
$bearer,
$page_id,
$signup_link_cache,
$signup_link_ids,
$pui_status_cache,
$dcc_status_cache,
$container->get( 'wcgateway.settings' ),
$container->get( 'wcgateway.settings.fields' ),
$container->get( 'webhook.registrar' ),
$container->get( 'api.paypal-bearer-cache' ),
$container->get( 'onboarding.state' ), // Correct.
$container->get( 'api.bearer' ),
$container->get( 'wcgateway.current-ppcp-settings-page-id' ),
$container->get( 'onboarding.signup-link-cache' ),
$container->get( 'onboarding.signup-link-ids' ),
$container->get( 'pui.status-cache' ),
$container->get( 'dcc.status-cache' ),
$container->get( 'http.redirector' ),
$container->get( 'api.partner_merchant_id-production' ),
$container->get( 'api.partner_merchant_id-sandbox' ),
$container->get( 'api.endpoint.billing-agreements' ),
$logger,
$container->get( 'woocommerce.logger.woocommerce' ),
new Cache( 'ppcp-client-credentials-cache' )
);
},
@ -607,7 +549,7 @@ return array(
$threed_secure = $container->get( 'button.helper.three-d-secure' );
$authorized_payments_processor = $container->get( 'wcgateway.processor.authorized-payments' );
$settings = $container->get( 'wcgateway.settings' );
$environment = $container->get( 'onboarding.environment' );
$environment = $container->get( 'settings.environment' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
$order_helper = $container->get( 'api.order-helper' );
@ -728,6 +670,8 @@ return array(
return array();
}
// Legacy settings service, correct use of `State` class.
$state = $container->get( 'onboarding.state' );
assert( $state instanceof State );
@ -1485,12 +1429,12 @@ return array(
$container->get( 'wcgateway.pay-upon-invoice-order-endpoint' ),
$container->get( 'api.factory.purchase-unit' ),
$container->get( 'wcgateway.pay-upon-invoice-payment-source-factory' ),
$container->get( 'onboarding.environment' ),
$container->get( 'settings.environment' ),
$container->get( 'wcgateway.transaction-url-provider' ),
$container->get( 'woocommerce.logger.woocommerce' ),
$container->get( 'wcgateway.pay-upon-invoice-helper' ),
$container->get( 'wcgateway.checkout-helper' ),
$container->get( 'onboarding.state' ),
$container->get( 'settings.flag.is-connected' ),
$container->get( 'wcgateway.processor.refunds' ),
$container->get( 'wcgateway.url' )
);
@ -1524,7 +1468,7 @@ return array(
$container->get( 'wcgateway.pay-upon-invoice-order-endpoint' ),
$container->get( 'woocommerce.logger.woocommerce' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'onboarding.state' ),
$container->get( 'settings.flag.is-connected' ),
$container->get( 'wcgateway.current-ppcp-settings-page-id' ),
$container->get( 'wcgateway.pay-upon-invoice-product-status' ),
$container->get( 'wcgateway.pay-upon-invoice-helper' ),
@ -1549,7 +1493,7 @@ return array(
$container->get( 'api.factory.shipping-preference' ),
$container->get( 'wcgateway.url' ),
$container->get( 'wcgateway.transaction-url-provider' ),
$container->get( 'onboarding.environment' ),
$container->get( 'settings.environment' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},
@ -1717,15 +1661,15 @@ return array(
return 'https://www.paypal.com/bizsignup/entry?product=ADVANCED_VAULTING';
},
'wcgateway.settings.connection.dcc-status-text' => static function ( ContainerInterface $container ): string {
$state = $container->get( 'onboarding.state' );
if ( $state->current_state() < State::STATE_ONBOARDED ) {
$is_connected = $container->get( 'settings.flag.is-connected' );
if ( ! $is_connected ) {
return '';
}
$dcc_product_status = $container->get( 'wcgateway.helper.dcc-product-status' );
assert( $dcc_product_status instanceof DCCProductStatus );
$environment = $container->get( 'onboarding.environment' );
$environment = $container->get( 'settings.environment' );
assert( $environment instanceof Environment );
$dcc_enabled = $dcc_product_status->is_active();
@ -1755,7 +1699,7 @@ return array(
);
},
'wcgateway.settings.connection.reference-transactions-status-text' => static function ( ContainerInterface $container ): string {
$environment = $container->get( 'onboarding.environment' );
$environment = $container->get( 'settings.environment' );
assert( $environment instanceof Environment );
$billing_agreements_endpoint = $container->get( 'api.endpoint.billing-agreements' );
@ -1788,15 +1732,15 @@ return array(
);
},
'wcgateway.settings.connection.pui-status-text' => static function ( ContainerInterface $container ): string {
$state = $container->get( 'onboarding.state' );
if ( $state->current_state() < State::STATE_ONBOARDED ) {
$is_connected = $container->get( 'settings.flag.is-connected' );
if ( ! $is_connected ) {
return '';
}
$pui_product_status = $container->get( 'wcgateway.pay-upon-invoice-product-status' );
assert( $pui_product_status instanceof PayUponInvoiceProductStatus );
$environment = $container->get( 'onboarding.environment' );
$environment = $container->get( 'settings.environment' );
assert( $environment instanceof Environment );
$pui_enabled = $pui_product_status->is_active();
@ -1917,7 +1861,7 @@ return array(
$container->get( 'wcgateway.url' ),
$container->get( 'ppcp.asset-version' ),
$container->get( 'wcgateway.fraudnet' ),
$container->get( 'onboarding.environment' ),
$container->get( 'settings.environment' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'wcgateway.gateway-repository' ),
$container->get( 'session.handler' ),

View file

@ -62,6 +62,7 @@ class SettingsCommand {
$value = false;
}
// TODO new-ux: The setting must also be updated in the new settings.
$this->settings->set( $key, $value );
$this->settings->persist();

View file

@ -14,7 +14,6 @@ use Psr\Log\LoggerInterface;
use WC_Order;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait;
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
@ -70,13 +69,6 @@ class CardButtonGateway extends \WC_Payment_Gateway {
*/
private $refund_processor;
/**
* The state.
*
* @var State
*/
protected $state;
/**
* Service able to provide transaction url for an order.
*
@ -141,7 +133,7 @@ class CardButtonGateway extends \WC_Payment_Gateway {
* @param ContainerInterface $config The settings.
* @param SessionHandler $session_handler The Session Handler.
* @param RefundProcessor $refund_processor The Refund Processor.
* @param State $state The state.
* @param bool $is_connected Whether onboarding was completed.
* @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order.
* @param SubscriptionHelper $subscription_helper The subscription helper.
* @param bool $default_enabled Whether the gateway should be enabled by default.
@ -157,7 +149,7 @@ class CardButtonGateway extends \WC_Payment_Gateway {
ContainerInterface $config,
SessionHandler $session_handler,
RefundProcessor $refund_processor,
State $state,
bool $is_connected,
TransactionUrlProvider $transaction_url_provider,
SubscriptionHelper $subscription_helper,
bool $default_enabled,
@ -173,12 +165,11 @@ class CardButtonGateway extends \WC_Payment_Gateway {
$this->config = $config;
$this->session_handler = $session_handler;
$this->refund_processor = $refund_processor;
$this->state = $state;
$this->transaction_url_provider = $transaction_url_provider;
$this->subscription_helper = $subscription_helper;
$this->default_enabled = $default_enabled;
$this->environment = $environment;
$this->onboarded = $state->current_state() === State::STATE_ONBOARDED;
$this->onboarded = $is_connected;
$this->payment_token_repository = $payment_token_repository;
$this->logger = $logger;
$this->paypal_checkout_url_factory = $paypal_checkout_url_factory;

View file

@ -19,7 +19,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
use WooCommerce\PayPalCommerce\Vaulting\VaultedCreditCardHandler;
@ -109,13 +108,6 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
*/
private $refund_processor;
/**
* The state.
*
* @var State
*/
protected $state;
/**
* Service to get transaction url for an order.
*
@ -204,7 +196,6 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
* @param string $module_url The URL to the module.
* @param SessionHandler $session_handler The Session Handler.
* @param RefundProcessor $refund_processor The refund processor.
* @param State $state The state.
* @param TransactionUrlProvider $transaction_url_provider Service able to provide view transaction url base.
* @param SubscriptionHelper $subscription_helper The subscription helper.
* @param PaymentsEndpoint $payments_endpoint The payments endpoint.
@ -226,7 +217,6 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
string $module_url,
SessionHandler $session_handler,
RefundProcessor $refund_processor,
State $state,
TransactionUrlProvider $transaction_url_provider,
SubscriptionHelper $subscription_helper,
PaymentsEndpoint $payments_endpoint,
@ -247,7 +237,6 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
$this->module_url = $module_url;
$this->session_handler = $session_handler;
$this->refund_processor = $refund_processor;
$this->state = $state;
$this->transaction_url_provider = $transaction_url_provider;
$this->subscription_helper = $subscription_helper;
$this->payments_endpoint = $payments_endpoint;

View file

@ -19,7 +19,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens;
use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait;
@ -104,13 +103,6 @@ class PayPalGateway extends \WC_Payment_Gateway {
*/
private $refund_processor;
/**
* The state.
*
* @var State
*/
protected $state;
/**
* Service able to provide transaction url for an order.
*
@ -137,7 +129,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
*
* @var bool
*/
private $onboarded;
private bool $onboarded;
/**
* ID of the current PPCP gateway settings page, or empty if it is not such page.
@ -225,7 +217,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
* @param ContainerInterface $config The settings.
* @param SessionHandler $session_handler The Session Handler.
* @param RefundProcessor $refund_processor The Refund Processor.
* @param State $state The state.
* @param bool $is_connected Whether onboarding was completed.
* @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order.
* @param SubscriptionHelper $subscription_helper The subscription helper.
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
@ -249,7 +241,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
ContainerInterface $config,
SessionHandler $session_handler,
RefundProcessor $refund_processor,
State $state,
bool $is_connected,
TransactionUrlProvider $transaction_url_provider,
SubscriptionHelper $subscription_helper,
string $page_id,
@ -273,12 +265,11 @@ class PayPalGateway extends \WC_Payment_Gateway {
$this->config = $config;
$this->session_handler = $session_handler;
$this->refund_processor = $refund_processor;
$this->state = $state;
$this->transaction_url_provider = $transaction_url_provider;
$this->subscription_helper = $subscription_helper;
$this->page_id = $page_id;
$this->environment = $environment;
$this->onboarded = $state->current_state() === State::STATE_ONBOARDED;
$this->onboarded = $is_connected;
$this->payment_token_repository = $payment_token_repository;
$this->logger = $logger;
$this->api_shop_country = $api_shop_country;

View file

@ -19,7 +19,6 @@ use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus;
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
@ -62,11 +61,11 @@ class PayUponInvoice {
protected $pui_helper;
/**
* The onboarding state.
* Whether onboarding was completed and the merchant is connected to PayPal.
*
* @var State
* @var bool
*/
protected $state;
protected bool $is_connected;
/**
* Current PayPal settings page id.
@ -102,7 +101,7 @@ class PayUponInvoice {
* @param PayUponInvoiceOrderEndpoint $pui_order_endpoint The PUI order endpoint.
* @param LoggerInterface $logger The logger.
* @param Settings $settings The settings.
* @param State $state The onboarding state.
* @param bool $is_connected Whether onboarding was completed.
* @param string $current_ppcp_settings_page_id Current PayPal settings page id.
* @param PayUponInvoiceProductStatus $pui_product_status The PUI product status.
* @param PayUponInvoiceHelper $pui_helper The PUI helper.
@ -113,7 +112,7 @@ class PayUponInvoice {
PayUponInvoiceOrderEndpoint $pui_order_endpoint,
LoggerInterface $logger,
Settings $settings,
State $state,
bool $is_connected,
string $current_ppcp_settings_page_id,
PayUponInvoiceProductStatus $pui_product_status,
PayUponInvoiceHelper $pui_helper,
@ -123,7 +122,7 @@ class PayUponInvoice {
$this->pui_order_endpoint = $pui_order_endpoint;
$this->logger = $logger;
$this->settings = $settings;
$this->state = $state;
$this->is_connected = $is_connected;
$this->current_ppcp_settings_page_id = $current_ppcp_settings_page_id;
$this->pui_product_status = $pui_product_status;
$this->pui_helper = $pui_helper;
@ -138,6 +137,10 @@ class PayUponInvoice {
*/
public function init(): void {
if ( $this->pui_helper->is_pui_gateway_enabled() ) {
/*
* TODO new-ux: Check if we still support this setting, or if it's always enabled.
* If fraudnet is not configurable in new UI, we can ignore this.
*/
$this->settings->set( 'fraudnet_enabled', true );
$this->settings->persist();
}
@ -437,7 +440,7 @@ class PayUponInvoice {
function ( $methods ) {
if (
! is_array( $methods )
|| State::STATE_ONBOARDED !== $this->state->current_state()
|| ! $this->is_connected
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|| ! ( is_checkout() || isset( $_GET['pay_for_order'] ) && $_GET['pay_for_order'] === 'true' )
) {

View file

@ -17,7 +17,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
@ -89,13 +88,6 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway {
*/
protected $checkout_helper;
/**
* The onboarding state.
*
* @var State
*/
protected $state;
/**
* The refund processor.
*
@ -121,7 +113,7 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway {
* @param LoggerInterface $logger The logger.
* @param PayUponInvoiceHelper $pui_helper The PUI helper.
* @param CheckoutHelper $checkout_helper The checkout helper.
* @param State $state The onboarding state.
* @param bool $is_connected Whether the onboarding was completed.
* @param RefundProcessor $refund_processor The refund processor.
* @param string $module_url The module URL.
*/
@ -134,7 +126,7 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway {
LoggerInterface $logger,
PayUponInvoiceHelper $pui_helper,
CheckoutHelper $checkout_helper,
State $state,
bool $is_connected,
RefundProcessor $refund_processor,
string $module_url
) {
@ -169,8 +161,7 @@ class PayUponInvoiceGateway extends WC_Payment_Gateway {
$this->module_url = $module_url;
$this->icon = apply_filters( 'woocommerce_paypal_payments_pay_upon_invoice_gateway_icon', esc_url( $this->module_url ) . 'assets/images/ratepay.svg' );
$this->state = $state;
if ( $state->current_state() === State::STATE_ONBOARDED ) {
if ( $is_connected ) {
$this->supports = array( 'refunds' );
}
$this->refund_processor = $refund_processor;

View file

@ -0,0 +1,135 @@
<?php
/**
* Describes the current API connection state (connected vs onboarding).
*
* @package WooCommerce\PayPalCommerce\WcGateway\Helper
*/
declare( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Helper;
/**
* Class ConnectionState
*
* Manages the merchants' connection status details and provides inspection
* methods to describe the current state.
*/
class ConnectionState {
/**
* The connection status.
*
* A merchant can be either "connected" (true) or "onboarding" (false).
* During the "onboarding" phase, the environment is undefined.
*
* @var bool
*/
private bool $is_connected;
/**
* The environment instance, which is managed by this class because it's
* hierarchically coupled to the connection status: Only connected merchants
* have a defined environment.
*
* @var Environment
*/
private Environment $environment;
/**
* Constructor.
*
* @param bool $is_connected Initial connection status.
* @param Environment $environment The environment instance.
*/
public function __construct( bool $is_connected, Environment $environment ) {
$this->is_connected = $is_connected;
$this->environment = $environment;
}
/**
* Set connection status to "connected to PayPal" (end onboarding).
*
* @param bool $is_sandbox Whether to connect to a sandbox environment.
*/
public function connect( bool $is_sandbox = false ) : void {
if ( ! $this->is_connected ) {
/**
* Action that fires before the connection status changes from
* disconnected to connected.
*/
do_action( 'woocommerce_paypal_payments_merchant_connection_change', true );
}
$this->is_connected = true;
$this->environment->set_environment( $is_sandbox );
}
/**
* Set connection status to "not connected to PayPal" (start onboarding).
*/
public function disconnect() : void {
if ( $this->is_connected ) {
/**
* Action that fires before the connection status changes from
* connected to disconnected.
*/
do_action( 'woocommerce_paypal_payments_merchant_connection_change', false );
}
$this->is_connected = false;
}
/**
* Returns the managed environment instance.
*
* @return Environment The environment instance.
*/
public function get_environment() : Environment {
return $this->environment;
}
/**
* Is the merchant connected to a PayPal account?
*
* @return bool True, if onboarding was completed and connection details are present.
*/
public function is_connected() : bool {
return $this->is_connected;
}
/**
* Is the merchant currently in the "onboarding phase"?
*
* @return bool True, if we don't know merchant connection details.
*/
public function is_onboarding() : bool {
return ! $this->is_connected;
}
/**
* Is the merchant connected to a sandbox environment?
*
* @return bool True, if connected to a sandbox environment.
*/
public function is_sandbox() : bool {
return $this->is_connected && $this->environment->is_sandbox();
}
/**
* Is the merchant connected to a production environment and can receive payments?
*
* @return bool True, if connected to a production environment.
*/
public function is_production() : bool {
return $this->is_connected && $this->environment->is_production();
}
/**
* Returns the current environment's name.
*
* @return string Name of the currently connected environment; empty string if not connected.
*/
public function current_environment() : string {
return $this->is_connected ? $this->environment->current_environment() : '';
}
}

View file

@ -103,6 +103,7 @@ class DCCProductStatus extends ProductStatus {
continue;
}
// Settings used as a cache; `settings->set` is compatible with new UI.
if ( in_array( 'CUSTOM_CARD_PROCESSING', $product->capabilities(), true ) ) {
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
$this->settings->persist();

View file

@ -9,8 +9,6 @@ declare( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Helper;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
/**
* Class Environment
*/
@ -27,36 +25,73 @@ class Environment {
public const SANDBOX = 'sandbox';
/**
* The Settings.
* Name of the current environment.
*
* @var ContainerInterface
* @var string
*/
private ContainerInterface $settings;
private string $environment_name;
/**
* Environment constructor.
*
* @param ContainerInterface $settings The settings.
* @param bool $is_sandbox Whether this instance represents a sandbox environment.
*/
public function __construct( ContainerInterface $settings ) {
$this->settings = $settings;
public function __construct( bool $is_sandbox = false ) {
$this->environment_name = $this->prepare_environment_name( $is_sandbox );
}
/**
* Returns the current environment.
* Returns a valid environment name based on the provided argument.
*
* @param bool $is_sandbox Whether this instance represents a sandbox environment.
* @return string The environment name.
*/
private function prepare_environment_name( bool $is_sandbox ) : string {
if ( $is_sandbox ) {
return self::SANDBOX;
}
return self::PRODUCTION;
}
/**
* Updates the current environment.
*
* @param bool $is_sandbox Whether this instance represents a sandbox environment.
*/
public function set_environment( bool $is_sandbox ) : void {
$new_environment = $this->prepare_environment_name( $is_sandbox );
if ( $new_environment !== $this->environment_name ) {
/**
* Action that fires before the environment status changes.
*
* @param string $new_environment The new environment name.
* @param string $old_environment The previous environment name.
*/
do_action(
'woocommerce_paypal_payments_merchant_environment_change',
$new_environment,
$this->environment_name
);
}
$this->environment_name = $new_environment;
}
/**
* Returns the current environment's name.
*
* @return string
*/
public function current_environment() : string {
return (
$this->settings->has( 'sandbox_on' ) && $this->settings->get( 'sandbox_on' )
) ? self::SANDBOX : self::PRODUCTION;
return $this->environment_name;
}
/**
* Detect whether the current environment equals $environment
*
* @deprecated Use the is_sandbox() and is_production() methods instead.
* @deprecated 3.0.0 - Use the is_sandbox() and is_production() methods instead.
* These methods provide better encapsulation, are less error-prone,
* and improve code readability by removing the need to pass environment constants.
* @param string $environment The value to check against.

View file

@ -96,6 +96,7 @@ class PayUponInvoiceProductStatus extends ProductStatus {
continue;
}
// Settings used as a cache; `settings->set` is compatible with new UI.
if ( in_array( 'PAY_UPON_INVOICE', $product->capabilities(), true ) ) {
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
$this->settings->persist();

View file

@ -10,7 +10,6 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Notice;
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
@ -20,11 +19,11 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
class ConnectAdminNotice {
/**
* The state.
* Whether the merchant completed the onboarding and is connected to PayPal.
*
* @var State
* @var bool
*/
private $state;
private bool $is_connected;
/**
* The settings.
@ -43,12 +42,16 @@ class ConnectAdminNotice {
/**
* ConnectAdminNotice constructor.
*
* @param State $state The state.
* @param bool $is_connected Whether onboarding was completed.
* @param ContainerInterface $settings The settings.
* @param bool $is_current_country_send_only Whether the current store's country is classified as a send-only country.
*/
public function __construct( State $state, ContainerInterface $settings, bool $is_current_country_send_only ) {
$this->state = $state;
public function __construct(
bool $is_connected,
ContainerInterface $settings,
bool $is_current_country_send_only
) {
$this->is_connected = $is_connected;
$this->settings = $settings;
$this->is_current_country_send_only = $is_current_country_send_only;
}
@ -77,9 +80,13 @@ class ConnectAdminNotice {
/**
* Whether the message should display.
*
* Only display the "almost ready" message for merchants that did not complete
* the onboarding wizard. Also, ensure their store country is eligible for
* collecting PayPal payments.
*
* @return bool
*/
protected function should_display(): bool {
return $this->state->current_state() !== State::STATE_ONBOARDED && $this->is_current_country_send_only === false;
return ! $this->is_connected && ! $this->is_current_country_send_only;
}
}

View file

@ -11,7 +11,6 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Notice;
use WC_Payment_Gateway;
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
@ -32,11 +31,11 @@ class GatewayWithoutPayPalAdminNotice {
private $id;
/**
* The state.
* Whether the merchant completed onboarding.
*
* @var State
* @var bool
*/
private $state;
private bool $is_connected;
/**
* The settings.
@ -70,7 +69,7 @@ class GatewayWithoutPayPalAdminNotice {
* ConnectAdminNotice constructor.
*
* @param string $id The gateway ID.
* @param State $state The state.
* @param bool $is_connected Whether onboading was completed.
* @param ContainerInterface $settings The settings.
* @param bool $is_payments_page Whether the current page is the WC payment page.
* @param bool $is_ppcp_settings_page Whether the current page is the PPCP settings page.
@ -78,14 +77,14 @@ class GatewayWithoutPayPalAdminNotice {
*/
public function __construct(
string $id,
State $state,
bool $is_connected,
ContainerInterface $settings,
bool $is_payments_page,
bool $is_ppcp_settings_page,
?SettingsStatus $settings_status = null
) {
$this->id = $id;
$this->state = $state;
$this->is_connected = $is_connected;
$this->settings = $settings;
$this->is_payments_page = $is_payments_page;
$this->is_ppcp_settings_page = $is_ppcp_settings_page;
@ -161,7 +160,7 @@ class GatewayWithoutPayPalAdminNotice {
* @return string One of the NOTICE_* constants.
*/
protected function check(): string {
if ( State::STATE_ONBOARDED !== $this->state->current_state() ||
if ( ! $this->is_connected ||
( ! $this->is_payments_page && ! $this->is_ppcp_settings_page ) ) {
return self::NOTICE_OK;
}

View file

@ -10,7 +10,6 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Notice;
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
use WooCommerce\PayPalCommerce\Onboarding\State;
/**
* Creates an admin message that notifies user about send only country.
@ -47,9 +46,9 @@ class SendOnlyCountryNotice {
/**
* Onboarding state
*
* @var int
* @var bool
*/
private int $onboarding_state;
private bool $is_connected;
/**
* AdminNotice constructor.
@ -58,20 +57,20 @@ class SendOnlyCountryNotice {
* @param bool $is_send_only_country Determines if current WC country is a send only country.
* @param bool $is_ppcp_settings_page Determines if current page is ppcp settings page.
* @param bool $is_wc_gateways_list_page Determines if current page is ppcp gateway list page.
* @param int $onboarding_state Determines current onboarding state.
* @param bool $is_connected Whether onboarding was completed.
*/
public function __construct(
string $message_text,
bool $is_send_only_country,
bool $is_ppcp_settings_page,
bool $is_wc_gateways_list_page,
int $onboarding_state
bool $is_connected
) {
$this->message_text = $message_text;
$this->is_send_only_country = $is_send_only_country;
$this->is_ppcp_settings_page = $is_ppcp_settings_page;
$this->is_wc_gateways_list_page = $is_wc_gateways_list_page;
$this->onboarding_state = $onboarding_state;
$this->is_connected = $is_connected;
}
/**
@ -80,10 +79,9 @@ class SendOnlyCountryNotice {
* @return Message|null
*/
public function message(): ?Message {
if ( ! $this->is_send_only_country ||
! $this->is_ppcp_page() ||
$this->onboarding_state === State::STATE_START
! $this->is_connected ||
! $this->is_ppcp_page()
) {
return null;
}

View file

@ -11,9 +11,6 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Notice;
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
use WooCommerce\PayPalCommerce\ApiClient\Helper\CurrencyGetter;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
/**
* Class UnsupportedCurrencyAdminNotice
@ -21,11 +18,11 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
class UnsupportedCurrencyAdminNotice {
/**
* The state.
* Whether the merchant completed onboarding.
*
* @var State
* @var bool
*/
private $state;
private bool $is_connected;
/**
* The supported currencies.
@ -58,20 +55,20 @@ class UnsupportedCurrencyAdminNotice {
/**
* UnsupportedCurrencyAdminNotice constructor.
*
* @param State $state The state.
* @param bool $is_connected Whether the merchant completed onboarding.
* @param CurrencyGetter $shop_currency The shop currency.
* @param array $supported_currencies The supported currencies.
* @param bool $is_wc_gateways_list_page Indicates if we're on the WooCommerce gateways list page.
* @param bool $is_ppcp_settings_page Indicates if we're on a PPCP Settings page.
*/
public function __construct(
State $state,
bool $is_connected,
CurrencyGetter $shop_currency,
array $supported_currencies,
bool $is_wc_gateways_list_page,
bool $is_ppcp_settings_page
) {
$this->state = $state;
$this->is_connected = $is_connected;
$this->shop_currency = $shop_currency;
$this->supported_currencies = $supported_currencies;
$this->is_wc_gateways_list_page = $is_wc_gateways_list_page;
@ -110,7 +107,7 @@ class UnsupportedCurrencyAdminNotice {
* @return bool
*/
protected function should_display(): bool {
return $this->state->current_state() === State::STATE_ONBOARDED
return $this->is_connected
&& ! $this->currency_supported()
&& ( $this->is_wc_gateways_list_page || $this->is_ppcp_settings_page );
}

View file

@ -28,6 +28,8 @@ return function ( ContainerInterface $container, array $fields ): array {
return $fields;
}
// Legacy settings module, use of `State` class is correct.
$state = $container->get( 'onboarding.state' );
assert( $state instanceof State );

View file

@ -11,7 +11,6 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Settings;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOGateway;
@ -35,11 +34,11 @@ class SectionsRenderer {
protected $page_id;
/**
* The onboarding state.
* Whether onboarding was completed and the merchant is connected to PayPal.
*
* @var State
* @var bool
*/
private $state;
private bool $is_connected;
/**
* The DCC product status
@ -69,18 +68,11 @@ class SectionsRenderer {
*/
private $pui_product_status;
/**
* SectionsRenderer constructor.
*
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
* @param State $state The onboarding state.
*/
/**
* SectionsRenderer constructor.
*
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
* @param State $state The onboarding state.
* @param bool $is_connected Whether the merchant completed onboarding.
* @param DCCProductStatus $dcc_product_status The DCC product status.
* @param DccApplies $dcc_applies The DCC applies.
* @param MessagesApply $messages_apply The Messages apply.
@ -88,14 +80,14 @@ class SectionsRenderer {
*/
public function __construct(
string $page_id,
State $state,
bool $is_connected,
DCCProductStatus $dcc_product_status,
DccApplies $dcc_applies,
MessagesApply $messages_apply,
PayUponInvoiceProductStatus $pui_product_status
) {
$this->page_id = $page_id;
$this->state = $state;
$this->is_connected = $is_connected;
$this->dcc_product_status = $dcc_product_status;
$this->dcc_applies = $dcc_applies;
$this->messages_apply = $messages_apply;
@ -108,9 +100,7 @@ class SectionsRenderer {
* @return bool
*/
public function should_render() : bool {
return ! empty( $this->page_id ) &&
( $this->state->production_state() === State::STATE_ONBOARDED ||
$this->state->sandbox_state() === State::STATE_ONBOARDED );
return $this->page_id && $this->is_connected;
}
/**

View file

@ -120,7 +120,10 @@ class Settings implements ContainerInterface {
* @return bool
*/
public function has( string $id ) {
if ( $this->settings_map_helper->has_mapped_key( $id ) ) {
if (
$this->settings_map_helper->has_mapped_key( $id )
&& ! is_null( $this->settings_map_helper->mapped_value( $id ) )
) {
return true;
}

View file

@ -214,6 +214,8 @@ class SettingsListener {
Cache $client_credentials_cache
) {
// This is a legacy settings class, it's correctly relying on the `Status` class.
$this->settings = $settings;
$this->setting_fields = $setting_fields;
$this->webhook_registrar = $webhook_registrar;
@ -261,6 +263,8 @@ class SettingsListener {
// phpcs:enable WordPress.Security.NonceVerification.Missing
// phpcs:enable WordPress.Security.NonceVerification.Recommended
// This method is only used for legacy UI, `settings->set` is valid here.
$this->settings->set( 'merchant_id', $merchant_id );
$this->settings->set( 'merchant_email', $merchant_email );
@ -363,6 +367,8 @@ class SettingsListener {
return;
}
// This method is only used for legacy UI, `settings->set` is valid here.
try {
$token = $this->bearer->bearer();
if ( ! $token->vaulting_available() ) {
@ -472,6 +478,10 @@ class SettingsListener {
&& 1 === absint( $_POST['woocommerce_ppcp-gateway_enabled'] );
}
// This method initializes a feature cache. This initialization is not
// required by the new UI; we can ignore the `settings->set` usage.
// TODO new-ux: Test, if this method is called or some non-settings parts must be converted.
// phpcs:enable phpcs:disable WordPress.Security.NonceVerification.Missing
// phpcs:enable phpcs:disable WordPress.Security.NonceVerification.Missing
if ( $credentials_change_status ) {
@ -720,6 +730,8 @@ class SettingsListener {
/**
* Prevent enabling tracking if it is not enabled for merchant account.
*
* This method is not used anywhere. Not relevant for new-ux.
*
* @throws RuntimeException When API request fails.
*/
public function listen_for_tracking_enabled(): void {
@ -765,6 +777,8 @@ class SettingsListener {
return;
}
// This method is only used for legacy UI, `settings->set` is valid here.
$existing_setting_value = $this->settings->has( $setting_slug ) ? $this->settings->get( $setting_slug ) : null;
if ( $condition ) {

View file

@ -114,6 +114,8 @@ class SettingsRenderer {
string $api_shop_country
) {
// This is a legacy settings class, it's correctly relying on the `Status` class.
$this->settings = $settings;
$this->state = $state;
$this->fields = $fields;

View file

@ -14,8 +14,6 @@ use Psr\Log\LoggerInterface;
use Throwable;
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\Orders;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
@ -33,7 +31,6 @@ use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn;
use WooCommerce\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail;
@ -59,7 +56,6 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\SectionsRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsListener;
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Registrar\TaskRegistrarInterface;
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCGatewayConfiguration;
@ -209,7 +205,7 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul
$c->get( 'button.client_id_for_admin' ),
$c->get( 'api.shop.currency.getter' ),
$c->get( 'api.shop.country' ),
$c->get( 'onboarding.environment' ),
$c->get( 'settings.environment' ),
$settings_status->is_pay_later_button_enabled(),
$settings->has( 'disable_funding' ) ? $settings->get( 'disable_funding' ) : array(),
$c->get( 'wcgateway.settings.funding-sources' ),
@ -607,16 +603,14 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul
$methods[] = $paypal_gateway;
$onboarding_state = $container->get( 'onboarding.state' );
assert( $onboarding_state instanceof State );
$settings = $container->get( 'wcgateway.settings' );
assert( $settings instanceof ContainerInterface );
$is_our_page = $container->get( 'wcgateway.is-ppcp-settings-page' );
$is_gateways_list_page = $container->get( 'wcgateway.is-wc-gateways-list-page' );
$is_connected = $container->get( 'settings.flag.is-connected' );
if ( $onboarding_state->current_state() !== State::STATE_ONBOARDED ) {
if ( ! $is_connected ) {
return $methods;
}

View file

@ -28,7 +28,7 @@ return array(
$endpoint = $container->get( 'api.endpoint.order' );
$purchase_unit_factory = $container->get( 'api.factory.purchase-unit' );
$payer_factory = $container->get( 'api.factory.payer' );
$environment = $container->get( 'onboarding.environment' );
$environment = $container->get( 'settings.environment' );
$settings = $container->get( 'wcgateway.settings' );
$authorized_payments_processor = $container->get( 'wcgateway.processor.authorized-payments' );
$funding_source_renderer = $container->get( 'wcgateway.funding-source.renderer' );

View file

@ -21,6 +21,7 @@ use WCS_Manual_Renewal_Manager;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WP_Query;
/**
* Class SubscriptionHelper
@ -342,4 +343,30 @@ class SubscriptionHelper {
return '';
}
/**
* Checks if any subscription products exist.
*
* @return bool
*/
public function has_subscription_products(): bool {
// Query for subscription products.
$args = array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => 1,
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
'tax_query' => array(
array(
'taxonomy' => 'product_type',
'field' => 'slug',
'terms' => 'subscription',
),
),
);
$subscription_products = new WP_Query( $args );
return $subscription_products->have_posts();
}
}

View file

@ -10,7 +10,6 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Webhooks;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\WebhookEndpoint;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
return array(
@ -18,8 +17,9 @@ return array(
$endpoint = $container->get( 'api.endpoint.webhook' );
assert( $endpoint instanceof WebhookEndpoint );
$state = $container->get( 'onboarding.state' );
if ( $state->current_state() >= State::STATE_ONBOARDED ) {
$is_connected = $container->get( 'settings.flag.is-connected' );
if ( $is_connected ) {
return $endpoint->list();
}

View file

@ -11,10 +11,8 @@ namespace WooCommerce\PayPalCommerce\Webhooks;
use Exception;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\WebhookEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Webhook;
use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookFactory;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\ResubscribeEndpoint;
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulateEndpoint;
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulationStateEndpoint;
@ -183,7 +181,7 @@ return array(
return new WebhooksStatusPageAssets(
$container->get( 'webhook.module-url' ),
$container->get( 'ppcp.asset-version' ),
$container->get( 'onboarding.environment' )
$container->get( 'settings.environment' )
);
},

View file

@ -9,8 +9,6 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Webhooks;
use WC_Order;
use WooCommerce\PayPalCommerce\Onboarding\State;
use Exception;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
@ -18,7 +16,6 @@ use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\FactoryModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\ResubscribeEndpoint;
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulateEndpoint;
@ -142,9 +139,10 @@ class WebhookModule implements ServiceModule, FactoryModule, ExtendingModule, Ex
);
try {
$webhooks = $container->get( 'webhook.status.registered-webhooks' );
$state = $container->get( 'onboarding.state' );
if ( empty( $webhooks ) && $state->current_state() >= State::STATE_ONBOARDED ) {
$webhooks = $container->get( 'webhook.status.registered-webhooks' );
$is_connected = $container->get( 'settings.flag.is-connected' );
if ( empty( $webhooks ) && $is_connected ) {
$registrar = $container->get( 'webhook.registrar' );
assert( $registrar instanceof WebhookRegistrar );
$registrar->register();

View file

@ -10,7 +10,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\TestCase;
use WooCommerce\PayPalCommerce\Vaulting\VaultedCreditCardHandler;
@ -34,7 +33,6 @@ class CreditCardGatewayTest extends TestCase
private $moduleUrl;
private $sessionHandler;
private $refundProcessor;
private $state;
private $transactionUrlProvider;
private $subscriptionHelper;
private $captureCardPayment;
@ -60,7 +58,6 @@ class CreditCardGatewayTest extends TestCase
$this->moduleUrl = '';
$this->sessionHandler = Mockery::mock(SessionHandler::class);
$this->refundProcessor = Mockery::mock(RefundProcessor::class);
$this->state = Mockery::mock(State::class);
$this->transactionUrlProvider = Mockery::mock(TransactionUrlProvider::class);
$this->subscriptionHelper = Mockery::mock(SubscriptionHelper::class);
$this->captureCardPayment = Mockery::mock(CaptureCardPayment::class);
@ -73,7 +70,6 @@ class CreditCardGatewayTest extends TestCase
$this->environment = Mockery::mock(Environment::class);
$this->orderEndpoint = Mockery::mock(OrderEndpoint::class);
$this->state->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED);
$this->config->shouldReceive('has')->andReturn(true);
$this->config->shouldReceive('get')->andReturn('');
@ -92,7 +88,6 @@ class CreditCardGatewayTest extends TestCase
$this->moduleUrl,
$this->sessionHandler,
$this->refundProcessor,
$this->state,
$this->transactionUrlProvider,
$this->subscriptionHelper,
$this->paymentsEndpoint,

View file

@ -11,7 +11,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\TestCase;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper;
@ -30,7 +29,7 @@ class PayUponInvoiceGatewayTest extends TestCase
private $testee;
private $pui_helper;
private $checkout_helper;
private $state;
private $is_connected;
private $refund_processor;
public function setUp(): void
@ -45,9 +44,7 @@ class PayUponInvoiceGatewayTest extends TestCase
$this->transaction_url_provider = Mockery::mock(TransactionUrlProvider::class);
$this->pui_helper = Mockery::mock(PayUponInvoiceHelper::class);
$this->checkout_helper = Mockery::mock(CheckoutHelper::class);
$this->state = Mockery::mock(State::class);
$this->state->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED);
$this->is_connected = true;
$this->refund_processor = Mockery::mock(RefundProcessor::class);
@ -63,7 +60,7 @@ class PayUponInvoiceGatewayTest extends TestCase
$this->logger,
$this->pui_helper,
$this->checkout_helper,
$this->state,
$this->is_connected,
$this->refund_processor,
''
);

View file

@ -10,7 +10,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens;
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
@ -38,7 +37,7 @@ class WcGatewayTest extends TestCase
private $orderProcessor;
private $settings;
private $refundProcessor;
private $onboardingState;
private $isConnected;
private $transactionUrlProvider;
private $subscriptionHelper;
private $environment;
@ -63,7 +62,7 @@ class WcGatewayTest extends TestCase
$this->settings = Mockery::mock(Settings::class);
$this->sessionHandler = Mockery::mock(SessionHandler::class);
$this->refundProcessor = Mockery::mock(RefundProcessor::class);
$this->onboardingState = Mockery::mock(State::class);
$this->isConnected = true;
$this->transactionUrlProvider = Mockery::mock(TransactionUrlProvider::class);
$this->subscriptionHelper = Mockery::mock(SubscriptionHelper::class);
$this->environment = Mockery::mock(Environment::class);
@ -76,8 +75,6 @@ class WcGatewayTest extends TestCase
$this->apiShopCountry = 'DE';
$this->orderEndpoint = Mockery::mock(OrderEndpoint::class);
$this->onboardingState->shouldReceive('current_state')->andReturn(State::STATE_ONBOARDED);
$this->sessionHandler
->shouldReceive('funding_source')
->andReturnUsing(function () {
@ -108,7 +105,7 @@ class WcGatewayTest extends TestCase
$this->settings,
$this->sessionHandler,
$this->refundProcessor,
$this->onboardingState,
$this->isConnected,
$this->transactionUrlProvider,
$this->subscriptionHelper,
PayPalGateway::ID,
@ -159,7 +156,7 @@ class WcGatewayTest extends TestCase
->andReturn($wcOrder);
when('wc_get_checkout_url')
->justReturn('test');
->justReturn('test');
$woocommerce = Mockery::mock(\WooCommerce::class);
$cart = Mockery::mock(\WC_Cart::class);
@ -267,23 +264,6 @@ class WcGatewayTest extends TestCase
);
}
/**
* @dataProvider dataForTestNeedsSetup
*/
public function testNeedsSetup($currentState, $needSetup)
{
$this->isAdmin = true;
$this->onboardingState = Mockery::mock(State::class);
$this->onboardingState
->expects('current_state')
->andReturn($currentState);
$testee = $this->createGateway();
$this->assertSame($needSetup, $testee->needs_setup());
}
/**
* @dataProvider dataForFundingSource
*/
@ -315,14 +295,6 @@ class WcGatewayTest extends TestCase
];
}
public function dataForTestNeedsSetup(): array
{
return [
[State::STATE_START, true],
[State::STATE_ONBOARDED, false]
];
}
public function dataForFundingSource(): array
{
return [

View file

@ -37,7 +37,7 @@ class OrderProcessorTest extends TestCase
public function setUp(): void {
parent::setUp();
$this->environment = new Environment(new ReadOnlyContainer([], [], [], []));
$this->environment = new Environment( false );
}
public function testAuthorize() {