From 6f6d4f71b824f69e65f10929262de31d2c17e583 Mon Sep 17 00:00:00 2001 From: Francesco Date: Fri, 12 Apr 2024 22:20:50 +0200 Subject: [PATCH] feat: BNPL April announcement (#8605) Co-authored-by: Samir Merchant Co-authored-by: Brett Shumaker --- assets/images/bnpl_announcement_afterpay.png | Bin 0 -> 10256 bytes assets/images/bnpl_announcement_clearpay.png | Bin 0 -> 10259 bytes changelog/feat-bnpl-announcement-apr | 4 + client/bnpl-announcement/index.js | 99 +++++++++ client/bnpl-announcement/style.scss | 68 ++++++ client/connect-account-page/index.tsx | 12 +- .../class-wc-payments-bnpl-announcement.php | 209 ++++++++++++++++++ includes/class-wc-payments.php | 4 + ...st-class-wc-payments-bnpl-announcement.php | 136 ++++++++++++ tests/unit/bootstrap.php | 1 + webpack/shared.js | 1 + woocommerce-payments.php | 2 + 12 files changed, 530 insertions(+), 6 deletions(-) create mode 100644 assets/images/bnpl_announcement_afterpay.png create mode 100644 assets/images/bnpl_announcement_clearpay.png create mode 100644 changelog/feat-bnpl-announcement-apr create mode 100644 client/bnpl-announcement/index.js create mode 100644 client/bnpl-announcement/style.scss create mode 100644 includes/admin/class-wc-payments-bnpl-announcement.php create mode 100644 tests/unit/admin/test-class-wc-payments-bnpl-announcement.php diff --git a/assets/images/bnpl_announcement_afterpay.png b/assets/images/bnpl_announcement_afterpay.png new file mode 100644 index 0000000000000000000000000000000000000000..ec2fd1666d022299092591261783c1f79322c207 GIT binary patch literal 10256 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 nRY}pQ4Kf>NYPFA7qH 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 literal 0 HcmV?d00001 diff --git a/changelog/feat-bnpl-announcement-apr b/changelog/feat-bnpl-announcement-apr new file mode 100644 index 00000000000..7d339efbfeb --- /dev/null +++ b/changelog/feat-bnpl-announcement-apr @@ -0,0 +1,4 @@ +Significance: patch +Type: add + +feat: BNPL April announcement. diff --git a/client/bnpl-announcement/index.js b/client/bnpl-announcement/index.js new file mode 100644 index 00000000000..7a64c93d005 --- /dev/null +++ b/client/bnpl-announcement/index.js @@ -0,0 +1,99 @@ +/** + * External dependencies + */ +import React, { useEffect, useState } from 'react'; +import ReactDOM from 'react-dom'; +import { __ } from '@wordpress/i18n'; +import { Button, ExternalLink } from '@wordpress/components'; +import { recordEvent } from 'tracks'; + +/** + * Internal dependencies + */ +import './style.scss'; +import ConfirmationModal from 'wcpay/components/confirmation-modal'; +import AfterpayBanner from 'assets/images/bnpl_announcement_afterpay.png?asset'; +import ClearpayBanner from 'assets/images/bnpl_announcement_clearpay.png?asset'; + +const BannerIcon = + window.wcpayBnplAnnouncement?.accountCountry === 'GB' + ? ClearpayBanner + : AfterpayBanner; + +const Dialog = () => { + useEffect( () => { + recordEvent( 'wcpay_bnpl_april15_feature_announcement_view' ); + }, [] ); + + const [ isHidden, setIsHidden ] = useState( false ); + + if ( isHidden ) return null; + + return ( + setIsHidden( true ) } + actions={ + <> + { + recordEvent( + 'wcpay_bnpl_april15_feature_announcement_learn_click' + ); + setIsHidden( true ); + } } + href="https://woo.com/document/woopayments/payment-methods/buy-now-pay-later/" + > + { __( 'Learn more', 'woocommerce-payments' ) } + + + + } + > +
+ { +
+

+ { __( 'Buy now, pay later is here', 'woocommerce-payments' ) } +

+

+ { __( + // eslint-disable-next-line max-len + 'Boost conversions and give your shoppers additional buying power, with buy now, pay later — now available in your WooPayments dashboard.*', + 'woocommerce-payments' + ) } +

+

+ { __( + '*Subject to country availability', + 'woocommerce-payments' + ) } +

+
+ ); +}; + +const container = document.getElementById( 'wpwrap' ); +if ( container ) { + const dialogWrapper = document.createElement( 'div' ); + container.appendChild( dialogWrapper ); + + ReactDOM.createRoot( dialogWrapper ).render( ); +} diff --git a/client/bnpl-announcement/style.scss b/client/bnpl-announcement/style.scss new file mode 100644 index 00000000000..4a6ca64e4cf --- /dev/null +++ b/client/bnpl-announcement/style.scss @@ -0,0 +1,68 @@ +.wcpay-bnpl-announcement { + &.wcpay-confirmation-modal.wcpay-confirmation-modal { + margin-top: auto; + height: auto; + + @media screen and ( min-width: 600px ) { + max-width: 400px; + } + + .components-modal__header { + padding: 0; + + .components-button.has-icon { + position: absolute; + top: 18px; + left: auto; + right: 18px; + } + } + + .components-modal__content { + padding: 0 20px 100px; + margin-top: 60px; + + @media screen and ( min-width: 600px ) { + padding: 0 35px 24px; + } + } + + .wcpay-confirmation-modal__separator { + opacity: 0; + } + } + + &__payment-icons { + display: flex; + justify-content: center; + align-items: flex-start; + flex-wrap: wrap; + gap: 17px; + margin-bottom: 20px; + + .payment-method__icon { + margin-right: 0; + max-height: 35px; + outline: none; + + &[alt='Affirm'] { + max-height: 30px; + } + } + } + + h1 { + text-align: left; + width: 100%; + } + + p { + text-align: left; + } + + .components-external-link { + padding: 6px 12px; + align-items: center; + display: flex; + } +} diff --git a/client/connect-account-page/index.tsx b/client/connect-account-page/index.tsx index 1696db52b2d..7cb4fdf9a15 100644 --- a/client/connect-account-page/index.tsx +++ b/client/connect-account-page/index.tsx @@ -31,6 +31,12 @@ import strings from './strings'; import './style.scss'; import InlineNotice from 'components/inline-notice'; +const SandboxModeNotice = () => ( + + { strings.sandboxModeNotice } + +); + const ConnectAccountPage: React.FC = () => { const firstName = wcSettings.admin?.currentUserData?.first_name; const incentive = wcpaySettings.connectIncentive; @@ -50,12 +56,6 @@ const ConnectAccountPage: React.FC = () => { const isCountrySupported = !! availableCountries[ country ]; - const SandboxModeNotice = () => ( - - { strings.sandboxModeNotice } - - ); - useEffect( () => { recordEvent( 'page_view', { path: 'payments_connect_v2', diff --git a/includes/admin/class-wc-payments-bnpl-announcement.php b/includes/admin/class-wc-payments-bnpl-announcement.php new file mode 100644 index 00000000000..8fca5c3d561 --- /dev/null +++ b/includes/admin/class-wc-payments-bnpl-announcement.php @@ -0,0 +1,209 @@ +gateway = $gateway; + $this->account = $account; + $this->current_time = $current_time; + } + + /** + * Initializes this class's WP hooks. + * + * @return void + */ + public function init_hooks() { + add_action( 'current_screen', [ $this, 'maybe_enqueue_scripts' ] ); + } + + /** + * Needs to run after `current_screen`, to determine which page we're currently on. + * + * @return void + */ + public function maybe_enqueue_scripts() { + if ( ! is_admin() ) { + return; + } + + // Only shown once to each Administrator and Shop Manager users. + if ( ! current_user_can( 'manage_woocommerce' ) ) { + return; + } + + // Time boxed - Campaign expires after 90 days. + if ( $this->current_time > strtotime( '2024-07-15 00:00:00' ) ) { + return; + } + + if ( get_user_meta( get_current_user_id(), '_wcpay_bnpl_april15_viewed', true ) === '1' ) { + return; + } + + // Only displayed to BNPL eligible countries - AU, NZ, US, AT, BE, CA, CZ, DK, FI, FR, DE, GR, IE, IT, NO, PL, PT, ES, SE, CH, NL, UK, US. + if ( ! in_array( + $this->account->get_account_country(), + [ + \WCPay\Constants\Country_Code::AUSTRALIA, + \WCPay\Constants\Country_Code::AUSTRIA, + \WCPay\Constants\Country_Code::NEW_ZEALAND, + \WCPay\Constants\Country_Code::UNITED_STATES, + \WCPay\Constants\Country_Code::BELGIUM, + \WCPay\Constants\Country_Code::CANADA, + \WCPay\Constants\Country_Code::CZECHIA, + \WCPay\Constants\Country_Code::DENMARK, + \WCPay\Constants\Country_Code::FINLAND, + \WCPay\Constants\Country_Code::FRANCE, + \WCPay\Constants\Country_Code::GERMANY, + \WCPay\Constants\Country_Code::GREECE, + \WCPay\Constants\Country_Code::IRELAND, + \WCPay\Constants\Country_Code::ITALY, + \WCPay\Constants\Country_Code::NORWAY, + \WCPay\Constants\Country_Code::POLAND, + \WCPay\Constants\Country_Code::PORTUGAL, + \WCPay\Constants\Country_Code::SPAIN, + \WCPay\Constants\Country_Code::SWEDEN, + \WCPay\Constants\Country_Code::SWITZERLAND, + \WCPay\Constants\Country_Code::NETHERLANDS, + \WCPay\Constants\Country_Code::UNITED_KINGDOM, + ], + true + ) ) { + return; + } + + // just to be safe for older versions. + if ( ! class_exists( '\Automattic\WooCommerce\Admin\PageController' ) ) { + return; + } + + // Target page to be displayed on - Any WooPayments page except disputes. + $current_page = \Automattic\WooCommerce\Admin\PageController::get_instance()->get_current_page(); + if ( ! WC_Payments_Utils::is_payments_settings_page() && ( empty( $current_page ) || ! in_array( + $current_page['id'], + [ + 'wc-payments', + 'wc-payments-deposits', + 'wc-payments-transactions', + 'wc-payments-deposit-details', + 'wc-payments-transaction-details', + 'wc-payments-multi-currency-setup', + ], + true + ) ) ) { + return; + } + + // at least 3 purchases (on any payment method). + $woopayments_successful_orders_count = $this->get_woopayments_successful_orders_count(); + if ( $woopayments_successful_orders_count < 3 ) { + return; + } + + // don't display the promo if the merchant already has BNPL methods enabled. + $enabled_bnpl_payment_methods = array_intersect( + \WCPay\Constants\Payment_Method::BNPL_PAYMENT_METHODS, + $this->gateway->get_upe_enabled_payment_method_ids() + ); + if ( ! empty( $enabled_bnpl_payment_methods ) ) { + return; + } + + add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); + + add_user_meta( get_current_user_id(), '_wcpay_bnpl_april15_viewed', '1' ); + } + + /** + * Enqueues the script & styles for the BNPL announcement dialog. + * + * @return void + */ + public function enqueue_scripts() { + WC_Payments::register_script_with_dependencies( 'WCPAY_BNPL_ANNOUNCEMENT', 'dist/bnpl-announcement' ); + wp_set_script_translations( 'WCPAY_BNPL_ANNOUNCEMENT', 'woocommerce-payments' ); + WC_Payments_Utils::register_style( + 'WCPAY_BNPL_ANNOUNCEMENT', + plugins_url( 'dist/bnpl-announcement.css', WCPAY_PLUGIN_FILE ), + [ 'wc-components' ], + WC_Payments::get_file_version( 'dist/bnpl-announcement.css' ), + 'all' + ); + // conditionally show afterpay/clearpay based on account country. + $wcpay_bnpl_announcement = rawurlencode( wp_json_encode( [ 'accountCountry' => $this->account->get_account_country() ] ) ); + wp_add_inline_script( + 'WCPAY_BNPL_ANNOUNCEMENT', + " + var wcpayBnplAnnouncement = wcpayBnplAnnouncement || JSON.parse( decodeURIComponent( '" . esc_js( $wcpay_bnpl_announcement ) . "' ) ); + ", + 'before' + ); + + wp_enqueue_script( 'WCPAY_BNPL_ANNOUNCEMENT' ); + wp_enqueue_style( 'WCPAY_BNPL_ANNOUNCEMENT' ); + } + + /** + * Returns the number of successful orders paid with any WooPayments payment method. + * + * @return int + */ + private function get_woopayments_successful_orders_count() { + // using a transient to store the value of a previous calculation, since it can be expensive on each page load. + $wcpay_successful_orders_count = get_transient( 'wcpay_bnpl_april15_successful_purchases_count' ); + if ( false !== $wcpay_successful_orders_count ) { + return intval( $wcpay_successful_orders_count ); + } + + $orders = wc_get_orders( + [ + // we don't need them all, just more than 3. + 'limit' => 5, + 'status' => [ 'completed', 'processing' ], + ] + ); + $orders_count = count( $orders ); + + // storing the transient for a couple of days is probably sufficient, in case the value is too low (less than 3). + set_transient( 'wcpay_bnpl_april15_successful_purchases_count', $orders_count, 2 * DAY_IN_SECONDS ); + + return $orders_count; + } +} diff --git a/includes/class-wc-payments.php b/includes/class-wc-payments.php index 3fc25562912..22c21a3c4f4 100644 --- a/includes/class-wc-payments.php +++ b/includes/class-wc-payments.php @@ -635,6 +635,10 @@ public static function init() { $admin_settings = new WC_Payments_Admin_Settings( self::get_gateway() ); $admin_settings->init_hooks(); + include_once WCPAY_ABSPATH . 'includes/admin/class-wc-payments-bnpl-announcement.php'; + $bnpl_announcement = new WC_Payments_Bnpl_Announcement( self::get_gateway(), self::get_account_service(), time() ); + $bnpl_announcement->init_hooks(); + // Use tracks loader only in admin screens because it relies on WC_Tracks loaded by WC_Admin. include_once WCPAY_ABSPATH . 'includes/admin/tracks/tracks-loader.php'; diff --git a/tests/unit/admin/test-class-wc-payments-bnpl-announcement.php b/tests/unit/admin/test-class-wc-payments-bnpl-announcement.php new file mode 100644 index 00000000000..4bbcbfa5a96 --- /dev/null +++ b/tests/unit/admin/test-class-wc-payments-bnpl-announcement.php @@ -0,0 +1,136 @@ +gateway_mock = $this->getMockBuilder( WC_Payment_Gateway_WCPay::class ) + ->disableOriginalConstructor() + ->setMethods( [ 'get_upe_enabled_payment_method_ids' ] ) + ->getMock(); + + $this->account_service_mock = $this->getMockBuilder( WC_Payments_Account::class )->disableOriginalConstructor()->setMethods( [ 'get_account_country' ] )->getMock(); + + $this->bnpl_announcement = new WC_Payments_Bnpl_Announcement( $this->gateway_mock, $this->account_service_mock, strtotime( '2024-06-06' ) ); + } + + protected function tearDown(): void { + parent::tearDown(); + + wp_deregister_script( 'WCPAY_BNPL_ANNOUNCEMENT' ); + } + + public function test_it_enqueues_scripts_for_eligible_users() { + global $current_section, $current_tab, $wp_actions; + + // mocking the settings page URL. + $current_section = 'woocommerce_payments'; + $current_tab = 'checkout'; + $this->set_is_admin( true ); + + // mocking the "did action" for 'current_screen'. + $wp_actions['current_screen'] = true; // phpcs:ignore: WordPress.WP.GlobalVariablesOverride.Prohibited + + wp_set_current_user( self::factory()->user->create( [ 'role' => 'administrator' ] ) ); + WC_Payments::mode()->live(); + $this->set_current_user_can( true ); + $this->account_service_mock->method( 'get_account_country' )->willReturn( 'US' ); + $this->gateway_mock->method( 'get_upe_enabled_payment_method_ids' )->willReturn( [ 'card' ] ); + + $this->bnpl_announcement->maybe_enqueue_scripts(); + + do_action( 'admin_enqueue_scripts' ); + + // ensuring the dialog has been marked as "viewed". + $this->assertEquals( '1', get_user_meta( get_current_user_id(), '_wcpay_bnpl_april15_viewed', true ) ); + $this->assertTrue( wp_script_is( 'WCPAY_BNPL_ANNOUNCEMENT', 'registered' ) ); + } + + public function test_it_does_not_enqueues_scripts_for_users_that_have_already_seen_the_message() { + global $current_section, $current_tab, $wp_actions; + + // mocking the settings page URL. + $current_section = 'woocommerce_payments'; + $current_tab = 'checkout'; + $this->set_is_admin( true ); + + // mocking the "did action" for 'current_screen'. + $wp_actions['current_screen'] = true; // phpcs:ignore: WordPress.WP.GlobalVariablesOverride.Prohibited + + wp_set_current_user( self::factory()->user->create( [ 'role' => 'administrator' ] ) ); + WC_Payments::mode()->live(); + $this->set_current_user_can( true ); + $this->account_service_mock->method( 'get_account_country' )->willReturn( 'US' ); + $this->gateway_mock->method( 'get_upe_enabled_payment_method_ids' )->willReturn( [ 'card' ] ); + + // marking it as "already viewed" for the current user. + add_user_meta( get_current_user_id(), '_wcpay_bnpl_april15_viewed', '1' ); + + $this->bnpl_announcement->maybe_enqueue_scripts(); + + do_action( 'admin_enqueue_scripts' ); + + $this->assertFalse( wp_script_is( 'WCPAY_BNPL_ANNOUNCEMENT', 'registered' ) ); + } + + private function set_current_user_can( bool $can ) { + global $current_user_can; + + // phpcs:ignore: WordPress.WP.GlobalVariablesOverride.Prohibited + $current_user_can = $this->getMockBuilder( \stdClass::class ) + ->addMethods( [ 'current_user_can' ] ) + ->getMock(); + + $current_user_can->method( 'current_user_can' )->willReturn( $can ); + } + + /** + * @param bool $is_admin + */ + private function set_is_admin( bool $is_admin ) { + global $current_screen; + + if ( ! $is_admin ) { + $current_screen = null; // phpcs:ignore: WordPress.WP.GlobalVariablesOverride.Prohibited + + return; + } + + // phpcs:ignore: WordPress.WP.GlobalVariablesOverride.Prohibited + $current_screen = $this->getMockBuilder( \stdClass::class ) + ->setMethods( [ 'in_admin' ] ) + ->getMock(); + + $current_screen->method( 'in_admin' )->willReturn( $is_admin ); + $current_screen->id = 'wc-payments-deposits'; + $current_screen->action = null; + } +} diff --git a/tests/unit/bootstrap.php b/tests/unit/bootstrap.php index e5afd62b0e5..a73e0120e4a 100755 --- a/tests/unit/bootstrap.php +++ b/tests/unit/bootstrap.php @@ -96,6 +96,7 @@ function () { require_once $_plugin_dir . 'includes/class-woopay-tracker.php'; require_once $_plugin_dir . 'includes/admin/class-wc-rest-payments-customer-controller.php'; require_once $_plugin_dir . 'includes/admin/class-wc-rest-payments-refunds-controller.php'; + require_once $_plugin_dir . 'includes/admin/class-wc-payments-bnpl-announcement.php'; // Load currency helper class early to ensure its implementation is used over the one resolved during further test initialization. require_once __DIR__ . '/helpers/class-wc-helper-site-currency.php'; diff --git a/webpack/shared.js b/webpack/shared.js index 63bff86aa23..63a71ca649a 100644 --- a/webpack/shared.js +++ b/webpack/shared.js @@ -10,6 +10,7 @@ module.exports = { entry: mapValues( { index: './client/index.js', + 'bnpl-announcement': './client/bnpl-announcement/index.js', settings: './client/settings/index.js', 'blocks-checkout': './client/checkout/blocks/index.js', woopay: './client/checkout/woopay/index.js', diff --git a/woocommerce-payments.php b/woocommerce-payments.php index eaa03a07f8a..13540148710 100644 --- a/woocommerce-payments.php +++ b/woocommerce-payments.php @@ -55,6 +55,8 @@ function wcpay_activated() { function wcpay_deactivated() { require_once WCPAY_ABSPATH . '/includes/class-wc-payments.php'; WC_Payments::remove_woo_admin_notes(); + delete_user_meta( get_current_user_id(), '_wcpay_bnpl_april15_viewed' ); + delete_transient( 'wcpay_bnpl_april15_successful_purchases_count' ); } register_activation_hook( __FILE__, 'wcpay_activated' );