diff --git a/modules/ppcp-api-client/src/ApiModule.php b/modules/ppcp-api-client/src/ApiModule.php index ed09ecddc..42bc1f117 100644 --- a/modules/ppcp-api-client/src/ApiModule.php +++ b/modules/ppcp-api-client/src/ApiModule.php @@ -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 ); } diff --git a/modules/ppcp-api-client/src/Helper/OrderTransient.php b/modules/ppcp-api-client/src/Helper/OrderTransient.php index b6b7a0d99..efe980928 100644 --- a/modules/ppcp-api-client/src/Helper/OrderTransient.php +++ b/modules/ppcp-api-client/src/Helper/OrderTransient.php @@ -102,7 +102,7 @@ class OrderTransient { $transient = array(); } - if ( ! is_array( $transient['notes'] ) ) { + if ( ! is_array( $transient['notes'] ?? null ) ) { $transient['notes'] = array(); } diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js index 19c03c5d2..f9c188741 100644 --- a/modules/ppcp-applepay/resources/js/ApplepayButton.js +++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js @@ -25,7 +25,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() diff --git a/modules/ppcp-applepay/resources/js/Context/BaseHandler.js b/modules/ppcp-applepay/resources/js/Context/BaseHandler.js index dea7de698..f08c2d6a1 100644 --- a/modules/ppcp-applepay/resources/js/Context/BaseHandler.js +++ b/modules/ppcp-applepay/resources/js/Context/BaseHandler.js @@ -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,25 @@ 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); } } diff --git a/modules/ppcp-applepay/resources/js/Context/ContextHandlerFactory.js b/modules/ppcp-applepay/resources/js/Context/ContextHandlerFactory.js index 72ee4f5fa..04338354b 100644 --- a/modules/ppcp-applepay/resources/js/Context/ContextHandlerFactory.js +++ b/modules/ppcp-applepay/resources/js/Context/ContextHandlerFactory.js @@ -4,6 +4,7 @@ import CheckoutHandler from "./CheckoutHandler"; import CartBlockHandler from "./CartBlockHandler"; import CheckoutBlockHandler from "./CheckoutBlockHandler"; import MiniCartHandler from "./MiniCartHandler"; +import PayNowHandler from "./PayNowHandler"; class ContextHandlerFactory { @@ -14,8 +15,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': diff --git a/modules/ppcp-applepay/resources/js/Context/PayNowHandler.js b/modules/ppcp-applepay/resources/js/Context/PayNowHandler.js new file mode 100644 index 000000000..add275608 --- /dev/null +++ b/modules/ppcp-applepay/resources/js/Context/PayNowHandler.js @@ -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; diff --git a/modules/ppcp-applepay/resources/js/Context/SingleProductHandler.js b/modules/ppcp-applepay/resources/js/Context/SingleProductHandler.js index f9adb7774..309694a77 100644 --- a/modules/ppcp-applepay/resources/js/Context/SingleProductHandler.js +++ b/modules/ppcp-applepay/resources/js/Context/SingleProductHandler.js @@ -48,20 +48,7 @@ class SingleProductHandler extends BaseHandler { }); } - createOrder() { - return this.actionHandler().configuration().createOrder(); - } - - products() { - return this.actionHandler().getProducts(); - } - actionHandler() { - const errorHandler = new ErrorHandler( - this.ppcpConfig.labels.error.generic, - document.querySelector('.woocommerce-notices-wrapper') - ); - return new SingleProductActionHandler( this.ppcpConfig, new UpdateCart( @@ -69,10 +56,16 @@ 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; diff --git a/modules/ppcp-applepay/services.php b/modules/ppcp-applepay/services.php index a49ab7fad..f345409e6 100644 --- a/modules/ppcp-applepay/services.php +++ b/modules/ppcp-applepay/services.php @@ -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', - ), ) ); }, diff --git a/modules/ppcp-applepay/src/ApplepayModule.php b/modules/ppcp-applepay/src/ApplepayModule.php index d6c47a341..d29bcc240 100644 --- a/modules/ppcp-applepay/src/ApplepayModule.php +++ b/modules/ppcp-applepay/src/ApplepayModule.php @@ -90,7 +90,8 @@ class ApplepayModule implements ModuleInterface { $module->render_buttons( $c, $apple_payment_method ); $apple_payment_method->bootstrap_ajax_request(); } - } + }, + 1 ); add_filter( diff --git a/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php b/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php index 0b15fbe78..882776cf1 100644 --- a/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php +++ b/modules/ppcp-applepay/src/Assets/DataToAppleButtonScripts.php @@ -148,6 +148,7 @@ class DataToAppleButtonScripts { 'totalLabel' => $total_label, ), 'ajax_url' => admin_url( 'admin-ajax.php' ), + 'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ), ); } @@ -195,6 +196,7 @@ class DataToAppleButtonScripts { 'totalLabel' => $total_label, ), 'ajax_url' => admin_url( 'admin-ajax.php' ), + 'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ), ); } } diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 2c2762970..049b1ab00 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -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 ' +'; + } + ); + } + 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 '
'; + + /** + * 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; + } + } } diff --git a/modules/ppcp-button/src/Helper/ContextTrait.php b/modules/ppcp-button/src/Helper/ContextTrait.php index a7381b1b1..db6735424 100644 --- a/modules/ppcp-button/src/Helper/ContextTrait.php +++ b/modules/ppcp-button/src/Helper/ContextTrait.php @@ -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'; } diff --git a/modules/ppcp-compat/resources/js/tracking-compat.js b/modules/ppcp-compat/resources/js/tracking-compat.js index b62158ff1..f6d2c80e4 100644 --- a/modules/ppcp-compat/resources/js/tracking-compat.js +++ b/modules/ppcp-compat/resources/js/tracking-compat.js @@ -9,7 +9,9 @@ document.addEventListener( const loadLocation = location.href + " " + orderTrackingContainerSelector + ">*"; const gzdSyncEnabled = config.gzd_sync_enabled; const wcShipmentSyncEnabled = config.wc_shipment_sync_enabled; + const wcShippingTaxSyncEnabled = config.wc_shipping_tax_sync_enabled; const wcShipmentSaveButton = document.querySelector('#woocommerce-shipment-tracking .button-save-form'); + const wcShipmentTaxBuyLabelButtonSelector = '.components-modal__screen-overlay .label-purchase-modal__sidebar .purchase-section button.components-button'; const toggleLoaderVisibility = function() { const loader = document.querySelector('.ppcp-tracking-loader'); @@ -45,5 +47,20 @@ document.addEventListener( waitForTrackingUpdate(jQuery('#shipment-tracking-form')); }) } + + if (wcShippingTaxSyncEnabled && typeof(wcShippingTaxSyncEnabled) != 'undefined' && wcShippingTaxSyncEnabled != null) { + document.addEventListener('click', function(event) { + const wcShipmentTaxBuyLabelButton = event.target.closest(wcShipmentTaxBuyLabelButtonSelector); + + if (wcShipmentTaxBuyLabelButton) { + toggleLoaderVisibility(); + setTimeout(function () { + jQuery(orderTrackingContainerSelector).load(loadLocation, "", function(){ + toggleLoaderVisibility(); + }); + }, 10000); + } + }); + } }, ); diff --git a/modules/ppcp-compat/services.php b/modules/ppcp-compat/services.php index cb21e195b..9907a15c0 100644 --- a/modules/ppcp-compat/services.php +++ b/modules/ppcp-compat/services.php @@ -65,6 +65,12 @@ return array( 'compat.ywot.is_supported_plugin_version_active' => function (): bool { return function_exists( 'yith_ywot_init' ); }, + 'compat.shipstation.is_supported_plugin_version_active' => function (): bool { + return function_exists( 'woocommerce_shipstation_init' ); + }, + 'compat.wc_shipping_tax.is_supported_plugin_version_active' => function (): bool { + return class_exists( 'WC_Connect_Loader' ); + }, 'compat.module.url' => static function ( ContainerInterface $container ): string { /** @@ -84,6 +90,7 @@ return array( $container->get( 'ppcp.asset-version' ), $container->get( 'compat.gzd.is_supported_plugin_version_active' ), $container->get( 'compat.wc_shipment_tracking.is_supported_plugin_version_active' ), + $container->get( 'compat.wc_shipping_tax.is_supported_plugin_version_active' ), $container->get( 'api.bearer' ) ); }, diff --git a/modules/ppcp-compat/src/Assets/CompatAssets.php b/modules/ppcp-compat/src/Assets/CompatAssets.php index c1febf93f..85e0bde7b 100644 --- a/modules/ppcp-compat/src/Assets/CompatAssets.php +++ b/modules/ppcp-compat/src/Assets/CompatAssets.php @@ -47,6 +47,13 @@ class CompatAssets { */ protected $is_wc_shipment_active; + /** + * Whether WC Shipping & Tax plugin is active + * + * @var bool + */ + private $is_wc_shipping_tax_active; + /** * The bearer. * @@ -61,6 +68,7 @@ class CompatAssets { * @param string $version The assets version. * @param bool $is_gzd_active Whether Germanized plugin is active. * @param bool $is_wc_shipment_active Whether WC Shipments plugin is active. + * @param bool $is_wc_shipping_tax_active Whether WC Shipping & Tax plugin is active. * @param Bearer $bearer The bearer. */ public function __construct( @@ -68,14 +76,16 @@ class CompatAssets { string $version, bool $is_gzd_active, bool $is_wc_shipment_active, + bool $is_wc_shipping_tax_active, Bearer $bearer ) { - $this->module_url = $module_url; - $this->version = $version; - $this->is_gzd_active = $is_gzd_active; - $this->is_wc_shipment_active = $is_wc_shipment_active; - $this->bearer = $bearer; + $this->module_url = $module_url; + $this->version = $version; + $this->is_gzd_active = $is_gzd_active; + $this->is_wc_shipment_active = $is_wc_shipment_active; + $this->is_wc_shipping_tax_active = $is_wc_shipping_tax_active; + $this->bearer = $bearer; } /** @@ -99,6 +109,7 @@ class CompatAssets { array( 'gzd_sync_enabled' => apply_filters( 'woocommerce_paypal_payments_sync_gzd_tracking', true ) && $this->is_gzd_active, 'wc_shipment_sync_enabled' => apply_filters( 'woocommerce_paypal_payments_sync_wc_shipment_tracking', true ) && $this->is_wc_shipment_active, + 'wc_shipping_tax_sync_enabled' => apply_filters( 'woocommerce_paypal_payments_sync_wc_shipping_tax', true ) && $this->is_wc_shipping_tax_active, ) ); } diff --git a/modules/ppcp-compat/src/CompatModule.php b/modules/ppcp-compat/src/CompatModule.php index 790c3edb0..edb4f0e67 100644 --- a/modules/ppcp-compat/src/CompatModule.php +++ b/modules/ppcp-compat/src/CompatModule.php @@ -9,22 +9,13 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Compat; -use Vendidero\Germanized\Shipments\ShipmentItem; -use WooCommerce\PayPalCommerce\OrderTracking\Shipment\ShipmentFactoryInterface; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; -use Exception; use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; -use Psr\Log\LoggerInterface; -use Vendidero\Germanized\Shipments\Shipment; -use WC_Order; use WooCommerce\PayPalCommerce\Compat\Assets\CompatAssets; -use WooCommerce\PayPalCommerce\OrderTracking\Endpoint\OrderTrackingEndpoint; use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; -use WP_REST_Request; -use WP_REST_Response; /** * Class CompatModule @@ -124,230 +115,11 @@ class CompatModule implements ModuleInterface { * @return void */ protected function initialize_tracking_compat_layer( ContainerInterface $c ): void { - $is_gzd_active = $c->get( 'compat.gzd.is_supported_plugin_version_active' ); - $is_wc_shipment_tracking_active = $c->get( 'compat.wc_shipment_tracking.is_supported_plugin_version_active' ); - $is_ywot_active = $c->get( 'compat.ywot.is_supported_plugin_version_active' ); + $order_tracking_integrations = $c->get( 'order-tracking.integrations' ); - if ( $is_gzd_active ) { - $this->initialize_gzd_compat_layer( $c ); - } - - if ( $is_wc_shipment_tracking_active ) { - $this->initialize_wc_shipment_tracking_compat_layer( $c ); - } - - if ( $is_ywot_active ) { - $this->initialize_ywot_compat_layer( $c ); - } - } - - /** - * Sets up the Germanized for WooCommerce - * plugin compatibility layer. - * - * @link https://wordpress.org/plugins/woocommerce-germanized/ - * - * @param ContainerInterface $c The Container. - * @return void - */ - protected function initialize_gzd_compat_layer( ContainerInterface $c ): void { - add_action( - 'woocommerce_gzd_shipment_status_shipped', - function( int $shipment_id, Shipment $shipment ) use ( $c ) { - if ( ! apply_filters( 'woocommerce_paypal_payments_sync_gzd_tracking', true ) ) { - return; - } - - $wc_order = $shipment->get_order(); - - if ( ! is_a( $wc_order, WC_Order::class ) ) { - return; - } - - $order_id = $wc_order->get_id(); - $transaction_id = $wc_order->get_transaction_id(); - $tracking_number = $shipment->get_tracking_id(); - $carrier = $shipment->get_shipping_provider(); - $items = array_map( - function ( ShipmentItem $item ): int { - return $item->get_order_item_id(); - }, - $shipment->get_items() - ); - - if ( ! $tracking_number || ! $carrier || ! $transaction_id ) { - return; - } - - $this->create_tracking( $c, $order_id, $transaction_id, $tracking_number, $carrier, $items ); - }, - 500, - 2 - ); - } - - /** - * Sets up the Shipment Tracking - * plugin compatibility layer. - * - * @link https://woocommerce.com/document/shipment-tracking/ - * - * @param ContainerInterface $c The Container. - * @return void - */ - protected function initialize_wc_shipment_tracking_compat_layer( ContainerInterface $c ): void { - add_action( - 'wp_ajax_wc_shipment_tracking_save_form', - function() use ( $c ) { - check_ajax_referer( 'create-tracking-item', 'security', true ); - - if ( ! apply_filters( 'woocommerce_paypal_payments_sync_wc_shipment_tracking', true ) ) { - return; - } - - $order_id = (int) wc_clean( wp_unslash( $_POST['order_id'] ?? '' ) ); - $wc_order = wc_get_order( $order_id ); - if ( ! is_a( $wc_order, WC_Order::class ) ) { - return; - } - - $transaction_id = $wc_order->get_transaction_id(); - $tracking_number = wc_clean( wp_unslash( $_POST['tracking_number'] ?? '' ) ); - $carrier = wc_clean( wp_unslash( $_POST['tracking_provider'] ?? '' ) ); - $carrier_other = wc_clean( wp_unslash( $_POST['custom_tracking_provider'] ?? '' ) ); - $carrier = $carrier ?: $carrier_other ?: ''; - - if ( ! $tracking_number || ! is_string( $tracking_number ) || ! $carrier || ! is_string( $carrier ) || ! $transaction_id ) { - return; - } - - $this->create_tracking( $c, $order_id, $transaction_id, $tracking_number, $carrier, array() ); - } - ); - - add_filter( - 'woocommerce_rest_prepare_order_shipment_tracking', - function( WP_REST_Response $response, array $tracking_item, WP_REST_Request $request ) use ( $c ): WP_REST_Response { - if ( ! apply_filters( 'woocommerce_paypal_payments_sync_wc_shipment_tracking', true ) ) { - return $response; - } - - $callback = $request->get_attributes()['callback']['1'] ?? ''; - if ( $callback !== 'create_item' ) { - return $response; - } - - $order_id = $tracking_item['order_id'] ?? 0; - $wc_order = wc_get_order( $order_id ); - if ( ! is_a( $wc_order, WC_Order::class ) ) { - return $response; - } - - $transaction_id = $wc_order->get_transaction_id(); - $tracking_number = $tracking_item['tracking_number'] ?? ''; - $carrier = $tracking_item['tracking_provider'] ?? ''; - $carrier_other = $tracking_item['custom_tracking_provider'] ?? ''; - $carrier = $carrier ?: $carrier_other ?: ''; - - if ( ! $tracking_number || ! $carrier || ! $transaction_id ) { - return $response; - } - - $this->create_tracking( $c, $order_id, $transaction_id, $tracking_number, $carrier, array() ); - - return $response; - }, - 10, - 3 - ); - } - - /** - * Sets up the YITH WooCommerce Order & Shipment Tracking - * plugin compatibility layer. - * - * @link https://wordpress.org/plugins/yith-woocommerce-order-tracking/ - * - * @param ContainerInterface $c The Container. - * @return void - */ - protected function initialize_ywot_compat_layer( ContainerInterface $c ): void { - add_action( - 'woocommerce_process_shop_order_meta', - function( int $order_id ) use ( $c ) { - if ( ! apply_filters( 'woocommerce_paypal_payments_sync_ywot_tracking', true ) ) { - return; - } - - $wc_order = wc_get_order( $order_id ); - if ( ! is_a( $wc_order, WC_Order::class ) ) { - return; - } - - $transaction_id = $wc_order->get_transaction_id(); - // phpcs:ignore WordPress.Security.NonceVerification.Missing - $tracking_number = wc_clean( wp_unslash( $_POST['ywot_tracking_code'] ?? '' ) ); - // phpcs:ignore WordPress.Security.NonceVerification.Missing - $carrier = wc_clean( wp_unslash( $_POST['ywot_carrier_name'] ?? '' ) ); - - if ( ! $tracking_number || ! is_string( $tracking_number ) || ! $carrier || ! is_string( $carrier ) || ! $transaction_id ) { - return; - } - - $this->create_tracking( $c, $order_id, $transaction_id, $tracking_number, $carrier, array() ); - }, - 500, - 1 - ); - } - - /** - * Creates PayPal tracking. - * - * @param ContainerInterface $c The Container. - * @param int $wc_order_id The WC order ID. - * @param string $transaction_id The transaction ID. - * @param string $tracking_number The tracking number. - * @param string $carrier The shipment carrier. - * @param int[] $line_items The list of shipment line item IDs. - * @return void - */ - protected function create_tracking( - ContainerInterface $c, - int $wc_order_id, - string $transaction_id, - string $tracking_number, - string $carrier, - array $line_items - ) { - $endpoint = $c->get( 'order-tracking.endpoint.controller' ); - assert( $endpoint instanceof OrderTrackingEndpoint ); - - $logger = $c->get( 'woocommerce.logger.woocommerce' ); - assert( $logger instanceof LoggerInterface ); - - $shipment_factory = $c->get( 'order-tracking.shipment.factory' ); - assert( $shipment_factory instanceof ShipmentFactoryInterface ); - - try { - $ppcp_shipment = $shipment_factory->create_shipment( - $wc_order_id, - $transaction_id, - $tracking_number, - 'SHIPPED', - 'OTHER', - $carrier, - $line_items - ); - - $tracking_information = $endpoint->get_tracking_information( $wc_order_id, $tracking_number ); - - $tracking_information - ? $endpoint->update_tracking_information( $ppcp_shipment, $wc_order_id ) - : $endpoint->add_tracking_information( $ppcp_shipment, $wc_order_id ); - - } catch ( Exception $exception ) { - $logger->error( "Couldn't sync tracking information: " . $exception->getMessage() ); + foreach ( $order_tracking_integrations as $integration ) { + assert( $integration instanceof Integration ); + $integration->integrate(); } } diff --git a/modules/ppcp-compat/src/Integration.php b/modules/ppcp-compat/src/Integration.php new file mode 100644 index 000000000..f8aaf8bc6 --- /dev/null +++ b/modules/ppcp-compat/src/Integration.php @@ -0,0 +1,18 @@ + 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', - ), ) ); }, diff --git a/modules/ppcp-googlepay/src/GooglepayModule.php b/modules/ppcp-googlepay/src/GooglepayModule.php index 94f70ea09..083f8eba7 100644 --- a/modules/ppcp-googlepay/src/GooglepayModule.php +++ b/modules/ppcp-googlepay/src/GooglepayModule.php @@ -155,7 +155,8 @@ class GooglepayModule implements ModuleInterface { } ); - } + }, + 1 ); } diff --git a/modules/ppcp-order-tracking/resources/css/order-edit-page.scss b/modules/ppcp-order-tracking/resources/css/order-edit-page.scss index c9a112ead..fc0c511b2 100644 --- a/modules/ppcp-order-tracking/resources/css/order-edit-page.scss +++ b/modules/ppcp-order-tracking/resources/css/order-edit-page.scss @@ -49,6 +49,8 @@ h4 { display: inline-block; margin: 10px 0px; + max-width: 83%; + overflow: hidden; } button { diff --git a/modules/ppcp-order-tracking/services.php b/modules/ppcp-order-tracking/services.php index 144460571..37154cd65 100644 --- a/modules/ppcp-order-tracking/services.php +++ b/modules/ppcp-order-tracking/services.php @@ -9,15 +9,16 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\OrderTracking; -use WC_Order; -use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; -use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; +use WooCommerce\PayPalCommerce\OrderTracking\Integration\GermanizedShipmentIntegration; +use WooCommerce\PayPalCommerce\OrderTracking\Integration\ShipmentTrackingIntegration; +use WooCommerce\PayPalCommerce\OrderTracking\Integration\ShipStationIntegration; +use WooCommerce\PayPalCommerce\OrderTracking\Integration\WcShippingTaxIntegration; +use WooCommerce\PayPalCommerce\OrderTracking\Integration\YithShipmentIntegration; use WooCommerce\PayPalCommerce\OrderTracking\Shipment\ShipmentFactoryInterface; use WooCommerce\PayPalCommerce\OrderTracking\Shipment\ShipmentFactory; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\OrderTracking\Assets\OrderEditPageAssets; use WooCommerce\PayPalCommerce\OrderTracking\Endpoint\OrderTrackingEndpoint; -use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; return array( 'order-tracking.assets' => function( ContainerInterface $container ) : OrderEditPageAssets { @@ -92,4 +93,39 @@ return array( 'order-tracking.is-merchant-country-us' => static function ( ContainerInterface $container ): bool { return $container->get( 'api.shop.country' ) === 'US'; }, + 'order-tracking.integrations' => static function ( ContainerInterface $container ): array { + $shipment_factory = $container->get( 'order-tracking.shipment.factory' ); + $logger = $container->get( 'woocommerce.logger.woocommerce' ); + $endpoint = $container->get( 'order-tracking.endpoint.controller' ); + + $is_gzd_active = $container->get( 'compat.gzd.is_supported_plugin_version_active' ); + $is_wc_shipment_active = $container->get( 'compat.wc_shipment_tracking.is_supported_plugin_version_active' ); + $is_yith_ywot_active = $container->get( 'compat.ywot.is_supported_plugin_version_active' ); + $is_ship_station_active = $container->get( 'compat.shipstation.is_supported_plugin_version_active' ); + $is_wc_shipping_tax_active = $container->get( 'compat.wc_shipping_tax.is_supported_plugin_version_active' ); + + $integrations = array(); + + if ( $is_gzd_active ) { + $integrations[] = new GermanizedShipmentIntegration( $shipment_factory, $logger, $endpoint ); + } + + if ( $is_wc_shipment_active ) { + $integrations[] = new ShipmentTrackingIntegration( $shipment_factory, $logger, $endpoint ); + } + + if ( $is_yith_ywot_active ) { + $integrations[] = new YithShipmentIntegration( $shipment_factory, $logger, $endpoint ); + } + + if ( $is_ship_station_active ) { + $integrations[] = new ShipStationIntegration( $shipment_factory, $logger, $endpoint ); + } + + if ( $is_wc_shipping_tax_active ) { + $integrations[] = new WcShippingTaxIntegration( $shipment_factory, $logger, $endpoint ); + } + + return $integrations; + }, ); diff --git a/modules/ppcp-order-tracking/src/Integration/GermanizedShipmentIntegration.php b/modules/ppcp-order-tracking/src/Integration/GermanizedShipmentIntegration.php new file mode 100644 index 000000000..7e44fd765 --- /dev/null +++ b/modules/ppcp-order-tracking/src/Integration/GermanizedShipmentIntegration.php @@ -0,0 +1,123 @@ +shipment_factory = $shipment_factory; + $this->logger = $logger; + $this->endpoint = $endpoint; + } + + /** + * {@inheritDoc} + */ + public function integrate(): void { + + add_action( + 'woocommerce_gzd_shipment_status_shipped', + function( int $shipment_id, Shipment $shipment ) { + if ( ! apply_filters( 'woocommerce_paypal_payments_sync_gzd_tracking', true ) ) { + return; + } + + $wc_order = $shipment->get_order(); + + if ( ! is_a( $wc_order, WC_Order::class ) ) { + return; + } + + $wc_order_id = $wc_order->get_id(); + $transaction_id = $wc_order->get_transaction_id(); + $tracking_number = $shipment->get_tracking_id(); + $carrier = $shipment->get_shipping_provider(); + + $items = array_map( + function ( ShipmentItem $item ): int { + return $item->get_order_item_id(); + }, + $shipment->get_items() + ); + + if ( ! $tracking_number || ! $carrier || ! $transaction_id ) { + return; + } + + try { + $ppcp_shipment = $this->shipment_factory->create_shipment( + $wc_order_id, + $transaction_id, + $tracking_number, + 'SHIPPED', + 'OTHER', + $carrier, + $items + ); + + $tracking_information = $this->endpoint->get_tracking_information( $wc_order_id, $tracking_number ); + + $tracking_information + ? $this->endpoint->update_tracking_information( $ppcp_shipment, $wc_order_id ) + : $this->endpoint->add_tracking_information( $ppcp_shipment, $wc_order_id ); + + } catch ( Exception $exception ) { + $this->logger->error( "Couldn't sync tracking information: " . $exception->getMessage() ); + } + }, + 500, + 2 + ); + } +} diff --git a/modules/ppcp-order-tracking/src/Integration/ShipStationIntegration.php b/modules/ppcp-order-tracking/src/Integration/ShipStationIntegration.php new file mode 100644 index 000000000..8d294f724 --- /dev/null +++ b/modules/ppcp-order-tracking/src/Integration/ShipStationIntegration.php @@ -0,0 +1,117 @@ +shipment_factory = $shipment_factory; + $this->logger = $logger; + $this->endpoint = $endpoint; + } + + /** + * {@inheritDoc} + */ + public function integrate(): void { + + add_action( + 'woocommerce_shipstation_shipnotify', + /** + * Param type for $wc_order can be different. + * + * @psalm-suppress MissingClosureParamType + */ + function( $wc_order, array $data ) { + if ( ! apply_filters( 'woocommerce_paypal_payments_sync_ship_station_tracking', true ) ) { + return; + } + + if ( ! is_a( $wc_order, WC_Order::class ) ) { + return; + } + + $order_id = $wc_order->get_id(); + $transaction_id = $wc_order->get_transaction_id(); + $tracking_number = $data['tracking_number'] ?? ''; + $carrier = $data['carrier'] ?? ''; + + if ( ! $tracking_number || ! is_string( $tracking_number ) || ! $carrier || ! is_string( $carrier ) || ! $transaction_id ) { + return; + } + + try { + $ppcp_shipment = $this->shipment_factory->create_shipment( + $order_id, + $transaction_id, + $tracking_number, + 'SHIPPED', + 'OTHER', + $carrier, + array() + ); + + $tracking_information = $this->endpoint->get_tracking_information( $order_id, $tracking_number ); + + $tracking_information + ? $this->endpoint->update_tracking_information( $ppcp_shipment, $order_id ) + : $this->endpoint->add_tracking_information( $ppcp_shipment, $order_id ); + + } catch ( Exception $exception ) { + $this->logger->error( "Couldn't sync tracking information: " . $exception->getMessage() ); + } + }, + 500, + 1 + ); + } +} diff --git a/modules/ppcp-order-tracking/src/Integration/ShipmentTrackingIntegration.php b/modules/ppcp-order-tracking/src/Integration/ShipmentTrackingIntegration.php new file mode 100644 index 000000000..6c506a5e8 --- /dev/null +++ b/modules/ppcp-order-tracking/src/Integration/ShipmentTrackingIntegration.php @@ -0,0 +1,174 @@ +shipment_factory = $shipment_factory; + $this->logger = $logger; + $this->endpoint = $endpoint; + } + + /** + * {@inheritDoc} + */ + public function integrate(): void { + + add_action( + 'wp_ajax_wc_shipment_tracking_save_form', + function() { + check_ajax_referer( 'create-tracking-item', 'security', true ); + + if ( ! apply_filters( 'woocommerce_paypal_payments_sync_wc_shipment_tracking', true ) ) { + return; + } + + $order_id = (int) wc_clean( wp_unslash( $_POST['order_id'] ?? '' ) ); + $wc_order = wc_get_order( $order_id ); + if ( ! is_a( $wc_order, WC_Order::class ) ) { + return; + } + + $transaction_id = $wc_order->get_transaction_id(); + $tracking_number = wc_clean( wp_unslash( $_POST['tracking_number'] ?? '' ) ); + $carrier = wc_clean( wp_unslash( $_POST['tracking_provider'] ?? '' ) ); + $carrier_other = wc_clean( wp_unslash( $_POST['custom_tracking_provider'] ?? '' ) ); + $carrier = $carrier ?: $carrier_other ?: ''; + + if ( ! $tracking_number || ! is_string( $tracking_number ) || ! $carrier || ! is_string( $carrier ) || ! $transaction_id ) { + return; + } + + $this->sync_tracking( $order_id, $transaction_id, $tracking_number, $carrier ); + } + ); + + /** + * Support the case when tracking is added via REST. + */ + add_filter( + 'woocommerce_rest_prepare_order_shipment_tracking', + function( WP_REST_Response $response, array $tracking_item, WP_REST_Request $request ): WP_REST_Response { + if ( ! apply_filters( 'woocommerce_paypal_payments_sync_wc_shipment_tracking', true ) ) { + return $response; + } + + $callback = $request->get_attributes()['callback']['1'] ?? ''; + if ( $callback !== 'create_item' ) { + return $response; + } + + $order_id = $tracking_item['order_id'] ?? 0; + $wc_order = wc_get_order( $order_id ); + if ( ! is_a( $wc_order, WC_Order::class ) ) { + return $response; + } + + $transaction_id = $wc_order->get_transaction_id(); + $tracking_number = $tracking_item['tracking_number'] ?? ''; + $carrier = $tracking_item['tracking_provider'] ?? ''; + $carrier_other = $tracking_item['custom_tracking_provider'] ?? ''; + $carrier = $carrier ?: $carrier_other ?: ''; + + if ( ! $tracking_number || ! $carrier || ! $transaction_id ) { + return $response; + } + + $this->sync_tracking( $order_id, $transaction_id, $tracking_number, $carrier ); + + return $response; + }, + 10, + 3 + ); + } + + /** + * Syncs (add | update) the PayPal tracking with given info. + * + * @param int $wc_order_id The WC order ID. + * @param string $transaction_id The transaction ID. + * @param string $tracking_number The tracking number. + * @param string $carrier The shipment carrier. + * @return void + */ + protected function sync_tracking( + int $wc_order_id, + string $transaction_id, + string $tracking_number, + string $carrier + ) { + try { + $ppcp_shipment = $this->shipment_factory->create_shipment( + $wc_order_id, + $transaction_id, + $tracking_number, + 'SHIPPED', + 'OTHER', + $carrier, + array() + ); + + $tracking_information = $this->endpoint->get_tracking_information( $wc_order_id, $tracking_number ); + + $tracking_information + ? $this->endpoint->update_tracking_information( $ppcp_shipment, $wc_order_id ) + : $this->endpoint->add_tracking_information( $ppcp_shipment, $wc_order_id ); + + } catch ( Exception $exception ) { + $this->logger->error( "Couldn't sync tracking information: " . $exception->getMessage() ); + } + } +} diff --git a/modules/ppcp-order-tracking/src/Integration/WcShippingTaxIntegration.php b/modules/ppcp-order-tracking/src/Integration/WcShippingTaxIntegration.php new file mode 100644 index 000000000..a808c6ade --- /dev/null +++ b/modules/ppcp-order-tracking/src/Integration/WcShippingTaxIntegration.php @@ -0,0 +1,159 @@ +shipment_factory = $shipment_factory; + $this->logger = $logger; + $this->endpoint = $endpoint; + } + + /** + * {@inheritDoc} + */ + public function integrate(): void { + + add_filter( + 'rest_post_dispatch', + function( WP_HTTP_Response $response, WP_REST_Server $server, WP_REST_Request $request ): WP_HTTP_Response { + if ( ! apply_filters( 'woocommerce_paypal_payments_sync_wc_shipping_tax', true ) ) { + return $response; + } + + $params = $request->get_params(); + $order_id = (int) ( $params['order_id'] ?? 0 ); + $label_id = (int) ( $params['label_ids'] ?? 0 ); + + if ( ! $order_id || "/wc/v1/connect/label/{$order_id}/{$label_id}" !== $request->get_route() ) { + return $response; + } + + $data = $response->get_data() ?? array(); + $labels = $data['labels'] ?? array(); + + foreach ( $labels as $label ) { + $tracking_number = $label['tracking'] ?? ''; + if ( ! $tracking_number ) { + continue; + } + + $wc_order = wc_get_order( $order_id ); + if ( ! is_a( $wc_order, WC_Order::class ) ) { + continue; + } + + $transaction_id = $wc_order->get_transaction_id(); + $carrier = $label['carrier_id'] ?? $label['service_name'] ?? ''; + $items = array_map( 'intval', $label['product_ids'] ?? array() ); + + if ( ! $carrier || ! $transaction_id ) { + continue; + } + + $this->sync_tracking( $order_id, $transaction_id, $tracking_number, $carrier, $items ); + } + + return $response; + }, + 10, + 3 + ); + + } + + /** + * Syncs (add | update) the PayPal tracking with given info. + * + * @param int $wc_order_id The WC order ID. + * @param string $transaction_id The transaction ID. + * @param string $tracking_number The tracking number. + * @param string $carrier The shipment carrier. + * @param int[] $items The list of line items IDs. + * @return void + */ + protected function sync_tracking( + int $wc_order_id, + string $transaction_id, + string $tracking_number, + string $carrier, + array $items + ) { + try { + $ppcp_shipment = $this->shipment_factory->create_shipment( + $wc_order_id, + $transaction_id, + $tracking_number, + 'SHIPPED', + 'OTHER', + $carrier, + $items + ); + + $tracking_information = $this->endpoint->get_tracking_information( $wc_order_id, $tracking_number ); + + $tracking_information + ? $this->endpoint->update_tracking_information( $ppcp_shipment, $wc_order_id ) + : $this->endpoint->add_tracking_information( $ppcp_shipment, $wc_order_id ); + + } catch ( Exception $exception ) { + $this->logger->error( "Couldn't sync tracking information: " . $exception->getMessage() ); + } + } +} diff --git a/modules/ppcp-order-tracking/src/Integration/YithShipmentIntegration.php b/modules/ppcp-order-tracking/src/Integration/YithShipmentIntegration.php new file mode 100644 index 000000000..57a36babf --- /dev/null +++ b/modules/ppcp-order-tracking/src/Integration/YithShipmentIntegration.php @@ -0,0 +1,114 @@ +shipment_factory = $shipment_factory; + $this->logger = $logger; + $this->endpoint = $endpoint; + } + + /** + * {@inheritDoc} + */ + public function integrate(): void { + + add_action( + 'woocommerce_process_shop_order_meta', + function( int $order_id ) { + if ( ! apply_filters( 'woocommerce_paypal_payments_sync_ywot_tracking', true ) ) { + return; + } + + $wc_order = wc_get_order( $order_id ); + if ( ! is_a( $wc_order, WC_Order::class ) ) { + return; + } + + $transaction_id = $wc_order->get_transaction_id(); + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $tracking_number = wc_clean( wp_unslash( $_POST['ywot_tracking_code'] ?? '' ) ); + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $carrier = wc_clean( wp_unslash( $_POST['ywot_carrier_name'] ?? '' ) ); + + if ( ! $tracking_number || ! is_string( $tracking_number ) || ! $carrier || ! is_string( $carrier ) || ! $transaction_id ) { + return; + } + + try { + $ppcp_shipment = $this->shipment_factory->create_shipment( + $order_id, + $transaction_id, + $tracking_number, + 'SHIPPED', + 'OTHER', + $carrier, + array() + ); + + $tracking_information = $this->endpoint->get_tracking_information( $order_id, $tracking_number ); + + $tracking_information + ? $this->endpoint->update_tracking_information( $ppcp_shipment, $order_id ) + : $this->endpoint->add_tracking_information( $ppcp_shipment, $order_id ); + + } catch ( Exception $exception ) { + $this->logger->error( "Couldn't sync tracking information: " . $exception->getMessage() ); + } + }, + 500, + 1 + ); + } +} diff --git a/modules/ppcp-order-tracking/src/OrderTrackingModule.php b/modules/ppcp-order-tracking/src/OrderTrackingModule.php index 94dc1ffd0..ae6bfecc2 100644 --- a/modules/ppcp-order-tracking/src/OrderTrackingModule.php +++ b/modules/ppcp-order-tracking/src/OrderTrackingModule.php @@ -102,7 +102,7 @@ class OrderTrackingModule implements ModuleInterface { __( 'PayPal Package Tracking', 'woocommerce-paypal-payments' ), array( $meta_box_renderer, 'render' ), $screen, - 'normal' + 'side' ); }, 10, diff --git a/modules/ppcp-wc-gateway/resources/js/gateway-settings.js b/modules/ppcp-wc-gateway/resources/js/gateway-settings.js index 7886131d9..afec3a426 100644 --- a/modules/ppcp-wc-gateway/resources/js/gateway-settings.js +++ b/modules/ppcp-wc-gateway/resources/js/gateway-settings.js @@ -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; } diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 6c9eef157..063afa9a3 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -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' ), + ) + ); + }, ); diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php index 82fa1d5f3..b5a7e527c 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php @@ -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',