🔀 Merge branch 'fastlane'

This commit is contained in:
Philipp Stracker 2024-09-17 14:56:03 +02:00
commit d7cdc9eada
No known key found for this signature in database
30 changed files with 645 additions and 289 deletions

View file

@ -1,5 +1,21 @@
*** Changelog ***
= 2.9.1 - xxxx-xx-xx =
* Fix - Improve card fields hiding #2574
* Fix - Google Pay: Shipping callback not calculating totals correctly on Single Product page #2513
* Fix - Fix shipping callback condition in status report #2578
* Fix - Can't Disconnect Account #2539
* Fix - Google Pay billing data without shipping callback #2525
* Fix - Standard payment tab - Google Pay and Apple Pay button - Shape from one location is applied to all until saving changes #2419
* Enhancement - Allow to override the list of Pay Later supported countries #2563
* Enhancement - Add more feature statuses into system report #2550
* Enhancement - Use SVG for APM gateway icons #2509
* Enhancement - Add inline notice to inform users about ACDC block Checkout support if the store uses a Classic Checkout setup #2422
* Enhancement - Remove leftover console.log #2589
* Enhancement - Require PHP 7.4+, WP 6.3+, WC 6.9+ #2556
* Enhancement - Modularity module migration #1944
* Enhancement - Keep only 5 tags in readme.txt #2562
= 2.9.0 - 2024-09-02 =
* Fix - Fatal error in Block Editor when using WooCommerce blocks #2534
* Fix - Can't pay from block pages when the shipping callback is enabled and no shipping methods defined #2429

View file

@ -29,7 +29,6 @@ return function ( string $root_dir ): iterable {
( require "$modules_dir/ppcp-vaulting/module.php" )(),
( require "$modules_dir/ppcp-order-tracking/module.php" )(),
( require "$modules_dir/ppcp-uninstall/module.php" )(),
( require "$modules_dir/ppcp-axo-block/module.php" )(),
( require "$modules_dir/ppcp-blocks/module.php" )(),
( require "$modules_dir/ppcp-paypal-subscriptions/module.php" )(),
( require "$modules_dir/ppcp-local-alternative-payment-methods/module.php" )(),
@ -88,6 +87,7 @@ return function ( string $root_dir ): iterable {
getenv( 'PCP_AXO_ENABLED' ) === '1'
) ) {
$modules[] = ( require "$modules_dir/ppcp-axo/module.php" )();
$modules[] = ( require "$modules_dir/ppcp-axo-block/module.php" )();
}
return $modules;

View file

@ -27,7 +27,7 @@ $fast-transition-duration: 0.5s;
.wc-block-checkout-axo-block-card {
@include flex-center;
width: 100%;
margin-bottom: 10px;
margin-bottom: 2em;
&__inner {
display: flex;
@ -124,7 +124,9 @@ $fast-transition-duration: 0.5s;
#shipping-fields,
#billing-fields,
#shipping-option,
#order-notes {
#order-notes,
.wp-block-woocommerce-checkout-terms-block,
.wp-block-woocommerce-checkout-actions-block {
display: none;
}
}
@ -138,11 +140,12 @@ $fast-transition-duration: 0.5s;
.wp-block-woocommerce-checkout-contact-information-block .wc-block-components-text-input {
display: grid;
grid-template-areas:
"input button"
"watermark watermark"
"error error";
grid-template-columns: 1fr auto;
gap: 6px 8px;
"input"
"button"
"watermark"
"error";
grid-template-columns: 1fr;
gap: 6px;
align-items: start;
input[type="email"] {
@ -152,16 +155,17 @@ $fast-transition-duration: 0.5s;
}
#email {
align-self: center;
align-self: stretch;
}
// 4.5 Email Submit Button
.wc-block-axo-email-submit-button-container {
grid-area: button;
align-self: center;
align-self: stretch;
.wc-block-components-button {
white-space: nowrap;
width: 100%;
}
}
@ -177,6 +181,29 @@ $fast-transition-duration: 0.5s;
width: 100%;
margin-top: 4px;
}
@media (min-width: 783px) {
.wp-block-woocommerce-checkout-contact-information-block .wc-block-components-text-input {
grid-template-areas:
"input button"
"watermark watermark"
"error error";
grid-template-columns: 1fr auto;
gap: 6px 8px;
}
#email {
align-self: center;
}
.wc-block-axo-email-submit-button-container {
align-self: center;
.wc-block-components-button {
width: auto;
}
}
}
}
// 5. Shipping/Card Change Link

View file

@ -72,9 +72,9 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule
function () use ( $c ) {
add_filter(
'woocommerce_paypal_payments_localized_script_data',
function( array $localized_script_data ) use ($c) {
function( array $localized_script_data ) use ( $c ) {
$module = $this;
$api = $c->get( 'api.sdk-client-token' );
$api = $c->get( 'api.sdk-client-token' );
assert( $api instanceof SdkClientToken );
$logger = $c->get( 'woocommerce.logger.woocommerce' );
@ -106,31 +106,6 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule
}
);
// woocommerce_store_api_register_payment_requirements(
// array(
// 'data_callback' => function() use ( $c ): array {
// $smart_button = $c->get( 'button.smart-button' );
// assert( $smart_button instanceof SmartButtonInterface );
//
// if ( isset( $smart_button->script_data()['continuation'] ) ) {
// return array( 'ppcp_continuation' );
// }
//
// return array();
// },
// )
// );
// add_action(
// 'wc_ajax_' . UpdateShippingEndpoint::ENDPOINT,
// static function () use ( $c ) {
// $endpoint = $c->get( 'blocks.endpoint.update-shipping' );
// assert( $endpoint instanceof UpdateShippingEndpoint );
//
// $endpoint->handle_request();
// }
// );
// Enqueue frontend scripts.
add_action(
'wp_enqueue_scripts',

View file

@ -148,6 +148,15 @@ class AxoManager {
return !! this.data.card;
}
/**
* CSS selector to target the Fastlane Card Component wrapper.
*
* @return {string} CSS selector.
*/
get cardFormSelector() {
return this.el.paymentContainer.selector + '-form';
}
registerEventHandlers() {
this.$( document ).on(
'change',
@ -803,8 +812,8 @@ class AxoManager {
this.emailInput.value = this.stripSpaces( this.emailInput.value );
this.$( this.el.paymentContainer.selector + '-detail' ).html( '' );
this.$( this.el.paymentContainer.selector + '-form' ).html( '' );
this.$( this.el.paymentContainer.selector + '-details' ).html( '' );
this.removeFastlaneComponent();
this.setStatus( 'validEmail', false );
this.setStatus( 'hasProfile', false );
@ -1064,7 +1073,7 @@ class AxoManager {
return Promise.resolve();
}
const elem = this.el.paymentContainer.selector + '-form';
const elem = this.cardFormSelector;
const config = this.cardComponentData();
this.cardComponent =
@ -1073,6 +1082,18 @@ class AxoManager {
return this.cardComponent.render( elem );
}
/**
* Reverts the changes made by `initializeFastlaneComponent()`.
*
* Calling this method will lose any input that the user made inside the
* Fastlane Card Component.
*/
removeFastlaneComponent() {
document.querySelector( this.cardFormSelector ).innerHTML = '';
this.cardComponent = null;
}
/**
* Updates the prefill-values in the UI component. This method only updates empty fields.
*

View file

@ -28,7 +28,7 @@ class CardView {
const cardIcons = {
VISA: 'visa-light.svg',
MASTER_CARD: 'mastercard-light.svg',
MASTERCARD: 'mastercard-light.svg',
AMEX: 'amex-light.svg',
DISCOVER: 'discover-light.svg',
DINERS: 'dinersclub-light.svg',

View file

@ -77,8 +77,7 @@ return array(
$container->get( 'wcgateway.url' ),
$container->get( 'session.handler' ),
$container->get( 'wcgateway.order-processor' ),
$container->get( 'axo.card_icons' ),
$container->get( 'axo.card_icons.axo' ),
$container->get( 'wcgateway.credit-card-icons' ),
$container->get( 'api.endpoint.order' ),
$container->get( 'api.factory.purchase-unit' ),
$container->get( 'api.factory.shipping-preference' ),
@ -88,60 +87,6 @@ return array(
);
},
'axo.card_icons' => static function ( ContainerInterface $container ): array {
return array(
array(
'title' => 'Visa',
'file' => 'visa-dark.svg',
),
array(
'title' => 'MasterCard',
'file' => 'mastercard-dark.svg',
),
array(
'title' => 'American Express',
'file' => 'amex.svg',
),
array(
'title' => 'Discover',
'file' => 'discover.svg',
),
);
},
'axo.card_icons.axo' => static function ( ContainerInterface $container ): array {
return array(
array(
'title' => 'Visa',
'file' => 'visa-light.svg',
),
array(
'title' => 'MasterCard',
'file' => 'mastercard-light.svg',
),
array(
'title' => 'Amex',
'file' => 'amex-light.svg',
),
array(
'title' => 'Discover',
'file' => 'discover-light.svg',
),
array(
'title' => 'Diners Club',
'file' => 'dinersclub-light.svg',
),
array(
'title' => 'JCB',
'file' => 'jcb-light.svg',
),
array(
'title' => 'UnionPay',
'file' => 'unionpay-light.svg',
),
);
},
/**
* The matrix which countries and currency combinations can be used for AXO.
*/

View file

@ -73,13 +73,6 @@ class AxoGateway extends WC_Payment_Gateway {
*/
protected $card_icons;
/**
* The AXO card icons.
*
* @var array
*/
protected $card_icons_axo;
/**
* The order endpoint.
*
@ -132,19 +125,18 @@ class AxoGateway extends WC_Payment_Gateway {
/**
* AXOGateway constructor.
*
* @param SettingsRenderer $settings_renderer The settings renderer.
* @param ContainerInterface $ppcp_settings The settings.
* @param string $wcgateway_module_url The WcGateway module URL.
* @param SessionHandler $session_handler The session handler.
* @param OrderProcessor $order_processor The Order processor.
* @param array $card_icons The card icons.
* @param array $card_icons_axo The card icons.
* @param OrderEndpoint $order_endpoint The order endpoint.
* @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
* @param ShippingPreferenceFactory $shipping_preference_factory Shipping preference factory.
* @param TransactionUrlProvider $transaction_url_provider The transaction url provider.
* @param Environment $environment The environment.
* @param LoggerInterface $logger The logger.
* @param SettingsRenderer $settings_renderer The settings renderer.
* @param ContainerInterface $ppcp_settings The settings.
* @param string $wcgateway_module_url The WcGateway module URL.
* @param SessionHandler $session_handler The Session Handler.
* @param OrderProcessor $order_processor The Order processor.
* @param array $card_icons The card icons.
* @param OrderEndpoint $order_endpoint The order endpoint.
* @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
* @param ShippingPreferenceFactory $shipping_preference_factory The shipping preference factory.
* @param TransactionUrlProvider $transaction_url_provider The transaction url provider.
* @param Environment $environment The environment.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
SettingsRenderer $settings_renderer,
@ -153,7 +145,6 @@ class AxoGateway extends WC_Payment_Gateway {
SessionHandler $session_handler,
OrderProcessor $order_processor,
array $card_icons,
array $card_icons_axo,
OrderEndpoint $order_endpoint,
PurchaseUnitFactory $purchase_unit_factory,
ShippingPreferenceFactory $shipping_preference_factory,
@ -169,7 +160,6 @@ class AxoGateway extends WC_Payment_Gateway {
$this->session_handler = $session_handler;
$this->order_processor = $order_processor;
$this->card_icons = $card_icons;
$this->card_icons_axo = $card_icons_axo;
$this->method_title = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' );
$this->method_description = __( 'Fastlane accelerates the checkout experience for guest shoppers and autofills their details so they can pay in seconds. When enabled, Fastlane is presented as the default payment method for guests.', 'woocommerce-paypal-payments' );
@ -311,16 +301,10 @@ class AxoGateway extends WC_Payment_Gateway {
* @return string
*/
public function get_icon() {
$icon = parent::get_icon();
$icons = $this->card_icons;
$icons_src = esc_url( $this->wcgateway_module_url ) . 'assets/images/';
$icon = parent::get_icon();
$icons = $this->card_icons;
if ( $this->card_icons_axo ) {
$icons = $this->card_icons_axo;
$icons_src = esc_url( $this->wcgateway_module_url ) . 'assets/images/axo/';
}
if ( empty( $this->card_icons ) ) {
if ( ! $icons ) {
return $icon;
}
@ -329,8 +313,8 @@ class AxoGateway extends WC_Payment_Gateway {
foreach ( $icons as $card ) {
$images[] = '<img
class="ppcp-card-icon"
title="' . $card['title'] . '"
src="' . $icons_src . $card['file'] . '"
title="' . esc_attr( $card['title'] ) . '"
src="' . esc_url( $card['url'] ) . '"
> ';
}

View file

@ -62,7 +62,7 @@ class SettingsNoticeGenerator {
public function generate_checkout_notice(): string {
$checkout_page_link = esc_url( get_edit_post_link( wc_get_page_id( 'checkout' ) ) ?? '' );
$block_checkout_docs_link = __(
'https://woocommerce.com/document/cart-checkout-blocks-status/#reverting-to-the-cart-and-checkout-shortcodes',
'https://woocommerce.com/document/woocommerce-store-editing/customizing-cart-and-checkout/#using-the-cart-and-checkout-blocks',
'woocommerce-paypal-payments'
);
@ -72,27 +72,17 @@ class SettingsNoticeGenerator {
$notice_content = sprintf(
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
__(
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the <code>Elementor Checkout widget</code>. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the <code>Elementor Checkout widget</code>. To enable Fastlane and accelerate payments, the page must include either the <code>Checkout</code> block, <code>Classic Checkout</code>, or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the Checkout block.',
'woocommerce-paypal-payments'
),
esc_url( $checkout_page_link ),
esc_url( $block_checkout_docs_link )
);
} elseif ( CartCheckoutDetector::has_block_checkout() ) {
} elseif ( ! CartCheckoutDetector::has_classic_checkout() && ! CartCheckoutDetector::has_block_checkout() ) {
$notice_content = sprintf(
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
__(
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the WooCommerce <code>Checkout</code> block. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
'woocommerce-paypal-payments'
),
esc_url( $checkout_page_link ),
esc_url( $block_checkout_docs_link )
);
} elseif ( ! CartCheckoutDetector::has_classic_checkout() ) {
$notice_content = sprintf(
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
__(
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store does not seem to be properly configured or uses an incompatible <code>third-party Checkout</code> solution. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store does not seem to be properly configured or uses an incompatible <code>third-party Checkout</code> solution. To enable Fastlane and accelerate payments, the page must include either the <code>Checkout</code> block, <code>Classic Checkout</code>, or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the Checkout block.',
'woocommerce-paypal-payments'
),
esc_url( $checkout_page_link ),

View file

@ -68,7 +68,7 @@ class BlocksModule implements ServiceModule, ExtendingModule, ExecutableModule {
'woocommerce_blocks_payment_method_type_registration',
function( PaymentMethodRegistry $payment_method_registry ) use ( $c ): void {
$payment_method_registry->register( $c->get( 'blocks.method' ) );
// $payment_method_registry->register( $c->get( 'blocks.advanced-card-method' ) );
$payment_method_registry->register( $c->get( 'blocks.advanced-card-method' ) );
}
);

View file

@ -228,7 +228,6 @@ class Renderer {
};
shouldEnableShippingCallback = () => {
console.log(this.defaultSettings.context, this.defaultSettings)
let needShipping = this.defaultSettings.needShipping || this.defaultSettings.context === 'product'
return this.defaultSettings.should_handle_shipping_in_paypal && needShipping
};

View file

@ -29,6 +29,11 @@ use WooCommerce\PayPalCommerce\WcGateway\Endpoint\CaptureCardPayment;
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\RefreshFeatureStatusEndpoint;
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
use WooCommerce\PayPalCommerce\WcGateway\Helper\FeesUpdater;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Factory\SimpleRedirectTaskFactory;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Factory\SimpleRedirectTaskFactoryInterface;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Registrar\TaskRegistrar;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Registrar\TaskRegistrarInterface;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Tasks\SimpleRedirectTask;
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer;
@ -127,10 +132,13 @@ return array(
$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,
$icons,
$module_url,
$session_handler,
$refund_processor,
@ -148,6 +156,68 @@ return array(
$logger
);
},
'wcgateway.credit-card-labels' => static function ( ContainerInterface $container ) : array {
return array(
'visa' => _x(
'Visa',
'Name of credit card',
'woocommerce-paypal-payments'
),
'mastercard' => _x(
'Mastercard',
'Name of credit card',
'woocommerce-paypal-payments'
),
'amex' => _x(
'American Express',
'Name of credit card',
'woocommerce-paypal-payments'
),
'discover' => _x(
'Discover',
'Name of credit card',
'woocommerce-paypal-payments'
),
'jcb' => _x(
'JCB',
'Name of credit card',
'woocommerce-paypal-payments'
),
'elo' => _x(
'Elo',
'Name of credit card',
'woocommerce-paypal-payments'
),
'hiper' => _x(
'Hiper',
'Name of credit card',
'woocommerce-paypal-payments'
),
);
},
'wcgateway.credit-card-icons' => static function ( ContainerInterface $container ) : array {
$settings = $container->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$icons = $settings->has( 'card_icons' ) ? (array) $settings->get( 'card_icons' ) : array();
$labels = $container->get( 'wcgateway.credit-card-labels' );
$module_url = $container->get( 'wcgateway.url' );
$url_root = esc_url( $module_url ) . 'assets/images/';
$icons_with_label = array();
foreach ( $icons as $icon ) {
$type = str_replace( '-dark', '', $icon );
$icons_with_label[] = array(
'type' => $type,
'title' => ucwords( $labels[ $type ] ?? $type ),
'url' => "$url_root/$icon.svg",
);
}
return $icons_with_label;
},
'wcgateway.card-button-gateway' => static function ( ContainerInterface $container ): CardButtonGateway {
return new CardButtonGateway(
$container->get( 'wcgateway.settings.render' ),
@ -1280,17 +1350,12 @@ return array(
$container->get( 'wcgateway.processor.refunds' )
);
},
'wcgateway.fraudnet-session-id' => static function ( ContainerInterface $container ): FraudNetSessionId {
return new FraudNetSessionId();
},
'wcgateway.fraudnet-source-website-id' => static function ( ContainerInterface $container ): FraudNetSourceWebsiteId {
return new FraudNetSourceWebsiteId( $container->get( 'api.merchant_id' ) );
},
'wcgateway.fraudnet' => static function ( ContainerInterface $container ): FraudNet {
$session_id = $container->get( 'wcgateway.fraudnet-session-id' );
$source_website_id = $container->get( 'wcgateway.fraudnet-source-website-id' );
return new FraudNet(
(string) $session_id(),
(string) $source_website_id()
);
},
@ -1762,4 +1827,62 @@ return array(
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'wcgateway.settings.wc-tasks.simple-redirect-task-factory' => static function(): SimpleRedirectTaskFactoryInterface {
return new SimpleRedirectTaskFactory();
},
'wcgateway.settings.wc-tasks.task-registrar' => static function(): TaskRegistrarInterface {
return new TaskRegistrar();
},
/**
* A configuration for simple redirect wc tasks.
*
* @returns array<array{
* id: string,
* title: string,
* description: string,
* redirect_url: string
* }>
*/
'wcgateway.settings.wc-tasks.simple-redirect-tasks-config' => static function( ContainerInterface $container ): array {
$section_id = PayPalGateway::ID;
$pay_later_tab_id = Settings::PAY_LATER_TAB_ID;
$list_of_config = array();
if ( $container->get( 'paylater-configurator.is-available' ) ) {
$list_of_config[] = array(
'id' => 'pay-later-messaging-task',
'title' => __( 'Configure PayPal Pay Later messaging', 'woocommerce-paypal-payments' ),
'description' => __( 'Decide where you want dynamic Pay Later messaging to show up and how you want it to look on your site.', 'woocommerce-paypal-payments' ),
'redirect_url' => admin_url( "admin.php?page=wc-settings&tab=checkout&section={$section_id}&ppcp-tab={$pay_later_tab_id}" ),
);
}
return $list_of_config;
},
/**
* Retrieves the list of simple redirect task instances.
*
* @returns SimpleRedirectTask[]
*/
'wcgateway.settings.wc-tasks.simple-redirect-tasks' => static function( ContainerInterface $container ): array {
$simple_redirect_tasks_config = $container->get( 'wcgateway.settings.wc-tasks.simple-redirect-tasks-config' );
$simple_redirect_task_factory = $container->get( 'wcgateway.settings.wc-tasks.simple-redirect-task-factory' );
assert( $simple_redirect_task_factory instanceof SimpleRedirectTaskFactoryInterface );
$simple_redirect_tasks = array();
foreach ( $simple_redirect_tasks_config as $config ) {
$id = $config['id'] ?? '';
$title = $config['title'] ?? '';
$description = $config['description'] ?? '';
$redirect_url = $config['redirect_url'] ?? '';
$simple_redirect_tasks[] = $simple_redirect_task_factory->create_task( $id, $title, $description, $redirect_url );
}
return $simple_redirect_tasks;
},
);

View file

@ -14,13 +14,6 @@ namespace WooCommerce\PayPalCommerce\WcGateway\FraudNet;
*/
class FraudNet {
/**
* The session ID.
*
* @var string
*/
protected $session_id;
/**
* The source website ID.
*
@ -31,21 +24,40 @@ class FraudNet {
/**
* FraudNet constructor.
*
* @param string $session_id The session ID.
* @param string $source_website_id The source website ID.
*/
public function __construct( string $session_id, string $source_website_id ) {
$this->session_id = $session_id;
public function __construct( string $source_website_id ) {
$this->source_website_id = $source_website_id;
}
/**
* Returns the session ID.
* Returns the Fraudnet session ID.
*
* @return string
*/
public function session_id(): string {
return $this->session_id;
if ( WC()->session === null ) {
return '';
}
$fraudnet_session_id = WC()->session->get( 'ppcp_fraudnet_session_id' );
if ( is_string( $fraudnet_session_id ) && $fraudnet_session_id !== '' ) {
return $fraudnet_session_id;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['pay_for_order'] ) && $_GET['pay_for_order'] === 'true' ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$pui_pay_for_order_session_id = wc_clean( wp_unslash( $_POST['pui_pay_for_order_session_id'] ?? '' ) );
if ( is_string( $pui_pay_for_order_session_id ) && $pui_pay_for_order_session_id !== '' ) {
return $pui_pay_for_order_session_id;
}
}
$session_id = bin2hex( random_bytes( 16 ) );
WC()->session->set( 'ppcp_fraudnet_session_id', $session_id );
return $session_id;
}
/**

View file

@ -1,48 +0,0 @@
<?php
/**
* Fraudnet session id.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\FraudNet;
use Exception;
/**
* Class FraudNetSessionId.
*/
class FraudNetSessionId {
/**
* Generates a session ID or use the existing one from WC session.
*
* @return array|string
* @throws Exception When there is a problem with the session ID.
*/
public function __invoke() {
if ( WC()->session === null ) {
return '';
}
if ( WC()->session->get( 'ppcp_fraudnet_session_id' ) ) {
return WC()->session->get( 'ppcp_fraudnet_session_id' );
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['pay_for_order'] ) && 'true' === $_GET['pay_for_order'] ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$pui_pay_for_order_session_id = wc_clean( wp_unslash( $_POST['pui_pay_for_order_session_id'] ?? '' ) );
if ( $pui_pay_for_order_session_id && '' !== $pui_pay_for_order_session_id ) {
return $pui_pay_for_order_session_id;
}
}
$session_id = bin2hex( random_bytes( 16 ) );
WC()->session->set( 'ppcp_fraudnet_session_id', $session_id );
return $session_id;
}
}

View file

@ -59,6 +59,13 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
*/
protected $order_processor;
/**
* The card icons.
*
* @var array
*/
protected $card_icons;
/**
* The settings.
*
@ -184,6 +191,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
* @param SettingsRenderer $settings_renderer The Settings Renderer.
* @param OrderProcessor $order_processor The Order processor.
* @param ContainerInterface $config The settings.
* @param array $card_icons The card icons.
* @param string $module_url The URL to the module.
* @param SessionHandler $session_handler The Session Handler.
* @param RefundProcessor $refund_processor The refund processor.
@ -204,6 +212,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
SettingsRenderer $settings_renderer,
OrderProcessor $order_processor,
ContainerInterface $config,
array $card_icons,
string $module_url,
SessionHandler $session_handler,
RefundProcessor $refund_processor,
@ -264,6 +273,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
$this->config->get( 'dcc_gateway_title' ) : $this->method_title;
$this->description = $this->config->has( 'dcc_gateway_description' ) ?
$this->config->get( 'dcc_gateway_description' ) : $this->method_description;
$this->card_icons = $card_icons;
$this->init_form_fields();
$this->init_settings();
@ -339,74 +349,26 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
* @return string
*/
public function get_icon() {
$icon = parent::get_icon();
$icon = parent::get_icon();
$icons = $this->card_icons;
$icons = $this->config->has( 'card_icons' ) ? (array) $this->config->get( 'card_icons' ) : array();
if ( empty( $icons ) ) {
if ( ! $icons ) {
return $icon;
}
$title_options = $this->card_labels();
$images = array_map(
function ( string $type ) use ( $title_options ): string {
$striped_dark = str_replace( '-dark', '', $type );
return '<img
title="' . esc_attr( $title_options[ $striped_dark ] ) . '"
src="' . esc_url( $this->module_url ) . 'assets/images/' . esc_attr( $type ) . '.svg"
class="ppcp-card-icon"
> ';
},
$icons
);
$images = array();
foreach ( $icons as $card ) {
$images[] = '<img
class="ppcp-card-icon"
title="' . esc_attr( $card['title'] ) . '"
src="' . esc_url( $card['url'] ) . '"
> ';
}
return implode( '', $images );
}
/**
* Returns an array of credit card names.
*
* @return array
*/
private function card_labels(): array {
return array(
'visa' => _x(
'Visa',
'Name of credit card',
'woocommerce-paypal-payments'
),
'mastercard' => _x(
'Mastercard',
'Name of credit card',
'woocommerce-paypal-payments'
),
'amex' => _x(
'American Express',
'Name of credit card',
'woocommerce-paypal-payments'
),
'discover' => _x(
'Discover',
'Name of credit card',
'woocommerce-paypal-payments'
),
'jcb' => _x(
'JCB',
'Name of credit card',
'woocommerce-paypal-payments'
),
'elo' => _x(
'Elo',
'Name of credit card',
'woocommerce-paypal-payments'
),
'hiper' => _x(
'Hiper',
'Name of credit card',
'woocommerce-paypal-payments'
),
);
}
/**
* Whether the gateway is available or not.
*

View file

@ -0,0 +1,23 @@
<?php
/**
* A factory to create simple redirect task.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Settings
*/
namespace WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Factory;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Tasks\SimpleRedirectTask;
/**
* A factory to create simple redirect task.
*/
class SimpleRedirectTaskFactory implements SimpleRedirectTaskFactoryInterface {
/**
* {@inheritDoc}
*/
public function create_task( string $id, string $title, string $description, string $redirect_url ): SimpleRedirectTask {
return new SimpleRedirectTask( $id, $title, $description, $redirect_url );
}
}

View file

@ -0,0 +1,26 @@
<?php
/**
* Responsible for creating the simple redirect task.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Settings
*/
declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Factory;
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Tasks\SimpleRedirectTask;
interface SimpleRedirectTaskFactoryInterface {
/**
* Creates the simple redirect task.
*
* @param string $id The task ID.
* @param string $title The task title.
* @param string $description The task description.
* @param string $redirect_url The redirection URL.
* @return SimpleRedirectTask The task.
*/
public function create_task( string $id, string $title, string $description, string $redirect_url ): SimpleRedirectTask;
}

View file

@ -0,0 +1,32 @@
<?php
/**
* Registers the tasks inside the "Things to do next" WC section.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Settings
*/
namespace WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Registrar;
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists;
use RuntimeException;
use WP_Error;
/**
* Registers the tasks inside the "Things to do next" WC section.
*/
class TaskRegistrar implements TaskRegistrarInterface {
/**
* {@inheritDoc}
*
* @throws RuntimeException If problem registering.
*/
public function register( array $tasks ): void {
foreach ( $tasks as $task ) {
$added_task = TaskLists::add_task( 'extended', $task );
if ( $added_task instanceof WP_Error ) {
throw new RuntimeException( $added_task->get_error_message() );
}
}
}
}

View file

@ -0,0 +1,25 @@
<?php
/**
* Responsible for registering the tasks inside the "Things to do next" WC section.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Settings
*/
declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Registrar;
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
use RuntimeException;
interface TaskRegistrarInterface {
/**
* Registers the tasks inside "Things to do next" WC section.
*
* @param Task[] $tasks The list of tasks.
* @return void
* @throws RuntimeException If problem registering.
*/
public function register( array $tasks ): void;
}

View file

@ -0,0 +1,120 @@
<?php
/**
* Represents the Task for simple redirection. See "Things to do next" WC section.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Settings
*/
declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Tasks;
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
/**
* Class SimpleRedirectTask
*/
class SimpleRedirectTask extends Task {
/**
* The task ID.
*
* @var string
*/
protected string $id;
/**
* The task title.
*
* @var string
*/
protected string $title;
/**
* The task description.
*
* @var string
*/
protected string $description;
/**
* The redirection URL.
*
* @var string
*/
protected string $redirect_url;
/**
* SimpleRedirectTask constructor.
*
* @param string $id The task ID.
* @param string $title The task title.
* @param string $description The task description.
* @param string $redirect_url The redirection URL.
*/
public function __construct( string $id, string $title, string $description, string $redirect_url ) {
parent::__construct();
$this->id = $id;
$this->title = $title;
$this->description = $description;
$this->redirect_url = $redirect_url;
}
/**
* The task ID.
*
* @return string
*/
public function get_id(): string {
return $this->id;
}
/**
* The task title.
*
* @return string
*/
public function get_title(): string {
return $this->title;
}
/**
* The task content.
*
* @return string
*/
public function get_content(): string {
return '';
}
/**
* The task time.
*
* @return string
*/
public function get_time(): string {
return $this->description;
}
/**
* The task redirection URL.
*
* @return string
*/
public function get_action_url(): string {
return $this->redirect_url;
}
/**
* The task completion.
*
* We need to set the task completed when the redirection happened for the first time.
* So this method of a parent class should be overridden.
*
* @return bool
*/
public function is_complete(): bool {
return parent::is_visited();
}
}

View file

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway;
use Exception;
use Psr\Log\LoggerInterface;
use Throwable;
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
@ -55,6 +56,7 @@ 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;
/**
* Class WcGatewayModule
@ -86,6 +88,7 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul
$this->register_order_functionality( $c );
$this->register_columns( $c );
$this->register_checkout_paypal_address_preset( $c );
$this->register_wc_tasks( $c );
add_action(
'woocommerce_sections_checkout',
@ -831,4 +834,34 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul
2
);
}
/**
* Registers the tasks inside "Things to do next" WC section.
*
* @param ContainerInterface $container The container.
* @return void
*/
protected function register_wc_tasks( ContainerInterface $container ): void {
$simple_redirect_tasks = $container->get( 'wcgateway.settings.wc-tasks.simple-redirect-tasks' );
if ( empty( $simple_redirect_tasks ) ) {
return;
}
$task_registrar = $container->get( 'wcgateway.settings.wc-tasks.task-registrar' );
assert( $task_registrar instanceof TaskRegistrarInterface );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
assert( $logger instanceof LoggerInterface );
add_action(
'init',
static function () use ( $simple_redirect_tasks, $task_registrar, $logger ): void {
try {
$task_registrar->register( $simple_redirect_tasks );
} catch ( Exception $exception ) {
$logger->error( "Failed to create a task in the 'Things to do next' section of WC. " . $exception->getMessage() );
}
},
);
}
}

View file

@ -0,0 +1,28 @@
<?php
/**
* The webhook module factories.
*
* @package WooCommerce\PayPalCommerce\Webhooks
*/
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(
'webhook.status.registered-webhooks' => function( ContainerInterface $container ) : array {
$endpoint = $container->get( 'api.endpoint.webhook' );
assert( $endpoint instanceof WebhookEndpoint );
$state = $container->get( 'onboarding.state' );
if ( $state->current_state() >= State::STATE_ONBOARDED ) {
return $endpoint->list();
}
return array();
},
);

View file

@ -131,18 +131,6 @@ return array(
return $container->get( 'webhook.current' ) !== null;
},
'webhook.status.registered-webhooks' => function( ContainerInterface $container ) : array {
$endpoint = $container->get( 'api.endpoint.webhook' );
assert( $endpoint instanceof WebhookEndpoint );
$state = $container->get( 'onboarding.state' );
if ( $state->current_state() >= State::STATE_ONBOARDED ) {
return $endpoint->list();
}
return array();
},
'webhook.status.registered-webhooks-data' => function( ContainerInterface $container ) : array {
$empty_placeholder = __( 'No webhooks found.', 'woocommerce-paypal-payments' );

View file

@ -14,6 +14,7 @@ use WooCommerce\PayPalCommerce\Onboarding\State;
use Exception;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
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;
@ -27,7 +28,7 @@ use WooCommerce\PayPalCommerce\Webhooks\Status\Assets\WebhooksStatusPageAssets;
/**
* Class WebhookModule
*/
class WebhookModule implements ServiceModule, ExtendingModule, ExecutableModule {
class WebhookModule implements ServiceModule, FactoryModule, ExtendingModule, ExecutableModule {
use ModuleClassNameIdTrait;
/**
@ -37,6 +38,13 @@ class WebhookModule implements ServiceModule, ExtendingModule, ExecutableModule
return require __DIR__ . '/../services.php';
}
/**
* {@inheritDoc}
*/
public function factories(): array {
return require __DIR__ . '/../factories.php';
}
/**
* {@inheritDoc}
*/

View file

@ -1,6 +1,6 @@
{
"name": "woocommerce-paypal-payments",
"version": "2.9.0",
"version": "2.9.1",
"description": "WooCommerce PayPal Payments",
"repository": "https://github.com/woocommerce/woocommerce-paypal-payments",
"license": "GPL-2.0",

View file

@ -4,7 +4,7 @@ Tags: woocommerce, paypal, payments, ecommerce, credit card
Requires at least: 6.3
Tested up to: 6.6
Requires PHP: 7.4
Stable tag: 2.9.0
Stable tag: 2.9.1
License: GPLv2
License URI: http://www.gnu.org/licenses/gpl-2.0.html
@ -179,6 +179,22 @@ If you encounter issues with the PayPal buttons not appearing after an update, p
== Changelog ==
= 2.9.1 - xxxx-xx-xx =
* Fix - Improve card fields hiding #2574
* Fix - Google Pay: Shipping callback not calculating totals correctly on Single Product page #2513
* Fix - Fix shipping callback condition in status report #2578
* Fix - Can't Disconnect Account #2539
* Fix - Google Pay billing data without shipping callback #2525
* Fix - Standard payment tab - Google Pay and Apple Pay button - Shape from one location is applied to all until saving changes #2419
* Enhancement - Allow to override the list of Pay Later supported countries #2563
* Enhancement - Add more feature statuses into system report #2550
* Enhancement - Use SVG for APM gateway icons #2509
* Enhancement - Add inline notice to inform users about ACDC block Checkout support if the store uses a Classic Checkout setup #2422
* Enhancement - Remove leftover console.log #2589
* Enhancement - Require PHP 7.4+, WP 6.3+, WC 6.9+ #2556
* Enhancement - Modularity module migration #1944
* Enhancement - Keep only 5 tags in readme.txt #2562
= 2.9.0 - 2024-09-02 =
* Fix - Fatal error in Block Editor when using WooCommerce blocks #2534
* Fix - Can't pay from block pages when the shipping callback is enabled and no shipping methods defined #2429

View file

@ -28,6 +28,7 @@ class CreditCardGatewayTest extends TestCase
private $settingsRenderer;
private $orderProcessor;
private $config;
private $creditCardIcons;
private $moduleUrl;
private $sessionHandler;
private $refundProcessor;
@ -52,6 +53,7 @@ class CreditCardGatewayTest extends TestCase
$this->settingsRenderer = Mockery::mock(SettingsRenderer::class);
$this->orderProcessor = Mockery::mock(OrderProcessor::class);
$this->config = Mockery::mock(ContainerInterface::class);
$this->creditCardIcons = [];
$this->moduleUrl = '';
$this->sessionHandler = Mockery::mock(SessionHandler::class);
$this->refundProcessor = Mockery::mock(RefundProcessor::class);
@ -78,6 +80,7 @@ class CreditCardGatewayTest extends TestCase
$this->settingsRenderer,
$this->orderProcessor,
$this->config,
$this->creditCardIcons,
$this->moduleUrl,
$this->sessionHandler,
$this->refundProcessor,

View file

@ -10,5 +10,6 @@ require_once TESTS_ROOT_DIR . '/stubs/WC_Payment_Gateway.php';
require_once TESTS_ROOT_DIR . '/stubs/WC_Payment_Gateway_CC.php';
require_once TESTS_ROOT_DIR . '/stubs/WC_Ajax.php';
require_once TESTS_ROOT_DIR . '/stubs/WC_Checkout.php';
require_once TESTS_ROOT_DIR . '/stubs/Task.php';
Hamcrest\Util::registerGlobalFunctions();

47
tests/stubs/Task.php Normal file
View file

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Automattic\WooCommerce\Admin\Features\OnboardingTasks;
use Automattic\WooCommerce\Internal\Admin\WCAdminUser;
abstract class Task
{
/**
* Constructor
*
* @param TaskList|null $task_list Parent task list.
*/
public function __construct( $task_list = null ) {
$this->task_list = $task_list;
}
/**
* ID.
*
* @return string
*/
abstract public function get_id();
/**
* Title.
*
* @return string
*/
abstract public function get_title();
/**
* Content.
*
* @return string
*/
abstract public function get_content();
/**
* Time.
*
* @return string
*/
abstract public function get_time();
}

View file

@ -3,14 +3,14 @@
* Plugin Name: WooCommerce PayPal Payments
* Plugin URI: https://woocommerce.com/products/woocommerce-paypal-payments/
* Description: PayPal's latest complete payments processing solution. Accept PayPal, Pay Later, credit/debit cards, alternative digital wallets local payment types and bank accounts. Turn on only PayPal options or process a full suite of payment methods. Enable global transaction with extensive currency and country coverage.
* Version: 2.9.0
* Version: 2.9.1
* Author: WooCommerce
* Author URI: https://woocommerce.com/
* License: GPL-2.0
* Requires PHP: 7.4
* Requires Plugins: woocommerce
* WC requires at least: 6.9
* WC tested up to: 9.2
* WC tested up to: 9.3
* Text Domain: woocommerce-paypal-payments
*
* @package WooCommerce\PayPalCommerce
@ -26,7 +26,7 @@ define( 'PAYPAL_API_URL', 'https://api-m.paypal.com' );
define( 'PAYPAL_URL', 'https://www.paypal.com' );
define( 'PAYPAL_SANDBOX_API_URL', 'https://api-m.sandbox.paypal.com' );
define( 'PAYPAL_SANDBOX_URL', 'https://www.sandbox.paypal.com' );
define( 'PAYPAL_INTEGRATION_DATE', '2024-08-28' );
define( 'PAYPAL_INTEGRATION_DATE', '2024-09-16' );
define( 'PPCP_PAYPAL_BN_CODE', 'Woo_PPCP' );
! defined( 'CONNECT_WOO_CLIENT_ID' ) && define( 'CONNECT_WOO_CLIENT_ID', 'AcCAsWta_JTL__OfpjspNyH7c1GGHH332fLwonA5CwX4Y10mhybRZmHLA0GdRbwKwjQIhpDQy0pluX_P' );