ration only enabled local APMs when * allow_local_apm_gateways was truthy. When it was false, APMs were shown * inside the PayPal button, not as separate gateways. The new UI always * treats APMs as separate gateways, so skipping them left them invisible. * * @param false|string $previous_version The previously installed plugin version, * or false on first installation. */ static function ($previous_version) use ($container): void { if ($previous_version && version_compare($previous_version, '3.4.1', 'gt')) { return; } if (get_option(MigrationManager::OPTION_NAME_MIGRATION_IS_DONE) !== '1') { return; } $legacy_settings = (array) get_option('woocommerce-ppcp-settings', array()); // Only fix merchants who had allow_local_apm_gateways falsy. // Truthy merchants were migrated correctly. if (!empty($legacy_settings['allow_local_apm_gateways'])) { return; } $payment_settings = $container->get('settings.data.payment'); assert($payment_settings instanceof PaymentSettings); $local_apms = $container->get('ppcp-local-apms.payment-methods'); $disable_funding = (array) ($legacy_settings['disable_funding'] ?? array()); $changed = \false; foreach ($local_apms as $apm) { if (!in_array($apm['id'], $disable_funding, \true) && !$payment_settings->is_method_enabled($apm['id'])) { $payment_settings->toggle_method_state($apm['id'], \true); $changed = \true; } } if ($changed) { $payment_settings->save(); } } ); add_action( 'woocommerce_paypal_payments_gateway_migrate', /** * Migrates payment level processing setting during plugin update. * * For merchants updating from version 3.3.2 or older, disables Level 2/3 * processing if they previously opted out of automatic updates (stay_updated=false). * Merchants who opted into updates inherit the default enabled state. * * @param false|string $previous_version The previously installed plugin version, * or false on first installation. */ static function ($previous_version) use ($container): void { // Only run this migration logic when updating from version 3.3.2 or older. if ($previous_version && version_compare($previous_version, '3.3.2', 'gt')) { return; } try { $settings_model = $container->get('settings.data.settings'); assert($settings_model instanceof SettingsModel); if (!$settings_model->get_stay_updated()) { $settings_model->set_payment_level_processing(\false); $settings_model->save(); } } catch (Throwable $error) { // Something failed - ignore the error and assume there is no migration data. return; } } ); /** * Disable ACDC gateway for merchants not eligible for ACDC * after onboarding is completed. */ add_action('woocommerce_paypal_payments_toggle_payment_gateways', function (PaymentSettings $payment_methods, ConfigurationFlagsDTO $flags) use ($container) { $dcc_configuration = $container->get('wcgateway.configuration.card-configuration'); assert($dcc_configuration instanceof CardPaymentsConfiguration); if ($flags->is_business_seller && $flags->use_card_payments && !$dcc_configuration->use_acdc()) { $payment_methods->toggle_method_state(CreditCardGateway::ID, \false); } }, 10, 2); /** * Disable Apple Pay/Google Pay gateways for merchants not eligible * after onboarding is completed. */ add_action('woocommerce_paypal_payments_toggle_payment_gateways', function (PaymentSettings $payment_methods, ConfigurationFlagsDTO $flags) use ($container) { if (!$flags->is_business_seller || !$flags->use_digital_wallets) { return; } $applepay_product_status = $container->get('applepay.apple-product-status'); $applepay_eligibility = $container->get('applepay.eligibility.check'); $apple_pay_available = $applepay_product_status->is_active() && $applepay_eligibility(); if (!$apple_pay_available) { $payment_methods->toggle_method_state(ApplePayGateway::ID, \false); } $googlepay_product_status = $container->get('googlepay.helpers.apm-product-status'); $googlepay_eligibility = $container->get('googlepay.eligibility.check'); $google_pay_available = $googlepay_product_status->is_active() && $googlepay_eligibility(); if (!$google_pay_available) { $payment_methods->toggle_method_state(GooglePayGateway::ID, \false); } }, 10, 2); return \true; } /** * Pre-populates GeneralSettings with legacy credentials before migration * DI services are resolved. * * DI services like api.key, api.secret, api.merchant_id are resolved from * SettingsProvider → GeneralSettings → woocommerce-ppcp-data-common. * This option does not exist before migration, so these DI services resolve * to empty strings. Pre-populating ensures PartnersEndpoint has working * credentials during migration so seller_status() API call succeeds. * * @param ContainerInterface $container The DI container. */ private static function pre_populate_credentials(ContainerInterface $container): void { $general = $container->get('settings.data.general'); assert($general instanceof GeneralSettings); if ($general->is_merchant_connected()) { return; } $legacy = (array) get_option('woocommerce-ppcp-settings', array()); if (empty($legacy['client_id']) || empty($legacy['merchant_id'])) { return; } $general->set_merchant_data(new MerchantConnectionDTO(!empty($legacy['sandbox_on']), $legacy['client_id'], $legacy['client_secret'] ?? '', $legacy['merchant_id'], $legacy['merchant_email'] ?? '', '', SellerTypeEnum::UNKNOWN)); } /** * Checks the branded-only state and applies relevant site-wide feature limitations, if needed. * * @param ContainerInterface $container The DI container provider. * * @return void */ protected function apply_branded_only_limitations(ContainerInterface $container): void { $settings = $container->get('settings.data.general'); assert($settings instanceof GeneralSettings); if (!$settings->own_brand_only()) { return; } /** * Prevent white-label payment methods from being enabled during onboarding. * * During the onboarding flow, toggle_payment_gateways() enables ACDC, Apple Pay, * and Google Pay for business sellers. In branded-only mode, these white-label * methods should never be enabled. * * This hook runs during the 'woocommerce_paypal_payments_toggle_payment_gateways_apms' * action, immediately disabling these methods before payment settings are saved. * This prevents them from being enabled even temporarily during onboarding. * * Without this hook, white-label methods would be enabled during onboarding and * then disabled afterward, creating an inconsistent state during the upgrade process. */ add_action('woocommerce_paypal_payments_toggle_payment_gateways_apms', static function (PaymentSettings $payment_settings): void { $payment_settings->toggle_method_state(CreditCardGateway::ID, \false); $payment_settings->toggle_method_state(ApplePayGateway::ID, \false); $payment_settings->toggle_method_state(GooglePayGateway::ID, \false); }); $payment_settings = $container->get('settings.data.payment'); assert($payment_settings instanceof PaymentSettings); $gateway_name = CardButtonGateway::ID; $gateway_settings = get_option("woocommerce_{$gateway_name}_settings", array()); $gateway_enabled = $gateway_settings['enabled'] ?? \false; if ($payment_settings->is_method_enabled(CreditCardGateway::ID)) { $payment_settings->toggle_method_state(CreditCardGateway::ID, \false); if ($gateway_enabled === 'yes') { $payment_settings->toggle_method_state(CardButtonGateway::ID, \true); } $payment_settings->save(); } if ($payment_settings->is_method_enabled(ApplePayGateway::ID)) { $payment_settings->toggle_method_state(ApplePayGateway::ID, \false); $payment_settings->save(); } if ($payment_settings->is_method_enabled(GooglePayGateway::ID)) { $payment_settings->toggle_method_state(GooglePayGateway::ID, \false); $payment_settings->save(); } /** * In branded-only mode, we completely disable all white label features. */ add_filter('woocommerce_paypal_payments_is_eligible_for_applepay', '__return_false'); add_filter('woocommerce_paypal_payments_is_eligible_for_googlepay', '__return_false'); add_filter('woocommerce_paypal_payments_is_eligible_for_axo', '__return_false'); add_filter('woocommerce_paypal_payments_is_eligible_for_save_payment_methods', '__return_false'); add_filter('woocommerce_paypal_payments_is_eligible_for_card_fields', '__return_false'); add_filter('woocommerce_paypal_payments_is_acdc_active', '__return_false'); } /** * Initializes the branded-only flags if they are not set. * * This method can be called multiple times: * The flags are only initialized once but does not change afterward. * * Also, this check has no impact on performance for two reasons: * 1. The GeneralSettings class is already initialized and will short-circuit * the check if the settings are already initialized. * 2. The settings UI is a React app, this method only runs when the React app * is injected to the DOM, and not while the UI is used. * * @param ContainerInterface $container The DI container provider. * * @return void */ protected function initialize_branded_only(ContainerInterface $container): void { $path_repository = $container->get('settings.service.branded-experience.path-repository'); assert($path_repository instanceof PathRepository); $partner_attribution = $container->get('api.helper.partner-attribution'); assert($partner_attribution instanceof PartnerAttribution); $general_settings = $container->get('settings.data.general'); assert($general_settings instanceof GeneralSettings); $path_repository->persist(); $partner_attribution->initialize_bn_code($general_settings->get_installation_path()); } /** * Outputs the settings page header (title and back-link). * * @return void */ protected function render_header(): void { echo '

' . esc_html__('PayPal', 'woocommerce-paypal-payments'); wc_back_link(__('Return to payments', 'woocommerce-paypal-payments'), admin_url('admin.php?page=wc-settings&tab=checkout')); echo '

'; } /** * Renders the container for the React app. * * @return void */ protected function render_content(): void { echo '
'; } /** * Checks if the payment gateway with the given name is enabled. * * @param string $gateway_name The gateway name. * * @return bool True if the payment gateway with the given name is enabled, otherwise false. */ protected function is_gateway_enabled(string $gateway_name): bool { $gateway_settings = get_option("woocommerce_{$gateway_name}_settings", array()); $gateway_enabled = $gateway_settings['enabled'] ?? \false; return $gateway_enabled === 'yes'; } }