Merge branch 'trunk' into PCP-2186-apple-pay-button-preview

This commit is contained in:
Pedro Silva 2023-11-03 14:01:15 +00:00 committed by GitHub
commit 06087f7e67
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 391 additions and 103 deletions

View file

@ -48,8 +48,8 @@ class ApiModule implements ModuleInterface {
'ppcp_create_order_request_body_data',
function( array $data ) use ( $c ) {
foreach ( $data['purchase_units'] as $purchase_unit_index => $purchase_unit ) {
foreach ( $purchase_unit['items'] as $item_index => $item ) {
foreach ( ( $data['purchase_units'] ?? array() ) as $purchase_unit_index => $purchase_unit ) {
foreach ( ( $purchase_unit['items'] ?? array() ) as $item_index => $item ) {
$data['purchase_units'][ $purchase_unit_index ]['items'][ $item_index ]['name'] =
apply_filters( 'woocommerce_paypal_payments_cart_line_item_name', $item['name'], $item['cart_item_key'] ?? null );
}

View file

@ -102,7 +102,7 @@ class OrderTransient {
$transient = array();
}
if ( ! is_array( $transient['notes'] ) ) {
if ( ! is_array( $transient['notes'] ?? null ) ) {
$transient['notes'] = array();
}

View file

@ -26,7 +26,7 @@ class ApplepayButton {
this.updated_contact_info = []
this.selectedShippingMethod = []
this.nonce = document.getElementById('woocommerce-process-checkout-nonce')?.value
this.nonce = document.getElementById('woocommerce-process-checkout-nonce')?.value || buttonConfig.nonce
this.log = function() {
if ( this.buttonConfig.is_debug ) {
@ -243,6 +243,11 @@ class ApplepayButton {
requiredShippingContactFields: ["postalAddress", "email", "phone"],
requiredBillingContactFields: ["postalAddress", "email", "phone"],
}
if (!this.contextHandler.shippingAllowed()) {
baseRequest.requiredShippingContactFields = [];
}
const paymentDataRequest = Object.assign({}, baseRequest);
paymentDataRequest.currencyCode = buttonConfig.shop.currencyCode;
paymentDataRequest.total = {
@ -512,18 +517,53 @@ class ApplepayButton {
if (confirmOrderResponse && confirmOrderResponse.approveApplePayPayment) {
if (confirmOrderResponse.approveApplePayPayment.status === "APPROVED") {
try {
let data = {
billing_contact: event.payment.billingContact,
shipping_contact: event.payment.shippingContact,
paypal_order_id: id,
};
let authorizationResult = await processInWooAndCapture(data);
if (authorizationResult.result === "success") {
session.completePayment(ApplePaySession.STATUS_SUCCESS)
window.location.href = authorizationResult.redirect
if (!this.contextHandler.shippingAllowed()) {
// No shipping, expect immediate capture, ex: PayNow.
let approveFailed = false;
await this.contextHandler.approveOrder({
orderID: id
}, { // actions mock object.
restart: () => new Promise((resolve, reject) => {
approveFailed = true;
resolve();
}),
order: {
get: () => new Promise((resolve, reject) => {
resolve(null);
})
}
});
if (!approveFailed) {
this.log('onpaymentauthorized approveOrder OK');
session.completePayment(ApplePaySession.STATUS_SUCCESS);
} else {
this.log('onpaymentauthorized approveOrder FAIL');
session.completePayment(ApplePaySession.STATUS_FAILURE);
session.abort()
console.error(error);
}
} else {
session.completePayment(ApplePaySession.STATUS_FAILURE)
// Default payment.
let data = {
billing_contact: event.payment.billingContact,
shipping_contact: event.payment.shippingContact,
paypal_order_id: id,
};
let authorizationResult = await processInWooAndCapture(data);
if (authorizationResult.result === "success") {
session.completePayment(ApplePaySession.STATUS_SUCCESS)
window.location.href = authorizationResult.redirect
} else {
session.completePayment(ApplePaySession.STATUS_FAILURE)
}
}
} catch (error) {
session.completePayment(ApplePaySession.STATUS_FAILURE);
session.abort()

View file

@ -1,8 +1,6 @@
import ErrorHandler from "../../../../ppcp-button/resources/js/modules/ErrorHandler";
import CartActionHandler
from "../../../../ppcp-button/resources/js/modules/ActionHandler/CartActionHandler";
import onApprove
from "../../../../ppcp-button/resources/js/modules/OnApproveHandler/onApproveForContinue";
class BaseHandler {
@ -11,6 +9,10 @@ class BaseHandler {
this.ppcpConfig = ppcpConfig;
}
shippingAllowed() {
return true;
}
transactionInfo() {
return new Promise((resolve, reject) => {
@ -42,30 +44,32 @@ class BaseHandler {
}
createOrder() {
const errorHandler = new ErrorHandler(
this.ppcpConfig.labels.error.generic,
document.querySelector('.woocommerce-notices-wrapper')
);
const actionHandler = new CartActionHandler(
this.ppcpConfig,
errorHandler,
);
return actionHandler.configuration().createOrder(null, null);
return this.actionHandler().configuration().createOrder(null, null);
}
approveOrderForContinue(data, actions) {
const errorHandler = new ErrorHandler(
approveOrder(data, actions) {
return this.actionHandler().configuration().onApprove(data, actions);
}
actionHandler() {
return new CartActionHandler(
this.ppcpConfig,
this.errorHandler(),
);
}
errorHandler() {
return new ErrorHandler(
this.ppcpConfig.labels.error.generic,
document.querySelector('.woocommerce-notices-wrapper')
);
}
let onApproveHandler = onApprove({
config: this.ppcpConfig
}, errorHandler);
return onApproveHandler(data, actions);
errorHandler() {
return new ErrorHandler(
this.ppcpConfig.labels.error.generic,
document.querySelector('.woocommerce-notices-wrapper')
);
}
}

View file

@ -5,6 +5,7 @@ import CartBlockHandler from "./CartBlockHandler";
import CheckoutBlockHandler from "./CheckoutBlockHandler";
import MiniCartHandler from "./MiniCartHandler";
import PreviewHandler from "./PreviewHandler";
import PayNowHandler from "./PayNowHandler";
class ContextHandlerFactory {
@ -15,8 +16,9 @@ class ContextHandlerFactory {
case 'cart':
return new CartHandler(buttonConfig, ppcpConfig);
case 'checkout':
case 'pay-now': // same as checkout
return new CheckoutHandler(buttonConfig, ppcpConfig);
case 'pay-now':
return new PayNowHandler(buttonConfig, ppcpConfig);
case 'mini-cart':
return new MiniCartHandler(buttonConfig, ppcpConfig);
case 'cart-block':

View file

@ -0,0 +1,35 @@
import Spinner from "../../../../ppcp-button/resources/js/modules/Helper/Spinner";
import BaseHandler from "./BaseHandler";
import CheckoutActionHandler
from "../../../../ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler";
class PayNowHandler extends BaseHandler {
shippingAllowed() {
return false;
}
transactionInfo() {
return new Promise(async (resolve, reject) => {
const data = this.ppcpConfig['pay_now'];
resolve({
countryCode: data.country_code,
currencyCode: data.currency_code,
totalPriceStatus: 'FINAL',
totalPrice: data.total_str
});
});
}
actionHandler() {
return new CheckoutActionHandler(
this.ppcpConfig,
this.errorHandler(),
new Spinner()
);
}
}
export default PayNowHandler;

View file

@ -49,19 +49,14 @@ class SingleProductHandler extends BaseHandler {
}
createOrder() {
return this.actionHandler().configuration().createOrder();
}
products() {
return this.actionHandler().getProducts();
return this.actionHandler().configuration().createOrder(null, null, {
'updateCartOptions': {
'keepShipping': true
}
});
}
actionHandler() {
const errorHandler = new ErrorHandler(
this.ppcpConfig.labels.error.generic,
document.querySelector('.woocommerce-notices-wrapper')
);
return new SingleProductActionHandler(
this.ppcpConfig,
new UpdateCart(
@ -69,10 +64,13 @@ class SingleProductHandler extends BaseHandler {
this.ppcpConfig.ajax.change_cart.nonce,
),
document.querySelector('form.cart'),
errorHandler,
this.errorHandler(),
);
}
products() {
return this.actionHandler().getProducts();
}
}
export default SingleProductHandler;

View file

@ -130,6 +130,24 @@ return array(
return apply_filters(
'woocommerce_paypal_payments_applepay_supported_country_currency_matrix',
array(
'CA' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'GB' => array(
'AUD',
'CAD',
@ -148,6 +166,24 @@ return array(
'SGD',
'USD',
),
'IT' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'US' => array(
'AUD',
'CAD',
@ -156,24 +192,6 @@ return array(
'JPY',
'USD',
),
'CA' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
)
);
},

View file

@ -84,14 +84,16 @@ class ApplepayModule implements ModuleInterface {
return;
}
$module->load_assets( $c, $apple_payment_method );
$module->handle_validation_file( $c );
$module->render_buttons( $c, $apple_payment_method );
if ( $apple_payment_method->is_enabled() ) {
$module->load_assets( $c, $apple_payment_method );
$module->handle_validation_file( $c, $apple_payment_method );
$module->render_buttons( $c, $apple_payment_method );
$apple_payment_method->bootstrap_ajax_request();
}
$apple_payment_method->bootstrap_ajax_request();
$module->load_admin_assets( $c, $apple_payment_method );
}
$module->load_admin_assets( $c, $apple_payment_method );
},
1
);
add_filter(
@ -253,9 +255,13 @@ class ApplepayModule implements ModuleInterface {
* Handles the validation file.
*
* @param ContainerInterface $c The container.
* @param ApplePayButton $button The button.
* @return void
*/
public function handle_validation_file( ContainerInterface $c ): void {
public function handle_validation_file( ContainerInterface $c, ApplePayButton $button ): void {
if ( ! $button->is_enabled() ) {
return;
}
$env = $c->get( 'onboarding.environment' );
assert( $env instanceof Environment );
$is_sandobx = $env->current_environment_is( Environment::SANDBOX );

View file

@ -168,6 +168,7 @@ class DataToAppleButtonScripts {
'totalLabel' => $total_label,
),
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
);
}
@ -215,6 +216,7 @@ class DataToAppleButtonScripts {
'totalLabel' => $total_label,
),
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
);
}

View file

@ -143,7 +143,7 @@ class SingleProductActionHandler {
{
this.cartHelper = null;
return (data, actions) => {
return (data, actions, options = {}) => {
this.errorHandler.clear();
const onResolve = (purchase_units) => {
@ -178,7 +178,7 @@ class SingleProductActionHandler {
});
};
return this.updateCart.update(onResolve, this.getProducts());
return this.updateCart.update(onResolve, this.getProducts(), options.updateCartOptions || {});
};
}

View file

@ -11,9 +11,10 @@ class UpdateCart {
*
* @param onResolve
* @param {Product[]} products
* @param {Object} options
* @returns {Promise<unknown>}
*/
update(onResolve, products)
update(onResolve, products, options = {})
{
return new Promise((resolve, reject) => {
fetch(
@ -27,6 +28,7 @@ class UpdateCart {
body: JSON.stringify({
nonce: this.nonce,
products,
...options
})
}
).then(

View file

@ -395,14 +395,16 @@ class SmartButton implements SmartButtonInterface {
return false;
}
$get_hook = function ( string $location ): ?array {
$default_pay_order_hook = 'woocommerce_pay_order_before_submit';
$get_hook = function ( string $location ) use ( $default_pay_order_hook ): ?array {
switch ( $location ) {
case 'checkout':
return $this->messages_renderer_hook( $location, 'woocommerce_review_order_before_payment', 10 );
case 'cart':
return $this->messages_renderer_hook( $location, $this->proceed_to_checkout_button_renderer_hook(), 19 );
case 'pay-now':
return $this->messages_renderer_hook( 'pay_order', 'woocommerce_pay_order_before_submit', 10 );
return $this->messages_renderer_hook( $location, $default_pay_order_hook, 10 );
case 'product':
return $this->messages_renderer_hook( $location, $this->single_product_renderer_hook(), 30 );
case 'shop':
@ -425,6 +427,28 @@ class SmartButton implements SmartButtonInterface {
$hook['priority']
);
// Looks like there are no hooks like woocommerce_review_order_before_payment on the pay for order page, so have to move using JS.
if ( $location === 'pay-now' && $hook['name'] === $default_pay_order_hook &&
/**
* The filter returning true if Pay Later messages should be displayed before payment methods
* on the pay for order page, like in checkout.
*/
apply_filters(
'woocommerce_paypal_payments_put_pay_order_messages_before_payment_methods',
true
)
) {
add_action(
'ppcp_after_pay_order_message_wrapper',
function () {
echo '
<script>
document.querySelector("#payment").before(document.querySelector("#ppcp-messages"))
</script>';
}
);
}
return true;
}
@ -681,11 +705,12 @@ class SmartButton implements SmartButtonInterface {
/**
* Renders the HTML for the credit messaging.
*/
public function message_renderer() {
public function message_renderer(): void {
$product = wc_get_product();
$location = $this->location();
$location = $this->location();
$location_hook = $this->location_to_hook( $location );
if (
$location === 'product' && is_a( $product, WC_Product::class )
@ -697,7 +722,17 @@ class SmartButton implements SmartButtonInterface {
return;
}
/**
* A hook executed before rendering of the PCP Pay Later messages wrapper.
*/
do_action( "ppcp_before_{$location_hook}_message_wrapper" );
echo '<div id="ppcp-messages" data-partner-attribution-id="Woo_PPCP"></div>';
/**
* A hook executed after rendering of the PCP Pay Later messages wrapper.
*/
do_action( "ppcp_after_{$location_hook}_message_wrapper" );
}
/**
@ -1203,6 +1238,13 @@ class SmartButton implements SmartButtonInterface {
$params['enable-funding'] = implode( ',', array_unique( $enable_funding ) );
}
$locale = $this->settings->has( 'smart_button_language' ) ? $this->settings->get( 'smart_button_language' ) : '';
$locale = (string) apply_filters( 'woocommerce_paypal_payments_smart_buttons_locale', $locale );
if ( $locale ) {
$params['locale'] = $locale;
}
return $params;
}
@ -1399,18 +1441,20 @@ class SmartButton implements SmartButtonInterface {
* @return array An array with 'name' and 'priority' keys.
*/
private function messages_renderer_hook( string $location, string $default_hook, int $default_priority ): array {
$location_hook = $this->location_to_hook( $location );
/**
* The filter returning the action name that will be used for rendering Pay Later messages.
*/
$hook = (string) apply_filters(
"woocommerce_paypal_payments_${location}_messages_renderer_hook",
"woocommerce_paypal_payments_${location_hook}_messages_renderer_hook",
$default_hook
);
/**
* The filter returning the action priority that will be used for rendering Pay Later messages.
*/
$priority = (int) apply_filters(
"woocommerce_paypal_payments_${location}_messages_renderer_priority",
"woocommerce_paypal_payments_${location_hook}_messages_renderer_priority",
$default_priority
);
return array(
@ -1724,4 +1768,18 @@ class SmartButton implements SmartButtonInterface {
}
/**
* Converts the location name into the name used in hooks.
*
* @param string $location The location.
* @return string
*/
private function location_to_hook( string $location ): string {
switch ( $location ) {
case 'pay-now':
return 'pay_order';
default:
return $location;
}
}
}

View file

@ -71,6 +71,8 @@ class ChangeCartEndpoint extends AbstractCartEndpoint {
* @throws Exception On error.
*/
protected function handle_data(): bool {
$data = $this->request_data->read_request( $this->nonce() );
$this->cart_products->set_cart( $this->cart );
$products = $this->products_from_request();
@ -79,7 +81,9 @@ class ChangeCartEndpoint extends AbstractCartEndpoint {
return false;
}
$this->shipping->reset_shipping();
if ( ! ( $data['keepShipping'] ?? false ) ) {
$this->shipping->reset_shipping();
}
if ( ! $this->add_products( $products ) ) {
return false;

View file

@ -34,6 +34,28 @@ trait ContextTrait {
return false;
}
/**
* Checks WC is_cart() + WC cart ajax requests.
*/
private function is_cart(): bool {
if ( is_cart() ) {
return true;
}
/**
* The filter returning whether to detect WC cart ajax requests.
*/
if ( apply_filters( 'ppcp_check_ajax_cart', true ) ) {
// phpcs:ignore WordPress.Security
$wc_ajax = $_GET['wc-ajax'] ?? '';
if ( in_array( $wc_ajax, array( 'update_shipping_method' ), true ) ) {
return true;
}
}
return false;
}
/**
* The current context.
*
@ -56,7 +78,7 @@ trait ContextTrait {
return 'cart-block';
}
if ( is_cart() ) {
if ( $this->is_cart() ) {
return 'cart';
}

View file

@ -48,6 +48,14 @@ class SingleProductHandler extends BaseHandler {
});
}
createOrder() {
return this.actionHandler().configuration().createOrder(null, null, {
'updateCartOptions': {
'keepShipping': true
}
});
}
actionHandler() {
return new SingleProductActionHandler(
this.ppcpConfig,

View file

@ -90,6 +90,24 @@ return array(
return apply_filters(
'woocommerce_paypal_payments_googlepay_supported_country_currency_matrix',
array(
'CA' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'GB' => array(
'AUD',
'CAD',
@ -108,6 +126,24 @@ return array(
'SGD',
'USD',
),
'IT' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'US' => array(
'AUD',
'CAD',
@ -116,24 +152,6 @@ return array(
'JPY',
'USD',
),
'CA' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
)
);
},

View file

@ -155,7 +155,8 @@ class GooglepayModule implements ModuleInterface {
}
);
}
},
1
);
}

View file

@ -115,7 +115,7 @@ document.addEventListener(
'currency': PayPalCommerceGatewaySettings.currency,
'integration-date': PayPalCommerceGatewaySettings.integration_date,
'components': PayPalCommerceGatewaySettings.components,
'enable-funding': ['venmo', 'paylater'],
'enable-funding': ['venmo', 'paylater']
};
if (PayPalCommerceGatewaySettings.environment === 'sandbox') {
@ -134,6 +134,11 @@ document.addEventListener(
settings['disable-funding'] = disabledSources;
}
const smartButtonLocale = document.getElementById('ppcp-smart_button_language');
if (smartButtonLocale?.length > 0 && smartButtonLocale?.value !== '') {
settings['locale'] = smartButtonLocale.value;
}
return settings;
}

View file

@ -1422,4 +1422,52 @@ return array(
return new DisplayManager( $settings );
}
),
'wcgateway.wp-paypal-locales-map' => static function( ContainerInterface $container ): array {
return apply_filters(
'woocommerce_paypal_payments_button_locales',
array(
'' => __( 'Browser language', 'woocommerce-paypal-payments' ),
'ar_DZ' => __( 'Arabic (Algeria)', 'woocommerce-paypal-payments' ),
'ar_BH' => __( 'Arabic (Bahrain)', 'woocommerce-paypal-payments' ),
'ar_EG' => __( 'Arabic (Egypt)', 'woocommerce-paypal-payments' ),
'ar_JO' => __( 'Arabic (Jordan)', 'woocommerce-paypal-payments' ),
'ar_KW' => __( 'Arabic (Kuwait)', 'woocommerce-paypal-payments' ),
'ar_MA' => __( 'Arabic (Morocco)', 'woocommerce-paypal-payments' ),
'ar_SA' => __( 'Arabic (Saudi Arabia)', 'woocommerce-paypal-payments' ),
'cs_CZ' => __( 'Czech', 'woocommerce-paypal-payments' ),
'zh_CN' => __( 'Chinese (Simplified)', 'woocommerce-paypal-payments' ),
'zh_HK' => __( 'Chinese (Hong Kong)', 'woocommerce-paypal-payments' ),
'zh_TW' => __( 'Chinese (Traditional)', 'woocommerce-paypal-payments' ),
'da_DK' => __( 'Danish', 'woocommerce-paypal-payments' ),
'nl_NL' => __( 'Dutch', 'woocommerce-paypal-payments' ),
'en_AU' => __( 'English (Australia)', 'woocommerce-paypal-payments' ),
'en_GB' => __( 'English (United Kingdom)', 'woocommerce-paypal-payments' ),
'en_US' => __( 'English (United States)', 'woocommerce-paypal-payments' ),
'fi_FI' => __( 'Finnish', 'woocommerce-paypal-payments' ),
'fr_CA' => __( 'French (Canada)', 'woocommerce-paypal-payments' ),
'fr_FR' => __( 'French (France)', 'woocommerce-paypal-payments' ),
'de_DE' => __( 'German (Germany)', 'woocommerce-paypal-payments' ),
'de_CH' => __( 'German (Switzerland)', 'woocommerce-paypal-payments' ),
'de_AT' => __( 'German (Austria)', 'woocommerce-paypal-payments' ),
'de_LU' => __( 'German (Luxembourg)', 'woocommerce-paypal-payments' ),
'el_GR' => __( 'Greek', 'woocommerce-paypal-payments' ),
'he_IL' => __( 'Hebrew', 'woocommerce-paypal-payments' ),
'hu_HU' => __( 'Hungarian', 'woocommerce-paypal-payments' ),
'id_ID' => __( 'Indonesian', 'woocommerce-paypal-payments' ),
'it_IT' => __( 'Italian', 'woocommerce-paypal-payments' ),
'ja_JP' => __( 'Japanese', 'woocommerce-paypal-payments' ),
'ko_KR' => __( 'Korean', 'woocommerce-paypal-payments' ),
'no_NO' => __( 'Norwegian', 'woocommerce-paypal-payments' ),
'es_ES' => __( 'Spanish (Spain)', 'woocommerce-paypal-payments' ),
'es_MX' => __( 'Spanish (Mexico)', 'woocommerce-paypal-payments' ),
'pl_PL' => __( 'Polish', 'woocommerce-paypal-payments' ),
'pt_BR' => __( 'Portuguese (Brazil)', 'woocommerce-paypal-payments' ),
'pt_PT' => __( 'Portuguese (Portugal)', 'woocommerce-paypal-payments' ),
'ru_RU' => __( 'Russian', 'woocommerce-paypal-payments' ),
'sk_SK' => __( 'Slovak', 'woocommerce-paypal-payments' ),
'sv_SE' => __( 'Swedish', 'woocommerce-paypal-payments' ),
'th_TH' => __( 'Thai', 'woocommerce-paypal-payments' ),
)
);
},
);

View file

@ -71,6 +71,22 @@ return function ( ContainerInterface $container, array $fields ): array {
'requirements' => array(),
'gateway' => 'paypal',
),
'smart_button_language' => array(
'title' => __( 'Smart Button Language', 'woocommerce-paypal-payments' ),
'type' => 'select',
'desc_tip' => true,
'description' => __(
'The language and region used for the displayed PayPal Smart Buttons. The default value is the current language and region setting in a browser.',
'woocommerce-paypal-payments'
),
'class' => array(),
'input_class' => array( 'wc-enhanced-select' ),
'default' => 'en',
'options' => $container->get( 'wcgateway.wp-paypal-locales-map' ),
'screens' => array( State::STATE_ONBOARDED ),
'gateway' => 'paypal',
'requirements' => array(),
),
'smart_button_enable_styling_per_location' => array(
'title' => __( 'Customize Smart Buttons Per Location', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',

View file

@ -80,6 +80,7 @@ class ChangeCartEndpointTest extends TestCase
$requestData = Mockery::mock(RequestData::class);
$requestData
->expects('read_request')
->times(2)
->with(ChangeCartEndpoint::nonce())
->andReturn($data);