Merge pull request #3579 from woocommerce/PCP-5051-working-capital-things-to-do-next-wc-home-inbox

Add Working Capital notes (5001)
This commit is contained in:
Emili Castells 2025-08-08 14:59:05 +02:00 committed by GitHub
commit 522e5a05f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 386 additions and 2 deletions

View file

@ -55,6 +55,7 @@ class SettingsTabMapHelper {
'logging_enabled' => 'enable_logging',
'vault_enabled' => 'save_paypal_and_venmo',
'3d_secure_contingency' => 'three_d_secure',
'stay_updated' => 'stay_updated',
);
}

View file

@ -516,6 +516,17 @@ $services = array(
// TODO: This "merchant_capabilities" service is only used here. Could it be merged to make the code cleaner and less segmented?
$capabilities = $container->get( 'settings.service.merchant_capabilities' );
$settings = $container->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$is_working_capital_feature_flag_enabled = apply_filters(
// phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores -- feature flags use this convention
'woocommerce.feature-flags.woocommerce_paypal_payments.working_capital_enabled',
getenv( 'PCP_WORKING_CAPITAL_ENABLED' ) === '1'
);
$is_working_capital_eligible = $container->get( 'settings.data.general' )->get_merchant_country() === 'US' && $container->get( 'settings.data.settings' )->get_stay_updated();
/**
* 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.
@ -560,7 +571,8 @@ $services = array(
$container->get( 'googlepay.eligible' ) && $capabilities['acdc'] && ! $capabilities['google_pay'], // Add Google Pay to your account.
$container->get( 'applepay.eligible' ) && $capabilities['apple_pay'] && ! $gateways['apple_pay'], // Enable Apple Pay.
$container->get( 'googlepay.eligible' ) && $capabilities['google_pay'] && ! $gateways['google_pay'],
! $capabilities['installments'] && 'MX' === $container->get( 'settings.data.general' )->get_merchant_country() // Enable Installments for Mexico.
! $capabilities['installments'] && 'MX' === $container->get( 'settings.data.general' )->get_merchant_country(), // Enable Installments for Mexico.
$is_working_capital_feature_flag_enabled && $is_working_capital_eligible // Enable Working Capital.
);
},
'settings.rest.features' => static function ( ContainerInterface $container ) : FeaturesRestEndpoint {

View file

@ -234,6 +234,16 @@ class TodosDefinition {
),
'priority' => 13,
),
'apply_for_working_capital' => array(
'title' => __( 'Start your PayPal Working Capital application', 'woocommerce-paypal-payments' ),
'description' => __( 'Hey, you are eligible for credit. Click here to learn more and sign up', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['apply_for_working_capital'],
'action' => array(
'type' => 'external',
'url' => 'http://example.com/',
),
'priority' => 14,
),
);
$todo_items['check_settings_after_migration'] = array(

View file

@ -406,4 +406,22 @@ class SettingsModel extends AbstractDataModel {
$cards
);
}
/**
* Gets the Stay Updated setting.
*
* @return bool True if Stay Updated is enabled, false otherwise.
*/
public function get_stay_updated() : bool {
return $this->data['stay_updated'];
}
/**
* Sets the Stay Updated setting.
*
* @param bool $save Whether to save the Stay Updated.
*/
public function set_stay_updated( bool $save ) : void {
$this->data['stay_updated'] = $this->sanitizer->sanitize_bool( $save );
}
}

View file

@ -129,6 +129,8 @@ class TodosEligibilityService {
*/
private bool $is_enable_installments_eligible;
private bool $is_working_capital_eligible;
/**
* Constructor.
*
@ -148,6 +150,7 @@ class TodosEligibilityService {
* @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.
* @param bool $is_enable_installments_eligible Whether enabling Installments is eligible.
* @param bool $is_working_capital_eligible Whether applying for Working Capital is eligible.
*/
public function __construct(
bool $is_fastlane_eligible,
@ -165,7 +168,8 @@ class TodosEligibilityService {
bool $is_google_pay_eligible,
bool $is_enable_apple_pay_eligible,
bool $is_enable_google_pay_eligible,
bool $is_enable_installments_eligible
bool $is_enable_installments_eligible,
bool $is_working_capital_eligible
) {
$this->is_fastlane_eligible = $is_fastlane_eligible;
$this->is_pay_later_messaging_eligible = $is_pay_later_messaging_eligible;
@ -183,6 +187,7 @@ class TodosEligibilityService {
$this->is_enable_apple_pay_eligible = $is_enable_apple_pay_eligible;
$this->is_enable_google_pay_eligible = $is_enable_google_pay_eligible;
$this->is_enable_installments_eligible = $is_enable_installments_eligible;
$this->is_working_capital_eligible = $is_working_capital_eligible;
}
/**
@ -208,6 +213,7 @@ class TodosEligibilityService {
'enable_apple_pay' => fn() => $this->is_enable_apple_pay_eligible,
'enable_google_pay' => fn() => $this->is_enable_google_pay_eligible,
'enable_installments' => fn() => $this->is_enable_installments_eligible,
'apply_for_working_capital' => fn() => $this->is_working_capital_eligible,
);
}
}

View file

@ -11,6 +11,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway;
use Automattic\WooCommerce\Admin\Notes\Note;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ExperienceContext;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
@ -82,6 +83,10 @@ 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\WcGateway\Settings\WcInboxNotes\InboxNoteAction;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcInboxNotes\InboxNoteFactory;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcInboxNotes\InboxNoteInterface;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcInboxNotes\InboxNoteRegistrar;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Factory\SimpleRedirectTaskFactory;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Factory\SimpleRedirectTaskFactoryInterface;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Registrar\TaskRegistrar;
@ -2062,10 +2067,37 @@ return array(
return array();
},
'wcgateway.settings.wc-tasks.working-capital-config' => static function( ContainerInterface $container ): array {
$settings = $container->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$is_working_capital_feature_flag_enabled = apply_filters(
// phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores -- feature flags use this convention
'woocommerce.feature-flags.woocommerce_paypal_payments.working_capital_enabled',
getenv( 'PCP_WORKING_CAPITAL_ENABLED' ) === '1'
);
$is_working_capital_eligible = $container->get( 'api.shop.country' ) === 'US' && $settings->has( 'stay_updated' ) && $settings->get( 'stay_updated' );
if ( ! $is_working_capital_feature_flag_enabled || ! $is_working_capital_eligible ) {
return array();
}
return array(
array(
'id' => 'ppcp-working-capital-task',
'title' => __( 'Start your PayPal Working Capital application', 'woocommerce-paypal-payments' ),
'description' => __( 'Hey, you are eligible for credit. Click here to learn more and sign up', 'woocommerce-paypal-payments' ),
'redirect_url' => 'http://example.com/',
),
);
},
'wcgateway.settings.wc-tasks.task-config-services' => static function(): array {
return array(
'wcgateway.settings.wc-tasks.pay-later-task-config',
'wcgateway.settings.wc-tasks.connect-task-config',
'wcgateway.settings.wc-tasks.working-capital-config',
);
},
@ -2116,6 +2148,41 @@ return array(
return $simple_redirect_tasks;
},
'wcgateway.settings.inbox-note-factory' => static function(): InboxNoteFactory {
return new InboxNoteFactory();
},
'wcgateway.settings.inbox-note-registrar' => static function( ContainerInterface $container ): InboxNoteRegistrar {
return new InboxNoteRegistrar( $container->get( 'wcgateway.settings.inbox-notes' ), $container->get( 'ppcp.plugin' ) );
},
/**
* Retrieves the list of inbox note instances.
*
* @returns InboxNoteInterface[]
*/
'wcgateway.settings.inbox-notes' => static function( ContainerInterface $container ): array {
$inbox_note_factory = $container->get( 'wcgateway.settings.inbox-note-factory' );
assert( $inbox_note_factory instanceof InboxNoteFactory );
return array(
$inbox_note_factory->create_note(
__( 'PayPal Working Capital', 'woocommerce-paypal-payments' ),
__( 'Fast funds with payments that flex with your PayPal sales The PayPal Working Capital business loan is primarily based on your PayPal account history. Apply for $1,000-$200,000 (and up to $300,000 for repeat borrowers) with no credit check.† If approved, loans are funded in minutes.', 'woocommerce-paypal-payments' ),
Note::E_WC_ADMIN_NOTE_INFORMATIONAL,
'ppcp-working-capital-inbox-note',
Note::E_WC_ADMIN_NOTE_UNACTIONED,
new InboxNoteAction(
'apply_now',
__( 'Apply now', 'woocommerce-paypal-payments' ),
'http://example.com/',
Note::E_WC_ADMIN_NOTE_UNACTIONED,
true
)
),
);
},
'wcgateway.void-button.assets' => function( ContainerInterface $container ) : VoidButtonAssets {
return new VoidButtonAssets(
$container->get( 'wcgateway.url' ),

View file

@ -0,0 +1,61 @@
<?php
/**
* @package WooCommerce\PayPalCommerce\WcGateway\Settings
*/
declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Settings\WcInboxNotes;
/**
* A note that can be displayed in the WooCommerce inbox section.
*/
class InboxNote implements InboxNoteInterface {
protected string $title;
protected string $content;
protected string $type;
protected string $name;
protected string $status;
protected InboxNoteActionInterface $action;
public function __construct(
string $title,
string $content,
string $type,
string $name,
string $status,
InboxNoteActionInterface $action
) {
$this->title = $title;
$this->content = $content;
$this->type = $type;
$this->name = $name;
$this->status = $status;
$this->action = $action;
}
public function title(): string {
return $this->title;
}
public function content(): string {
return $this->content;
}
public function type(): string {
return $this->type;
}
public function name(): string {
return $this->name;
}
public function status(): string {
return $this->status;
}
public function action(): InboxNoteActionInterface {
return $this->action;
}
}

View file

@ -0,0 +1,49 @@
<?php
/**
* @package WooCommerce\PayPalCommerce\WcGateway\Settings
*/
declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Settings\WcInboxNotes;
/**
* An action that can be performed on a WooCommerce inbox note.
*/
class InboxNoteAction implements InboxNoteActionInterface {
protected string $name;
protected string $label;
protected string $url;
protected string $status;
protected bool $is_primary;
public function __construct( string $name, string $label, string $url, string $status, bool $is_primary ) {
$this->name = $name;
$this->label = $label;
$this->url = $url;
$this->status = $status;
$this->is_primary = $is_primary;
}
public function name(): string {
return $this->name;
}
public function label(): string {
return $this->label;
}
public function url(): string {
return $this->url;
}
public function status(): string {
return $this->status;
}
public function is_primary(): bool {
return $this->is_primary;
}
}

View file

@ -0,0 +1,19 @@
<?php
/**
* Represents an action that can be performed on a WooCommerce inbox note.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Settings
*/
declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Settings\WcInboxNotes;
interface InboxNoteActionInterface {
public function name(): string;
public function label(): string;
public function url(): string;
public function status(): string;
public function is_primary(): bool;
}

View file

@ -0,0 +1,25 @@
<?php
/**
* @package WooCommerce\PayPalCommerce\WcGateway\Settings
*/
declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Settings\WcInboxNotes;
/**
* A factory for creating inbox notes.
*/
class InboxNoteFactory {
public function create_note(
string $title,
string $content,
string $type,
string $name,
string $status,
InboxNoteActionInterface $action
): InboxNoteInterface {
return new InboxNote( $title, $content, $type, $name, $status, $action );
}
}

View file

@ -0,0 +1,20 @@
<?php
/**
* Represents a note that can be displayed in the WooCommerce inbox section.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Settings
*/
declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Settings\WcInboxNotes;
interface InboxNoteInterface {
public function title(): string;
public function content(): string;
public function type(): string;
public function name(): string;
public function status(): string;
public function action(): InboxNoteActionInterface;
}

View file

@ -0,0 +1,59 @@
<?php
/**
* @package WooCommerce\PayPalCommerce\WcGateway\Settings
*/
declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Settings\WcInboxNotes;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\Notes;
use WpOop\WordPress\Plugin\PluginInterface;
/**
* Registers inbox notes in the WooCommerce Admin inbox section.
*/
class InboxNoteRegistrar {
/**
* @var InboxNoteInterface[]
*/
protected array $inbox_notes;
protected PluginInterface $plugin;
public function __construct( array $inbox_notes, PluginInterface $plugin ) {
$this->inbox_notes = $inbox_notes;
$this->plugin = $plugin;
}
public function register(): void {
foreach ( $this->inbox_notes as $inbox_note ) {
$inbox_note_name = $inbox_note->name();
if ( Notes::get_note_by_name( $inbox_note_name ) ) {
continue;
}
$note = new Note();
$note->set_title( $inbox_note->title() );
$note->set_content( $inbox_note->content() );
$note->set_type( $inbox_note->type() );
$note->set_name( $inbox_note_name );
$note->set_source( $this->plugin->getBaseName() );
$note->set_status( $inbox_note->status() );
$inbox_note_action = $inbox_note->action();
$note->add_action(
$inbox_note_action->name(),
$inbox_note_action->label(),
$inbox_note_action->url(),
$inbox_note_action->status(),
$inbox_note_action->is_primary()
);
$note->save();
}
}
}

View file

@ -62,6 +62,7 @@ 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\WcGateway\Settings\WcInboxNotes\InboxNoteRegistrar;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Registrar\TaskRegistrarInterface;
/**
@ -95,6 +96,7 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul
$this->register_columns( $c );
$this->register_checkout_paypal_address_preset( $c );
$this->register_wc_tasks( $c );
$this->register_woo_inbox_notes( $c );
$this->register_void_button( $c );
if ( ! $c->get( 'wcgateway.settings.admin-settings-enabled' ) ) {
@ -957,6 +959,41 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul
);
}
/**
* Registers inbox notes in the WooCommerce Admin inbox section.
*/
protected function register_woo_inbox_notes( ContainerInterface $container ): void {
add_action(
'admin_init',
static function () use ( $container ): void {
$settings = $container->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$is_working_capital_feature_flag_enabled = apply_filters(
// phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores -- feature flags use this convention
'woocommerce.feature-flags.woocommerce_paypal_payments.working_capital_enabled',
getenv( 'PCP_WORKING_CAPITAL_ENABLED' ) === '1'
);
$is_working_capital_eligible = $container->get( 'api.shop.country' ) === 'US' && $settings->has( 'stay_updated' ) && $settings->get( 'stay_updated' );
if ( ! $is_working_capital_feature_flag_enabled || ! $is_working_capital_eligible ) {
return;
}
$logger = $container->get( 'woocommerce.logger.woocommerce' );
assert( $logger instanceof LoggerInterface );
try {
$inbox_note_registrar = $container->get( 'wcgateway.settings.inbox-note-registrar' );
assert( $inbox_note_registrar instanceof InboxNoteRegistrar );
$inbox_note_registrar->register();
} catch ( Exception $exception ) {
$logger->error( 'Failed to add note to the WooCommerce inbox section. ' . $exception->getMessage() );
}
},
);
}
/**
* Registers the assets and ajax endpoint for the void button.
*