Merge branch 'trunk' into PCP-114-sca-when-required

This commit is contained in:
dinamiko 2021-08-03 15:20:23 +02:00
commit 94aead33ba
41 changed files with 562 additions and 141 deletions

View file

@ -36,7 +36,7 @@ class AdminNotices implements ModuleInterface {
*
* @param ContainerInterface $container The container.
*/
public function run( ContainerInterface $container = null ) {
public function run( ContainerInterface $container ): void {
add_action(
'admin_notices',
function() use ( $container ) {

View file

@ -120,7 +120,11 @@ class PayPalBearer implements Bearer {
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
$error = new RuntimeException(
__( 'Could not create token.', 'woocommerce-paypal-payments' )
sprintf(
// translators: %s is the error description.
__( 'Could not create token. %s', 'woocommerce-paypal-payments' ),
isset( json_decode( $response['body'] )->error_description ) ? json_decode( $response['body'] )->error_description : ''
)
);
$this->logger->log(
'warning',

View file

@ -502,6 +502,14 @@ class OrderEndpoint {
return $order_to_update;
}
$patches_array = $patches->to_array();
if ( ! isset( $patches_array[0]['value']['shipping'] ) ) {
$shipping = isset( $order_to_update->purchase_units()[0] ) && null !== $order_to_update->purchase_units()[0]->shipping() ? $order_to_update->purchase_units()[0]->shipping() : null;
if ( $shipping ) {
$patches_array[0]['value']['shipping'] = $shipping->to_array();
}
}
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $order_to_update->id();
$args = array(
@ -514,7 +522,7 @@ class OrderEndpoint {
$order_to_update
),
),
'body' => wp_json_encode( $patches->to_array() ),
'body' => wp_json_encode( $patches_array ),
);
if ( $this->bn_code ) {
$args['headers']['PayPal-Partner-Attribution-Id'] = $this->bn_code;

View file

@ -24,6 +24,8 @@ trait RequestTrait {
*/
private function request( string $url, array $args ) {
$args['timeout'] = 30;
/**
* This filter can be used to alter the request args.
* For example, during testing, the PayPal-Mock-Response header could be

View file

@ -118,6 +118,24 @@ class DccApplies {
'JPY',
'USD',
),
'CA' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
);
/**
@ -157,6 +175,12 @@ class DccApplies {
'amex' => array( 'USD' ),
'discover' => array( 'USD' ),
),
'CA' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'CAD' ),
'jcb' => array( 'CAD' ),
),
);
/**

View file

@ -37,7 +37,7 @@ class ApiModule implements ModuleInterface {
*
* @param ContainerInterface $container The container.
*/
public function run( ContainerInterface $container = null ) {
public function run( ContainerInterface $container ): void {
}
/**

View file

@ -18,15 +18,17 @@ const bootstrap = () => {
const messageRenderer = new MessageRenderer(PayPalCommerceGateway.messages);
const context = PayPalCommerceGateway.context;
if (context === 'mini-cart' || context === 'product') {
const miniCartBootstrap = new MiniCartBootstap(
PayPalCommerceGateway,
renderer
);
if (PayPalCommerceGateway.mini_cart_buttons_enabled === '1') {
const miniCartBootstrap = new MiniCartBootstap(
PayPalCommerceGateway,
renderer
);
miniCartBootstrap.init();
miniCartBootstrap.init();
}
}
if (context === 'product') {
if (context === 'product' && PayPalCommerceGateway.single_product_buttons_enabled === '1') {
const singleProductBootstrap = new SingleProductBootstap(
PayPalCommerceGateway,
renderer,

View file

@ -21,6 +21,8 @@ class CheckoutActionHandler {
const formSelector = this.config.context === 'checkout' ? 'form.checkout' : 'form#order_review';
const formValues = jQuery(formSelector).serialize();
const createaccount = jQuery('#createaccount').is(":checked") ? true : false;
return fetch(this.config.ajax.create_order.endpoint, {
method: 'POST',
body: JSON.stringify({
@ -29,7 +31,8 @@ class CheckoutActionHandler {
bn_code:bnCode,
context:this.config.context,
order_id:this.config.order_id,
form:formValues
form:formValues,
createaccount: createaccount
})
}).then(function (res) {
return res.json();

View file

@ -39,4 +39,4 @@ class CartBootstrap {
}
}
export default CartBootstrap;
export default CartBootstrap;

View file

@ -7,6 +7,7 @@ class CreditCardRenderer {
this.errorHandler = errorHandler;
this.spinner = spinner;
this.cardValid = false;
this.formValid = false;
}
render(wrapper, contextConfig) {
@ -97,12 +98,8 @@ class CreditCardRenderer {
event.preventDefault();
}
this.errorHandler.clear();
const state = hostedFields.getState();
const formValid = Object.keys(state.fields).every(function (key) {
return state.fields[key].isValid;
});
if (formValid && this.cardValid) {
if (this.formValid && this.cardValid) {
const save_card = this.defaultConfig.save_card ? true : false;
const vault = document.getElementById('ppcp-credit-card-vault') ?
document.getElementById('ppcp-credit-card-vault').checked : save_card;
@ -134,6 +131,13 @@ class CreditCardRenderer {
const validCards = this.defaultConfig.hosted_fields.valid_cards;
this.cardValid = validCards.indexOf(event.cards[0].type) !== -1;
})
hostedFields.on('validityChange', (event) => {
const formValid = Object.keys(event.fields).every(function (key) {
return event.fields[key].isValid;
});
this.formValid = formValid;
})
document.querySelector(wrapper + ' button').addEventListener(
'click',
submitEvent

View file

@ -59,7 +59,6 @@ return array(
if ( $paypal_disabled ) {
return new DisabledSmartButton();
}
$payee_repository = $container->get( 'api.repository.payee' );
$payer_factory = $container->get( 'api.factory.payer' );
$request_data = $container->get( 'button.request-data' );
@ -69,11 +68,11 @@ return array(
$messages_apply = $container->get( 'button.helper.messages-apply' );
$environment = $container->get( 'onboarding.environment' );
$payment_token_repository = $container->get( 'subscription.repository.payment-token' );
$settings_status = $container->get( 'wcgateway.settings.status' );
return new SmartButton(
$container->get( 'button.url' ),
$container->get( 'session.handler' ),
$settings,
$payee_repository,
$payer_factory,
$client_id,
$request_data,
@ -81,7 +80,8 @@ return array(
$subscription_helper,
$messages_apply,
$environment,
$payment_token_repository
$payment_token_repository,
$settings_status
);
},
'button.url' => static function ( $container ): string {

View file

@ -11,7 +11,6 @@ namespace WooCommerce\PayPalCommerce\Button\Assets;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\ApiClient\Repository\PayeeRepository;
use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint;
@ -23,6 +22,7 @@ use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
use WooCommerce\PayPalCommerce\Subscription\Repository\PaymentTokenRepository;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use Woocommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
/**
@ -30,6 +30,13 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
*/
class SmartButton implements SmartButtonInterface {
/**
* The Settings status helper.
*
* @var SettingsStatus
*/
protected $settings_status;
/**
* The URL to the module.
*
@ -51,13 +58,6 @@ class SmartButton implements SmartButtonInterface {
*/
private $settings;
/**
* The Payee Repository.
*
* @var PayeeRepository
*/
private $payee_repository;
/**
* The Payer Factory.
*
@ -120,7 +120,6 @@ class SmartButton implements SmartButtonInterface {
* @param string $module_url The URL to the module.
* @param SessionHandler $session_handler The Session Handler.
* @param Settings $settings The Settings.
* @param PayeeRepository $payee_repository The Payee Repository.
* @param PayerFactory $payer_factory The Payer factory.
* @param string $client_id The client ID.
* @param RequestData $request_data The Request Data helper.
@ -129,12 +128,12 @@ class SmartButton implements SmartButtonInterface {
* @param MessagesApply $messages_apply The Messages apply helper.
* @param Environment $environment The environment object.
* @param PaymentTokenRepository $payment_token_repository The payment token repository.
* @param SettingsStatus $settings_status The Settings status helper.
*/
public function __construct(
string $module_url,
SessionHandler $session_handler,
Settings $settings,
PayeeRepository $payee_repository,
PayerFactory $payer_factory,
string $client_id,
RequestData $request_data,
@ -142,13 +141,13 @@ class SmartButton implements SmartButtonInterface {
SubscriptionHelper $subscription_helper,
MessagesApply $messages_apply,
Environment $environment,
PaymentTokenRepository $payment_token_repository
PaymentTokenRepository $payment_token_repository,
SettingsStatus $settings_status
) {
$this->module_url = $module_url;
$this->session_handler = $session_handler;
$this->settings = $settings;
$this->payee_repository = $payee_repository;
$this->payer_factory = $payer_factory;
$this->client_id = $client_id;
$this->request_data = $request_data;
@ -157,6 +156,7 @@ class SmartButton implements SmartButtonInterface {
$this->messages_apply = $messages_apply;
$this->environment = $environment;
$this->payment_token_repository = $payment_token_repository;
$this->settings_status = $settings_status;
}
/**
@ -202,7 +202,7 @@ class SmartButton implements SmartButtonInterface {
add_filter(
'woocommerce_credit_card_form_fields',
function ( $default_fields, $id ) {
if ( $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' ) && CreditCardGateway::ID === $id ) {
if ( is_user_logged_in() && $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' ) && CreditCardGateway::ID === $id ) {
$default_fields['card-vault'] = sprintf(
'<p class="form-row form-row-wide"><label for="vault"><input class="ppcp-credit-card-vault" type="checkbox" id="ppcp-credit-card-vault" name="vault">%s</label></p>',
esc_html__( 'Save your Credit Card', 'woocommerce-paypal-payments' )
@ -438,7 +438,7 @@ class SmartButton implements SmartButtonInterface {
*/
public function message_renderer() {
echo '<div id="ppcp-messages"></div>';
echo '<div id="ppcp-messages" data-partner-attribution-id="Woo_PPCP"></div>';
}
/**
@ -612,16 +612,16 @@ class SmartButton implements SmartButtonInterface {
$this->request_data->enqueue_nonce_fix();
$localize = array(
'script_attributes' => $this->attributes(),
'data_client_id' => array(
'script_attributes' => $this->attributes(),
'data_client_id' => array(
'set_attribute' => ( is_checkout() && $this->dcc_is_enabled() ) || $this->can_save_vault_token(),
'endpoint' => home_url( \WC_AJAX::get_endpoint( DataClientIdEndpoint::ENDPOINT ) ),
'nonce' => wp_create_nonce( DataClientIdEndpoint::nonce() ),
'user' => get_current_user_id(),
),
'redirect' => wc_get_checkout_url(),
'context' => $this->context(),
'ajax' => array(
'redirect' => wc_get_checkout_url(),
'context' => $this->context(),
'ajax' => array(
'change_cart' => array(
'endpoint' => home_url( \WC_AJAX::get_endpoint( ChangeCartEndpoint::ENDPOINT ) ),
'nonce' => wp_create_nonce( ChangeCartEndpoint::nonce() ),
@ -635,11 +635,11 @@ class SmartButton implements SmartButtonInterface {
'nonce' => wp_create_nonce( ApproveOrderEndpoint::nonce() ),
),
),
'enforce_vault' => $this->has_subscriptions(),
'save_card' => $this->can_save_vault_token(),
'bn_codes' => $this->bn_codes(),
'payer' => $this->payerData(),
'button' => array(
'enforce_vault' => $this->has_subscriptions(),
'save_card' => $this->can_save_vault_token(),
'bn_codes' => $this->bn_codes(),
'payer' => $this->payerData(),
'button' => array(
'wrapper' => '#ppc-button',
'mini_cart_wrapper' => '#ppc-button-minicart',
'cancel_wrapper' => '#ppcp-cancel',
@ -650,6 +650,7 @@ class SmartButton implements SmartButtonInterface {
'shape' => $this->style_for_context( 'shape', 'mini-cart' ),
'label' => $this->style_for_context( 'label', 'mini-cart' ),
'tagline' => $this->style_for_context( 'tagline', 'mini-cart' ),
'height' => $this->settings->has( 'button_mini-cart_height' ) && $this->settings->get( 'button_mini-cart_height' ) ? $this->normalize_height( (int) $this->settings->get( 'button_mini-cart_height' ) ) : 35,
),
'style' => array(
'layout' => $this->style_for_context( 'layout', $this->context() ),
@ -659,7 +660,7 @@ class SmartButton implements SmartButtonInterface {
'tagline' => $this->style_for_context( 'tagline', $this->context() ),
),
),
'hosted_fields' => array(
'hosted_fields' => array(
'wrapper' => '#ppcp-hosted-fields',
'mini_cart_wrapper' => '#ppcp-hosted-fields-mini-cart',
'labels' => array(
@ -677,8 +678,8 @@ class SmartButton implements SmartButtonInterface {
),
'valid_cards' => $this->dcc_applies->valid_cards(),
),
'messages' => $this->message_values(),
'labels' => array(
'messages' => $this->message_values(),
'labels' => array(
'error' => array(
'generic' => __(
'Something went wrong. Please try again or choose another payment source.',
@ -686,7 +687,9 @@ class SmartButton implements SmartButtonInterface {
),
),
),
'order_id' => 'pay-now' === $this->context() ? absint( $wp->query_vars['order-pay'] ) : 0,
'order_id' => 'pay-now' === $this->context() ? absint( $wp->query_vars['order-pay'] ) : 0,
'single_product_buttons_enabled' => $this->settings->has( 'button_product_enabled' ) && $this->settings->get( 'button_product_enabled' ),
'mini_cart_buttons_enabled' => $this->settings->has( 'button_mini-cart_enabled' ) && $this->settings->get( 'button_mini-cart_enabled' ),
);
if ( $this->style_for_context( 'layout', 'mini-cart' ) !== 'horizontal' ) {
@ -740,10 +743,6 @@ class SmartButton implements SmartButtonInterface {
) {
$params['buyer-country'] = WC()->customer->get_billing_country();
}
$payee = $this->payee_repository->payee();
if ( $payee->merchant_id() ) {
$params['merchant-id'] = $payee->merchant_id();
}
$disable_funding = $this->settings->has( 'disable_funding' ) ?
$this->settings->get( 'disable_funding' ) : array();
if ( ! is_checkout() ) {
@ -754,6 +753,14 @@ class SmartButton implements SmartButtonInterface {
$params['disable-funding'] = implode( ',', array_unique( $disable_funding ) );
}
$enable_funding = array( 'venmo' );
if ( $this->settings_status->pay_later_messaging_is_enabled() || ! in_array( 'credit', $disable_funding, true ) ) {
$enable_funding[] = 'paylater';
}
if ( count( $enable_funding ) > 0 ) {
$params['enable-funding'] = implode( ',', array_unique( $enable_funding ) );
}
$smart_button_url = add_query_arg( $params, 'https://www.paypal.com/sdk/js' );
return $smart_button_url;
}
@ -939,4 +946,21 @@ class SmartButton implements SmartButtonInterface {
}
return (string) $value;
}
/**
* Returns a value between 25 and 55.
*
* @param int $height The input value.
* @return int The normalized output value.
*/
private function normalize_height( int $height ): int {
if ( $height < 25 ) {
return 25;
}
if ( $height > 55 ) {
return 55;
}
return $height;
}
}

View file

@ -174,7 +174,11 @@ class CreateOrderEndpoint implements EndpointInterface {
$this->set_bn_code( $data );
if ( 'checkout' === $data['context'] ) {
$this->process_checkout_form( $data['form'] );
if ( isset( $data['createaccount'] ) && '1' === $data['createaccount'] ) {
$this->process_checkout_form_when_creating_account( $data['form'], $wc_order );
}
$this->process_checkout_form( $data['form'] );
}
if ( 'pay-now' === $data['context'] && get_option( 'woocommerce_terms_page_id', '' ) !== '' ) {
$this->validate_paynow_form( $data['form'] );
@ -201,6 +205,43 @@ class CreateOrderEndpoint implements EndpointInterface {
return false;
}
/**
* Once the checkout has been validated we execute this method.
*
* @param array $data The data.
* @param \WP_Error $errors The errors, which occurred.
*
* @return array
*/
public function after_checkout_validation( array $data, \WP_Error $errors ): array {
if ( ! $errors->errors ) {
$order = $this->create_paypal_order();
/**
* In case we are onboarded and everything is fine with the \WC_Order
* we want this order to be created. We will intercept it and leave it
* in the "Pending payment" status though, which than later will change
* during the "onApprove"-JS callback or the webhook listener.
*/
if ( ! $this->early_order_handler->should_create_early_order() ) {
wp_send_json_success( $order->to_array() );
}
$this->early_order_handler->register_for_order( $order );
return $data;
}
wp_send_json_error(
array(
'name' => '',
'message' => $errors->get_error_message(),
'code' => (int) $errors->get_error_code(),
'details' => array(),
)
);
return $data;
}
/**
* Creates the order in the PayPal, uses data from WC order if provided.
*
@ -341,39 +382,40 @@ class CreateOrderEndpoint implements EndpointInterface {
}
/**
* Once the checkout has been validated we execute this method.
* Processes checkout and creates the PayPal order after success form validation.
*
* @param array $data The data.
* @param \WP_Error $errors The errors, which occurred.
*
* @return array
* @param string $form_values The values of the form.
* @param \WC_Order|null $wc_order WC order to get data from.
* @throws \Exception On Error.
*/
public function after_checkout_validation( array $data, \WP_Error $errors ): array {
if ( ! $errors->errors ) {
private function process_checkout_form_when_creating_account( string $form_values, \WC_Order $wc_order = null ) {
$form_values = explode( '&', $form_values );
$parsed_values = array();
foreach ( $form_values as $field ) {
$field = explode( '=', $field );
$order = $this->create_paypal_order();
/**
* In case we are onboarded and everything is fine with the \WC_Order
* we want this order to be created. We will intercept it and leave it
* in the "Pending payment" status though, which than later will change
* during the "onApprove"-JS callback or the webhook listener.
*/
if ( ! $this->early_order_handler->should_create_early_order() ) {
wp_send_json_success( $order->to_array() );
if ( count( $field ) !== 2 ) {
continue;
}
$this->early_order_handler->register_for_order( $order );
return $data;
$parsed_values[ $field[0] ] = $field[1];
}
$_POST = $parsed_values;
$_REQUEST = $parsed_values;
wp_send_json_error(
array(
'name' => '',
'message' => $errors->get_error_message(),
'code' => (int) $errors->get_error_code(),
'details' => array(),
)
add_action(
'woocommerce_after_checkout_validation',
function ( array $data, \WP_Error $errors ) use ( $wc_order ) {
if ( ! $errors->errors ) {
$order = $this->create_paypal_order( $wc_order );
wp_send_json_success( $order->to_array() );
return true;
}
},
10,
2
);
return $data;
$checkout = \WC()->checkout();
$checkout->process_checkout();
}
}

View file

@ -43,7 +43,7 @@ class ButtonModule implements ModuleInterface {
*
* @param ContainerInterface|null $container The Container.
*/
public function run( ContainerInterface $container = null ) {
public function run( ContainerInterface $container ): void {
add_action(
'wp',

View file

@ -0,0 +1,12 @@
<?php
/**
* The compatibility module extensions.
*
* @package WooCommerce\PayPalCommerce\Compat
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Compat;
return array();

View file

@ -0,0 +1,16 @@
<?php
/**
* The compatibility module.
*
* @package WooCommerce\PayPalCommerce\Compat
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Compat;
use Dhii\Modular\Module\ModuleInterface;
return static function (): ModuleInterface {
return new CompatModule();
};

View file

@ -0,0 +1,12 @@
<?php
/**
* The compatibility module services.
*
* @package WooCommerce\PayPalCommerce\Compat
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Compat;
return array();

View file

@ -0,0 +1,49 @@
<?php
/**
* The compatibility module.
*
* @package WooCommerce\PayPalCommerce\Compat
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Compat;
use Dhii\Container\ServiceProvider;
use Dhii\Modular\Module\ModuleInterface;
use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
/**
* Class CompatModule
*/
class CompatModule implements ModuleInterface {
/**
* Setup the compatibility module.
*
* @return ServiceProviderInterface
*/
public function setup(): ServiceProviderInterface {
return new ServiceProvider(
require __DIR__ . '/../services.php',
require __DIR__ . '/../extensions.php'
);
}
/**
* Run the compatibility module.
*
* @param ContainerInterface|null $container The Container.
*/
public function run( ContainerInterface $container ): void {
}
/**
* Returns the key for the module.
*
* @return string|void
*/
public function getKey() {
}
}

View file

@ -40,7 +40,8 @@ class OnboardingModule implements ModuleInterface {
*
* @param ContainerInterface|null $container The container.
*/
public function run( ContainerInterface $container = null ) {
public function run( ContainerInterface $container ): void {
$asset_loader = $container->get( 'onboarding.assets' );
/**
* The OnboardingAssets.

View file

@ -37,7 +37,7 @@ class SessionModule implements ModuleInterface {
*
* @param ContainerInterface|null $container The container.
*/
public function run( ContainerInterface $container = null ) {
public function run( ContainerInterface $container ): void {
add_action(
'woocommerce_init',
function () use ( $container ) {

View file

@ -44,7 +44,7 @@ class SubscriptionModule implements ModuleInterface {
*
* @param ContainerInterface|null $container The container.
*/
public function run( ContainerInterface $container = null ) {
public function run( ContainerInterface $container ): void {
add_action(
'woocommerce_scheduled_subscription_payment_' . PayPalGateway::ID,
function ( $amount, $order ) use ( $container ) {

View file

@ -25,6 +25,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
use Woocommerce\PayPalCommerce\WcGateway\Helper\DccProductStatus;
use Woocommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
use WooCommerce\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice;
use WooCommerce\PayPalCommerce\WcGateway\Notice\ConnectAdminNotice;
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
@ -114,6 +115,10 @@ return array(
'wcgateway.settings.sections-renderer' => static function ( $container ): SectionsRenderer {
return new SectionsRenderer();
},
'wcgateway.settings.status' => static function ( $container ): SettingsStatus {
$settings = $container->get( 'wcgateway.settings' );
return new SettingsStatus( $settings );
},
'wcgateway.settings.render' => static function ( $container ): SettingsRenderer {
$settings = $container->get( 'wcgateway.settings' );
$state = $container->get( 'onboarding.state' );
@ -121,13 +126,15 @@ return array(
$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' );
return new SettingsRenderer(
$settings,
$state,
$fields,
$dcc_applies,
$messages_apply,
$dcc_product_status
$dcc_product_status,
$settings_status
);
},
'wcgateway.settings.listener' => static function ( $container ): SettingsListener {
@ -828,7 +835,7 @@ return array(
),
'requirements' => array( 'messages' ),
'gateway' => 'paypal',
'description' => str_replace( '<a>', '<a href="' . $messages_disclaimers->link_for_country() . '" target="_blank">', __( 'Displays Pay Later messaging for available offers. Restrictions apply. <a>Click here to learn more.</a>', 'woocommerce-paypal-payments' ) ),
'description' => str_replace( '<a>', '<a href="' . $messages_disclaimers->link_for_country() . '" target="_blank">', __( 'Displays Pay Later messaging for available offers. Restrictions apply. <a>Click here to learn more</a>. Pay Later button will show for eligible buyers and PayPal determines eligibility.', 'woocommerce-paypal-payments' ) ),
'class' => array( 'ppcp-subheading' ),
),
'message_enabled' => array(
@ -1131,7 +1138,7 @@ return array(
),
'requirements' => array( 'messages' ),
'gateway' => 'paypal',
'description' => str_replace( '<a>', '<a href="' . $messages_disclaimers->link_for_country() . '" target="_blank">', __( 'Displays Pay Later messaging for available offers. Restrictions apply. <a>Click here to learn more.</a>', 'woocommerce-paypal-payments' ) ),
'description' => str_replace( '<a>', '<a href="' . $messages_disclaimers->link_for_country() . '" target="_blank">', __( 'Displays Pay Later messaging for available offers. Restrictions apply. <a>Click here to learn more</a>. Pay Later button will show for eligible buyers and PayPal determines eligibility.', 'woocommerce-paypal-payments' ) ),
'class' => array( 'ppcp-subheading' ),
),
'message_product_enabled' => array(
@ -1434,7 +1441,7 @@ return array(
),
'requirements' => array( 'messages' ),
'gateway' => 'paypal',
'description' => str_replace( '<a>', '<a href="' . $messages_disclaimers->link_for_country() . '" target="_blank">', __( 'Displays Pay Later messaging for available offers. Restrictions apply. <a>Click here to learn more.</a>', 'woocommerce-paypal-payments' ) ),
'description' => str_replace( '<a>', '<a href="' . $messages_disclaimers->link_for_country() . '" target="_blank">', __( 'Displays Pay Later messaging for available offers. Restrictions apply. <a>Click here to learn more</a>. Pay Later button will show for eligible buyers and PayPal determines eligibility.', 'woocommerce-paypal-payments' ) ),
'class' => array( 'ppcp-subheading' ),
),
'message_cart_enabled' => array(
@ -1623,7 +1630,7 @@ return array(
'type' => 'select',
'class' => array(),
'input_class' => array( 'wc-enhanced-select' ),
'default' => 'horizontal',
'default' => 'vertical',
'desc_tip' => true,
'description' => __(
'If additional funding sources are available to the buyer through PayPal, such as Venmo, then multiple buttons are displayed in the space provided. Choose "vertical" for a dynamic list of alternative and local payment options, or "horizontal" when space is limited.',
@ -1727,6 +1734,19 @@ return array(
'requirements' => array(),
'gateway' => 'paypal',
),
'button_mini-cart_height' => array(
'title' => __( 'Button Height', 'woocommerce-paypal-payments' ),
'type' => 'number',
'default' => '35',
'desc_tip' => true,
'description' => __( 'Add a value from 25 to 55.', 'woocommerce-paypal-payments' ),
'screens' => array(
State::STATE_PROGRESSIVE,
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
'disable_cards' => array(
'title' => __( 'Disable specific credit cards', 'woocommerce-paypal-payments' ),

View file

@ -10,6 +10,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Assets;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class SettingsPageAssets
@ -111,13 +112,17 @@ class SettingsPageAssets {
true
);
$token = $bearer->bearer();
wp_localize_script(
'ppcp-gateway-settings',
'PayPalCommerceGatewaySettings',
array(
'vaulting_features_available' => $token->vaulting_available(),
)
);
try {
$token = $bearer->bearer();
wp_localize_script(
'ppcp-gateway-settings',
'PayPalCommerceGatewaySettings',
array(
'vaulting_features_available' => $token->vaulting_available(),
)
);
} catch ( RuntimeException $exception ) {
return;
}
}
}

View file

@ -96,6 +96,13 @@ class PayPalGateway extends \WC_Payment_Gateway {
*/
private $refund_processor;
/**
* Whether the plugin is in onboarded state.
*
* @var bool
*/
private $onboarded;
/**
* PayPalGateway constructor.
*
@ -132,8 +139,9 @@ class PayPalGateway extends \WC_Payment_Gateway {
$this->session_handler = $session_handler;
$this->refund_processor = $refund_processor;
$this->transaction_url_provider = $transaction_url_provider;
$this->onboarded = $state->current_state() === State::STATE_ONBOARDED;
if ( $state->current_state() === State::STATE_ONBOARDED ) {
if ( $this->onboarded ) {
$this->supports = array( 'refunds' );
}
if (
@ -185,7 +193,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
*/
public function needs_setup(): bool {
return true;
return ! $this->onboarded;
}
/**
@ -373,4 +381,20 @@ class PayPalGateway extends \WC_Payment_Gateway {
return parent::get_transaction_url( $order );
}
/**
* Updates WooCommerce gateway option.
*
* @param string $key The option key.
* @param string $value The option value.
* @return bool|void
*/
public function update_option( $key, $value = '' ) {
parent::update_option( $key, $value );
if ( 'enabled' === $key ) {
$this->config->set( 'enabled', 'yes' === $value );
$this->config->persist();
}
}
}

View file

@ -0,0 +1,55 @@
<?php
/**
* Helper to get settings status.
*
* @package Woocommerce\PayPalCommerce\WcGateway\Helper
*/
declare(strict_types=1);
namespace Woocommerce\PayPalCommerce\WcGateway\Helper;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
/**
* Class SettingsStatus
*/
class SettingsStatus {
/**
* The Settings.
*
* @var Settings
*/
protected $settings;
/**
* SettingsStatus constructor.
*
* @param Settings $settings The Settings.
*/
public function __construct( Settings $settings ) {
$this->settings = $settings;
}
/**
* Check whether Pay Later message is enabled either for checkout, cart or product page.
*
* @return bool
* @throws \WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException When a setting was not found.
*/
public function pay_later_messaging_is_enabled(): bool {
$pay_later_message_enabled_for_checkout = $this->settings->has( 'message_enabled' )
&& (bool) $this->settings->get( 'message_enabled' );
$pay_later_message_enabled_for_cart = $this->settings->has( 'message_cart_enabled' )
&& (bool) $this->settings->get( 'message_cart_enabled' );
$pay_later_message_enabled_for_product = $this->settings->has( 'message_product_enabled' )
&& (bool) $this->settings->get( 'message_product_enabled' );
return $pay_later_message_enabled_for_checkout ||
$pay_later_message_enabled_for_cart ||
$pay_later_message_enabled_for_product;
}
}

View file

@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Settings;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
@ -147,11 +148,23 @@ class SettingsListener {
return;
}
$token = $this->bearer->bearer();
if ( ! $token->vaulting_available() ) {
$this->settings->set( 'vault_enabled', false );
$this->settings->persist();
return;
try {
$token = $this->bearer->bearer();
if ( ! $token->vaulting_available() ) {
$this->settings->set( 'vault_enabled', false );
$this->settings->persist();
return;
}
} catch ( RuntimeException $exception ) {
add_action(
'admin_notices',
function () use ( $exception ) {
printf(
'<div class="notice notice-error"><p>%s</p></div>',
esc_attr( $exception->getMessage() )
);
}
);
}
/**
@ -315,6 +328,7 @@ class SettingsListener {
$settings[ $key ] = isset( $raw_data[ $key ] );
break;
case 'text':
case 'number':
case 'ppcp-text-input':
case 'ppcp-password':
$settings[ $key ] = isset( $raw_data[ $key ] ) ? sanitize_text_field( $raw_data[ $key ] ) : '';

View file

@ -16,12 +16,20 @@ use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use Psr\Container\ContainerInterface;
use Woocommerce\PayPalCommerce\WcGateway\Helper\DccProductStatus;
use Woocommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
/**
* Class SettingsRenderer
*/
class SettingsRenderer {
/**
* The Settings status helper.
*
* @var SettingsStatus
*/
protected $settings_status;
/**
* The settings.
*
@ -73,6 +81,7 @@ class SettingsRenderer {
* @param DccApplies $dcc_applies Whether DCC gateway can be shown.
* @param MessagesApply $messages_apply Whether messages can be shown.
* @param DccProductStatus $dcc_product_status The product status.
* @param SettingsStatus $settings_status The Settings status helper.
*/
public function __construct(
ContainerInterface $settings,
@ -80,7 +89,8 @@ class SettingsRenderer {
array $fields,
DccApplies $dcc_applies,
MessagesApply $messages_apply,
DccProductStatus $dcc_product_status
DccProductStatus $dcc_product_status,
SettingsStatus $settings_status
) {
$this->settings = $settings;
@ -89,6 +99,7 @@ class SettingsRenderer {
$this->dcc_applies = $dcc_applies;
$this->messages_apply = $messages_apply;
$this->dcc_product_status = $dcc_product_status;
$this->settings_status = $settings_status;
}
/**
@ -106,7 +117,7 @@ class SettingsRenderer {
$pay_later_messages_title = __( 'Pay Later Messaging', 'woocommerce-paypal-payments' );
$enabled = $this->paypal_vaulting_is_enabled() ? $vaulting_title : $pay_later_messages_title;
$disabled = $this->pay_later_messaging_is_enabled() ? $vaulting_title : $pay_later_messages_title;
$disabled = $this->settings_status->pay_later_messaging_is_enabled() ? $vaulting_title : $pay_later_messages_title;
$pay_later_messages_or_vaulting_text = sprintf(
// translators: %1$s and %2$s is translated PayPal vaulting and Pay Later Messaging strings.
@ -149,26 +160,6 @@ class SettingsRenderer {
return $this->settings->has( 'vault_enabled' ) && (bool) $this->settings->get( 'vault_enabled' );
}
/**
* Check whether Pay Later message is enabled either for checkout, cart or product page.
*
* @return bool
*/
private function pay_later_messaging_is_enabled(): bool {
$pay_later_message_enabled_for_checkout = $this->settings->has( 'message_enabled' )
&& (bool) $this->settings->get( 'message_enabled' );
$pay_later_message_enabled_for_cart = $this->settings->has( 'message_cart_enabled' )
&& (bool) $this->settings->get( 'message_cart_enabled' );
$pay_later_message_enabled_for_product = $this->settings->has( 'message_product_enabled' )
&& (bool) $this->settings->get( 'message_product_enabled' );
return $pay_later_message_enabled_for_checkout ||
$pay_later_message_enabled_for_cart ||
$pay_later_message_enabled_for_product;
}
/**
* Check if current screen is PayPal checkout settings screen.
*
@ -557,6 +548,6 @@ class SettingsRenderer {
}
return $this->is_paypal_checkout_screen() && $this->paypal_vaulting_is_enabled()
|| $this->is_paypal_checkout_screen() && $this->pay_later_messaging_is_enabled();
|| $this->is_paypal_checkout_screen() && $this->settings_status->pay_later_messaging_is_enabled();
}
}

View file

@ -53,7 +53,7 @@ class WcGatewayModule implements ModuleInterface {
*
* @param ContainerInterface|null $container The container.
*/
public function run( ContainerInterface $container = null ) {
public function run( ContainerInterface $container ): void {
$this->register_payment_gateways( $container );
$this->register_order_functionality( $container );
$this->register_columns( $container );
@ -135,6 +135,18 @@ class WcGatewayModule implements ModuleInterface {
$endpoint->handle_request();
}
);
add_filter(
'woocommerce_email_recipient_customer_on_hold_order',
function( $recipient, $order ) {
if ( $order->get_payment_method() === PayPalGateway::ID || $order->get_payment_method() === CreditCardGateway::ID ) {
$recipient = '';
}
return $recipient;
},
10,
2
);
}
/**

View file

@ -36,7 +36,7 @@ class WebhookModule implements ModuleInterface {
*
* @param ContainerInterface|null $container The Container.
*/
public function run( ContainerInterface $container = null ) {
public function run( ContainerInterface $container ): void {
add_action(
'rest_api_init',
static function () use ( $container ) {

View file

@ -36,7 +36,7 @@ class WooCommerceLoggingModule implements ModuleInterface {
*
* @param ContainerInterface $container The container.
*/
public function run( ContainerInterface $container = null ) {
public function run( ContainerInterface $container ): void {
}