Merge branch 'trunk' into pcp-157-blocks

This commit is contained in:
Alex P 2023-03-14 09:24:06 +02:00
commit 25b13e4467
No known key found for this signature in database
GPG key ID: 54487A734A204D71
40 changed files with 757 additions and 300 deletions

View file

@ -1 +1,10 @@
PPCP_E2E_WP_DIR=${ROOT_DIR}/.ddev/wordpress
BASEURL="https://woocommerce-paypal-payments.ddev.site"
CUSTOMER_EMAIL="customer@example.com"
CUSTOMER_PASSWORD="password"
CREDIT_CARD_NUMBER="1234567890"
CREDIT_CARD_EXPIRATION="01/2042"
CREDIT_CARD_CVV="123"

View file

@ -1,5 +1,31 @@
*** Changelog ***
= 2.0.3 - 2023-03-14 =
* Fix - `DEVICE_DATA_NOT_AVAILABLE` error message when FraudNet is enabled #1177
* Fix - Redirect to connection tab after manual credentials input #1201
* Fix - Asking for address fields in checkout when not using them #1089
* Fix - Validate before free trial #1170
* Fix - Validate new user creation #1131
* Fix - After Updating to 2.0.2, Site Health reports REST API error #1195
* Fix - Do not send buyer-country for previews in live mode to avoid error #1186
* Fix - PPEC compatibility layer does not take over subscriptions #1193
* Fix - Checkout conflict with "All products for subscriptions" plugin #629
* Fix - Pay Later on order pay page #1214
* Fix - High volume of traffic from merchant-integrations endpoint #1241
* Enhancement - Save checkout form before free trial redirect #1135
* Enhancement - Add filter for controlling the ditching of items/breakdown #1146
* Enhancement - Add patch order data filter #1147
* Enhancement - Add filter for disabling fees on wc order admin pages #1153
* Enhancement - Use wp_loaded for fraudnet loading to avoid warnings #1172
* Enhancement - reCaptcha for WooCommerce support #1093
* Enhancement - Make it possible to hide missing funding resource Trustly #1155
* Enhancement - Add white color option #1167
* Enhancement - Checkout validation for other fields #861
* Enhancement - Mention PUI only for German shops and add line breaks #1169
* Enhancement - Add filter to fallback tracking_data['carrier'] #1188
* Enhancement - Error notices in checkout do not update / or are shown twice #1168
* Enhancement - capture authorized payment by changing order status (or programmatically) #587
= 2.0.2 - 2023-01-31 =
* Fix - Do not call PayPal get order by ID if it does not exist #1029
* Fix - Type check error conflict with German Market #1056

View file

@ -28,7 +28,10 @@ const cardsSpinner = new Spinner('#ppcp-hosted-fields');
const bootstrap = () => {
const checkoutFormSelector = 'form.woocommerce-checkout';
const errorHandler = new ErrorHandler(PayPalCommerceGateway.labels.error.generic);
const errorHandler = new ErrorHandler(
PayPalCommerceGateway.labels.error.generic,
document.querySelector(checkoutFormSelector) ?? document.querySelector('.woocommerce-notices-wrapper')
);
const spinner = new Spinner();
const creditCardRenderer = new CreditCardRenderer(PayPalCommerceGateway, errorHandler, spinner);

View file

@ -62,9 +62,9 @@ class CheckoutActionHandler {
if (data.data.errors?.length > 0) {
errorHandler.messages(data.data.errors);
} else if (data.data.details?.length > 0) {
errorHandler.message(data.data.details.map(d => `${d.issue} ${d.description}`).join('<br/>'), true);
errorHandler.message(data.data.details.map(d => `${d.issue} ${d.description}`).join('<br/>'));
} else {
errorHandler.message(data.data.message, true);
errorHandler.message(data.data.message);
}
}

View file

@ -9,31 +9,17 @@ class SingleProductActionHandler {
constructor(
config,
updateCart,
showButtonCallback,
hideButtonCallback,
formElement,
errorHandler
) {
this.config = config;
this.updateCart = updateCart;
this.showButtonCallback = showButtonCallback;
this.hideButtonCallback = hideButtonCallback;
this.formElement = formElement;
this.errorHandler = errorHandler;
}
configuration()
{
if ( this.hasVariations() ) {
const observer = new ButtonsToggleListener(
this.formElement.querySelector('.single_add_to_cart_button'),
this.showButtonCallback,
this.hideButtonCallback
);
observer.init();
}
return {
createOrder: this.createOrder(),
onApprove: onApprove(this, this.errorHandler),

View file

@ -1,4 +1,5 @@
import CartActionHandler from '../ActionHandler/CartActionHandler';
import {setVisible} from "../Helper/Hiding";
class CartBootstrap {
constructor(gateway, renderer, errorHandler) {
@ -16,13 +17,31 @@ class CartBootstrap {
jQuery(document.body).on('updated_cart_totals updated_checkout', () => {
this.render();
fetch(
this.gateway.ajax.cart_script_params.endpoint,
{
method: 'GET',
credentials: 'same-origin',
}
)
.then(result => result.json())
.then(result => {
if (! result.success) {
return;
}
const newParams = result.data;
const reloadRequired = this.gateway.url_params.intent !== newParams.intent;
// TODO: should reload the script instead
setVisible(this.gateway.button.wrapper, !reloadRequired)
});
});
}
shouldRender() {
return document.querySelector(this.gateway.button.wrapper) !==
null || document.querySelector(this.gateway.hosted_fields.wrapper) !==
null;
return document.querySelector(this.gateway.button.wrapper) !== null;
}
render() {

View file

@ -1,5 +1,7 @@
import UpdateCart from "../Helper/UpdateCart";
import SingleProductActionHandler from "../ActionHandler/SingleProductActionHandler";
import {hide, show, setVisible} from "../Helper/Hiding";
import ButtonsToggleListener from "../Helper/ButtonsToggleListener";
class SingleProductBootstap {
constructor(gateway, renderer, messages, errorHandler) {
@ -12,10 +14,10 @@ class SingleProductBootstap {
handleChange() {
if (!this.shouldRender()) {
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper);
this.renderer.hideButtons(this.gateway.button.wrapper);
this.messages.hideMessages();
const shouldRender = this.shouldRender();
setVisible(this.gateway.button.wrapper, shouldRender);
setVisible(this.gateway.messages.wrapper, shouldRender);
if (!shouldRender) {
return;
}
@ -23,7 +25,6 @@ class SingleProductBootstap {
}
init() {
const form = document.querySelector('form.cart');
if (!form) {
return;
@ -32,20 +33,33 @@ class SingleProductBootstap {
form.addEventListener('change', this.handleChange.bind(this));
this.mutationObserver.observe(form, {childList: true, subtree: true});
const buttonObserver = new ButtonsToggleListener(
form.querySelector('.single_add_to_cart_button'),
() => {
show(this.gateway.button.wrapper);
show(this.gateway.messages.wrapper);
this.messages.renderWithAmount(this.priceAmount())
},
() => {
hide(this.gateway.button.wrapper);
hide(this.gateway.messages.wrapper);
},
);
buttonObserver.init();
if (!this.shouldRender()) {
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper);
this.messages.hideMessages();
hide(this.gateway.button.wrapper);
hide(this.gateway.messages.wrapper);
return;
}
this.render();
}
shouldRender() {
return document.querySelector('form.cart') !== null && !this.priceAmountIsZero();
return document.querySelector('form.cart') !== null
&& !this.priceAmountIsZero()
&& !this.isSubscriptionMode();
}
priceAmount() {
@ -74,6 +88,12 @@ class SingleProductBootstap {
return !price || price === 0;
}
isSubscriptionMode() {
// Check "All products for subscriptions" plugin.
return document.querySelector('.wcsatt-options-product:not(.wcsatt-options-product--hidden) .subscription-option input[type="radio"]:checked') !== null
|| document.querySelector('.wcsatt-options-prompt-label-subscription input[type="radio"]:checked') !== null; // grouped
}
render() {
const actionHandler = new SingleProductActionHandler(
this.gateway,
@ -81,16 +101,6 @@ class SingleProductBootstap {
this.gateway.ajax.change_cart.endpoint,
this.gateway.ajax.change_cart.nonce,
),
() => {
this.renderer.showButtons(this.gateway.button.wrapper);
this.renderer.showButtons(this.gateway.hosted_fields.wrapper);
this.messages.renderWithAmount(this.priceAmount())
},
() => {
this.renderer.hideButtons(this.gateway.button.wrapper);
this.renderer.hideButtons(this.gateway.hosted_fields.wrapper);
this.messages.hideMessages();
},
document.querySelector('form.cart'),
this.errorHandler,
);

View file

@ -1,47 +1,41 @@
class ErrorHandler {
constructor(genericErrorText)
/**
* @param {String} genericErrorText
* @param {Element} wrapper
*/
constructor(genericErrorText, wrapper)
{
this.genericErrorText = genericErrorText;
this.wrapper = document.querySelector('.woocommerce-notices-wrapper');
this.messagesList = document.querySelector('ul.woocommerce-error');
this.wrapper = wrapper;
}
genericError() {
if (this.wrapper.classList.contains('ppcp-persist')) {
return;
}
this.clear();
this.message(this.genericErrorText)
}
appendPreparedErrorMessageElement(errorMessageElement)
{
if (this.messagesList === null) {
this._prepareMessagesList();
}
this.messagesList.replaceWith(errorMessageElement);
this._getMessageContainer().replaceWith(errorMessageElement);
}
/**
* @param {String} text
* @param {Boolean} persist
*/
message(text, persist = false)
message(text)
{
this._addMessage(text, persist);
this._addMessage(text);
this._scrollToMessages();
}
/**
* @param {Array} texts
* @param {Boolean} persist
*/
messages(texts, persist = false)
messages(texts)
{
texts.forEach(t => this._addMessage(t, persist));
texts.forEach(t => this._addMessage(t));
this._scrollToMessages();
}
@ -49,26 +43,17 @@ class ErrorHandler {
/**
* @private
* @param {String} text
* @param {Boolean} persist
*/
_addMessage(text, persist = false)
_addMessage(text)
{
if(! typeof String || text.length === 0) {
throw new Error('A new message text must be a non-empty string.');
}
if (this.messagesList === null){
this._prepareMessagesList();
}
const messageContainer = this._getMessageContainer();
if (persist) {
this.wrapper.classList.add('ppcp-persist');
} else {
this.wrapper.classList.remove('ppcp-persist');
}
let messageNode = this._prepareMessagesListItem(text);
this.messagesList.appendChild(messageNode);
let messageNode = this._prepareMessageElement(text);
messageContainer.appendChild(messageNode);
}
/**
@ -76,26 +61,28 @@ class ErrorHandler {
*/
_scrollToMessages()
{
jQuery.scroll_to_notices(jQuery('.woocommerce-notices-wrapper'));
jQuery.scroll_to_notices(jQuery('.woocommerce-error'));
}
/**
* @private
*/
_prepareMessagesList()
_getMessageContainer()
{
if (this.messagesList === null) {
this.messagesList = document.createElement('ul');
this.messagesList.setAttribute('class', 'woocommerce-error');
this.messagesList.setAttribute('role', 'alert');
this.wrapper.appendChild(this.messagesList);
let messageContainer = document.querySelector('ul.woocommerce-error');
if (messageContainer === null) {
messageContainer = document.createElement('ul');
messageContainer.setAttribute('class', 'woocommerce-error');
messageContainer.setAttribute('role', 'alert');
jQuery(this.wrapper).prepend(messageContainer);
}
return messageContainer;
}
/**
* @private
*/
_prepareMessagesListItem(message)
_prepareMessageElement(message)
{
const li = document.createElement('li');
li.innerHTML = message;
@ -105,11 +92,7 @@ class ErrorHandler {
clear()
{
if (this.messagesList === null) {
return;
}
this.messagesList.innerHTML = '';
jQuery( '.woocommerce-error, .woocommerce-message' ).remove();
}
}

View file

@ -14,6 +14,9 @@ class ButtonsToggleListener {
init()
{
if (!this.element) {
return;
}
const config = { attributes : true };
const callback = () => {
if (this.element.classList.contains('disabled')) {
@ -33,4 +36,4 @@ class ButtonsToggleListener {
}
}
export default ButtonsToggleListener;
export default ButtonsToggleListener;

View file

@ -234,15 +234,15 @@ class CreditCardRenderer {
this.errorHandler.clear();
if (err.data?.details?.length) {
this.errorHandler.message(err.data.details.map(d => `${d.issue} ${d.description}`).join('<br/>'), true);
this.errorHandler.message(err.data.details.map(d => `${d.issue} ${d.description}`).join('<br/>'));
} else if (err.details?.length) {
this.errorHandler.message(err.details.map(d => `${d.issue} ${d.description}`).join('<br/>'), true);
this.errorHandler.message(err.details.map(d => `${d.issue} ${d.description}`).join('<br/>'));
} else if (err.data?.errors?.length > 0) {
this.errorHandler.messages(err.data.errors);
} else if (err.data?.message) {
this.errorHandler.message(err.data.message, true);
this.errorHandler.message(err.data.message);
} else if (err.message) {
this.errorHandler.message(err.message, true);
this.errorHandler.message(err.message);
} else {
this.errorHandler.genericError();
}

View file

@ -53,14 +53,5 @@ class MessageRenderer {
}
return true;
}
hideMessages() {
const domElement = document.querySelector(this.config.wrapper);
if (! domElement ) {
return false;
}
domElement.style.display = 'none';
return true;
}
}
export default MessageRenderer;

View file

@ -99,24 +99,6 @@ class Renderer {
return this.renderedSources.has(wrapper + fundingSource ?? '');
}
hideButtons(element) {
const domElement = document.querySelector(element);
if (! domElement ) {
return false;
}
domElement.style.display = 'none';
return true;
}
showButtons(element) {
const domElement = document.querySelector(element);
if (! domElement ) {
return false;
}
domElement.style.display = 'block';
return true;
}
disableCreditCardFields() {
this.creditCardRenderer.disableFields();
}

View file

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Button;
use WooCommerce\PayPalCommerce\Button\Endpoint\CartScriptParamsEndpoint;
use WooCommerce\PayPalCommerce\Button\Helper\CheckoutFormSaver;
use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint;
use WooCommerce\PayPalCommerce\Button\Validation\CheckoutFormValidator;
@ -220,6 +221,12 @@ return array(
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'button.endpoint.cart-script-params' => static function ( ContainerInterface $container ): CartScriptParamsEndpoint {
return new CartScriptParamsEndpoint(
$container->get( 'button.smart-button' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'button.helper.three-d-secure' => static function ( ContainerInterface $container ): ThreeDSecure {
$logger = $container->get( 'woocommerce.logger.woocommerce' );
return new ThreeDSecure( $logger );

View file

@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\CartScriptParamsEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\DataClientIdEndpoint;
@ -405,7 +406,7 @@ class SmartButton implements SmartButtonInterface {
add_action(
$this->pay_order_renderer_hook(),
array( $this, 'message_renderer' ),
11
15
);
}
return true;
@ -478,7 +479,8 @@ class SmartButton implements SmartButtonInterface {
function (): void {
$this->button_renderer( PayPalGateway::ID );
$this->button_renderer( CardButtonGateway::ID );
}
},
20
);
add_action(
$this->checkout_button_renderer_hook(),
@ -807,6 +809,9 @@ class SmartButton implements SmartButtonInterface {
'endpoint' => \WC_AJAX::get_endpoint( ValidateCheckoutEndpoint::ENDPOINT ),
'nonce' => wp_create_nonce( ValidateCheckoutEndpoint::nonce() ),
),
'cart_script_params' => array(
'endpoint' => \WC_AJAX::get_endpoint( CartScriptParamsEndpoint::ENDPOINT ),
),
),
'enforce_vault' => $this->has_subscriptions(),
'can_save_vault_token' => $this->can_save_vault_token(),
@ -979,19 +984,20 @@ class SmartButton implements SmartButtonInterface {
$disable_funding = $all_sources;
}
if ( ! $this->settings_status->is_pay_later_button_enabled_for_context( $this->context() ) ) {
$disable_funding[] = 'credit';
$enable_funding = array( 'venmo' );
if ( $this->settings_status->is_pay_later_button_enabled_for_location( $this->context() ) ||
$this->settings_status->is_pay_later_messaging_enabled_for_location( $this->context() )
) {
$enable_funding[] = 'paylater';
} else {
$disable_funding[] = 'paylater';
}
if ( count( $disable_funding ) > 0 ) {
$params['disable-funding'] = implode( ',', array_unique( $disable_funding ) );
}
$enable_funding = array( 'venmo' );
if ( $this->settings_status->is_pay_later_messaging_enabled_for_location( $this->context() ) || ! in_array( 'credit', $disable_funding, true ) ) {
$enable_funding[] = 'paylater';
}
if ( $this->is_free_trial_cart() ) {
$enable_funding = array();
}
@ -1080,11 +1086,10 @@ class SmartButton implements SmartButtonInterface {
switch ( $this->context() ) {
case 'checkout':
case 'cart':
case 'pay-now':
return $smart_button_enabled_for_current_location || $messaging_enabled_for_current_location;
case 'product':
return $smart_button_enabled_for_current_location || $messaging_enabled_for_current_location || $smart_button_enabled_for_mini_cart;
case 'pay-now':
return true;
default:
return $smart_button_enabled_for_mini_cart;
}

View file

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Button;
use WooCommerce\PayPalCommerce\Button\Endpoint\CartScriptParamsEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
@ -177,6 +178,15 @@ class ButtonModule implements ModuleInterface {
$endpoint->handle_request();
}
);
add_action(
'wc_ajax_' . CartScriptParamsEndpoint::ENDPOINT,
static function () use ( $container ) {
$endpoint = $container->get( 'button.endpoint.cart-script-params' );
assert( $endpoint instanceof CartScriptParamsEndpoint );
$endpoint->handle_request();
}
);
}
/**

View file

@ -0,0 +1,80 @@
<?php
/**
* The endpoint for returning the PayPal SDK Script parameters for the current cart.
*
* @package WooCommerce\PayPalCommerce\Button\Endpoint
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Button\Endpoint;
use Psr\Log\LoggerInterface;
use Throwable;
use WooCommerce\PayPalCommerce\Button\Assets\SmartButton;
/**
* Class CartScriptParamsEndpoint.
*/
class CartScriptParamsEndpoint implements EndpointInterface {
const ENDPOINT = 'ppc-cart-script-params';
/**
* The SmartButton.
*
* @var SmartButton
*/
private $smart_button;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* CartScriptParamsEndpoint constructor.
*
* @param SmartButton $smart_button he SmartButton.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
SmartButton $smart_button,
LoggerInterface $logger
) {
$this->smart_button = $smart_button;
$this->logger = $logger;
}
/**
* Returns the nonce.
*
* @return string
*/
public static function nonce(): string {
return self::ENDPOINT;
}
/**
* Handles the request.
*
* @return bool
*/
public function handle_request(): bool {
try {
$script_data = $this->smart_button->script_data();
wp_send_json_success( $script_data['url_params'] );
return true;
} catch ( Throwable $error ) {
$this->logger->error( "CartScriptParamsEndpoint execution failed. {$error->getMessage()} {$error->getFile()}:{$error->getLine()}" );
wp_send_json_error();
return false;
}
}
}

View file

@ -155,7 +155,11 @@ class CompatModule implements ModuleInterface {
$provider = $shipment->get_shipping_provider();
if ( ! empty( $provider ) && $provider !== 'none' ) {
$tracking_data['carrier'] = 'DHL_DEUTSCHE_POST';
/**
* The filter allowing to change the default Germanized carrier for order tracking,
* such as DHL_DEUTSCHE_POST, DPD_DE, ...
*/
$tracking_data['carrier'] = (string) apply_filters( 'woocommerce_paypal_payments_default_gzd_carrier', 'DHL_DEUTSCHE_POST', $provider );
}
try {

View file

@ -183,18 +183,19 @@ class SubscriptionsHandler {
return true;
}
if ( function_exists( 'get_current_screen' ) && get_current_screen() ) {
// Are we on the WC > Subscriptions screen?
if ( 'edit-shop_subscription' === get_current_screen()->id ) {
return true;
}
// Are we on the WC > Subscriptions screen?
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$post_type = wc_clean( wp_unslash( $_GET['post_type'] ?? $_POST['post_type'] ?? '' ) );
if ( $post_type === 'shop_subscription' ) {
return true;
}
// Are we editing an order or subscription tied to PPEC?
if ( in_array( get_current_screen()->id, array( 'shop_subscription', 'shop_order' ), true ) ) {
$order = wc_get_order( $GLOBALS['post']->ID );
return ( $order && PPECHelper::PPEC_GATEWAY_ID === $order->get_payment_method() );
}
// Are we editing an order or subscription tied to PPEC?
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$order_id = wc_clean( wp_unslash( $_GET['post'] ?? $_POST['post_ID'] ?? '' ) );
if ( $order_id ) {
$order = wc_get_order( $order_id );
return ( $order && PPECHelper::PPEC_GATEWAY_ID === $order->get_payment_method() );
}
return false;

View file

@ -396,6 +396,10 @@ document.addEventListener(
{
value:'authorize',
selector:'#field-capture_for_virtual_only'
},
{
value:'authorize',
selector:'#field-capture_on_status_change'
}
]
);

View file

@ -50,7 +50,7 @@ class SubscriptionHelper {
if ( ! isset( $item['data'] ) || ! is_a( $item['data'], WC_Product::class ) ) {
continue;
}
if ( $item['data']->is_type( 'subscription' ) || $item['data']->is_type( 'subscription_variation' ) ) {
if ( WC_Subscriptions_Product::is_subscription( $item['data'] ) ) {
return true;
}
}

View file

@ -114,10 +114,13 @@ import {setVisibleByClass, isVisible} from "../../../ppcp-button/resources/js/mo
'integration-date': PayPalCommerceGatewaySettings.integration_date,
'components': ['buttons', 'funding-eligibility', 'messages'],
'enable-funding': ['venmo', 'paylater'],
'buyer-country': PayPalCommerceGatewaySettings.country,
};
if(payLaterButtonPreview?.length) {
if (PayPalCommerceGatewaySettings.environment === 'sandbox') {
settings['buyer-country'] = PayPalCommerceGatewaySettings.country;
}
if (payLaterButtonPreview?.length) {
disabledSources = Object.keys(PayPalCommerceGatewaySettings.all_funding_sources);
}

View file

@ -36,6 +36,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\GatewayRepository;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXO;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOEndpoint;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOGateway;
@ -538,6 +539,23 @@ return array(
'requirements' => array(),
'gateway' => 'paypal',
),
'capture_on_status_change' => array(
'title' => __( 'Capture On Status Change', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
'default' => false,
'desc_tip' => true,
'description' => __(
'The transaction will be captured automatically when the order status changes to Processing or Completed.',
'woocommerce-paypal-payments'
),
'label' => __( 'Capture On Status Change', 'woocommerce-paypal-payments' ),
'screens' => array(
State::STATE_START,
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => 'paypal',
),
'capture_for_virtual_only' => array(
'title' => __( 'Capture Virtual-Only Orders ', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
@ -921,7 +939,8 @@ return array(
$settings,
$partner_endpoint,
$container->get( 'dcc.status-cache' ),
$container->get( 'api.helpers.dccapplies' )
$container->get( 'api.helpers.dccapplies' ),
$container->get( 'onboarding.state' )
);
},
@ -989,7 +1008,8 @@ return array(
return new PayUponInvoiceProductStatus(
$container->get( 'wcgateway.settings' ),
$container->get( 'api.endpoint.partners' ),
$container->get( 'pui.status-cache' )
$container->get( 'pui.status-cache' ),
$container->get( 'onboarding.state' )
);
},
'wcgateway.pay-upon-invoice' => static function ( ContainerInterface $container ): PayUponInvoice {
@ -1348,19 +1368,10 @@ return array(
OXXOGateway::ID,
);
},
'wcgateway.enabled-ppcp-gateways' => static function ( ContainerInterface $container ): array {
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
$ppcp_gateways = $container->get( 'wcgateway.ppcp-gateways' );
$enabled_ppcp_gateways = array();
foreach ( $ppcp_gateways as $gateway ) {
if ( ! isset( $available_gateways[ $gateway ] ) ) {
continue;
}
$enabled_ppcp_gateways[] = $gateway;
}
return $enabled_ppcp_gateways;
'wcgateway.gateway-repository' => static function ( ContainerInterface $container ): GatewayRepository {
return new GatewayRepository(
$container->get( 'wcgateway.ppcp-gateways' )
);
},
'wcgateway.is-fraudnet-enabled' => static function ( ContainerInterface $container ): bool {
$settings = $container->get( 'wcgateway.settings' );
@ -1375,7 +1386,7 @@ return array(
$container->get( 'wcgateway.fraudnet' ),
$container->get( 'onboarding.environment' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'wcgateway.enabled-ppcp-gateways' ),
$container->get( 'wcgateway.gateway-repository' ),
$container->get( 'session.handler' ),
$container->get( 'wcgateway.is-fraudnet-enabled' )
);

View file

@ -14,6 +14,7 @@ use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\GatewayRepository;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
@ -63,10 +64,17 @@ class FraudNetAssets {
/**
* The list of enabled PayPal gateways.
*
* @var string[]
* @var string[]|null
*/
protected $enabled_ppcp_gateways;
/**
* The GatewayRepository.
*
* @var GatewayRepository
*/
protected $gateway_repository;
/**
* The session handler
*
@ -84,14 +92,14 @@ class FraudNetAssets {
/**
* Assets constructor.
*
* @param string $module_url The url of this module.
* @param string $version The assets version.
* @param FraudNet $fraud_net The FraudNet entity.
* @param Environment $environment The environment.
* @param Settings $settings The Settings.
* @param string[] $enabled_ppcp_gateways The list of enabled PayPal gateways.
* @param SessionHandler $session_handler The session handler.
* @param bool $is_fraudnet_enabled true if FraudNet support is enabled in settings, otherwise false.
* @param string $module_url The url of this module.
* @param string $version The assets version.
* @param FraudNet $fraud_net The FraudNet entity.
* @param Environment $environment The environment.
* @param Settings $settings The Settings.
* @param GatewayRepository $gateway_repository The GatewayRepository.
* @param SessionHandler $session_handler The session handler.
* @param bool $is_fraudnet_enabled true if FraudNet support is enabled in settings, otherwise false.
*/
public function __construct(
string $module_url,
@ -99,18 +107,18 @@ class FraudNetAssets {
FraudNet $fraud_net,
Environment $environment,
Settings $settings,
array $enabled_ppcp_gateways,
GatewayRepository $gateway_repository,
SessionHandler $session_handler,
bool $is_fraudnet_enabled
) {
$this->module_url = $module_url;
$this->version = $version;
$this->fraud_net = $fraud_net;
$this->environment = $environment;
$this->settings = $settings;
$this->enabled_ppcp_gateways = $enabled_ppcp_gateways;
$this->session_handler = $session_handler;
$this->is_fraudnet_enabled = $is_fraudnet_enabled;
$this->module_url = $module_url;
$this->version = $version;
$this->fraud_net = $fraud_net;
$this->environment = $environment;
$this->settings = $settings;
$this->gateway_repository = $gateway_repository;
$this->session_handler = $session_handler;
$this->is_fraudnet_enabled = $is_fraudnet_enabled;
}
/**
@ -149,12 +157,12 @@ class FraudNetAssets {
* @return bool true if FraudNet script should be loaded, otherwise false.
*/
protected function should_load_fraudnet_script(): bool {
if ( empty( $this->enabled_ppcp_gateways ) ) {
if ( empty( $this->enabled_ppcp_gateways() ) ) {
return false;
}
$is_pui_gateway_enabled = in_array( PayUponInvoiceGateway::ID, $this->enabled_ppcp_gateways, true );
$is_only_standard_gateway_enabled = $this->enabled_ppcp_gateways === array( PayPalGateway::ID );
$is_pui_gateway_enabled = in_array( PayUponInvoiceGateway::ID, $this->enabled_ppcp_gateways(), true );
$is_only_standard_gateway_enabled = $this->enabled_ppcp_gateways() === array( PayPalGateway::ID );
if ( $this->context() !== 'checkout' || $is_only_standard_gateway_enabled ) {
return $this->is_fraudnet_enabled && $this->are_buttons_enabled_for_context();
@ -169,7 +177,7 @@ class FraudNetAssets {
* @return bool true if enabled, otherwise false.
*/
protected function are_buttons_enabled_for_context() : bool {
if ( ! in_array( PayPalGateway::ID, $this->enabled_ppcp_gateways, true ) ) {
if ( ! in_array( PayPalGateway::ID, $this->enabled_ppcp_gateways(), true ) ) {
return false;
}
try {
@ -188,4 +196,16 @@ class FraudNetAssets {
return in_array( $this->context(), $button_locations, true );
}
/**
* Returns IDs of the currently enabled PPCP gateways.
*
* @return string[]
*/
protected function enabled_ppcp_gateways(): array {
if ( null === $this->enabled_ppcp_gateways ) {
$this->enabled_ppcp_gateways = $this->gateway_repository->get_enabled_ppcp_gateway_ids();
}
return $this->enabled_ppcp_gateways;
}
}

View file

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Assets;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
/**
@ -58,6 +59,13 @@ class SettingsPageAssets {
*/
private $country;
/**
* The environment object.
*
* @var Environment
*/
private $environment;
/**
* Whether Pay Later button is enabled either for checkout, cart or product page.
*
@ -88,6 +96,7 @@ class SettingsPageAssets {
* @param string $client_id The PayPal SDK client ID.
* @param string $currency 3-letter currency code of the shop.
* @param string $country 2-letter country code of the shop.
* @param Environment $environment The environment object.
* @param bool $is_pay_later_button_enabled Whether Pay Later button is enabled either for checkout, cart or product page.
* @param array $disabled_sources The list of disabled funding sources.
* @param array $all_funding_sources The list of all existing funding sources.
@ -99,6 +108,7 @@ class SettingsPageAssets {
string $client_id,
string $currency,
string $country,
Environment $environment,
bool $is_pay_later_button_enabled,
array $disabled_sources,
array $all_funding_sources
@ -109,6 +119,7 @@ class SettingsPageAssets {
$this->client_id = $client_id;
$this->currency = $currency;
$this->country = $country;
$this->environment = $environment;
$this->is_pay_later_button_enabled = $is_pay_later_button_enabled;
$this->disabled_sources = $disabled_sources;
$this->all_funding_sources = $all_funding_sources;
@ -191,6 +202,7 @@ class SettingsPageAssets {
'client_id' => $this->client_id,
'currency' => $this->currency,
'country' => $this->country,
'environment' => $this->environment->current_environment(),
'integration_date' => PAYPAL_INTEGRATION_DATE,
'is_pay_later_button_enabled' => $this->is_pay_later_button_enabled,
'disabled_sources' => $this->disabled_sources,

View file

@ -0,0 +1,47 @@
<?php
/**
* Operations with the WC gateways.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
/**
* Class GatewayRepository
*/
class GatewayRepository {
/**
* IDs of our gateways.
*
* @var string[]
*/
protected $ppcp_gateway_ids;
/**
* GatewayRepository constructor.
*
* @param string[] $ppcp_gateway_ids IDs of our gateways.
*/
public function __construct( array $ppcp_gateway_ids ) {
$this->ppcp_gateway_ids = $ppcp_gateway_ids;
}
/**
* Returns IDs of the currently enabled PPCP gateways.
*
* @return array
*/
public function get_enabled_ppcp_gateway_ids(): array {
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
return array_filter(
$this->ppcp_gateway_ids,
function ( string $id ) use ( $available_gateways ): bool {
return isset( $available_gateways[ $id ] );
}
);
}
}

View file

@ -422,7 +422,7 @@ class PayUponInvoice {
*
* @psalm-suppress MissingClosureParamType
*/
function ( $methods ): array {
function ( $methods ) {
if ( ! is_array( $methods ) || State::STATE_ONBOARDED !== $this->state->current_state() ) {
return $methods;
}

View file

@ -14,6 +14,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatusProduct;
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
/**
@ -57,6 +58,13 @@ class DCCProductStatus {
*/
protected $dcc_applies;
/**
* The onboarding state.
*
* @var State
*/
private $onboarding_state;
/**
* DccProductStatus constructor.
*
@ -64,17 +72,20 @@ class DCCProductStatus {
* @param PartnersEndpoint $partners_endpoint The Partner Endpoint.
* @param Cache $cache The cache.
* @param DccApplies $dcc_applies The dcc applies helper.
* @param State $onboarding_state The onboarding state.
*/
public function __construct(
Settings $settings,
PartnersEndpoint $partners_endpoint,
Cache $cache,
DccApplies $dcc_applies
DccApplies $dcc_applies,
State $onboarding_state
) {
$this->settings = $settings;
$this->partners_endpoint = $partners_endpoint;
$this->cache = $cache;
$this->dcc_applies = $dcc_applies;
$this->onboarding_state = $onboarding_state;
}
/**
@ -83,6 +94,10 @@ class DCCProductStatus {
* @return bool
*/
public function dcc_is_active() : bool {
if ( $this->onboarding_state->current_state() < State::STATE_ONBOARDED ) {
return false;
}
if ( $this->cache->has( self::DCC_STATUS_CACHE_KEY ) ) {
return (bool) $this->cache->get( self::DCC_STATUS_CACHE_KEY );
}

View file

@ -13,6 +13,7 @@ use Throwable;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatusProduct;
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
/**
@ -49,21 +50,31 @@ class PayUponInvoiceProductStatus {
*/
private $partners_endpoint;
/**
* The onboarding status
*
* @var State
*/
private $onboarding_state;
/**
* PayUponInvoiceProductStatus constructor.
*
* @param Settings $settings The Settings.
* @param PartnersEndpoint $partners_endpoint The Partner Endpoint.
* @param Cache $cache The cache.
* @param State $onboarding_state The onboarding state.
*/
public function __construct(
Settings $settings,
PartnersEndpoint $partners_endpoint,
Cache $cache
Cache $cache,
State $onboarding_state
) {
$this->settings = $settings;
$this->partners_endpoint = $partners_endpoint;
$this->cache = $cache;
$this->onboarding_state = $onboarding_state;
}
/**
@ -72,6 +83,10 @@ class PayUponInvoiceProductStatus {
* @return bool
*/
public function pui_is_active() : bool {
if ( $this->onboarding_state->current_state() < State::STATE_ONBOARDED ) {
return false;
}
if ( $this->cache->has( self::PUI_STATUS_CACHE_KEY ) ) {
return (bool) $this->cache->get( self::PUI_STATUS_CACHE_KEY );
}

View file

@ -37,7 +37,6 @@ class SettingsStatus {
* Check whether Pay Later message is enabled either for checkout, cart or product page.
*
* @return bool true if is enabled, otherwise false.
* @throws NotFoundException When a setting was not found.
*/
public function is_pay_later_messaging_enabled(): bool {
$messaging_enabled = $this->settings->has( 'pay_later_messaging_enabled' ) && $this->settings->get( 'pay_later_messaging_enabled' );
@ -51,27 +50,15 @@ class SettingsStatus {
*
* @param string $location The location setting name.
* @return bool true if is enabled, otherwise false.
* @throws NotFoundException When a setting was not found.
*/
public function is_pay_later_messaging_enabled_for_location( string $location ): bool {
if ( ! $this->is_pay_later_messaging_enabled() ) {
return false;
}
$selected_locations = $this->settings->has( 'pay_later_messaging_locations' ) ? $this->settings->get( 'pay_later_messaging_locations' ) : array();
if ( empty( $selected_locations ) ) {
return false;
}
return in_array( $location, $selected_locations, true );
return $this->is_pay_later_messaging_enabled() && $this->is_enabled_for_location( 'pay_later_messaging_locations', $location );
}
/**
* Check whether Pay Later button is enabled either for checkout, cart or product page.
*
* @return bool true if is enabled, otherwise false.
* @throws NotFoundException When a setting was not found.
*/
public function is_pay_later_button_enabled(): bool {
$pay_later_button_enabled = $this->settings->has( 'pay_later_button_enabled' ) && $this->settings->get( 'pay_later_button_enabled' );
@ -85,45 +72,9 @@ class SettingsStatus {
*
* @param string $location The location.
* @return bool true if is enabled, otherwise false.
* @throws NotFoundException When a setting was not found.
*/
public function is_pay_later_button_enabled_for_location( string $location ): bool {
if ( ! $this->is_pay_later_button_enabled() ) {
return false;
}
$selected_locations = $this->settings->has( 'pay_later_button_locations' ) ? $this->settings->get( 'pay_later_button_locations' ) : array();
if ( empty( $selected_locations ) ) {
return false;
}
return in_array( $location, $selected_locations, true );
}
/**
* Check whether Pay Later button is enabled for a given context.
*
* @param string $context The context.
* @return bool true if is enabled, otherwise false.
* @throws NotFoundException When a setting was not found.
*/
public function is_pay_later_button_enabled_for_context( string $context ): bool {
if ( ! $this->is_pay_later_button_enabled() ) {
return false;
}
$selected_locations = $this->settings->has( 'pay_later_button_locations' ) ? $this->settings->get( 'pay_later_button_locations' ) : array();
if ( empty( $selected_locations ) ) {
return false;
}
$enabled_for_current_location = $this->is_pay_later_button_enabled_for_location( $context );
$enabled_for_product = $this->is_pay_later_button_enabled_for_location( 'product' );
$enabled_for_mini_cart = $this->is_pay_later_button_enabled_for_location( 'mini-cart' );
return $context === 'product' ? $enabled_for_product || $enabled_for_mini_cart : $enabled_for_current_location;
return $this->is_pay_later_button_enabled() && $this->is_enabled_for_location( 'pay_later_button_locations', $location );
}
/**
@ -133,7 +84,33 @@ class SettingsStatus {
* @return bool true if is enabled, otherwise false.
*/
public function is_smart_button_enabled_for_location( string $location ): bool {
$selected_locations = $this->settings->has( 'smart_button_locations' ) ? $this->settings->get( 'smart_button_locations' ) : array();
return $this->is_enabled_for_location( 'smart_button_locations', $location );
}
/**
* Adapts the context value to match the location settings.
*
* @param string $location The location/context.
* @return string
*/
protected function normalize_location( string $location ): string {
if ( 'pay-now' === $location ) {
$location = 'checkout';
}
return $location;
}
/**
* Checks whether the locations field in the settings includes the given location.
*
* @param string $setting_name The name of the enabled locations field in the settings.
* @param string $location The location.
* @return bool
*/
protected function is_enabled_for_location( string $setting_name, string $location ): bool {
$location = $this->normalize_location( $location );
$selected_locations = $this->settings->has( $setting_name ) ? $this->settings->get( $setting_name ) : array();
if ( empty( $selected_locations ) ) {
return false;

View file

@ -193,10 +193,7 @@ class SettingsListener {
*/
do_action( 'woocommerce_paypal_payments_onboarding_before_redirect' );
/**
* The URL opened at the end of onboarding after saving the merchant ID/email.
*/
$redirect_url = apply_filters( 'woocommerce_paypal_payments_onboarding_redirect_url', admin_url( 'admin.php?page=wc-settings&tab=checkout&section=ppcp-gateway&ppcp-tab=' . Settings::CONNECTION_TAB_ID ) );
$redirect_url = $this->get_onboarding_redirect_url();
if ( ! $this->settings->has( 'client_id' ) || ! $this->settings->get( 'client_id' ) ) {
$redirect_url = add_query_arg( 'ppcp-onboarding-error', '1', $redirect_url );
}
@ -347,9 +344,17 @@ class SettingsListener {
$this->dcc_status_cache->delete( DCCProductStatus::DCC_STATUS_CACHE_KEY );
}
$redirect_url = false;
if ( self::CREDENTIALS_ADDED === $credentials_change_status ) {
$redirect_url = $this->get_onboarding_redirect_url();
}
if ( isset( $_GET['ppcp-onboarding-error'] ) ) {
$url = remove_query_arg( 'ppcp-onboarding-error' );
wp_safe_redirect( $url, 302 );
$redirect_url = remove_query_arg( 'ppcp-onboarding-error', $redirect_url );
}
if ( $redirect_url ) {
wp_safe_redirect( $redirect_url, 302 );
exit;
}
@ -357,6 +362,18 @@ class SettingsListener {
// phpcs:enable WordPress.Security.NonceVerification.Recommended
}
/**
* Returns the URL opened at the end of onboarding.
*
* @return string
*/
private function get_onboarding_redirect_url(): string {
/**
* The URL opened at the end of onboarding after saving the merchant ID/email.
*/
return apply_filters( 'woocommerce_paypal_payments_onboarding_redirect_url', admin_url( 'admin.php?page=wc-settings&tab=checkout&section=ppcp-gateway&ppcp-tab=' . Settings::CONNECTION_TAB_ID ) );
}
/**
* The actual used client credentials are stored in 'client_secret', 'client_id', 'merchant_id' and 'merchant_email'.
* This method populates those fields depending on the sandbox status.

View file

@ -9,6 +9,8 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway;
use Psr\Log\LoggerInterface;
use Throwable;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
use WC_Order;
@ -174,6 +176,7 @@ class WCGatewayModule implements ModuleInterface {
$c->get( 'button.client_id_for_admin' ),
$c->get( 'api.shop.currency' ),
$c->get( 'api.shop.country' ),
$c->get( 'onboarding.environment' ),
$settings_status->is_pay_later_button_enabled(),
$settings->has( 'disable_funding' ) ? $settings->get( 'disable_funding' ) : array(),
$c->get( 'wcgateway.all-funding-sources' )
@ -306,6 +309,57 @@ class WCGatewayModule implements ModuleInterface {
$endpoint->handle_request();
}
);
add_action(
'woocommerce_order_status_changed',
static function ( int $order_id, string $from, string $to ) use ( $c ) {
$wc_order = wc_get_order( $order_id );
if ( ! $wc_order instanceof WC_Order ) {
return;
}
$settings = $c->get( 'wcgateway.settings' );
assert( $settings instanceof ContainerInterface );
if ( ! $settings->has( 'capture_on_status_change' ) || ! $settings->get( 'capture_on_status_change' ) ) {
return;
}
$intent = strtoupper( (string) $wc_order->get_meta( PayPalGateway::INTENT_META_KEY ) );
$captured = wc_string_to_bool( $wc_order->get_meta( AuthorizedPaymentsProcessor::CAPTURED_META_KEY ) );
if ( $intent !== 'AUTHORIZE' || $captured ) {
return;
}
/**
* The filter returning the WC order statuses which trigger capturing of payment authorization.
*/
$capture_statuses = apply_filters( 'woocommerce_paypal_payments_auto_capture_statuses', array( 'processing', 'completed' ), $wc_order );
if ( ! in_array( $to, $capture_statuses, true ) ) {
return;
}
$authorized_payment_processor = $c->get( 'wcgateway.processor.authorized-payments' );
assert( $authorized_payment_processor instanceof AuthorizedPaymentsProcessor );
try {
if ( $authorized_payment_processor->capture_authorized_payment( $wc_order ) ) {
return;
}
} catch ( Throwable $error ) {
$logger = $c->get( 'woocommerce.logger.woocommerce' );
assert( $logger instanceof LoggerInterface );
$logger->error( "Capture failed. {$error->getMessage()} {$error->getFile()}:{$error->getLine()}" );
}
$wc_order->update_status(
'failed',
__( 'Could not capture the payment.', 'woocommerce-paypal-payments' )
);
},
10,
3
);
}
/**

View file

@ -1,13 +1,12 @@
{
"name": "woocommerce-paypal-payments",
"version": "2.0.2",
"version": "2.0.3",
"description": "WooCommerce PayPal Payments",
"repository": "https://github.com/woocommerce/woocommerce-paypal-payments",
"license": "GPL-2.0",
"author": "WooCommerce",
"scripts": {
"postinstall": "run-s install:modules:* && run-s build:modules",
"install:modules:ppcp-blocks": "cd modules/ppcp-blocks && yarn install",
"install:modules:ppcp-button": "cd modules/ppcp-button && yarn install",
"install:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn install",
@ -17,7 +16,6 @@
"install:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn install",
"install:modules:ppcp-compat": "cd modules/ppcp-compat && yarn install",
"install:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn install",
"build:modules:ppcp-blocks": "cd modules/ppcp-blocks && yarn run build",
"build:modules:ppcp-button": "cd modules/ppcp-button && yarn run build",
"build:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn run build",
@ -28,7 +26,6 @@
"build:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run build",
"build:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run build",
"build:modules": "run-p build:modules:*",
"watch:modules:ppcp-blocks": "cd modules/ppcp-blocks && yarn run watch",
"watch:modules:ppcp-button": "cd modules/ppcp-button && yarn run watch",
"watch:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn run watch",
@ -39,7 +36,6 @@
"watch:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run watch",
"watch:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run watch",
"watch:modules": "run-p watch:modules:*",
"ddev:setup": "ddev start && ddev orchestrate",
"ddev:start": "ddev start",
"ddev:stop": "ddev stop",
@ -50,7 +46,10 @@
"ddev:composer-update": "ddev composer update && ddev composer update --lock",
"ddev:unit-tests": "ddev exec phpunit",
"ddev:e2e-tests": "cp -n .env.e2e.example .env.e2e && ddev php tests/e2e/PHPUnit/setup.php && ddev exec phpunit -c tests/e2e/phpunit.xml.dist",
"ddev:test": "yarn run ddev:unit-tests && yarn run ddev:e2e-tests",
"ddev:pw-install": "ddev exec npx playwright install --with-deps",
"ddev:pw-tests-ci": "ddev exec npx playwright test --grep @ci",
"ddev:pw-tests-headed": "ddev exec npx playwright test --headed",
"ddev:test": "yarn run ddev:unit-tests && yarn run ddev:e2e-tests && yarn run ddev:pw-tests-ci",
"ddev:lint": "yarn ddev:phpcs && yarn ddev:psalm",
"ddev:phpcs": "ddev exec phpcs --parallel=8 -s",
"ddev:psalm": "ddev exec psalm --show-info=false --threads=8 --diff",
@ -58,19 +57,21 @@
"ddev:xdebug-on": "ddev xdebug",
"ddev:xdebug-off": "ddev xdebug",
"ddev:build-package": "ddev yarn build",
"prebuild": "rm -rf ./vendor && find . -name 'node_modules' -type d -maxdepth 3 -exec rm -rf {} +",
"build": "composer install --no-dev && yarn install && yarn run archive",
"prearchive": "rm -rf $npm_package_name.zip",
"archive": "zip -r $npm_package_name.zip . -x **.git/\\* **node_modules/\\*",
"postarchive": "yarn run archive:cleanup && rm -rf $npm_package_name && unzip $npm_package_name.zip -d $npm_package_name && rm $npm_package_name.zip && zip -r $npm_package_name.zip $npm_package_name && rm -rf $npm_package_name",
"archive:cleanup": "zip -d $npm_package_name.zip .env* .ddev/\\* \\*.idea/\\* .editorconfig tests/\\* .github/\\* .psalm/\\* wordpress_org_assets/\\* \\*.DS_Store \\*README.md \\*.gitattributes \\*.gitignore \\*composer.json \\*composer.lock patchwork.json phpunit.xml.dist .phpunit.result.cache phpcs.xml* psalm*.xml* \\*.babelrc \\*package.json \\*webpack.config.js \\*yarn.lock \\*.travis.yml"
"archive:cleanup": "zip -d $npm_package_name.zip .env* .ddev/\\* \\*.idea/\\* .editorconfig tests/\\* .github/\\* .psalm/\\* wordpress_org_assets/\\* \\*.DS_Store \\*README.md \\*.gitattributes \\*.gitignore \\*composer.json \\*composer.lock patchwork.json phpunit.xml.dist .phpunit.result.cache phpcs.xml* psalm*.xml* playwright.config.js \\*.babelrc \\*package.json \\*webpack.config.js \\*yarn.lock \\*.travis.yml\\"
},
"config": {
"wp_org_slug": "woocommerce-paypal-payments"
},
"dependencies": {
"dotenv": "^16.0.3",
"npm-run-all": "^4.1.5"
},
"devDependencies": {}
"devDependencies": {
"@playwright/test": "^1.31.1"
}
}

11
playwright.config.js Normal file
View file

@ -0,0 +1,11 @@
require('dotenv').config({ path: '.env.e2e' });
const config = {
testDir: './tests/playwright',
timeout: 30000,
use: {
baseURL: process.env.BASEURL,
},
};
module.exports = config;

View file

@ -4,7 +4,7 @@ Tags: woocommerce, paypal, payments, ecommerce, e-commerce, store, sales, sell,
Requires at least: 5.3
Tested up to: 6.1
Requires PHP: 7.2
Stable tag: 2.0.2
Stable tag: 2.0.3
License: GPLv2
License URI: http://www.gnu.org/licenses/gpl-2.0.html
@ -81,9 +81,35 @@ Follow the steps below to connect the plugin to your PayPal account:
== Changelog ==
= 2.0.2 =
= 2.0.3 - 2023-03-14 =
* Fix - `DEVICE_DATA_NOT_AVAILABLE` error message when FraudNet is enabled #1177
* Fix - Redirect to connection tab after manual credentials input #1201
* Fix - Asking for address fields in checkout when not using them #1089
* Fix - Validate before free trial #1170
* Fix - Validate new user creation #1131
* Fix - After Updating to 2.0.2, Site Health reports REST API error #1195
* Fix - Do not send buyer-country for previews in live mode to avoid error #1186
* Fix - PPEC compatibility layer does not take over subscriptions #1193
* Fix - Checkout conflict with "All products for subscriptions" plugin #629
* Fix - Pay Later on order pay page #1214
* Fix - High volume of traffic from merchant-integrations endpoint #1241
* Enhancement - Save checkout form before free trial redirect #1135
* Enhancement - Add filter for controlling the ditching of items/breakdown #1146
* Enhancement - Add patch order data filter #1147
* Enhancement - Add filter for disabling fees on wc order admin pages #1153
* Enhancement - Use wp_loaded for fraudnet loading to avoid warnings #1172
* Enhancement - reCaptcha for WooCommerce support #1093
* Enhancement - Make it possible to hide missing funding resource Trustly #1155
* Enhancement - Add white color option #1167
* Enhancement - Checkout validation for other fields #861
* Enhancement - Mention PUI only for German shops and add line breaks #1169
* Enhancement - Add filter to fallback tracking_data['carrier'] #1188
* Enhancement - Error notices in checkout do not update / or are shown twice #1168
* Enhancement - capture authorized payment by changing order status (or programmatically) #587
= 2.0.2 - 2023-01-31 =
* Fix - Do not call PayPal get order by ID if it does not exist #1029
* Fix - Type check error conflict with German Market #1056
* Fix - Type check error conflict with German Market #1056
* Fix - Backend Storage for the PayPalRequestIdRepository does not scale #983
* Fix - Ensure WC()->payment_gateways is not null #1128
* Enhancement - Remove plugin data after uninstalling #1075
@ -93,7 +119,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Enhancement - When PUI is enabled FraudNet should be also enabled #1129
* Enhancement - Add PayPal-Request-Id if payment source exist #1132
= 2.0.1 =
= 2.0.1 - 2022-12-13 =
* Fix - Error while syncing tracking data to PayPal -> Sync GZD Tracking #1020
* Fix - Fix product price retrieval for variable product buttons #1000
* Fix - All tabs hidden on OXXO tab visit #1048
@ -106,12 +132,12 @@ Follow the steps below to connect the plugin to your PayPal account:
* Fix - Execute WC validation only for smart buttons in checkout #1074
* Enhancement - Param types removed in closure to avoid third-party issues #1046
= 2.0.0 =
* Add - Option to separate JSSDK APM payment buttons into individual WooCommerce gateways #671
* Add - OXXO APM (Alternative Payment Method) #684
= 2.0.0 - 2022-11-21 =
* Add - Option to separate JSSDK APM payment buttons into individual WooCommerce gateways #671
* Add - OXXO APM (Alternative Payment Method) #684
* Add - Pay Later tab #961
* Add - Button preview in settings #929
* Fix - Prevent Enter key submit for our non-standard button gateways #981
* Fix - Prevent Enter key submit for our non-standard button gateways #981
* Fix - Pay Upon Invoice - Stock correction on failed orders #964
* Fix - Check that WC session exists before using it #846
* Fix - Compatibility with One Page Checkout Extension #356
@ -120,11 +146,11 @@ Follow the steps below to connect the plugin to your PayPal account:
* Enhancement - PHP 8.1 warning: Constant FILTER_SANITIZE_STRING is deprecated #867
* Enhancement - Execute server-side WC validation when clicking button #942
* Enhancement - Update order with order note if payment failed after billing agreement canceled at PayPal #886
* Enhancement - Missing PUI refund functionality from WC order #937
* Enhancement - Missing PUI refund functionality from WC order #937
* Enhancement - Hide Pay upon Invoice tab if not available for merchant #978
* Enhancement - Handle synced sub without upfront payment like free trial #936
* Enhancement - Isolate container and modularity deps #972
**NOTE**: if you were extending/modifying the plugin using the modularity system,
**NOTE**: if you were extending/modifying the plugin using the modularity system,
you will need to add the `WooCommerce\PayPalCommerce\Vendor\` prefix for the container/modularity namespaces in your code,
that is `Psr\Container\ContainerInterface` becomes `WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface`,
and `Dhii\Modular\Module\ModuleInterface` becomes `WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface`.
@ -132,7 +158,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Enhancement - When Brand Name field is left empty, PUI purchase fails #916
* Enhancement - Improve styling when using separate buttons #996
= 1.9.5 =
= 1.9.5 - 2022-11-01 =
* Fix - Invalid tracking number in logs when adding tracking #903
* Fix - Tracking on Connection tab always enabled #900
* Fix - PUI payment instructions printed in the refund email #873
@ -148,7 +174,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Enhancement - Onboard with PUI Checkbox automatically set when shop is set to Germany #876
* Enhancement - Update all plugin strings #946
= 1.9.4 =
= 1.9.4 - 2022-10-11 =
* Add - Create new connection tab #801
* Add - Functionality to choose subscription failure behavior #728
* Fix - Virtual-only orders always move order status to completed #868
@ -160,7 +186,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Enhancement - PUI-relevant webhook not subscribed to #842
* Enhancement - Remove WC logo during onboarding #881
= 1.9.3 =
= 1.9.3 - 2022-08-31 =
* Add - Tracking API #792
* Fix - Improve compatibility with Siteground Optimizer plugin #797
* Fix - Transaction ID in order not updated when manually capturing authorized payment from WC #766
@ -175,7 +201,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Enhancement - PUI add option for a phone number field next to the Birth Date field #742
* Enhancement - PUI gateway availability on pay for order page with unsupported currency #744
= 1.9.2 =
= 1.9.2 - 2022-08-09 =
* Fix - Do not allow birth date older than 100 years for PUI. #743
* Fix - Store the customer id for vaulted payment method in usermeta to not lose vaulted methods after the invoice prefix change. #698
* Fix - Capture Virtual-Only Orders setting did not auto-capture subscription renewal payments. #626
@ -188,28 +214,28 @@ Follow the steps below to connect the plugin to your PayPal account:
* Enhancement - Improve Checkout Field Validation Message. #739
* Enhancement - Handle PAYER_ACTION_REQUIRED error. #759
= 1.9.1 =
= 1.9.1 - 2022-07-25 =
* Fix - ITEM_TOTAL_MISMATCH error when checking out with multiple products #721
* Fix - Unable to purchase a product with Credit card button in pay for order page #718
* Fix - Pay Later messaging only displayed when smart button is active on the same page #283
* Fix - Pay Later messaging displayed for out of stock variable products or with no variation selected #667
* Fix - Placeholders and card type detection not working for PayPal Card Processing (260) #685
* Fix - PUI gateway is displayed with unsupported store currency #711
* Fix - Wrong PUI locale sent causing error PAYMENT_SOURCE_CANNOT_BE_USED #741
* Fix - Wrong PUI locale sent causing error PAYMENT_SOURCE_CANNOT_BE_USED #741
* Enhancement - Missing PayPal fee in WC order details for PUI purchase #714
* Enhancement - Skip loading of PUI js file on all pages where PUI gateway is not displayed #723
* Enhancement - PUI feature capitalization not consistent #724
* Enhancement - PUI feature capitalization not consistent #724
= 1.9.0 =
= 1.9.0 - 2022-07-04 =
* Add - New Feature - Pay Upon Invoice (Germany only) #608
* Fix - Order not approved: payment via vaulted PayPal account fails #677
* Fix - Cant' refund : "ERROR Refund failed: No country given for address." #639
* Fix - Something went wrong error in Virtual products when using vaulted payment #673
* Fix - Something went wrong error in Virtual products when using vaulted payment #673
* Fix - PayPal smart buttons are not displayed for product variations when parent product is set to out of stock #669
* Fix - Pay Later messaging displayed for out of stock variable products or with no variation selected #667
* Fix - "Capture Virtual-Only Orders" intent sets virtual+downloadable product orders to "Processing" instead of "Completed" #665
* Fix - Free trial period causing incorrerct disable-funding parameters with DCC disabled #661
* Fix - Smart button not visible on single product page when product price is below 1 and decimal is "," #654
* Fix - Pay Later messaging displayed for out of stock variable products or with no variation selected #667
* Fix - "Capture Virtual-Only Orders" intent sets virtual+downloadable product orders to "Processing" instead of "Completed" #665
* Fix - Free trial period causing incorrerct disable-funding parameters with DCC disabled #661
* Fix - Smart button not visible on single product page when product price is below 1 and decimal is "," #654
* Fix - Checkout using an email address containing a + symbol results in a "[INVALID_REQUEST]" error #523
* Fix - Order details are sometimes empty in PayPal dashboard #689
* Fix - Incorrect TAX details on PayPal order overview #541
@ -217,7 +243,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Fix - DCC causes checkout continuation state after checkout validation error #695
* Enhancement - Improve checkout validation & order creation #513
= 1.8.1 =
= 1.8.1 - 2022-05-31 =
* Fix - Manual orders return an error for guest users when paying with PayPal Card Processing #530
* Fix - "No PayPal order found in the current WooCommerce session" error for guests on Pay for Order page #605
* Fix - Error on order discount by third-party plugins #548
@ -233,7 +259,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Enhancement - Add Fraud Processor Response as an order note #616
* Enhancement - Add the Paypal Fee to the Meta Custom Field for export purposes #591
= 1.8.0 =
= 1.8.0 - 2022-05-03 =
* Add - Allow free trial subscriptions #580
* Fix - The Card Processing does not appear as an available payment method when manually creating an order #562
* Fix - Express buttons & Pay Later visible on variable Subscription products /w disabled vaulting #281
@ -244,7 +270,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Enhancement - Onboarding errors improvements #558
* Enhancement - "Place order" button visible during gateway load time when DCC gateway is selected as the default #560
= 1.7.1 =
= 1.7.1 - 2022-04-06 =
* Fix - Hide smart buttons for free products and zero-sum carts #499
* Fix - Unprocessable Entity when paying with AMEX card #516
* Fix - Multisite path doubled in ajax URLs #528
@ -257,7 +283,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Enhancement - Improve payment token checking for subscriptions #525
* Enhancement - Add Spain and Italy to messaging #497
= 1.7.0 =
= 1.7.0 - 2022-02-28 =
* Fix - DCC orders randomly failing #503
* Fix - Multi-currency broke #481
* Fix - Address information from PayPal shortcut flow not loaded #451
@ -277,7 +303,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Enhancement - PayPal Payments doesn't set transaction fee metadata #467
* Enhancement - Show PayPal fee information in order #489
= 1.6.5 =
= 1.6.5 - 2022-01-31 =
* Fix - Allow guest users to purchase subscription products from checkout page #422
* Fix - Transaction ID missing for renewal order #424
* Fix - Save your credit card checkbox should be removed in pay for order for subscriptions #420
@ -291,7 +317,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Fix - Improve DCC error notice when not available #435
* Enhancement - Add View Logs link #416
= 1.6.4 =
= 1.6.4 - 2021-12-27 =
* Fix - Non admin user cannot save changes to the plugin settings #278
* Fix - Empty space in invoice prefix causes smart buttons to not load #390
* Fix - woocommerce_payment_complete action not triggered for payments completed via webhook #399
@ -300,7 +326,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Enhancement - Allow formatted text for the Description field #407
* Enhancement - Remove filter to prevent On-Hold emails #411
= 1.6.3 =
= 1.6.3 - 2021-12-14 =
* Fix - Payments fail when using custom order numbers #354
* Fix - Do not display saved payments on PayPal buttons if vault option is disabled #358
* Fix - Double "Place Order" button #362
@ -311,7 +337,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Fix - Error messages are not cleared even when checkout is re-attempted (DCC) #366
* Add - New additions for system report status #377
= 1.6.2 =
= 1.6.2 - 2021-11-22 =
* Fix - Order of WooCommerce checkout actions causing incompatibility with AvaTax address validation #335
* Fix - Can't checkout to certain countries with optional postcode #330
* Fix - Prevent subscription from being purchased when saving payment fails #308
@ -328,14 +354,14 @@ Follow the steps below to connect the plugin to your PayPal account:
* Fix - When paying for a subscription and vaulting fails, cart is cleared #367
* Fix - Fatal error when activating PayPal Checkout plugin #363
= 1.6.1 =
= 1.6.1 - 2021-10-12 =
* Fix - Handle authorization capture failures #312
* Fix - Handle denied payment authorization #302
* Fix - Handle failed authorizations when capturing order #303
* Fix - Transactions cannot be voided #293
* Fix - Fatal error: get_3ds_contingency() #310
= 1.6.0 =
= 1.6.0 - 2021-09-29 =
* Add - Webhook status. #246 #273
* Add - Show CC gateway in admin payments list. #236
* Add - Add 3d secure contingency settings. #230
@ -349,14 +375,14 @@ Follow the steps below to connect the plugin to your PayPal account:
* Fix - REFUND_CAPTURE_CURRENCY_MISMATCH on multicurrency sites. #225
* Fix - Can't checkout to certain countries with optional postcode. #224
= 1.5.1 =
= 1.5.1 - 2021-08-19 =
* Fix - Set 3DS contingencies to "SCA_WHEN_REQUIRED". #178
* Fix - Plugin conflict blocking line item details. #221
* Fix - WooCommerce orders left in "Pending Payment" after a decline. #222
* Fix - Do not send decimals when currency does not support them. #202
* Fix - Gateway can be activated without a connected PayPal account. #205
= 1.5.0 =
= 1.5.0 - 2021-08-09 =
* Add - Filter to modify plugin modules list. #203
* Add - Filters to move PayPal buttons and Pay Later messages. #203
* Fix - Remove redirection when enabling payment gateway with setup already done. #206
@ -365,7 +391,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Fix - Hide mini cart height field when mini cart is disabled. #213
* Fix - Address possible error on frontend pages due to an empty gateway description. #214
= 1.4.0 =
= 1.4.0 - 2021-07-27 =
* Add - Venmo update #169
* Add - Pay Later Button Global Expansion #182
* Add - Add Canada to advanced credit and debit card #180
@ -381,7 +407,7 @@ Follow the steps below to connect the plugin to your PayPal account:
* Fix - Remove merchant-id query parameter in JSSDK #179
* Fix - Error on Plugin activation with Zettle POS Integration for WooCommerce #195
= 1.3.2 =
= 1.3.2 - 2021-06-08 =
* Fix - Improve Subscription plugin support. #161
* Fix - Disable vault setting if vaulting feature is not available. #150
* Fix - Cast item get_quantity into int. #168
@ -392,10 +418,10 @@ Follow the steps below to connect the plugin to your PayPal account:
* Fix - Fix pay later messaging options. #141
* Fix - UI/UX for vaulting settings. #166
= 1.3.1 =
= 1.3.1 - 2021-04-30 =
* Fix - Fix Credit Card fields for non logged-in users. #152
= 1.3.0 =
= 1.3.0 - 2021-04-28 =
* Add - Client-side vaulting and allow WooCommerce Subscriptions product renewals through payment tokens. #134
* Add - Send transaction ids to woocommerce. #125
* Fix - Validate checkout form before sending request to PayPal #137
@ -405,31 +431,31 @@ Follow the steps below to connect the plugin to your PayPal account:
* Fix - Remove disabling credit for UK. #127
* Fix - Show WC message on account creating error. #136
= 1.2.1 =
= 1.2.1 - 2021-03-08 =
* Fix - Address compatibility issue with Jetpack.
= 1.2.0 =
= 1.2.0 - 2021-03-08 =
* Add - Rework onboarding code and add REST controller for integration with the OBW. #121
* Fix - Remove spinner on click, on cancel and on error. #124
= 1.1.0 =
= 1.1.0 - 2021-02-01 =
* Add - Buy Now Pay Later for UK. #104
* Add - DE now has 12 month installments. #106
* Fix - Check phone for empty string. #102
= 1.0.4 =
= 1.0.4 - 2021-01-18 =
* Fix - Check if WooCommerce is active before initialize. #99
* Fix - Payment buttons only visible on order-pay site when Mini Cart is enabled; payment fails. #96
* Fix - High volume of failed calls to /v1/notifications/webhooks #93
* Fix - GB country has ACDC blocked. #91
= 1.0.3 =
= 1.0.3 - 2020-11-30 =
* Fix - Order with Payment received when Hosted Fields transaction is declined. #88
= 1.0.2 =
= 1.0.2 - 2020-11-09 =
* Fix - Purchases over 1.000 USD fail. #84
= 1.0.1 =
= 1.0.1 - 2020-11-05 =
* Fix - PayPal Smart buttons don't load when using a production/live account and `WP_Debug` is turned on/true. #66
* Fix - [Card Processing] SCA/Visa Verification form loads underneath the Checkout blockUI element. #63
* Fix - Attempting to checkout without country selected results in unexpected error message. #67
@ -438,5 +464,5 @@ Follow the steps below to connect the plugin to your PayPal account:
* Fix - "The value of a field does not conform to the expected format" error when using certain e-mail addresses. #56
* Fix - HTML tags in Product description. #79
= 1.0.0 =
= 1.0.0 - 2020-10-15 =
* Initial release.

View file

@ -3,6 +3,7 @@
namespace WooCommerce\PayPalCommerce\WcGateway\Assets;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
use WooCommerce\PayPalCommerce\TestCase;
use function Brain\Monkey\Functions\when;
@ -23,6 +24,7 @@ class SettingsPagesAssetsTest extends TestCase
'123',
'EUR',
'DE',
Mockery::mock(Environment::class),
true,
array(),
array()

3
tests/playwright/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
test-results/
playwright-report/
.cache/

View file

@ -0,0 +1,15 @@
# Playwright Testing
## Local Environment Variables
Allows using environment variables inside the tests.
- Duplicate `.env.e2e.example` and rename it as `.env.e2e`, set values and add new variables if needed.
## Run Tests
```
$ npx playwright test
$ npx playwright test --grep @ci
$ npx playwright test example.spec.js --headed
$ npx playwright test example.spec.js --debug
$ npx playwright test -g "Test name here"
```

View file

@ -0,0 +1,75 @@
const {test, expect} = require('@playwright/test');
const {
CUSTOMER_EMAIL,
CUSTOMER_PASSWORD,
CREDIT_CARD_NUMBER,
CREDIT_CARD_EXPIRATION,
CREDIT_CARD_CVV
} = process.env;
async function fillCheckoutForm(page) {
await page.fill('#billing_first_name', 'John');
await page.fill('#billing_last_name', 'Doe');
await page.selectOption('select#billing_country', 'DE');
await page.fill('#billing_address_1', 'Badensche Str. 24');
await page.fill('#billing_postcode', '10715');
await page.fill('#billing_city', '10715');
await page.fill('#billing_phone', '1234567890');
await page.fill('#billing_email', CUSTOMER_EMAIL);
}
test('PayPal button place order from Product page', async ({page}) => {
await page.goto('/product/product/');
const [popup] = await Promise.all([
page.waitForEvent('popup'),
page.frameLocator('.component-frame').locator('[data-funding-source="paypal"]').click(),
]);
await popup.waitForLoadState();
await popup.click("text=Log in");
await popup.fill('#email', CUSTOMER_EMAIL);
await popup.locator('#btnNext').click();
await popup.fill('#password', CUSTOMER_PASSWORD);
await popup.locator('#btnLogin').click();
await popup.locator('#payment-submit-btn').click();
await fillCheckoutForm(page);
await Promise.all([
page.waitForNavigation(),
page.locator('#place_order').click(),
]);
const title = await page.locator('.entry-title');
await expect(title).toHaveText('Order received');
});
test('Advanced Credit and Debit Card (ACDC) place order from Checkout page @ci', async ({page}) => {
await page.goto('/product/product/');
await page.locator('.single_add_to_cart_button').click();
await page.goto('/checkout/');
await fillCheckoutForm(page);
await page.click("text=Credit Cards");
const creditCardNumber = page.frameLocator('#braintree-hosted-field-number').locator('#credit-card-number');
await creditCardNumber.fill(CREDIT_CARD_NUMBER);
const expirationDate = page.frameLocator('#braintree-hosted-field-expirationDate').locator('#expiration');
await expirationDate.fill(CREDIT_CARD_EXPIRATION);
const cvv = page.frameLocator('#braintree-hosted-field-cvv').locator('#cvv');
await cvv.fill(CREDIT_CARD_CVV);
await Promise.all([
page.waitForNavigation(),
page.locator('.ppcp-dcc-order-button').click(),
]);
const title = await page.locator('.entry-title');
await expect(title).toHaveText('Order received');
});

View file

@ -3,13 +3,13 @@
* 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.0.2
* Version: 2.0.3
* Author: WooCommerce
* Author URI: https://woocommerce.com/
* License: GPL-2.0
* Requires PHP: 7.2
* WC requires at least: 3.9
* WC tested up to: 7.2
* WC tested up to: 7.4
* Text Domain: woocommerce-paypal-payments
*
* @package WooCommerce\PayPalCommerce
@ -23,7 +23,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
define( 'PAYPAL_API_URL', 'https://api.paypal.com' );
define( 'PAYPAL_SANDBOX_API_URL', 'https://api.sandbox.paypal.com' );
define( 'PAYPAL_INTEGRATION_DATE', '2023-01-11' );
define( 'PAYPAL_INTEGRATION_DATE', '2023-02-21' );
define( 'PPCP_FLAG_SUBSCRIPTION', true );

View file

@ -2,6 +2,21 @@
# yarn lockfile v1
"@playwright/test@^1.31.1":
version "1.31.1"
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.31.1.tgz#39d6873dc46af135f12451d79707db7d1357455d"
integrity sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA==
dependencies:
"@types/node" "*"
playwright-core "1.31.1"
optionalDependencies:
fsevents "2.3.2"
"@types/node@*":
version "18.14.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.1.tgz#90dad8476f1e42797c49d6f8b69aaf9f876fc69f"
integrity sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ==
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@ -75,6 +90,11 @@ define-properties@^1.1.3, define-properties@^1.1.4:
has-property-descriptors "^1.0.0"
object-keys "^1.1.1"
dotenv@^16.0.3:
version "16.0.3"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07"
integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==
error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
@ -126,6 +146,11 @@ escape-string-regexp@^1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
fsevents@2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@ -422,6 +447,11 @@ pify@^3.0.0:
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==
playwright-core@1.31.1:
version "1.31.1"
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.31.1.tgz#4deeebbb8fb73b512593fe24bea206d8fd85ff7f"
integrity sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ==
read-pkg@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"