Merge pull request #134 from woocommerce/feature/PCP-88-payment-token-vaulting

Introduce client-side vaulting and allow Subscription renewals through payment tokens
This commit is contained in:
Emili Castells 2021-04-08 11:09:15 +02:00 committed by GitHub
commit d257b270df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 5357 additions and 185 deletions

View file

@ -0,0 +1,21 @@
{
"name": "ppcp-wc-gateway",
"version": "1.0.0",
"license": "GPL-3.0-or-later",
"main": "resources/js/gateway-settings.js",
"devDependencies": {
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.5",
"babel-loader": "^8.1.0",
"cross-env": "^5.0.1",
"file-loader": "^4.2.0",
"webpack": "^4.42.1",
"webpack-cli": "^3.1.2",
"babel-plugin-transform-object-rest-spread": "^6.26.0"
},
"scripts": {
"build": "cross-env BABEL_ENV=default NODE_ENV=production webpack",
"watch": "cross-env BABEL_ENV=default NODE_ENV=production webpack --watch",
"dev": "cross-env BABEL_ENV=default webpack --watch"
}
}

View file

@ -0,0 +1,34 @@
;document.addEventListener(
'DOMContentLoaded',
() => {
const payLaterMessagingCheckboxes = document.querySelectorAll(
"#ppcp-message_enabled, #ppcp-message_cart_enabled, #ppcp-message_product_enabled"
)
const vaultingCheckboxes = document.querySelectorAll(
"#ppcp-vault_enabled, #ppcp-save_paypal_account"
)
function atLeastOneChecked(checkboxesNodeList) {
return Array.prototype.slice.call(checkboxesNodeList).filter(node => !node.disabled && node.checked).length > 0
}
function disableAll(nodeList){
nodeList.forEach(node => node.setAttribute('disabled', 'true'))
}
function enableAll(nodeList){
nodeList.forEach(node => node.removeAttribute('disabled'))
}
function updateCheckboxes() {
atLeastOneChecked(payLaterMessagingCheckboxes) ? disableAll(vaultingCheckboxes) : enableAll(vaultingCheckboxes)
atLeastOneChecked(vaultingCheckboxes) ? disableAll(payLaterMessagingCheckboxes) : enableAll(payLaterMessagingCheckboxes)
}
updateCheckboxes()
payLaterMessagingCheckboxes.forEach(node => node.addEventListener('change', updateCheckboxes))
vaultingCheckboxes.forEach(node => node.addEventListener('change', updateCheckboxes));
}
);

View file

@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\Button\Helper\MessagesDisclaimers;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn;
@ -69,7 +70,10 @@ return array(
$refund_processor = $container->get( 'wcgateway.processor.refunds' );
$state = $container->get( 'onboarding.state' );
$transaction_url_provider = $container->get( 'wcgateway.transaction-url-provider' );
$payment_token_repository = $container->get( 'subscription.repository.payment-token' );
$purchase_unit_factory = $container->get( 'api.factory.purchase-unit' );
$payer_factory = $container->get( 'api.factory.payer' );
$order_endpoint = $container->get( 'api.endpoint.order' );
return new CreditCardGateway(
$settings_renderer,
$order_processor,
@ -80,7 +84,11 @@ return array(
$session_handler,
$refund_processor,
$state,
$transaction_url_provider
$transaction_url_provider,
$payment_token_repository,
$purchase_unit_factory,
$payer_factory,
$order_endpoint
);
},
'wcgateway.disabler' => static function ( $container ): DisableGateways {
@ -174,13 +182,8 @@ return array(
'wcgateway.settings.fields' => static function ( $container ): array {
$state = $container->get( 'onboarding.state' );
/**
* The state.
*
* @var State $state
*/
$settings = $container->get( 'wcgateway.settings' );
$messages_disclaimers = $container->get( 'button.helper.messages-disclaimers' );
$fields = array(
'sandbox_on' => array(
@ -485,6 +488,34 @@ return array(
),
'gateway' => 'dcc',
),
'dcc_vault_enabled' => array(
'title' => __( 'Vaulting For Credit Cards', 'woocommerce-paypal-payments' ),
'desc_tip' => true,
'description' => __( 'Enable Payment Tokens for WooCommerce Subscription renewals', 'woocommerce-paypal-payments' ),
'label' => __( 'Enable Vaulting For Credit Cards', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
'default' => false,
'gateway' => 'dcc',
'requirements' => array(
'dcc',
),
'screens' => array(
State::STATE_ONBOARDED,
),
),
'dcc_save_card' => array(
'title' => __( 'Save Credit Card', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
'desc_tip' => true,
'label' => __( 'Allow Registered Buyers to Save Credit Card', 'woocommerce-paypal-payments' ),
'description' => __( 'Buyers that create an account on your store may save their Credit Card for faster checkout.', 'woocommerce-paypal-payments' ),
'default' => true,
'screens' => array(
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'dcc',
),
'description' => array(
'title' => __( 'Description', 'woocommerce-paypal-payments' ),
'type' => 'text',
@ -628,19 +659,31 @@ return array(
'gateway' => 'paypal',
),
'vault_enabled' => array(
'title' => __( 'Vaulting', 'woocommerce-paypal-payments' ),
'title' => __( 'Vaulting for PayPal Accounts', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
'desc_tip' => true,
'label' => __( 'Enable vaulting', 'woocommerce-paypal-payments' ),
'description' => __( 'Enables you to store payment tokens for subscriptions.', 'woocommerce-paypal-payments' ),
'default' => true,
'label' => __( 'Enable Vaulting for PayPal Accounts', 'woocommerce-paypal-payments' ),
'description' => __( 'Enable Payment Tokens for WooCommerce Subscription renewals', 'woocommerce-paypal-payments' ),
'default' => false,
'screens' => array(
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
'save_paypal_account' => array(
'title' => __( 'Save PayPal Account', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
'desc_tip' => true,
'label' => __( 'Allow Registered Buyers to Save PayPal Account', 'woocommerce-paypal-payments' ),
'description' => __( 'Buyers that create an account on your store may save their PayPal account for faster checkout. Note that you may not present Pay Later messages when using this feature.', 'woocommerce-paypal-payments' ),
'default' => false,
'screens' => array(
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
'logging_enabled' => array(
'title' => __( 'Logging', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
@ -659,8 +702,13 @@ return array(
'title' => __( 'Invoice prefix', 'woocommerce-paypal-payments' ),
'type' => 'text',
'desc_tip' => true,
'description' => __( 'If you use your PayPal account with more than one installation, please use a distinct prefix to seperate those installations. Please do not use numbers in your prefix.', 'woocommerce-paypal-payments' ),
'default' => 'WC-',
'description' => __( 'If you use your PayPal account with more than one installation, please use a distinct prefix to separate those installations. Please do not use numbers in your prefix.', 'woocommerce-paypal-payments' ),
'default' => ( static function (): string {
$site_url = get_site_url( get_current_blog_id() );
$hash = md5( $site_url );
$letters = preg_replace( '~\d~', '', $hash );
return substr( $letters, 0, 6 ) . '-';
} )(),
'screens' => array(
State::STATE_PROGRESSIVE,
State::STATE_ONBOARDED,
@ -811,7 +859,7 @@ return array(
),
'requirements' => array( 'messages' ),
'gateway' => 'paypal',
'description' => str_replace( '<a>', '<a href="https://www.paypal.com/us/business/buy-now-pay-later">', __( 'Customize the appearance of <a>Pay Later messages</a> on checkout to promote special financing offers, which help increase sales.', '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>', 'woocommerce-paypal-payments' ) ),
'class' => array( 'ppcp-subheading' ),
),
'message_enabled' => array(
@ -1114,7 +1162,7 @@ return array(
),
'requirements' => array( 'messages' ),
'gateway' => 'paypal',
'description' => str_replace( '<a>', '<a href="https://www.paypal.com/us/business/buy-now-pay-later">', __( 'Customize the appearance of <a>Pay Later messages</a> on product pages to promote special financing offers, which help increase sales.', '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>', 'woocommerce-paypal-payments' ) ),
'class' => array( 'ppcp-subheading' ),
),
'message_product_enabled' => array(
@ -1417,7 +1465,7 @@ return array(
),
'requirements' => array( 'messages' ),
'gateway' => 'paypal',
'description' => str_replace( '<a>', '<a href="https://www.paypal.com/us/business/buy-now-pay-later">', __( 'Customize the appearance of <a>Pay Later messages</a> on your cart page to promote special financing offers, which help increase sales.', '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>', 'woocommerce-paypal-payments' ) ),
'class' => array( 'ppcp-subheading' ),
),
'message_cart_enabled' => array(
@ -1792,51 +1840,6 @@ return array(
unset( $fields['disable_funding']['options']['card'] );
}
/**
* Set Pay in 3 heading and description for UK.
*/
if ( 'GB' === $country ) {
$fields['message_heading']['heading'] = __( 'Pay Later Messaging on Checkout', 'woocommerce-paypal-payments' );
$fields['message_heading']['description'] = __( 'Display pay later messaging on your site for offers like Pay in 3, which lets customers pay with 3 interest-free monthly payments. Well show messages on your site to promote this feature for you. You may not promote pay later offers with any other content, marketing, or materials.', 'woocommerce-paypal-payments' );
$fields['message_product_heading']['heading'] = __( 'Pay Later Messaging on Single Product Page', 'woocommerce-paypal-payments' );
$fields['message_product_heading']['description'] = __( 'Display pay later messaging on your site for offers like Pay in 3, which lets customers pay with 3 interest-free monthly payments. Well show messages on your site to promote this feature for you. You may not promote pay later offers with any other content, marketing, or materials.', 'woocommerce-paypal-payments' );
$fields['message_cart_heading']['heading'] = __( 'Pay Later Messaging on Cart', 'woocommerce-paypal-payments' );
$fields['message_cart_heading']['description'] = __( 'Display pay later messaging on your site for offers like Pay in 3, which lets customers pay with 3 interest-free monthly payments. Well show messages on your site to promote this feature for you. You may not promote pay later offers with any other content, marketing, or materials.', 'woocommerce-paypal-payments' );
}
if ( 'FR' === $country ) {
// todo: replace this with the text in English and use this text for French translation when it will be created.
$french_pay_later_description = 'Affichez le Paiement en 4X PayPal sur votre site.' .
'Le Paiement en 4X PayPal permet aux consommateurs français de payer en 4 versements égaux.' .
'Vous pouvez promouvoir le Paiement en 4X PayPal uniquement si vous êtes un commerçant basé en France, ' .
'avec un site internet en français et uneintégration PayPal standard. ' .
'Les marchands ayantloutil Vaulting(coffre-fort numérique) ou une intégration de paiements récurrents/abonnement, ' .
'ainsi que ceux présentant certaines activités (vente de biens numériques / de biens non physiques) ' .
'ne sont pas éligibles pour promouvoir le Paiement en 4X PayPal.' .
'Nous afficherons des messages sur votre site pour promouvoir le Paiement en 4X PayPal. ' .
'Vous ne pouvez pas promouvoir le Paiement en 4X PayPal avec un autre contenu, quel quil soit.';
$fields['message_heading']['heading'] = __( 'Pay Later Messaging on Checkout', 'woocommerce-paypal-payments' );
$fields['message_heading']['description'] = $french_pay_later_description;
$fields['message_product_heading']['heading'] = __( 'Pay Later Messaging on Single Product Page', 'woocommerce-paypal-payments' );
$fields['message_product_heading']['description'] = $french_pay_later_description;
$fields['message_cart_heading']['heading'] = __( 'Pay Later Messaging on Cart', 'woocommerce-paypal-payments' );
$fields['message_cart_heading']['description'] = $french_pay_later_description;
}
/**
* Set Pay Later link for DE
*/
if ( 'DE' === $country ) {
$fields['message_heading']['description'] = str_replace( '<a>', '<a href="https://www.paypal.com/de/webapps/mpp/installments">', __( 'Customize the appearance of <a>Pay Later messages</a> on checkout to promote special financing offers, which help increase sales.', 'woocommerce-paypal-payments' ) );
$fields['message_product_heading']['description'] = str_replace( '<a>', '<a href="https://www.paypal.com/de/webapps/mpp/installments">', __( 'Customize the appearance of <a>Pay Later messages</a> on checkout to promote special financing offers, which help increase sales.', 'woocommerce-paypal-payments' ) );
$fields['message_cart_heading']['description'] = str_replace( '<a>', '<a href="https://www.paypal.com/de/webapps/mpp/installments">', __( 'Customize the appearance of <a>Pay Later messages</a> on checkout to promote special financing offers, which help increase sales.', 'woocommerce-paypal-payments' ) );
}
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
/**
* Depending on your store location, some credit cards can't be used.
@ -1866,10 +1869,19 @@ return array(
},
'wcgateway.url' => static function ( $container ): string {
return plugins_url(
'/modules/ppcp-wc-gateway/',
$container->get( 'wcgateway.relative-path' ),
dirname( __FILE__, 3 ) . '/woocommerce-paypal-payments.php'
);
},
'wcgateway.relative-path' => static function( $container ): string {
return 'modules/ppcp-wc-gateway/';
},
'wcgateway.absolute-path' => static function( $container ): string {
return plugin_dir_path(
dirname( __FILE__, 3 ) . '/woocommerce-paypal-payments.php'
) .
$container->get( 'wcgateway.relative-path' );
},
'wcgateway.endpoint.return-url' => static function ( $container ) : ReturnUrlEndpoint {
$gateway = $container->get( 'wcgateway.paypal-gateway' );
$endpoint = $container->get( 'api.endpoint.order' );
@ -1902,4 +1914,8 @@ return array(
$partner_endpoint = $container->get( 'api.endpoint.partners' );
return new DccProductStatus( $settings, $partner_endpoint );
},
'button.helper.messages-disclaimers' => static function ( $container ): MessagesDisclaimers {
return new MessagesDisclaimers();
},
);

View file

@ -0,0 +1,99 @@
<?php
/**
* Register and configure assets provided by this module.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Assets
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Assets;
/**
* Class SettingsPageAssets
*/
class SettingsPageAssets {
/**
* The URL of this module.
*
* @var string
*/
private $module_url;
/**
* The filesystem path to the module dir.
*
* @var string
*/
private $module_path;
/**
* Assets constructor.
*
* @param string $module_url The url of this module.
* @param string $module_path The filesystem path to this module.
*/
public function __construct( string $module_url, string $module_path ) {
$this->module_url = $module_url;
$this->module_path = $module_path;
}
/**
* Register assets provided by this module.
*/
public function register_assets() {
add_action(
'admin_enqueue_scripts',
function() {
if ( ! is_admin() || is_ajax() ) {
return;
}
if ( ! $this->is_paypal_payment_method_page() ) {
return;
}
$this->register_admin_assets();
}
);
}
/**
* Check whether the current page is PayPal payment method settings.
*
* @return bool
*/
private function is_paypal_payment_method_page(): bool {
if ( ! function_exists( 'get_current_screen' ) ) {
return false;
}
$screen = get_current_screen();
$tab = filter_input( INPUT_GET, 'tab', FILTER_SANITIZE_STRING );
$section = filter_input( INPUT_GET, 'section', FILTER_SANITIZE_STRING );
if ( ! 'woocommerce_page_wc-settings' === $screen->id ) {
return false;
}
return 'checkout' === $tab && 'ppcp-gateway' === $section;
}
/**
* Register assets for admin pages.
*/
private function register_admin_assets() {
$gateway_settings_script_path = trailingslashit( $this->module_path ) . 'assets/js/gateway-settings.js';
wp_enqueue_script(
'ppcp-gateway-settings',
trailingslashit( $this->module_url ) . 'assets/js/gateway-settings.js',
array(),
file_exists( $gateway_settings_script_path ) ? (string) filemtime( $gateway_settings_script_path ) : null,
true
);
}
}

View file

@ -79,7 +79,10 @@ class DisableGateways {
}
if ( $this->is_credit_card() ) {
return array( CreditCardGateway::ID => $methods[ CreditCardGateway::ID ] );
return array(
CreditCardGateway::ID => $methods[ CreditCardGateway::ID ],
PayPalGateway::ID => $methods[ PayPalGateway::ID ],
);
}
return array( PayPalGateway::ID => $methods[ PayPalGateway::ID ] );
}

View file

@ -9,8 +9,12 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Subscription\Repository\PaymentTokenRepository;
use WooCommerce\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice;
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
@ -48,6 +52,34 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
*/
private $refund_processor;
/**
* The payment token repository.
*
* @var PaymentTokenRepository
*/
private $payment_token_repository;
/**
* The purchase unit factory.
*
* @var PurchaseUnitFactory
*/
private $purchase_unit_factory;
/**
* The payer factory.
*
* @var PayerFactory
*/
private $payer_factory;
/**
* The order endpoint.
*
* @var OrderEndpoint
*/
private $order_endpoint;
/**
* CreditCardGateway constructor.
*
@ -61,6 +93,10 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
* @param RefundProcessor $refund_processor The refund processor.
* @param State $state The state.
* @param TransactionUrlProvider $transaction_url_provider Service able to provide view transaction url base.
* @param PaymentTokenRepository $payment_token_repository The payment token repository.
* @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
* @param PayerFactory $payer_factory The payer factory.
* @param OrderEndpoint $order_endpoint The order endpoint.
*/
public function __construct(
SettingsRenderer $settings_renderer,
@ -72,7 +108,11 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
SessionHandler $session_handler,
RefundProcessor $refund_processor,
State $state,
TransactionUrlProvider $transaction_url_provider
TransactionUrlProvider $transaction_url_provider,
PaymentTokenRepository $payment_token_repository,
PurchaseUnitFactory $purchase_unit_factory,
PayerFactory $payer_factory,
OrderEndpoint $order_endpoint
) {
$this->id = self::ID;
@ -90,8 +130,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
if (
defined( 'PPCP_FLAG_SUBSCRIPTION' )
&& PPCP_FLAG_SUBSCRIPTION
&& $this->config->has( 'vault_enabled' )
&& $this->config->get( 'vault_enabled' )
&& $this->vault_settings_enabled()
) {
$this->supports = array(
'refunds',
@ -106,7 +145,6 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
'subscription_payment_method_change_customer',
'subscription_payment_method_change_admin',
'multiple_subscriptions',
'credit_card_form_cvc_on_saved_method',
);
}
@ -135,6 +173,10 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
);
$this->module_url = $module_url;
$this->payment_token_repository = $payment_token_repository;
$this->purchase_unit_factory = $purchase_unit_factory;
$this->payer_factory = $payer_factory;
$this->order_endpoint = $order_endpoint;
$this->transaction_url_provider = $transaction_url_provider;
}

View file

@ -129,8 +129,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
if (
defined( 'PPCP_FLAG_SUBSCRIPTION' )
&& PPCP_FLAG_SUBSCRIPTION
&& $this->config->has( 'vault_enabled' )
&& $this->config->get( 'vault_enabled' )
&& $this->vault_settings_enabled()
) {
$this->supports = array(
'refunds',

View file

@ -9,6 +9,7 @@ declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
@ -40,6 +41,56 @@ trait ProcessPaymentTrait {
return $failure_data;
}
/**
* If customer has chosed a saved credit card payment.
*/
$saved_credit_card = filter_input( INPUT_POST, 'saved_credit_card', FILTER_SANITIZE_STRING );
if ( $saved_credit_card ) {
$user_id = (int) $wc_order->get_customer_id();
$customer = new \WC_Customer( $user_id );
$tokens = $this->payment_token_repository->all_for_user_id( (int) $customer->get_id() );
$selected_token = null;
foreach ( $tokens as $token ) {
if ( $token->id() === $saved_credit_card ) {
$selected_token = $token;
break;
}
}
if ( ! $selected_token ) {
return null;
}
$purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order );
$payer = $this->payer_factory->from_customer( $customer );
try {
$order = $this->order_endpoint->create(
array( $purchase_unit ),
$payer,
$selected_token
);
if ( $order->status()->is( OrderStatus::COMPLETED ) && $order->intent() === 'CAPTURE' ) {
$wc_order->update_status(
'processing',
__( 'Payment received.', 'woocommerce-paypal-payments' )
);
$this->session_handler->destroy_session_data();
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $wc_order ),
);
}
} catch ( RuntimeException $error ) {
$this->session_handler->destroy_session_data();
wc_add_notice( $error->getMessage(), 'error' );
return null;
}
}
/**
* If the WC_Order is payed through the approved webhook.
*/
@ -95,4 +146,20 @@ trait ProcessPaymentTrait {
return $failure_data;
}
/**
* Checks if vault enabled setting for PayPal or credit card is enabled.
*
* @return bool Whether vault settings are enabled or not.
* @throws \WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException When a setting hasn't been found.
*/
protected function vault_settings_enabled(): bool {
if ( $this->config->has( 'vault_enabled' ) && $this->config->get( 'vault_enabled' ) ) {
return true;
}
if ( $this->config->has( 'dcc_vault_enabled' ) && $this->config->get( 'dcc_vault_enabled' ) ) {
return true;
}
return false;
}
}

View file

@ -129,6 +129,34 @@ class SettingsListener {
exit;
}
/**
* Prevent enabling both Pay Later messaging and PayPal vaulting
*/
public function listen_for_vaulting_enabled() {
if ( ! $this->is_valid_site_request() ) {
return;
}
/**
* No need to verify nonce here.
*
* phpcs:disable WordPress.Security.NonceVerification.Missing
* phpcs:disable WordPress.Security.NonceVerification.Recommended
*/
if ( ! isset( $_POST['ppcp']['vault_enabled'] ) && ! isset( $_POST['ppcp']['save_paypal_account'] ) ) {
return;
}
$this->settings->set( 'message_enabled', false );
$this->settings->set( 'message_product_enabled', false );
$this->settings->set( 'message_cart_enabled', false );
$this->settings->persist();
$redirect_url = admin_url( 'admin.php?page=wc-settings&tab=checkout&section=ppcp-gateway' );
wp_safe_redirect( $redirect_url, 302 );
exit;
}
/**
* Listens to the request.
*

View file

@ -10,7 +10,6 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Settings;
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
use WooCommerce\PayPalCommerce\Onboarding\State;
@ -93,33 +92,113 @@ class SettingsRenderer {
}
/**
* Returns the notice, when onboarding failed.
* Returns notices list.
*
* @return array
*/
public function messages() : array {
$messages = array();
if ( $this->is_paypal_checkout_screen() && $this->paypal_vaulting_is_enabled()
|| $this->is_paypal_checkout_screen() && $this->pay_later_messaging_is_enabled() ) {
$vaulting_title = __( 'PayPal vaulting', 'woocommerce-paypal-payments' );
$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;
$pay_later_messages_or_vaulting_text = sprintf(
// translators: %1$s and %2$s is translated PayPal vaulting and Pay Later Messaging strings.
__(
'You have %1$s enabled, that\'s why %2$s options are unavailable now. You cannot use both features at the same time',
'woocommerce-paypal-payments'
),
$enabled,
$disabled
);
$messages[] = new Message( $pay_later_messages_or_vaulting_text, 'warning' );
}
//phpcs:disable WordPress.Security.NonceVerification.Recommended
//phpcs:disable WordPress.Security.NonceVerification.Missing
if ( ! isset( $_GET['ppcp-onboarding-error'] ) || ! empty( $_POST ) ) {
return array();
return $messages;
}
//phpcs:enable WordPress.Security.NonceVerification.Recommended
//phpcs:enable WordPress.Security.NonceVerification.Missing
$messages = array(
new Message(
__(
'We could not complete the onboarding process. Some features, such as card processing, will not be available. To fix this, please try again.',
'woocommerce-paypal-payments'
),
'error',
false
$messages[] = new Message(
__(
'We could not complete the onboarding process. Some features, such as card processing, will not be available. To fix this, please try again.',
'woocommerce-paypal-payments'
),
'error',
false
);
return $messages;
}
/**
* Check whether PayPal vaulting is enabled.
*
* @return bool
*/
private function paypal_vaulting_is_enabled(): bool {
$saving_paypal_account_is_enabled = $this->settings->has( 'save_paypal_account' ) &&
(bool) $this->settings->get( 'save_paypal_account' );
$vault_is_enabled = $this->settings->has( 'vault_enabled' ) &&
(bool) $this->settings->get( 'vault_enabled' );
return $saving_paypal_account_is_enabled || $vault_is_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.
*
* @return bool Whether is PayPal checkout screen or not.
*/
private function is_paypal_checkout_screen(): bool {
$current_screen = get_current_screen();
//phpcs:disable WordPress.Security.NonceVerification.Recommended
//phpcs:disable WordPress.Security.NonceVerification.Missing
if ( isset( $current_screen->id ) && 'woocommerce_page_wc-settings' === $current_screen->id
&& isset( $_GET['section'] ) && 'ppcp-gateway' === $_GET['section'] ) {
if ( isset( $_GET['ppcp-tab'] ) && 'ppcp-gateway' !== $_GET['ppcp-tab'] ) {
return false;
}
return true;
}
//phpcs:enable
return false;
}
/**
* Renders the multiselect field.
*
@ -295,7 +374,7 @@ class SettingsRenderer {
$key = 'ppcp[' . $field . ']';
$id = 'ppcp-' . $field;
$config['id'] = $id;
$th_td = 'ppcp-heading' !== $config['type'] ? 'td' : 'td';
$th_td = 'ppcp-heading' !== $config['type'] ? 'td' : 'th';
$colspan = 'ppcp-heading' !== $config['type'] ? 1 : 2;
$classes = isset( $config['classes'] ) ? $config['classes'] : array();
$classes[] = sprintf( 'ppcp-settings-field-%s', str_replace( 'ppcp-', '', $config['type'] ) );

View file

@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository;
use WooCommerce\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn;
use WooCommerce\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail;
use WooCommerce\PayPalCommerce\WcGateway\Admin\RenderAuthorizeAction;
use WooCommerce\PayPalCommerce\WcGateway\Assets\SettingsPageAssets;
use WooCommerce\PayPalCommerce\WcGateway\Checkout\CheckoutPayPalAddressPreset;
use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways;
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
@ -72,6 +73,14 @@ class WcGatewayModule implements ModuleInterface {
}
);
if ( $container->has( 'wcgateway.url' ) ) {
$assets = new SettingsPageAssets(
$container->get( 'wcgateway.url' ),
$container->get( 'wcgateway.absolute-path' )
);
$assets->register_assets();
}
add_filter(
Repository::NOTICES_FILTER,
static function ( $notices ) use ( $container ): array {
@ -223,6 +232,7 @@ class WcGatewayModule implements ModuleInterface {
* @var SettingsListener $listener
*/
$listener->listen_for_merchant_id();
$listener->listen_for_vaulting_enabled();
}
);

View file

@ -0,0 +1,22 @@
const path = require('path');
const isProduction = process.env.NODE_ENV === 'production';
module.exports = {
devtool: 'sourcemap',
mode: isProduction ? 'production' : 'development',
target: 'web',
entry: {
'gateway-settings': path.resolve('./resources/js/gateway-settings.js'),
},
output: {
path: path.resolve(__dirname, 'assets/'),
filename: 'js/[name].js',
},
module: {
rules: [{
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel-loader',
}]
}
};

File diff suppressed because it is too large Load diff