mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 13:44:42 +08:00
Merge branch 'trunk' into PCP-1462-order-pay-later
This commit is contained in:
commit
098ec6f057
22 changed files with 393 additions and 115 deletions
|
@ -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"
|
||||
|
|
|
@ -6,7 +6,6 @@ import PayNowBootstrap from "./modules/ContextBootstrap/PayNowBootstrap";
|
|||
import Renderer from './modules/Renderer/Renderer';
|
||||
import ErrorHandler from './modules/ErrorHandler';
|
||||
import CreditCardRenderer from "./modules/Renderer/CreditCardRenderer";
|
||||
import dataClientIdAttributeHandler from "./modules/DataClientIdAttributeHandler";
|
||||
import MessageRenderer from "./modules/Renderer/MessageRenderer";
|
||||
import Spinner from "./modules/Helper/Spinner";
|
||||
import {
|
||||
|
@ -19,6 +18,7 @@ import {isChangePaymentPage} from "./modules/Helper/Subscriptions";
|
|||
import FreeTrialHandler from "./modules/ActionHandler/FreeTrialHandler";
|
||||
import FormSaver from './modules/Helper/FormSaver';
|
||||
import FormValidator from "./modules/Helper/FormValidator";
|
||||
import {loadPaypalScript} from "./modules/Helper/ScriptLoading";
|
||||
|
||||
// TODO: could be a good idea to have a separate spinner for each gateway,
|
||||
// but I think we care mainly about the script loading, so one spinner should be enough.
|
||||
|
@ -258,24 +258,10 @@ document.addEventListener(
|
|||
hideOrderButtonIfPpcpGateway();
|
||||
});
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.addEventListener('load', (event) => {
|
||||
loadPaypalScript(PayPalCommerceGateway, () => {
|
||||
bootstrapped = true;
|
||||
|
||||
bootstrap();
|
||||
});
|
||||
script.setAttribute('src', PayPalCommerceGateway.button.url);
|
||||
Object.entries(PayPalCommerceGateway.script_attributes).forEach(
|
||||
(keyValue) => {
|
||||
script.setAttribute(keyValue[0], keyValue[1]);
|
||||
}
|
||||
);
|
||||
|
||||
if (PayPalCommerceGateway.data_client_id.set_attribute) {
|
||||
dataClientIdAttributeHandler(script, PayPalCommerceGateway.data_client_id);
|
||||
return;
|
||||
}
|
||||
|
||||
document.body.appendChild(script);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import dataClientIdAttributeHandler from "../DataClientIdAttributeHandler";
|
||||
|
||||
export const loadPaypalScript = (config, onLoaded) => {
|
||||
if (typeof paypal !== 'undefined') {
|
||||
onLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.addEventListener('load', onLoaded);
|
||||
script.setAttribute('src', config.url);
|
||||
Object.entries(config.script_attributes).forEach(
|
||||
(keyValue) => {
|
||||
script.setAttribute(keyValue[0], keyValue[1]);
|
||||
}
|
||||
);
|
||||
|
||||
if (config.data_client_id.set_attribute) {
|
||||
dataClientIdAttributeHandler(script, config.data_client_id);
|
||||
return;
|
||||
}
|
||||
|
||||
document.body.appendChild(script);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -24,12 +24,16 @@ class DisabledSmartButton implements SmartButtonInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Enqueues necessary scripts.
|
||||
*
|
||||
* @return bool
|
||||
* Whether the scripts should be loaded.
|
||||
*/
|
||||
public function enqueue(): bool {
|
||||
return true;
|
||||
public function should_load(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues necessary scripts.
|
||||
*/
|
||||
public function enqueue(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,4 +45,13 @@ class DisabledSmartButton implements SmartButtonInterface {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The configuration for the smart buttons.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function script_data(): array {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -507,17 +508,25 @@ class SmartButton implements SmartButtonInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Enqueues the script.
|
||||
*
|
||||
* @return bool
|
||||
* @throws NotFoundException When a setting was not found.
|
||||
* Whether the scripts should be loaded.
|
||||
*/
|
||||
public function enqueue(): bool {
|
||||
public function should_load(): bool {
|
||||
$buttons_enabled = $this->settings->has( 'enabled' ) && $this->settings->get( 'enabled' );
|
||||
if ( ! is_checkout() && ! $buttons_enabled ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the scripts.
|
||||
*/
|
||||
public function enqueue(): void {
|
||||
if ( ! $this->should_load() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$load_script = false;
|
||||
if ( is_checkout() && $this->settings->has( 'dcc_enabled' ) && $this->settings->get( 'dcc_enabled' ) ) {
|
||||
$load_script = true;
|
||||
|
@ -555,10 +564,9 @@ class SmartButton implements SmartButtonInterface {
|
|||
wp_localize_script(
|
||||
'ppcp-smart-button',
|
||||
'PayPalCommerceGateway',
|
||||
$this->localize_script()
|
||||
$this->script_data()
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -752,18 +760,22 @@ class SmartButton implements SmartButtonInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* The localized data for the smart button.
|
||||
* The configuration for the smart buttons.
|
||||
*
|
||||
* @return array
|
||||
* @throws NotFoundException If a setting hasn't been found.
|
||||
*/
|
||||
private function localize_script(): array {
|
||||
public function script_data(): array {
|
||||
global $wp;
|
||||
|
||||
$is_free_trial_cart = $this->is_free_trial_cart();
|
||||
|
||||
$url_params = $this->url_params();
|
||||
|
||||
$this->request_data->enqueue_nonce_fix();
|
||||
$localize = array(
|
||||
'url' => add_query_arg( $url_params, 'https://www.paypal.com/sdk/js' ),
|
||||
'url_params' => $url_params,
|
||||
'script_attributes' => $this->attributes(),
|
||||
'data_client_id' => array(
|
||||
'set_attribute' => ( is_checkout() && $this->dcc_is_enabled() ) || $this->can_save_vault_token(),
|
||||
|
@ -799,6 +811,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(),
|
||||
|
@ -810,7 +825,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
'wrapper' => '#ppc-button-' . PayPalGateway::ID,
|
||||
'mini_cart_wrapper' => '#ppc-button-minicart',
|
||||
'cancel_wrapper' => '#ppcp-cancel',
|
||||
'url' => $this->url(),
|
||||
'mini_cart_style' => array(
|
||||
'layout' => $this->style_for_context( 'layout', 'mini-cart' ),
|
||||
'color' => $this->style_for_context( 'color', 'mini-cart' ),
|
||||
|
@ -917,12 +931,12 @@ class SmartButton implements SmartButtonInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* The JavaScript SDK url to load.
|
||||
* The JavaScript SDK url parameters.
|
||||
*
|
||||
* @return string
|
||||
* @return array
|
||||
* @throws NotFoundException If a setting was not found.
|
||||
*/
|
||||
private function url(): string {
|
||||
private function url_params(): array {
|
||||
$intent = ( $this->settings->has( 'intent' ) ) ? $this->settings->get( 'intent' ) : 'capture';
|
||||
$product_intent = $this->subscription_helper->current_product_is_subscription() ? 'authorize' : $intent;
|
||||
$other_context_intent = $this->subscription_helper->cart_contains_subscription() ? 'authorize' : $intent;
|
||||
|
@ -995,8 +1009,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
$params['enable-funding'] = implode( ',', array_unique( $enable_funding ) );
|
||||
}
|
||||
|
||||
$smart_button_url = add_query_arg( $params, 'https://www.paypal.com/sdk/js' );
|
||||
return $smart_button_url;
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,11 +22,14 @@ interface SmartButtonInterface {
|
|||
public function render_wrapper(): bool;
|
||||
|
||||
/**
|
||||
* Enqueues the necessary scripts.
|
||||
*
|
||||
* @return bool
|
||||
* Whether the scripts should be loaded.
|
||||
*/
|
||||
public function enqueue(): bool;
|
||||
public function should_load(): bool;
|
||||
|
||||
/**
|
||||
* Enqueues the necessary scripts.
|
||||
*/
|
||||
public function enqueue(): void;
|
||||
|
||||
/**
|
||||
* Whether the running installation could save vault tokens or not.
|
||||
|
@ -34,4 +37,11 @@ interface SmartButtonInterface {
|
|||
* @return bool
|
||||
*/
|
||||
public function can_save_vault_token(): bool;
|
||||
|
||||
/**
|
||||
* The configuration for the smart buttons.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function script_data(): array;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
17
package.json
17
package.json
|
@ -7,7 +7,6 @@
|
|||
"author": "WooCommerce",
|
||||
"scripts": {
|
||||
"postinstall": "run-s install:modules:* && run-s build:modules",
|
||||
|
||||
"install:modules:ppcp-button": "cd modules/ppcp-button && yarn install",
|
||||
"install:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn install",
|
||||
"install:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn install",
|
||||
|
@ -16,7 +15,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-button": "cd modules/ppcp-button && yarn run build",
|
||||
"build:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn run build",
|
||||
"build:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn run build",
|
||||
|
@ -26,7 +24,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-button": "cd modules/ppcp-button && yarn run watch",
|
||||
"watch:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn run watch",
|
||||
"watch:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn run watch",
|
||||
|
@ -36,7 +33,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",
|
||||
|
@ -47,7 +43,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",
|
||||
|
@ -55,19 +54,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
11
playwright.config.js
Normal 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;
|
3
tests/playwright/.gitignore
vendored
Normal file
3
tests/playwright/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
test-results/
|
||||
playwright-report/
|
||||
.cache/
|
15
tests/playwright/README.md
Normal file
15
tests/playwright/README.md
Normal 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"
|
||||
```
|
75
tests/playwright/place-order.spec.js
Normal file
75
tests/playwright/place-order.spec.js
Normal 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');
|
||||
});
|
30
yarn.lock
30
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue