diff --git a/.psalm/gzd.php b/.psalm/gzd.php index 6ececb60d..1b2b02256 100644 --- a/.psalm/gzd.php +++ b/.psalm/gzd.php @@ -50,6 +50,27 @@ namespace Vendidero\Germanized\Shipments { public function add_note( $note, $added_by_user = false ) { } + + /** + * Return an array of items within this shipment. + * + * @return ShipmentItem[] + */ + public function get_items() { + } + } + + class ShipmentItem extends WC_Data { + + /** + * Get order ID this meta belongs to. + * + * @param string $context What the value is for. Valid values are 'view' and 'edit'. + * @return int + */ + public function get_order_item_id( $context = 'view' ) { + } + } } diff --git a/composer.json b/composer.json index 4a94eb54b..bb2ccbf7c 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,7 @@ "autoload": { "psr-4": { "WooCommerce\\PayPalCommerce\\": "src", + "WooCommerce\\PayPalCommerce\\Common\\": "lib/common/", "WooCommerce\\PayPalCommerce\\Vendor\\": "lib/packages/" }, "files": [ diff --git a/composer.lock b/composer.lock index 295f011ee..450c96e96 100644 --- a/composer.lock +++ b/composer.lock @@ -521,16 +521,16 @@ }, { "name": "wikimedia/composer-merge-plugin", - "version": "v2.0.1", + "version": "v2.1.0", "source": { "type": "git", "url": "https://github.com/wikimedia/composer-merge-plugin.git", - "reference": "8ca2ed8ab97c8ebce6b39d9943e9909bb4f18912" + "reference": "a03d426c8e9fb2c9c569d9deeb31a083292788bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wikimedia/composer-merge-plugin/zipball/8ca2ed8ab97c8ebce6b39d9943e9909bb4f18912", - "reference": "8ca2ed8ab97c8ebce6b39d9943e9909bb4f18912", + "url": "https://api.github.com/repos/wikimedia/composer-merge-plugin/zipball/a03d426c8e9fb2c9c569d9deeb31a083292788bc", + "reference": "a03d426c8e9fb2c9c569d9deeb31a083292788bc", "shasum": "" }, "require": { @@ -539,9 +539,12 @@ }, "require-dev": { "composer/composer": "^1.1||^2.0", - "php-parallel-lint/php-parallel-lint": "~1.1.0", + "ext-json": "*", + "mediawiki/mediawiki-phan-config": "0.11.1", + "php-parallel-lint/php-parallel-lint": "~1.3.1", + "phpspec/prophecy": "~1.15.0", "phpunit/phpunit": "^8.5||^9.0", - "squizlabs/php_codesniffer": "~3.5.4" + "squizlabs/php_codesniffer": "~3.7.1" }, "type": "composer-plugin", "extra": { @@ -568,9 +571,9 @@ "description": "Composer plugin to merge multiple composer.json files", "support": { "issues": "https://github.com/wikimedia/composer-merge-plugin/issues", - "source": "https://github.com/wikimedia/composer-merge-plugin/tree/v2.0.1" + "source": "https://github.com/wikimedia/composer-merge-plugin/tree/v2.1.0" }, - "time": "2021-02-24T05:28:06+00:00" + "time": "2023-04-15T19:07:00+00:00" }, { "name": "wp-oop/wordpress-interface", @@ -1842,16 +1845,16 @@ }, { "name": "netresearch/jsonmapper", - "version": "v4.1.0", + "version": "v4.2.0", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f" + "reference": "f60565f8c0566a31acf06884cdaa591867ecc956" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/f60565f8c0566a31acf06884cdaa591867ecc956", + "reference": "f60565f8c0566a31acf06884cdaa591867ecc956", "shasum": "" }, "require": { @@ -1887,22 +1890,22 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.2.0" }, - "time": "2022-12-08T20:46:14+00:00" + "time": "2023-04-09T17:37:40+00:00" }, { "name": "nikic/php-parser", - "version": "v4.15.4", + "version": "v4.16.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "19526a33fb561ef417e822e85f08a00db4059c17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17", + "reference": "19526a33fb561ef417e822e85f08a00db4059c17", "shasum": "" }, "require": { @@ -1943,9 +1946,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0" }, - "time": "2023-03-05T19:49:14+00:00" + "time": "2023-06-25T14:52:30+00:00" }, { "name": "openlss/lib-array2xml", @@ -2157,27 +2160,25 @@ }, { "name": "php-stubs/wordpress-stubs", - "version": "v5.9.5", + "version": "v5.9.6", "source": { "type": "git", "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "13ecf204a7e6d215a7c0d23e2aa27940fe617717" + "reference": "6a18d938d0aef39d091505a4a35b025fb6c10098" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/13ecf204a7e6d215a7c0d23e2aa27940fe617717", - "reference": "13ecf204a7e6d215a7c0d23e2aa27940fe617717", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/6a18d938d0aef39d091505a4a35b025fb6c10098", + "reference": "6a18d938d0aef39d091505a4a35b025fb6c10098", "shasum": "" }, - "replace": { - "giacocorsiglia/wordpress-stubs": "*" - }, "require-dev": { "nikic/php-parser": "< 4.12.0", "php": "~7.3 || ~8.0", - "php-stubs/generator": "^0.8.1", + "php-stubs/generator": "^0.8.3", "phpdocumentor/reflection-docblock": "^5.3", - "phpstan/phpstan": "^1.2" + "phpstan/phpstan": "^1.10.12", + "phpunit/phpunit": "^9.5" }, "suggest": { "paragonie/sodium_compat": "Pure PHP implementation of libsodium", @@ -2198,9 +2199,9 @@ ], "support": { "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v5.9.5" + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v5.9.6" }, - "time": "2022-11-09T05:32:14+00:00" + "time": "2023-05-18T04:34:27+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -3136,16 +3137,16 @@ }, { "name": "sebastian/diff", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" + "reference": "6296a0c086dd0117c1b78b059374d7fcbe7545ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/6296a0c086dd0117c1b78b059374d7fcbe7545ae", + "reference": "6296a0c086dd0117c1b78b059374d7fcbe7545ae", "shasum": "" }, "require": { @@ -3190,7 +3191,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/diff/tree/3.0.4" }, "funding": [ { @@ -3198,7 +3199,7 @@ "type": "github" } ], - "time": "2020-11-30T07:59:04+00:00" + "time": "2023-05-07T05:30:20+00:00" }, { "name": "sebastian/environment", @@ -3793,16 +3794,16 @@ }, { "name": "symfony/console", - "version": "v5.4.21", + "version": "v5.4.26", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c77433ddc6cdc689caf48065d9ea22ca0853fbd9" + "reference": "b504a3d266ad2bb632f196c0936ef2af5ff6e273" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c77433ddc6cdc689caf48065d9ea22ca0853fbd9", - "reference": "c77433ddc6cdc689caf48065d9ea22ca0853fbd9", + "url": "https://api.github.com/repos/symfony/console/zipball/b504a3d266ad2bb632f196c0936ef2af5ff6e273", + "reference": "b504a3d266ad2bb632f196c0936ef2af5ff6e273", "shasum": "" }, "require": { @@ -3867,12 +3868,12 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.21" + "source": "https://github.com/symfony/console/tree/v5.4.26" }, "funding": [ { @@ -3888,7 +3889,7 @@ "type": "tidelift" } ], - "time": "2023-02-25T16:59:41+00:00" + "time": "2023-07-19T20:11:33+00:00" }, { "name": "symfony/deprecation-contracts", @@ -4451,16 +4452,16 @@ }, { "name": "symfony/string", - "version": "v5.4.21", + "version": "v5.4.26", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "edac10d167b78b1d90f46a80320d632de0bd9f2f" + "reference": "1181fe9270e373537475e826873b5867b863883c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/edac10d167b78b1d90f46a80320d632de0bd9f2f", - "reference": "edac10d167b78b1d90f46a80320d632de0bd9f2f", + "url": "https://api.github.com/repos/symfony/string/zipball/1181fe9270e373537475e826873b5867b863883c", + "reference": "1181fe9270e373537475e826873b5867b863883c", "shasum": "" }, "require": { @@ -4517,7 +4518,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.21" + "source": "https://github.com/symfony/string/tree/v5.4.26" }, "funding": [ { @@ -4533,7 +4534,7 @@ "type": "tidelift" } ], - "time": "2023-02-22T08:00:55+00:00" + "time": "2023-06-28T12:46:07+00:00" }, { "name": "theseer/tokenizer", diff --git a/lib/README.md b/lib/README.md index 9114c43ef..d38e3edd4 100644 --- a/lib/README.md +++ b/lib/README.md @@ -1,5 +1,10 @@ +## packages The packages that are likely to cause conflicts with other plugins (by loading multiple incompatible versions). Their namespaces are isolated by [Mozart](https://github.com/coenjacobs/mozart). Currently, the packages are simply added in the repo to avoid making the build process more complex (Mozart has different PHP requirements). We need to isolate only PSR-11 containers and Dhii modularity packages, which are not supposed to change often. + +## common +This folder contains reusable classes or components that do not fit into any specific module. +They are designed to be versatile and can be used by any module within the plugin. diff --git a/lib/common/Pattern/SingletonDecorator.php b/lib/common/Pattern/SingletonDecorator.php new file mode 100644 index 000000000..54282dc91 --- /dev/null +++ b/lib/common/Pattern/SingletonDecorator.php @@ -0,0 +1,68 @@ +callable = $callable; + } + + /** + * The make constructor. + * + * @param callable $callable + * @return self + */ + public static function make( callable $callable ): self { + return new static( $callable ); + } + + /** + * Invokes a callable once and returns the same result on subsequent invokes. + * + * @param mixed ...$args Arguments to be passed to the callable. + * @return mixed + */ + public function __invoke( ...$args ) { + if ( ! $this->executed ) { + $this->result = call_user_func_array( $this->callable, $args ); + $this->executed = true; + } + + return $this->result; + } +} diff --git a/lib/common/Pattern/SingletonTrait.php b/lib/common/Pattern/SingletonTrait.php new file mode 100644 index 000000000..cf09570c7 --- /dev/null +++ b/lib/common/Pattern/SingletonTrait.php @@ -0,0 +1,41 @@ +type = $type; $this->message = $message; $this->dismissable = $dismissable; + $this->wrapper = $wrapper; } /** @@ -74,4 +83,13 @@ class Message { public function is_dismissable(): bool { return $this->dismissable; } + + /** + * Returns the wrapper selector that will contain the notice. + * + * @return string + */ + public function wrapper(): string { + return $this->wrapper; + } } diff --git a/modules/ppcp-admin-notices/src/Renderer/Renderer.php b/modules/ppcp-admin-notices/src/Renderer/Renderer.php index b67da1c30..1202d2ab4 100644 --- a/modules/ppcp-admin-notices/src/Renderer/Renderer.php +++ b/modules/ppcp-admin-notices/src/Renderer/Renderer.php @@ -41,9 +41,10 @@ class Renderer implements RendererInterface { $messages = $this->repository->current_message(); foreach ( $messages as $message ) { printf( - '

%s

', + '

%s

', $message->type(), ( $message->is_dismissable() ) ? 'is-dismissible' : '', + ( $message->wrapper() ? sprintf( 'data-ppcp-wrapper="%s"', esc_attr( $message->wrapper() ) ) : '' ), wp_kses_post( $message->message() ) ); } diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index 41ba10ff2..ec73e16fd 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient; +use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\CatalogProducts; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingPlans; @@ -62,6 +63,8 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookFactory; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderHelper; +use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient; +use WooCommerce\PayPalCommerce\ApiClient\Helper\PurchaseUnitSanitizer; use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository; use WooCommerce\PayPalCommerce\ApiClient\Repository\CustomerRepository; use WooCommerce\PayPalCommerce\ApiClient\Repository\OrderRepository; @@ -311,6 +314,7 @@ return array( $payments_factory = $container->get( 'api.factory.payments' ); $prefix = $container->get( 'api.prefix' ); $soft_descriptor = $container->get( 'wcgateway.soft-descriptor' ); + $sanitizer = $container->get( 'api.helper.purchase-unit-sanitizer' ); return new PurchaseUnitFactory( $amount_factory, @@ -320,7 +324,8 @@ return array( $shipping_factory, $payments_factory, $prefix, - $soft_descriptor + $soft_descriptor, + $sanitizer ); }, 'api.factory.patch-collection-factory' => static function ( ContainerInterface $container ): PatchCollectionFactory { @@ -836,4 +841,19 @@ return array( 'api.order-helper' => static function( ContainerInterface $container ): OrderHelper { return new OrderHelper(); }, + 'api.helper.order-transient' => static function( ContainerInterface $container ): OrderTransient { + $cache = new Cache( 'ppcp-paypal-bearer' ); + $purchase_unit_sanitizer = $container->get( 'api.helper.purchase-unit-sanitizer' ); + return new OrderTransient( $cache, $purchase_unit_sanitizer ); + }, + 'api.helper.purchase-unit-sanitizer' => SingletonDecorator::make( + static function( ContainerInterface $container ): PurchaseUnitSanitizer { + $settings = $container->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); + + $behavior = $settings->has( 'subtotal_mismatch_behavior' ) ? $settings->get( 'subtotal_mismatch_behavior' ) : null; + $line_name = $settings->has( 'subtotal_mismatch_line_name' ) ? $settings->get( 'subtotal_mismatch_line_name' ) : null; + return new PurchaseUnitSanitizer( $behavior, $line_name ); + } + ), ); diff --git a/modules/ppcp-api-client/src/ApiModule.php b/modules/ppcp-api-client/src/ApiModule.php index 95a0efd14..62e18373f 100644 --- a/modules/ppcp-api-client/src/ApiModule.php +++ b/modules/ppcp-api-client/src/ApiModule.php @@ -9,10 +9,13 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient; +use WC_Order; +use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; +use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; /** * Class ApiModule @@ -40,6 +43,44 @@ class ApiModule implements ModuleInterface { WC()->session->set( 'ppcp_fees', $fees ); } ); + add_filter( + '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 ) { + $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'] ); + } + } + + return $data; + } + ); + add_action( + 'woocommerce_paypal_payments_paypal_order_created', + function ( Order $order ) use ( $c ) { + $transient = $c->has( 'api.helper.order-transient' ) ? $c->get( 'api.helper.order-transient' ) : null; + + if ( $transient instanceof OrderTransient ) { + $transient->on_order_created( $order ); + } + }, + 10, + 1 + ); + add_action( + 'woocommerce_paypal_payments_woocommerce_order_created', + function ( WC_Order $wc_order, Order $order ) use ( $c ) { + $transient = $c->has( 'api.helper.order-transient' ) ? $c->get( 'api.helper.order-transient' ) : null; + + if ( $transient instanceof OrderTransient ) { + $transient->on_woocommerce_order_created( $wc_order, $order ); + } + }, + 10, + 2 + ); } /** diff --git a/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php b/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php index 233c41fe2..5ad4665ca 100644 --- a/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php @@ -135,7 +135,7 @@ class BillingAgreementsEndpoint { ); } finally { $this->is_request_logging_enabled = true; - set_transient( 'ppcp_reference_transaction_enabled', true, 3 * MONTH_IN_SECONDS ); + set_transient( 'ppcp_reference_transaction_enabled', true, MONTH_IN_SECONDS ); } return true; diff --git a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php index 3b0954fae..3b232c7f6 100644 --- a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php @@ -281,6 +281,9 @@ class OrderEndpoint { throw $error; } $order = $this->order_factory->from_paypal_response( $json ); + + do_action( 'woocommerce_paypal_payments_paypal_order_created', $order ); + return $order; } diff --git a/modules/ppcp-api-client/src/Endpoint/PayUponInvoiceOrderEndpoint.php b/modules/ppcp-api-client/src/Endpoint/PayUponInvoiceOrderEndpoint.php index 285ebda70..49bccf7a7 100644 --- a/modules/ppcp-api-client/src/Endpoint/PayUponInvoiceOrderEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/PayUponInvoiceOrderEndpoint.php @@ -112,7 +112,7 @@ class PayUponInvoiceOrderEndpoint { 'processing_instruction' => 'ORDER_COMPLETE_ON_PAYMENT_APPROVAL', 'purchase_units' => array_map( static function ( PurchaseUnit $item ): array { - return $item->to_array( false ); + return $item->to_array( true, false ); }, $items ), @@ -166,8 +166,11 @@ class PayUponInvoiceOrderEndpoint { throw new PayPalApiException( $json, $status_code ); } + $order = $this->order_factory->from_paypal_response( $json ); - return $this->order_factory->from_paypal_response( $json ); + do_action( 'woocommerce_paypal_payments_paypal_order_created', $order ); + + return $order; } /** diff --git a/modules/ppcp-api-client/src/Endpoint/WebhookEndpoint.php b/modules/ppcp-api-client/src/Endpoint/WebhookEndpoint.php index d8cc06dca..3df4c3def 100644 --- a/modules/ppcp-api-client/src/Endpoint/WebhookEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/WebhookEndpoint.php @@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookEventFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookFactory; use Psr\Log\LoggerInterface; +use WP_Error; /** * Class WebhookEndpoint @@ -193,7 +194,7 @@ class WebhookEndpoint { ); $response = $this->request( $url, $args ); - if ( is_wp_error( $response ) ) { + if ( $response instanceof WP_Error ) { throw new RuntimeException( __( 'Not able to delete the webhook.', 'woocommerce-paypal-payments' ) ); @@ -202,6 +203,11 @@ class WebhookEndpoint { $status_code = (int) wp_remote_retrieve_response_code( $response ); if ( 204 !== $status_code ) { $json = null; + /** + * Use in array as consistency check. + * + * @psalm-suppress RedundantConditionGivenDocblockType + */ if ( is_array( $response ) ) { $json = json_decode( $response['body'] ); } diff --git a/modules/ppcp-api-client/src/Entity/Item.php b/modules/ppcp-api-client/src/Entity/Item.php index efcaa179d..7d38e5f78 100644 --- a/modules/ppcp-api-client/src/Entity/Item.php +++ b/modules/ppcp-api-client/src/Entity/Item.php @@ -66,6 +66,20 @@ class Item { */ private $category; + /** + * The product url. + * + * @var string + */ + protected $url; + + /** + * The product image url. + * + * @var string + */ + protected $image_url; + /** * The tax rate. * @@ -90,6 +104,8 @@ class Item { * @param Money|null $tax The tax. * @param string $sku The SKU. * @param string $category The category. + * @param string $url The product url. + * @param string $image_url The product image url. * @param float $tax_rate The tax rate. * @param ?string $cart_item_key The cart key for this item. */ @@ -101,6 +117,8 @@ class Item { Money $tax = null, string $sku = '', string $category = 'PHYSICAL_GOODS', + string $url = '', + string $image_url = '', float $tax_rate = 0, string $cart_item_key = null ) { @@ -111,8 +129,9 @@ class Item { $this->description = $description; $this->tax = $tax; $this->sku = $sku; - $this->category = ( self::DIGITAL_GOODS === $category ) ? self::DIGITAL_GOODS : self::PHYSICAL_GOODS; $this->category = $category; + $this->url = $url; + $this->image_url = $image_url; $this->tax_rate = $tax_rate; $this->cart_item_key = $cart_item_key; } @@ -180,6 +199,24 @@ class Item { return $this->category; } + /** + * Returns the url. + * + * @return string + */ + public function url():string { + return $this->url; + } + + /** + * Returns the image url. + * + * @return string + */ + public function image_url():string { + return $this->image_url; + } + /** * Returns the tax rate. * @@ -203,7 +240,7 @@ class Item { * * @return array */ - public function to_array() { + public function to_array(): array { $item = array( 'name' => $this->name(), 'unit_amount' => $this->unit_amount()->to_array(), @@ -211,6 +248,8 @@ class Item { 'description' => $this->description(), 'sku' => $this->sku(), 'category' => $this->category(), + 'url' => $this->url(), + 'image_url' => $this->image_url(), ); if ( $this->tax() ) { diff --git a/modules/ppcp-api-client/src/Entity/PurchaseUnit.php b/modules/ppcp-api-client/src/Entity/PurchaseUnit.php index f2c64c73a..820df82af 100644 --- a/modules/ppcp-api-client/src/Entity/PurchaseUnit.php +++ b/modules/ppcp-api-client/src/Entity/PurchaseUnit.php @@ -9,6 +9,8 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient\Entity; +use WooCommerce\PayPalCommerce\ApiClient\Helper\PurchaseUnitSanitizer; + /** * Class PurchaseUnit */ @@ -91,6 +93,13 @@ class PurchaseUnit { */ private $contains_physical_goods = false; + /** + * The sanitizer for this purchase unit output. + * + * @var PurchaseUnitSanitizer|null + */ + private $sanitizer; + /** * PurchaseUnit constructor. * @@ -220,6 +229,16 @@ class PurchaseUnit { $this->custom_id = $custom_id; } + /** + * Sets the sanitizer for this purchase unit output. + * + * @param PurchaseUnitSanitizer|null $sanitizer The sanitizer. + * @return void + */ + public function set_sanitizer( ?PurchaseUnitSanitizer $sanitizer ) { + $this->sanitizer = $sanitizer; + } + /** * Returns the invoice id. * @@ -277,11 +296,12 @@ class PurchaseUnit { /** * Returns the object as array. * - * @param bool $ditch_items_when_mismatch Whether ditch items when mismatch or not. + * @param bool $sanitize_output Whether output should be sanitized for PayPal consumption. + * @param bool $allow_ditch_items Whether to allow items to be ditched. * * @return array */ - public function to_array( bool $ditch_items_when_mismatch = true ): array { + public function to_array( bool $sanitize_output = true, bool $allow_ditch_items = true ): array { $purchase_unit = array( 'reference_id' => $this->reference_id(), 'amount' => $this->amount()->to_array(), @@ -294,17 +314,6 @@ class PurchaseUnit { ), ); - $ditch = $ditch_items_when_mismatch && $this->ditch_items_when_mismatch( $this->amount(), ...$this->items() ); - /** - * The filter can be used to control when the items and totals breakdown are removed from PayPal order info. - */ - $ditch = apply_filters( 'ppcp_ditch_items_breakdown', $ditch, $this ); - - if ( $ditch ) { - unset( $purchase_unit['items'] ); - unset( $purchase_unit['amount']['breakdown'] ); - } - if ( $this->payee() ) { $purchase_unit['payee'] = $this->payee()->to_array(); } @@ -325,101 +334,45 @@ class PurchaseUnit { if ( $this->soft_descriptor() ) { $purchase_unit['soft_descriptor'] = $this->soft_descriptor(); } - return $purchase_unit; + + $has_ditched_items_breakdown = false; + + if ( $sanitize_output && isset( $this->sanitizer ) ) { + $purchase_unit = ( $this->sanitizer->sanitize( $purchase_unit, $allow_ditch_items ) ); + $has_ditched_items_breakdown = $this->sanitizer->has_ditched_items_breakdown(); + } + + return $this->apply_ditch_items_mismatch_filter( + $has_ditched_items_breakdown, + $purchase_unit + ); } /** - * All money values send to PayPal can only have 2 decimal points. WooCommerce internally does - * not have this restriction. Therefore the totals of the cart in WooCommerce and the totals - * of the rounded money values of the items, we send to PayPal, can differ. In those cases, - * we can not send the line items. + * Applies the ppcp_ditch_items_breakdown filter. + * If true purchase_unit items and breakdown are ditched from PayPal. * - * @param Amount $amount The amount. - * @param Item ...$items The items. - * @return bool + * @param bool $ditched_items_breakdown If the breakdown and items were already ditched. + * @param array $purchase_unit The purchase_unit array. + * @return array */ - private function ditch_items_when_mismatch( Amount $amount, Item ...$items ): bool { - $breakdown = $amount->breakdown(); - if ( ! $breakdown ) { - return false; - } + public function apply_ditch_items_mismatch_filter( bool $ditched_items_breakdown, array $purchase_unit ): array { + /** + * The filter can be used to control when the items and totals breakdown are removed from PayPal order info. + */ + $ditch = apply_filters( 'ppcp_ditch_items_breakdown', $ditched_items_breakdown, $this ); - $item_total = $breakdown->item_total(); - if ( $item_total ) { - $remaining_item_total = array_reduce( - $items, - function ( float $total, Item $item ): float { - return $total - (float) $item->unit_amount()->value_str() * (float) $item->quantity(); - }, - (float) $item_total->value_str() - ); + if ( $ditch ) { + unset( $purchase_unit['items'] ); + unset( $purchase_unit['amount']['breakdown'] ); - $remaining_item_total = round( $remaining_item_total, 2 ); - - if ( 0.0 !== $remaining_item_total ) { - return true; + if ( isset( $this->sanitizer ) && ( $ditch !== $ditched_items_breakdown ) ) { + $this->sanitizer->set_last_message( + __( 'Ditch items breakdown filter. Items and breakdown ditched.', 'woocommerce-paypal-payments' ) + ); } } - $tax_total = $breakdown->tax_total(); - $items_with_tax = array_filter( - $this->items, - function ( Item $item ): bool { - return null !== $item->tax(); - } - ); - if ( $tax_total && ! empty( $items_with_tax ) ) { - $remaining_tax_total = array_reduce( - $items, - function ( float $total, Item $item ): float { - $tax = $item->tax(); - if ( $tax ) { - $total -= (float) $tax->value_str() * (float) $item->quantity(); - } - return $total; - }, - (float) $tax_total->value_str() - ); - - $remaining_tax_total = round( $remaining_tax_total, 2 ); - - if ( 0.0 !== $remaining_tax_total ) { - return true; - } - } - - $shipping = $breakdown->shipping(); - $discount = $breakdown->discount(); - $shipping_discount = $breakdown->shipping_discount(); - $handling = $breakdown->handling(); - $insurance = $breakdown->insurance(); - - $amount_total = 0.0; - if ( $shipping ) { - $amount_total += (float) $shipping->value_str(); - } - if ( $item_total ) { - $amount_total += (float) $item_total->value_str(); - } - if ( $discount ) { - $amount_total -= (float) $discount->value_str(); - } - if ( $tax_total ) { - $amount_total += (float) $tax_total->value_str(); - } - if ( $shipping_discount ) { - $amount_total -= (float) $shipping_discount->value_str(); - } - if ( $handling ) { - $amount_total += (float) $handling->value_str(); - } - if ( $insurance ) { - $amount_total += (float) $insurance->value_str(); - } - - $amount_str = $amount->value_str(); - $amount_total_str = ( new Money( $amount_total, $amount->currency_code() ) )->value_str(); - $needs_to_ditch = $amount_str !== $amount_total_str; - return $needs_to_ditch; + return $purchase_unit; } } diff --git a/modules/ppcp-api-client/src/Factory/ItemFactory.php b/modules/ppcp-api-client/src/Factory/ItemFactory.php index ab3f0a95b..903adf213 100644 --- a/modules/ppcp-api-client/src/Factory/ItemFactory.php +++ b/modules/ppcp-api-client/src/Factory/ItemFactory.php @@ -53,6 +53,7 @@ class ItemFactory { * @var \WC_Product $product */ $quantity = (int) $item['quantity']; + $image = wp_get_attachment_image_src( (int) $product->get_image_id(), 'full' ); $price = (float) $item['line_subtotal'] / (float) $item['quantity']; return new Item( @@ -63,6 +64,8 @@ class ItemFactory { null, $product->get_sku(), ( $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS, + $product->get_permalink(), + $image[0] ?? '', 0, $cart_item_key ); @@ -128,6 +131,7 @@ class ItemFactory { $quantity = (int) $item->get_quantity(); $price_without_tax = (float) $order->get_item_subtotal( $item, false ); $price_without_tax_rounded = round( $price_without_tax, 2 ); + $image = $product instanceof WC_Product ? wp_get_attachment_image_src( (int) $product->get_image_id(), 'full' ) : ''; return new Item( mb_substr( $item->get_name(), 0, 127 ), @@ -136,7 +140,9 @@ class ItemFactory { $product instanceof WC_Product ? $this->prepare_description( $product->get_description() ) : '', null, $product instanceof WC_Product ? $product->get_sku() : '', - ( $product instanceof WC_Product && $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS + ( $product instanceof WC_Product && $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS, + $product instanceof WC_Product ? $product->get_permalink() : '', + $image[0] ?? '' ); } @@ -190,6 +196,8 @@ class ItemFactory { : null; $sku = ( isset( $data->sku ) ) ? $data->sku : ''; $category = ( isset( $data->category ) ) ? $data->category : 'PHYSICAL_GOODS'; + $url = ( isset( $data->url ) ) ? $data->url : ''; + $image_url = ( isset( $data->image_url ) ) ? $data->image_url : ''; return new Item( $data->name, @@ -198,7 +206,9 @@ class ItemFactory { $description, $tax, $sku, - $category + $category, + $url, + $image_url ); } diff --git a/modules/ppcp-api-client/src/Factory/PurchaseUnitFactory.php b/modules/ppcp-api-client/src/Factory/PurchaseUnitFactory.php index 8b06d17f5..ecb35592b 100644 --- a/modules/ppcp-api-client/src/Factory/PurchaseUnitFactory.php +++ b/modules/ppcp-api-client/src/Factory/PurchaseUnitFactory.php @@ -13,6 +13,7 @@ use WC_Session_Handler; use WooCommerce\PayPalCommerce\ApiClient\Entity\Item; use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; +use WooCommerce\PayPalCommerce\ApiClient\Helper\PurchaseUnitSanitizer; use WooCommerce\PayPalCommerce\ApiClient\Repository\PayeeRepository; use WooCommerce\PayPalCommerce\Webhooks\CustomIds; @@ -77,17 +78,25 @@ class PurchaseUnitFactory { */ private $soft_descriptor; + /** + * The sanitizer for purchase unit output data. + * + * @var PurchaseUnitSanitizer|null + */ + private $sanitizer; + /** * PurchaseUnitFactory constructor. * - * @param AmountFactory $amount_factory The amount factory. - * @param PayeeRepository $payee_repository The Payee repository. - * @param PayeeFactory $payee_factory The Payee factory. - * @param ItemFactory $item_factory The item factory. - * @param ShippingFactory $shipping_factory The shipping factory. - * @param PaymentsFactory $payments_factory The payments factory. - * @param string $prefix The prefix. - * @param string $soft_descriptor The soft descriptor. + * @param AmountFactory $amount_factory The amount factory. + * @param PayeeRepository $payee_repository The Payee repository. + * @param PayeeFactory $payee_factory The Payee factory. + * @param ItemFactory $item_factory The item factory. + * @param ShippingFactory $shipping_factory The shipping factory. + * @param PaymentsFactory $payments_factory The payments factory. + * @param string $prefix The prefix. + * @param string $soft_descriptor The soft descriptor. + * @param ?PurchaseUnitSanitizer $sanitizer The purchase unit to_array sanitizer. */ public function __construct( AmountFactory $amount_factory, @@ -97,7 +106,8 @@ class PurchaseUnitFactory { ShippingFactory $shipping_factory, PaymentsFactory $payments_factory, string $prefix = 'WC-', - string $soft_descriptor = '' + string $soft_descriptor = '', + PurchaseUnitSanitizer $sanitizer = null ) { $this->amount_factory = $amount_factory; @@ -108,6 +118,7 @@ class PurchaseUnitFactory { $this->payments_factory = $payments_factory; $this->prefix = $prefix; $this->soft_descriptor = $soft_descriptor; + $this->sanitizer = $sanitizer; } /** @@ -151,6 +162,9 @@ class PurchaseUnitFactory { $invoice_id, $soft_descriptor ); + + $this->init_purchase_unit( $purchase_unit ); + /** * Returns PurchaseUnit for the WC order. */ @@ -221,6 +235,8 @@ class PurchaseUnitFactory { $soft_descriptor ); + $this->init_purchase_unit( $purchase_unit ); + return $purchase_unit; } @@ -283,6 +299,9 @@ class PurchaseUnitFactory { $soft_descriptor, $payments ); + + $this->init_purchase_unit( $purchase_unit ); + return $purchase_unit; } @@ -313,4 +332,16 @@ class PurchaseUnitFactory { $countries = array( 'AE', 'AF', 'AG', 'AI', 'AL', 'AN', 'AO', 'AW', 'BB', 'BF', 'BH', 'BI', 'BJ', 'BM', 'BO', 'BS', 'BT', 'BW', 'BZ', 'CD', 'CF', 'CG', 'CI', 'CK', 'CL', 'CM', 'CO', 'CR', 'CV', 'DJ', 'DM', 'DO', 'EC', 'EG', 'ER', 'ET', 'FJ', 'FK', 'GA', 'GD', 'GH', 'GI', 'GM', 'GN', 'GQ', 'GT', 'GW', 'GY', 'HK', 'HN', 'HT', 'IE', 'IQ', 'IR', 'JM', 'JO', 'KE', 'KH', 'KI', 'KM', 'KN', 'KP', 'KW', 'KY', 'LA', 'LB', 'LC', 'LK', 'LR', 'LS', 'LY', 'ML', 'MM', 'MO', 'MR', 'MS', 'MT', 'MU', 'MW', 'MZ', 'NA', 'NE', 'NG', 'NI', 'NP', 'NR', 'NU', 'OM', 'PA', 'PE', 'PF', 'PY', 'QA', 'RW', 'SA', 'SB', 'SC', 'SD', 'SL', 'SN', 'SO', 'SR', 'SS', 'ST', 'SV', 'SY', 'TC', 'TD', 'TG', 'TL', 'TO', 'TT', 'TV', 'TZ', 'UG', 'UY', 'VC', 'VE', 'VG', 'VN', 'VU', 'WS', 'XA', 'XB', 'XC', 'XE', 'XL', 'XM', 'XN', 'XS', 'YE', 'ZM', 'ZW' ); return in_array( $country_code, $countries, true ); } + + /** + * Initializes a purchase unit object. + * + * @param PurchaseUnit $purchase_unit The purchase unit. + * @return void + */ + private function init_purchase_unit( PurchaseUnit $purchase_unit ): void { + if ( $this->sanitizer instanceof PurchaseUnitSanitizer ) { + $purchase_unit->set_sanitizer( $this->sanitizer ); + } + } } diff --git a/modules/ppcp-api-client/src/Helper/MoneyFormatter.php b/modules/ppcp-api-client/src/Helper/MoneyFormatter.php index 447ba0a0e..914adbe70 100644 --- a/modules/ppcp-api-client/src/Helper/MoneyFormatter.php +++ b/modules/ppcp-api-client/src/Helper/MoneyFormatter.php @@ -33,4 +33,16 @@ class MoneyFormatter { ? (string) round( $value, 0 ) : number_format( $value, 2, '.', '' ); } + + /** + * Returns the minimum amount a currency can be incremented or decremented. + * + * @param string $currency The 3-letter currency code. + * @return float + */ + public function minimum_increment( string $currency ): float { + return (float) in_array( $currency, $this->currencies_without_decimals, true ) + ? 1.00 + : 0.01; + } } diff --git a/modules/ppcp-api-client/src/Helper/OrderTransient.php b/modules/ppcp-api-client/src/Helper/OrderTransient.php new file mode 100644 index 000000000..d0c7d4a01 --- /dev/null +++ b/modules/ppcp-api-client/src/Helper/OrderTransient.php @@ -0,0 +1,160 @@ +cache = $cache; + $this->purchase_unit_sanitizer = $purchase_unit_sanitizer; + } + + /** + * Processes the created PayPal order. + * + * @param Order $order The PayPal order. + * @return void + */ + public function on_order_created( Order $order ): void { + $message = $this->purchase_unit_sanitizer->get_last_message(); + $this->add_order_note( $order, $message ); + } + + /** + * Processes the created WooCommerce order. + * + * @param WC_Order $wc_order The WooCommerce order. + * @param Order $order The PayPal order. + * @return void + */ + public function on_woocommerce_order_created( WC_Order $wc_order, Order $order ): void { + $cache_key = $this->cache_key( $order ); + + if ( ! $cache_key ) { + return; + } + + $this->apply_order_notes( $order, $wc_order ); + $this->cache->delete( $cache_key ); + } + + /** + * Adds an order note associated with a PayPal order. + * It can be added to a WooCommerce order associated with this PayPal order in the future. + * + * @param Order $order The PayPal order. + * @param string $message The message to be added to order notes. + * @return void + */ + private function add_order_note( Order $order, string $message ): void { + if ( ! $message ) { + return; + } + + $cache_key = $this->cache_key( $order ); + + if ( ! $cache_key ) { + return; + } + + $transient = $this->cache->get( $cache_key ); + + if ( ! is_array( $transient ) ) { + $transient = array(); + } + + if ( ! is_array( $transient['notes'] ) ) { + $transient['notes'] = array(); + } + + $transient['notes'][] = $message; + + $this->cache->set( $cache_key, $transient, self::CACHE_TIMEOUT ); + } + + /** + * Adds an order note associated with a PayPal order. + * It can be added to a WooCommerce order associated with this PayPal order in the future. + * + * @param Order $order The PayPal order. + * @param WC_Order $wc_order The WooCommerce order. + * @return void + */ + private function apply_order_notes( Order $order, WC_Order $wc_order ): void { + $cache_key = $this->cache_key( $order ); + + if ( ! $cache_key ) { + return; + } + + $transient = $this->cache->get( $cache_key ); + + if ( ! is_array( $transient ) ) { + return; + } + + if ( ! is_array( $transient['notes'] ) ) { + return; + } + + foreach ( $transient['notes'] as $note ) { + if ( ! is_string( $note ) ) { + continue; + } + $wc_order->add_order_note( $note ); + } + } + + /** + * Build cache key. + * + * @param Order $order The PayPal order. + * @return string|null + */ + private function cache_key( Order $order ): ?string { + if ( ! $order->id() ) { + return null; + } + return implode( '_', array( self::CACHE_KEY . $order->id() ) ); + } + +} diff --git a/modules/ppcp-api-client/src/Helper/PurchaseUnitSanitizer.php b/modules/ppcp-api-client/src/Helper/PurchaseUnitSanitizer.php new file mode 100644 index 000000000..7cb0c048f --- /dev/null +++ b/modules/ppcp-api-client/src/Helper/PurchaseUnitSanitizer.php @@ -0,0 +1,368 @@ +mode = $mode; + $this->extra_line_name = $extra_line_name; + } + + /** + * The purchase_unit amount. + * + * @return array + */ + private function amount(): array { + return $this->purchase_unit['amount'] ?? array(); + } + + /** + * The purchase_unit currency code. + * + * @return string + */ + private function currency_code(): string { + return (string) ( $this->amount()['currency_code'] ?? '' ); + } + + /** + * The purchase_unit breakdown. + * + * @return array + */ + private function breakdown(): array { + return $this->amount()['breakdown'] ?? array(); + } + + /** + * The purchase_unit breakdown. + * + * @param string $key The breakdown element to get the value from. + * @return float + */ + private function breakdown_value( string $key ): float { + if ( ! isset( $this->breakdown()[ $key ] ) ) { + return 0.0; + } + return (float) ( $this->breakdown()[ $key ]['value'] ?? 0.0 ); + } + + /** + * The purchase_unit items array. + * + * @return array + */ + private function items(): array { + return $this->purchase_unit['items'] ?? array(); + } + + /** + * The sanitizes the purchase_unit array. + * + * @param array $purchase_unit The purchase_unit array that should be sanitized. + * @param bool $allow_ditch_items Whether to allow items to be ditched. + * @return array + */ + public function sanitize( array $purchase_unit, bool $allow_ditch_items = true ): array { + $this->purchase_unit = $purchase_unit; + $this->allow_ditch_items = $allow_ditch_items; + $this->has_ditched_items_breakdown = false; + + $this->sanitize_item_amount_mismatch(); + $this->sanitize_item_tax_mismatch(); + $this->sanitize_breakdown_mismatch(); + return $this->purchase_unit; + } + + /** + * The sanitizes the purchase_unit items amount. + * + * @return void + */ + private function sanitize_item_amount_mismatch(): void { + $item_mismatch = $this->calculate_item_mismatch(); + + if ( $this->mode === self::MODE_EXTRA_LINE ) { + if ( $item_mismatch < 0 ) { + + // Do floors on item amounts so item_mismatch is a positive value. + foreach ( $this->purchase_unit['items'] as $index => $item ) { + // Get a more intelligent adjustment mechanism. + $increment = ( new MoneyFormatter() )->minimum_increment( $item['unit_amount']['currency_code'] ); + + $this->purchase_unit['items'][ $index ]['unit_amount'] = ( new Money( + ( (float) $item['unit_amount']['value'] ) - $increment, + $item['unit_amount']['currency_code'] + ) )->to_array(); + } + } + + $item_mismatch = $this->calculate_item_mismatch(); + + if ( $item_mismatch > 0 ) { + // Add extra line item with roundings. + $line_name = $this->extra_line_name; + $roundings_money = new Money( $item_mismatch, $this->currency_code() ); + $this->purchase_unit['items'][] = ( new Item( $line_name, $roundings_money, 1 ) )->to_array(); + + $this->set_last_message( + __( 'Item amount mismatch. Extra line added.', 'woocommerce-paypal-payments' ) + ); + } + + $item_mismatch = $this->calculate_item_mismatch(); + } + + if ( $item_mismatch !== 0.0 ) { + // Ditch items. + if ( $this->allow_ditch_items && isset( $this->purchase_unit['items'] ) ) { + unset( $this->purchase_unit['items'] ); + $this->set_last_message( + __( 'Item amount mismatch. Items ditched.', 'woocommerce-paypal-payments' ) + ); + } + } + } + + /** + * The sanitizes the purchase_unit items tax. + * + * @return void + */ + private function sanitize_item_tax_mismatch(): void { + $tax_mismatch = $this->calculate_tax_mismatch(); + + if ( $this->allow_ditch_items && $tax_mismatch !== 0.0 ) { + // Unset tax in items. + foreach ( $this->purchase_unit['items'] as $index => $item ) { + if ( isset( $this->purchase_unit['items'][ $index ]['tax'] ) ) { + unset( $this->purchase_unit['items'][ $index ]['tax'] ); + } + if ( isset( $this->purchase_unit['items'][ $index ]['tax_rate'] ) ) { + unset( $this->purchase_unit['items'][ $index ]['tax_rate'] ); + } + } + } + } + + /** + * The sanitizes the purchase_unit breakdown. + * + * @return void + */ + private function sanitize_breakdown_mismatch(): void { + $breakdown_mismatch = $this->calculate_breakdown_mismatch(); + + if ( $this->allow_ditch_items && $breakdown_mismatch !== 0.0 ) { + // Ditch breakdowns and items. + if ( isset( $this->purchase_unit['items'] ) ) { + unset( $this->purchase_unit['items'] ); + } + if ( isset( $this->purchase_unit['amount']['breakdown'] ) ) { + unset( $this->purchase_unit['amount']['breakdown'] ); + } + + $this->has_ditched_items_breakdown = true; + $this->set_last_message( + __( 'Breakdown mismatch. Items and breakdown ditched.', 'woocommerce-paypal-payments' ) + ); + } + } + + /** + * The calculates amount mismatch of items sums with breakdown. + * + * @return float + */ + private function calculate_item_mismatch(): float { + $item_total = $this->breakdown_value( 'item_total' ); + if ( ! $item_total ) { + return 0; + } + + $remaining_item_total = array_reduce( + $this->items(), + function ( float $total, array $item ): float { + return $total - (float) $item['unit_amount']['value'] * (float) $item['quantity']; + }, + $item_total + ); + + return round( $remaining_item_total, 2 ); + } + + /** + * The calculates tax mismatch of items sums with breakdown. + * + * @return float + */ + private function calculate_tax_mismatch(): float { + $tax_total = $this->breakdown_value( 'tax_total' ); + $items_with_tax = array_filter( + $this->items(), + function ( array $item ): bool { + return isset( $item['tax'] ); + } + ); + + if ( ! $tax_total || empty( $items_with_tax ) ) { + return 0; + } + + $remaining_tax_total = array_reduce( + $this->items(), + function ( float $total, array $item ): float { + $tax = $item['tax'] ?? false; + if ( $tax ) { + $total -= (float) $tax['value'] * (float) $item['quantity']; + } + return $total; + }, + $tax_total + ); + + return round( $remaining_tax_total, 2 ); + } + + /** + * The calculates mismatch of breakdown sums with total amount. + * + * @return float + */ + private function calculate_breakdown_mismatch(): float { + $breakdown = $this->breakdown(); + if ( ! $breakdown ) { + return 0; + } + + $amount_total = 0.0; + $amount_total += $this->breakdown_value( 'item_total' ); + $amount_total += $this->breakdown_value( 'tax_total' ); + $amount_total += $this->breakdown_value( 'shipping' ); + $amount_total -= $this->breakdown_value( 'discount' ); + $amount_total -= $this->breakdown_value( 'shipping_discount' ); + $amount_total += $this->breakdown_value( 'handling' ); + $amount_total += $this->breakdown_value( 'insurance' ); + + $amount_str = $this->amount()['value'] ?? 0; + $amount_total_str = ( new Money( $amount_total, $this->currency_code() ) )->value_str(); + + return $amount_str - $amount_total_str; + } + + /** + * Indicates if the items and breakdown were ditched. + * + * @return bool + */ + public function has_ditched_items_breakdown(): bool { + return $this->has_ditched_items_breakdown; + } + + /** + * Returns the last sanitization message. + * + * @return string + */ + public function get_last_message(): string { + return $this->last_message; + } + + /** + * Set the last sanitization message. + * + * @param string $message The message. + */ + public function set_last_message( string $message ): void { + $this->last_message = $message; + } + +} diff --git a/modules/ppcp-button/resources/js/modules/Helper/CartHelper.js b/modules/ppcp-button/resources/js/modules/Helper/CartHelper.js index 8220d7d00..1bc308413 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/CartHelper.js +++ b/modules/ppcp-button/resources/js/modules/Helper/CartHelper.js @@ -2,10 +2,19 @@ class CartHelper { constructor(cartItemKeys = []) { - this.endpoint = wc_cart_fragments_params.wc_ajax_url.toString().replace('%%endpoint%%', 'remove_from_cart'); this.cartItemKeys = cartItemKeys; } + getEndpoint() { + let ajaxUrl = "/?wc-ajax=%%endpoint%%"; + + if ((typeof wc_cart_fragments_params !== 'undefined') && wc_cart_fragments_params.wc_ajax_url) { + ajaxUrl = wc_cart_fragments_params.wc_ajax_url; + } + + return ajaxUrl.toString().replace('%%endpoint%%', 'remove_from_cart'); + } + addFromPurchaseUnits(purchaseUnits) { for (const purchaseUnit of purchaseUnits || []) { for (const item of purchaseUnit.items || []) { @@ -46,7 +55,7 @@ class CartHelper { continue; } - fetch(this.endpoint, { + fetch(this.getEndpoint(), { method: 'POST', credentials: 'same-origin', body: params diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index 02d8c7e8c..5ca7fb38f 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -155,7 +155,7 @@ return array( $session_handler = $container->get( 'session.handler' ); $settings = $container->get( 'wcgateway.settings' ); $early_order_handler = $container->get( 'button.helper.early-order-handler' ); - $registration_needed = $container->get( 'button.current-user-must-register' ); + $registration_needed = $container->get( 'button.current-user-must-register' ); $logger = $container->get( 'woocommerce.logger.woocommerce' ); return new CreateOrderEndpoint( $request_data, diff --git a/modules/ppcp-button/src/Endpoint/AbstractCartEndpoint.php b/modules/ppcp-button/src/Endpoint/AbstractCartEndpoint.php index d74d59c55..3133cf544 100644 --- a/modules/ppcp-button/src/Endpoint/AbstractCartEndpoint.php +++ b/modules/ppcp-button/src/Endpoint/AbstractCartEndpoint.php @@ -153,9 +153,10 @@ abstract class AbstractCartEndpoint implements EndpointInterface { /** * Handles errors. * + * @param bool $send_response If this error handling should return the response. * @return void */ - private function handle_error(): void { + protected function handle_error( bool $send_response = true ): void { $message = __( 'Something went wrong. Action aborted', @@ -173,14 +174,16 @@ abstract class AbstractCartEndpoint implements EndpointInterface { wc_clear_notices(); } - wp_send_json_error( - array( - 'name' => '', - 'message' => $message, - 'code' => 0, - 'details' => array(), - ) - ); + if ( $send_response ) { + wp_send_json_error( + array( + 'name' => '', + 'message' => $message, + 'code' => 0, + 'details' => array(), + ) + ); + } } /** @@ -259,7 +262,9 @@ abstract class AbstractCartEndpoint implements EndpointInterface { private function add_product( \WC_Product $product, int $quantity ): bool { $cart_item_key = $this->cart->add_to_cart( $product->get_id(), $quantity ); - $this->cart_item_keys[] = $cart_item_key; + if ( $cart_item_key ) { + $this->cart_item_keys[] = $cart_item_key; + } return false !== $cart_item_key; } @@ -294,7 +299,9 @@ abstract class AbstractCartEndpoint implements EndpointInterface { $variations ); - $this->cart_item_keys[] = $cart_item_key; + if ( $cart_item_key ) { + $this->cart_item_keys[] = $cart_item_key; + } return false !== $cart_item_key; } @@ -322,7 +329,9 @@ abstract class AbstractCartEndpoint implements EndpointInterface { $cart_item_key = $this->cart->add_to_cart( $product->get_id(), 1, 0, array(), $cart_item_data ); - $this->cart_item_keys[] = $cart_item_key; + if ( $cart_item_key ) { + $this->cart_item_keys[] = $cart_item_key; + } return false !== $cart_item_key; } @@ -333,6 +342,9 @@ abstract class AbstractCartEndpoint implements EndpointInterface { */ protected function remove_cart_items(): void { foreach ( $this->cart_item_keys as $cart_item_key ) { + if ( ! $cart_item_key ) { + continue; + } $this->cart->remove_cart_item( $cart_item_key ); } } diff --git a/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php b/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php index d4ddfb21f..4ee88c3c7 100644 --- a/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php +++ b/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php @@ -25,6 +25,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory; +use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient; use WooCommerce\PayPalCommerce\Button\Exception\ValidationException; use WooCommerce\PayPalCommerce\Button\Validation\CheckoutFormValidator; use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler; @@ -330,6 +331,8 @@ class CreateOrderEndpoint implements EndpointInterface { $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() ); $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); $wc_order->save_meta_data(); + + do_action( 'woocommerce_paypal_payments_woocommerce_order_created', $wc_order, $order ); } wp_send_json_success( $this->make_response( $order ) ); diff --git a/modules/ppcp-button/src/Endpoint/SimulateCartEndpoint.php b/modules/ppcp-button/src/Endpoint/SimulateCartEndpoint.php index 5e3796dbb..ead2f4229 100644 --- a/modules/ppcp-button/src/Endpoint/SimulateCartEndpoint.php +++ b/modules/ppcp-button/src/Endpoint/SimulateCartEndpoint.php @@ -27,6 +27,13 @@ class SimulateCartEndpoint extends AbstractCartEndpoint { */ private $smart_button; + /** + * The WooCommerce real active cart. + * + * @var \WC_Cart|null + */ + private $real_cart = null; + /** * ChangeCartEndpoint constructor. * @@ -65,24 +72,14 @@ class SimulateCartEndpoint extends AbstractCartEndpoint { return false; } - // Set WC default cart as the clone. - // Store a reference to the real cart. - $active_cart = WC()->cart; - WC()->cart = $this->cart; + $this->replace_real_cart(); - if ( ! $this->add_products( $products ) ) { - return false; - } + $this->add_products( $products ); $this->cart->calculate_totals(); $total = (float) $this->cart->get_total( 'numeric' ); - // Remove from cart because some plugins reserve resources internally when adding to cart. - $this->remove_cart_items(); - - // Restore cart and unset cart clone. - WC()->cart = $active_cart; - unset( $this->cart ); + $this->restore_real_cart(); // Process filters. $pay_later_enabled = true; @@ -119,4 +116,44 @@ class SimulateCartEndpoint extends AbstractCartEndpoint { return true; } + /** + * Handles errors. + * + * @param bool $send_response If this error handling should return the response. + * @return void + * + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod.Found + */ + protected function handle_error( bool $send_response = false ): void { + parent::handle_error( $send_response ); + } + + /** + * Replaces the real cart with the clone. + * + * @return void + */ + private function replace_real_cart() { + // Set WC default cart as the clone. + // Store a reference to the real cart. + $this->real_cart = WC()->cart; + WC()->cart = $this->cart; + } + + /** + * Restores the real cart. + * + * @return void + */ + private function restore_real_cart() { + // Remove from cart because some plugins reserve resources internally when adding to cart. + $this->remove_cart_items(); + + // Restore cart and unset cart clone. + if ( null !== $this->real_cart ) { + WC()->cart = $this->real_cart; + } + unset( $this->cart ); + } + } diff --git a/modules/ppcp-button/src/Helper/EarlyOrderHandler.php b/modules/ppcp-button/src/Helper/EarlyOrderHandler.php index 4ac8583d0..03d8fbeed 100644 --- a/modules/ppcp-button/src/Helper/EarlyOrderHandler.php +++ b/modules/ppcp-button/src/Helper/EarlyOrderHandler.php @@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Button\Helper; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; +use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; @@ -163,6 +164,10 @@ class EarlyOrderHandler { /** * Patch Order so we have the \WC_Order id added. */ - return $this->order_processor->patch_order( $wc_order, $order ); + $order = $this->order_processor->patch_order( $wc_order, $order ); + + do_action( 'woocommerce_paypal_payments_woocommerce_order_created', $wc_order, $order ); + + return $order; } } diff --git a/modules/ppcp-compat/package.json b/modules/ppcp-compat/package.json index d200ad7d4..9a4bde8e8 100644 --- a/modules/ppcp-compat/package.json +++ b/modules/ppcp-compat/package.json @@ -2,7 +2,7 @@ "name": "ppcp-compat", "version": "1.0.0", "license": "GPL-3.0-or-later", - "main": "resources/js/compat.js", + "main": "resources/js/tracking-compat.js", "browserslist": [ "> 0.5%", "Safari >= 8", diff --git a/modules/ppcp-compat/resources/js/gzd-compat.js b/modules/ppcp-compat/resources/js/gzd-compat.js deleted file mode 100644 index acf938b46..000000000 --- a/modules/ppcp-compat/resources/js/gzd-compat.js +++ /dev/null @@ -1,32 +0,0 @@ -document.addEventListener( - 'DOMContentLoaded', - () => { - const orderTrackingContainerId = "ppcp_order-tracking"; - const orderTrackingContainerSelector = "#ppcp_order-tracking"; - const gzdSaveButton = document.getElementById('order-shipments-save'); - const loadLocation = location.href + " " + orderTrackingContainerSelector + ">*"; - - const setEnabled = function (enabled) { - let childNodes = document.getElementById(orderTrackingContainerId).getElementsByTagName('*'); - for (let node of childNodes) { - node.disabled = !enabled; - } - } - - const waitForTrackingUpdate = function () { - if (jQuery('#order-shipments-save').css('display') !== 'none') { - setEnabled(false); - setTimeout(waitForTrackingUpdate, 100) - } else { - jQuery(orderTrackingContainerSelector).load(loadLocation,""); - } - } - - if (typeof(gzdSaveButton) != 'undefined' && gzdSaveButton != null) { - gzdSaveButton.addEventListener('click', function (event) { - waitForTrackingUpdate(); - setEnabled(true); - }) - } - }, -); diff --git a/modules/ppcp-compat/resources/js/tracking-compat.js b/modules/ppcp-compat/resources/js/tracking-compat.js new file mode 100644 index 000000000..b62158ff1 --- /dev/null +++ b/modules/ppcp-compat/resources/js/tracking-compat.js @@ -0,0 +1,49 @@ +document.addEventListener( + 'DOMContentLoaded', + () => { + const config = PayPalCommerceGatewayOrderTrackingCompat; + + const orderTrackingContainerId = "ppcp_order-tracking"; + const orderTrackingContainerSelector = "#ppcp_order-tracking .ppcp-tracking-column.shipments"; + const gzdSaveButton = document.getElementById('order-shipments-save'); + const loadLocation = location.href + " " + orderTrackingContainerSelector + ">*"; + const gzdSyncEnabled = config.gzd_sync_enabled; + const wcShipmentSyncEnabled = config.wc_shipment_sync_enabled; + const wcShipmentSaveButton = document.querySelector('#woocommerce-shipment-tracking .button-save-form'); + + const toggleLoaderVisibility = function() { + const loader = document.querySelector('.ppcp-tracking-loader'); + if (loader) { + if (loader.style.display === 'none' || loader.style.display === '') { + loader.style.display = 'block'; + } else { + loader.style.display = 'none'; + } + } + } + + const waitForTrackingUpdate = function (elementToCheck) { + if (elementToCheck.css('display') !== 'none') { + setTimeout(() => waitForTrackingUpdate(elementToCheck), 100); + } else { + jQuery(orderTrackingContainerSelector).load(loadLocation, "", function(){ + toggleLoaderVisibility(); + }); + } + } + + if (gzdSyncEnabled && typeof(gzdSaveButton) != 'undefined' && gzdSaveButton != null) { + gzdSaveButton.addEventListener('click', function (event) { + toggleLoaderVisibility(); + waitForTrackingUpdate(jQuery('#order-shipments-save')); + }) + } + + if (wcShipmentSyncEnabled && typeof(wcShipmentSaveButton) != 'undefined' && wcShipmentSaveButton != null) { + wcShipmentSaveButton.addEventListener('click', function (event) { + toggleLoaderVisibility(); + waitForTrackingUpdate(jQuery('#shipment-tracking-form')); + }) + } + }, +); diff --git a/modules/ppcp-compat/services.php b/modules/ppcp-compat/services.php index f4f4c4937..00b29f669 100644 --- a/modules/ppcp-compat/services.php +++ b/modules/ppcp-compat/services.php @@ -11,11 +11,10 @@ namespace WooCommerce\PayPalCommerce\Compat; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\Compat\Assets\CompatAssets; -use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; return array( - 'compat.ppec.mock-gateway' => static function( $container ) { + 'compat.ppec.mock-gateway' => static function( $container ) { $settings = $container->get( 'wcgateway.settings' ); $title = $settings->has( 'title' ) ? $settings->get( 'title' ) : __( 'PayPal', 'woocommerce-paypal-payments' ); $title = sprintf( @@ -27,20 +26,20 @@ return array( return new PPEC\MockGateway( $title ); }, - 'compat.ppec.subscriptions-handler' => static function ( ContainerInterface $container ) { + 'compat.ppec.subscriptions-handler' => static function ( ContainerInterface $container ) { $ppcp_renewal_handler = $container->get( 'subscription.renewal-handler' ); $gateway = $container->get( 'compat.ppec.mock-gateway' ); return new PPEC\SubscriptionsHandler( $ppcp_renewal_handler, $gateway ); }, - 'compat.ppec.settings_importer' => static function( ContainerInterface $container ) : PPEC\SettingsImporter { + 'compat.ppec.settings_importer' => static function( ContainerInterface $container ) : PPEC\SettingsImporter { $settings = $container->get( 'wcgateway.settings' ); return new PPEC\SettingsImporter( $settings ); }, - 'compat.plugin-script-names' => static function( ContainerInterface $container ) : array { + 'compat.plugin-script-names' => static function( ContainerInterface $container ) : array { return array( 'ppcp-smart-button', 'ppcp-oxxo', @@ -50,16 +49,24 @@ return array( 'ppcp-webhooks-status-page', 'ppcp-tracking', 'ppcp-fraudnet', - 'ppcp-gzd-compat', + 'ppcp-tracking-compat', 'ppcp-clear-db', ); }, - 'compat.gzd.is_supported_plugin_version_active' => function (): bool { + 'compat.gzd.is_supported_plugin_version_active' => function (): bool { return function_exists( 'wc_gzd_get_shipments_by_order' ); // 3.0+ }, - 'compat.module.url' => static function ( ContainerInterface $container ): string { + 'compat.wc_shipment_tracking.is_supported_plugin_version_active' => function (): bool { + return class_exists( 'WC_Shipment_Tracking' ); + }, + + 'compat.ywot.is_supported_plugin_version_active' => function (): bool { + return function_exists( 'yith_ywot_init' ); + }, + + 'compat.module.url' => static function ( ContainerInterface $container ): string { /** * The path cannot be false. * @@ -71,22 +78,13 @@ return array( ); }, - 'compat.assets' => function( ContainerInterface $container ) : CompatAssets { + 'compat.assets' => function( ContainerInterface $container ) : CompatAssets { return new CompatAssets( $container->get( 'compat.module.url' ), $container->get( 'ppcp.asset-version' ), - $container->get( 'compat.should-initialize-gzd-compat-layer' ) + $container->get( 'order-tracking.is-module-enabled' ), + $container->get( 'compat.gzd.is_supported_plugin_version_active' ), + $container->get( 'compat.wc_shipment_tracking.is_supported_plugin_version_active' ) ); }, - - 'compat.should-initialize-gzd-compat-layer' => function( ContainerInterface $container ) : bool { - $settings = $container->get( 'wcgateway.settings' ); - assert( $settings instanceof Settings ); - - $tracking_enabled = $settings->has( 'tracking_enabled' ) && $settings->get( 'tracking_enabled' ); - $is_gzd_active = $container->get( 'compat.gzd.is_supported_plugin_version_active' ); - - return $tracking_enabled && $is_gzd_active; - }, - ); diff --git a/modules/ppcp-compat/src/Assets/CompatAssets.php b/modules/ppcp-compat/src/Assets/CompatAssets.php index ca6f15fe9..5e8585b8d 100644 --- a/modules/ppcp-compat/src/Assets/CompatAssets.php +++ b/modules/ppcp-compat/src/Assets/CompatAssets.php @@ -28,23 +28,48 @@ class CompatAssets { private $version; /** - * Whether Germanized synchronization scripts should be loaded. + * Whether tracking compat scripts should be loaded. * * @var bool */ - protected $should_enqueue_gzd_scripts; + protected $should_enqueue_tracking_scripts; + + /** + * Whether Germanized plugin is active. + * + * @var bool + */ + protected $is_gzd_active; + + /** + * Whether WC Shipments plugin is active + * + * @var bool + */ + protected $is_wc_shipment_active; /** * Compat module assets constructor. * * @param string $module_url The URL to the module. * @param string $version The assets version. - * @param bool $should_enqueue_gzd_scripts Whether Germanized synchronization scripts should be loaded. + * @param bool $should_enqueue_tracking_scripts Whether Germanized synchronization scripts should be loaded. + * @param bool $is_gzd_active Whether Germanized plugin is active. + * @param bool $is_wc_shipment_active Whether WC Shipments plugin is active. */ - public function __construct( string $module_url, string $version, bool $should_enqueue_gzd_scripts ) { - $this->module_url = $module_url; - $this->version = $version; - $this->should_enqueue_gzd_scripts = $should_enqueue_gzd_scripts; + public function __construct( + string $module_url, + string $version, + bool $should_enqueue_tracking_scripts, + bool $is_gzd_active, + bool $is_wc_shipment_active + ) { + + $this->module_url = $module_url; + $this->version = $version; + $this->should_enqueue_tracking_scripts = $should_enqueue_tracking_scripts; + $this->is_gzd_active = $is_gzd_active; + $this->is_wc_shipment_active = $is_wc_shipment_active; } /** @@ -53,15 +78,23 @@ class CompatAssets { * @return void */ public function register(): void { - $gzd_sync_enabled = apply_filters( 'woocommerce_paypal_payments_sync_gzd_tracking', true ); - if ( $this->should_enqueue_gzd_scripts && $gzd_sync_enabled ) { + if ( $this->should_enqueue_tracking_scripts ) { wp_register_script( - 'ppcp-gzd-compat', - untrailingslashit( $this->module_url ) . '/assets/js/gzd-compat.js', + 'ppcp-tracking-compat', + untrailingslashit( $this->module_url ) . '/assets/js/tracking-compat.js', array( 'jquery' ), $this->version, true ); + + wp_localize_script( + 'ppcp-tracking-compat', + 'PayPalCommerceGatewayOrderTrackingCompat', + 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, + ) + ); } } @@ -71,9 +104,8 @@ class CompatAssets { * @return void */ public function enqueue(): void { - $gzd_sync_enabled = apply_filters( 'woocommerce_paypal_payments_sync_gzd_tracking', true ); - if ( $this->should_enqueue_gzd_scripts && $gzd_sync_enabled ) { - wp_enqueue_script( 'ppcp-gzd-compat' ); + if ( $this->should_enqueue_tracking_scripts ) { + wp_enqueue_script( 'ppcp-tracking-compat' ); } } } diff --git a/modules/ppcp-compat/src/CompatModule.php b/modules/ppcp-compat/src/CompatModule.php index 4841d23bd..a1468ce23 100644 --- a/modules/ppcp-compat/src/CompatModule.php +++ b/modules/ppcp-compat/src/CompatModule.php @@ -9,6 +9,8 @@ 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; @@ -20,17 +22,15 @@ 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\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; -use WP_Theme; +use WP_REST_Request; +use WP_REST_Response; /** * Class CompatModule */ class CompatModule implements ModuleInterface { - use AdminContextTrait; - /** * Setup the compatibility module. * @@ -49,10 +49,16 @@ class CompatModule implements ModuleInterface { * @throws NotFoundException */ public function run( ContainerInterface $c ): void { + $this->initialize_ppec_compat_layer( $c ); $this->fix_site_ground_optimizer_compatibility( $c ); + $this->initialize_tracking_compat_layer( $c ); - $this->initialize_gzd_compat_layer( $c ); + $asset_loader = $c->get( 'compat.assets' ); + assert( $asset_loader instanceof CompatAssets ); + + add_action( 'init', array( $asset_loader, 'register' ) ); + add_action( 'admin_enqueue_scripts', array( $asset_loader, 'enqueue' ) ); $this->migrate_pay_later_settings( $c ); $this->migrate_smart_button_settings( $c ); @@ -112,6 +118,30 @@ class CompatModule implements ModuleInterface { ); } + /** + * Sets up the 3rd party plugins compatibility layer for PayPal tracking. + * + * @param ContainerInterface $c The Container. + * @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' ); + + 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. @@ -122,84 +152,204 @@ class CompatModule implements ModuleInterface { * @return void */ protected function initialize_gzd_compat_layer( ContainerInterface $c ): void { - if ( ! $c->get( 'compat.should-initialize-gzd-compat-layer' ) ) { - return; - } - add_action( - 'admin_enqueue_scripts', - /** - * Param types removed to avoid third-party issues. - * - * @psalm-suppress MissingClosureParamType - */ - function( $hook ) use ( $c ): void { - if ( $hook !== 'post.php' || ! $this->is_paypal_order_edit_page() ) { + 'woocommerce_gzd_shipment_status_shipped', + function( int $shipment_id, Shipment $shipment ) use ( $c ) { + if ( ! apply_filters( 'woocommerce_paypal_payments_sync_gzd_tracking', true ) ) { return; } - $asset_loader = $c->get( 'compat.assets' ); - assert( $asset_loader instanceof CompatAssets ); + $wc_order = $shipment->get_order(); - $asset_loader->register(); - $asset_loader->enqueue(); + 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 ); - add_action( - 'woocommerce_gzd_shipment_status_shipped', - static function( int $shipment_id, Shipment $shipment ) use ( $endpoint, $logger ) { - if ( ! apply_filters( 'woocommerce_paypal_payments_sync_gzd_tracking', true ) ) { - return; - } + $shipment_factory = $c->get( 'order-tracking.shipment.factory' ); + assert( $shipment_factory instanceof ShipmentFactoryInterface ); - $wc_order = $shipment->get_order(); - if ( ! is_a( $wc_order, WC_Order::class ) ) { - return; - } + try { + $ppcp_shipment = $shipment_factory->create_shipment( + $wc_order_id, + $transaction_id, + $tracking_number, + 'SHIPPED', + 'OTHER', + $carrier, + $line_items + ); - $transaction_id = $wc_order->get_transaction_id(); - if ( empty( $transaction_id ) ) { - return; - } + $tracking_information = $endpoint->get_tracking_information( $wc_order_id, $tracking_number ); - $tracking_data = array( - 'transaction_id' => $transaction_id, - 'status' => 'SHIPPED', - ); + $tracking_information + ? $endpoint->update_tracking_information( $ppcp_shipment, $wc_order_id ) + : $endpoint->add_tracking_information( $ppcp_shipment, $wc_order_id ); - $provider = $shipment->get_shipping_provider(); - if ( ! empty( $provider ) && $provider !== 'none' ) { - /** - * The filter allowing to change the default Germanized carrier for order tracking, - * such as DHL_DEUTSCHE_POST, DPD_DE, ... - */ - $tracking_data['carrier'] = (string) apply_filters( 'woocommerce_paypal_payments_default_gzd_carrier', 'DHL_DEUTSCHE_POST', $provider ); - } - - try { - $tracking_information = $endpoint->get_tracking_information( $wc_order->get_id() ); - - $tracking_data['tracking_number'] = $tracking_information['tracking_number'] ?? ''; - - if ( $shipment->get_tracking_id() ) { - $tracking_data['tracking_number'] = $shipment->get_tracking_id(); - } - - ! $tracking_information ? $endpoint->add_tracking_information( $tracking_data, $wc_order->get_id() ) : $endpoint->update_tracking_information( $tracking_data, $wc_order->get_id() ); - } catch ( Exception $exception ) { - $logger->error( "Couldn't sync tracking information: " . $exception->getMessage() ); - } - }, - 500, - 2 - ); + } catch ( Exception $exception ) { + $logger->error( "Couldn't sync tracking information: " . $exception->getMessage() ); + } } /** diff --git a/modules/ppcp-compat/src/PPEC/PPECHelper.php b/modules/ppcp-compat/src/PPEC/PPECHelper.php index 0c175979d..791cb7a22 100644 --- a/modules/ppcp-compat/src/PPEC/PPECHelper.php +++ b/modules/ppcp-compat/src/PPEC/PPECHelper.php @@ -98,7 +98,7 @@ class PPECHelper { set_transient( 'ppcp_has_ppec_subscriptions', ! empty( $result ) ? 'true' : 'false', - 3 * MONTH_IN_SECONDS + MONTH_IN_SECONDS ); return ! empty( $result ); diff --git a/modules/ppcp-compat/webpack.config.js b/modules/ppcp-compat/webpack.config.js index be299aca8..567cd235b 100644 --- a/modules/ppcp-compat/webpack.config.js +++ b/modules/ppcp-compat/webpack.config.js @@ -6,7 +6,7 @@ module.exports = { mode: isProduction ? 'production' : 'development', target: 'web', entry: { - 'gzd-compat': path.resolve('./resources/js/gzd-compat.js'), + 'tracking-compat': path.resolve('./resources/js/tracking-compat.js'), }, output: { path: path.resolve(__dirname, 'assets/'), diff --git a/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php b/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php index 881d4295f..b00c9b250 100644 --- a/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php +++ b/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php @@ -64,7 +64,7 @@ class OnboardingUrl { * * @var int */ - private $cache_ttl = 3 * MONTH_IN_SECONDS; + private $cache_ttl = MONTH_IN_SECONDS; /** * The TTL for the previous token cache. diff --git a/modules/ppcp-order-tracking/carriers.php b/modules/ppcp-order-tracking/carriers.php index 5af0c9403..429f12a51 100644 --- a/modules/ppcp-order-tracking/carriers.php +++ b/modules/ppcp-order-tracking/carriers.php @@ -9,604 +9,1586 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\OrderTracking; -use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; + return (array) apply_filters( + 'woocommerce_paypal_payments_tracking_carriers', + array( + 'global' => array( + 'name' => 'Global', + 'items' => array( + '99MINUTOS' => _x( '99minutos', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'A2B_BA' => _x( 'A2B Express Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ABCUSTOM_SFTP' => _x( 'AB Custom Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ACILOGISTIX' => _x( 'ACI Logistix', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ACOMMERCE' => _x( 'ACOMMERCE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ACTIVOS24_API' => _x( 'Activos24', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ADS' => _x( 'ADS Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AEROFLASH' => _x( 'AEROFLASH', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AGEDISS_SFTP' => _x( 'Agediss', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AIR_21' => _x( 'AIR 21', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AIRSPEED' => _x( 'AIRSPEED', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AIRTERRA' => _x( 'Airterra', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AITWORLDWIDE_API' => _x( 'AIT', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AITWORLDWIDE_SFTP' => _x( 'AIT', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ALLIED_EXPRESS_FTP' => _x( 'Allied Express (FTP)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ALLJOY' => _x( 'ALLJOY SUPPLY CHAIN', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AMAZON_EMAIL_PUSH' => _x( 'Amazon', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AMAZON_ORDER' => _x( 'Amazon order', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AMAZON_UK_API' => _x( 'amazon_uk_api', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AMS_GRP' => _x( 'AMS Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ANDREANI_API' => _x( 'Andreani', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ANTERAJA' => _x( 'Anteraja', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ARAMEX' => _x( 'Aramex', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ARAMEX_API' => _x( 'Aramex', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ARASKARGO' => _x( 'Aras Cargo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ARGENTS_WEBHOOK' => _x( 'Argents Express Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ASENDIA_DE' => _x( 'asendia_de', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ATSHEALTHCARE_REFERENCE' => _x( 'ATS Healthcare', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ATSHEALTHCARE' => _x( 'ATS Healthcare', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AUEXPRESS' => _x( 'Au Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AUSTRALIA_POST_API' => _x( 'Australia Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AVERITT' => _x( 'Averitt Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AXLEHIRE_FTP' => _x( 'Axlehire', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AXLEHIRE' => _x( 'AxleHire', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BARQEXP' => _x( 'Barq', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BDMNET' => _x( 'BDMnet', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BEL_BELGIUM_POST' => _x( 'bel_belgium_post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BLR_BELPOST' => _x( 'Belpost', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BERT' => _x( 'BERT', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BESTTRANSPORT_SFTP' => _x( 'Best Transport', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BETTERTRUCKS' => _x( 'Better Trucks', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BIGSMART' => _x( 'Big Smart', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BIOCAIR_FTP' => _x( 'BioCair', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BJSHOMEDELIVERY' => _x( 'BJS Distribution courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BJSHOMEDELIVERY_FTP' => _x( 'BJS Distribution, Storage & Couriers - FTP', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BLUEDART' => _x( 'BLUEDART', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BLUEDART_API' => _x( 'Bluedart', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BOLLORE_LOGISTICS' => _x( 'Bollore Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BOMI' => _x( 'Bomi Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BE_BPOST' => _x( 'Bpost (www.bpost.be)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BPOST_API' => _x( 'Bpost API', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BPOST_INT' => _x( 'Bpost international', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BRT_IT_API' => _x( 'BRT Bartolini API', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BUFFALO' => _x( 'BUFFALO', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BURD' => _x( 'Burd Delivery', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CHROBINSON' => _x( 'C.H. Robinson Worldwide', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CAGO' => _x( 'Cago', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CANPAR' => _x( 'CANPAR', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CAPITAL' => _x( 'Capital Transport', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CARRY_FLAP' => _x( 'Carry-Flap Co.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CBL_LOGISTICA_API' => _x( 'CBL Logistica (API)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CDLDELIVERS' => _x( 'CDL Last Mile', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CELERITAS' => _x( 'Celeritas Transporte', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CEVA' => _x( 'CEVA LOGISTICS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CEVA_TRACKING' => _x( 'CEVA Package', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CHAZKI' => _x( 'Chazki', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CHIENVENTURE_WEBHOOK' => _x( 'Chienventure', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CHILEXPRESS' => _x( 'Chile Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CITY56_WEBHOOK' => _x( 'City Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CJ_GLS' => _x( 'CJ GLS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CJ_LOGISTICS' => _x( 'CJ Logistics International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CJ_PHILIPPINES' => _x( 'cj_philippines', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CLICKLINK_SFTP' => _x( 'ClickLink', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_LOGISTICS' => _x( 'CN Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COLLECTPLUS' => _x( 'COLLECTPLUS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COM1EXPRESS' => _x( 'ComOne Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CONCISE' => _x( 'Concise', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CONCISE_WEBHOOK' => _x( 'Concise', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CONCISE_API' => _x( 'Concise', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COORDINADORA_API' => _x( 'Coordinadora', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COPA_COURIER' => _x( 'Copa Airlines Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CORREOS_DE_ESPANA' => _x( 'CORREOS DE ESPANA', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CORREOSEXPRESS_API' => _x( 'Correos Express (API)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CORREOS_ES' => _x( 'correos Express (www.correos.es)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COURANT_PLUS_API' => _x( 'Courant Plus', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COURIER_POST' => _x( 'COURIER POST', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COURIERPLUS' => _x( 'COURIERPLUS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CRLEXPRESS' => _x( 'CRL Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CROSSFLIGHT' => _x( 'Crossflight Limited', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CRYOPDP_FTP' => _x( 'CryoPDP', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CESKAPOSTA_API' => _x( 'Czech Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DEXPRESS_WEBHOOK' => _x( 'D Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DACHSER' => _x( 'DACHSER', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DACHSER_WEB' => _x( 'DACHSER', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DAESHIN' => _x( 'Daeshin', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DAIICHI' => _x( 'Daiichi Freight System Inc', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DANNIAO' => _x( 'Danniao', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DAO365' => _x( 'DAO365', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DAYROSS' => _x( 'Day & Ross', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DYLT' => _x( 'Daylight Transport', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DBSCHENKER_API' => _x( 'DB Schenker', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DBSCHENKER_B2B' => _x( 'DB Schenker B2B', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DBSCHENKER_ICELAND' => _x( 'DB Schenker Iceland', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DDEXPRESS' => _x( 'DD Express Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DE_DHL' => _x( 'DE DHL', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DELCART_IN' => _x( 'delcart_in', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DELIVERYOURPARCEL_ZA' => _x( 'Deliver Your Parcel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DELIVER_IT' => _x( 'Deliver-iT', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DELIVERE' => _x( 'delivere', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DELIVERR_SFTP' => _x( 'Deliverr', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DELTEC_DE' => _x( 'DELTEC DE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DEMANDSHIP' => _x( 'DemandShip', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DEUTSCHE_DE' => _x( 'deutsche_de', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_API' => _x( 'DHL', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_REFERENCE_API' => _x( 'DHL (Reference number)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_ACTIVE_TRACING' => _x( 'DHL Active Tracing', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_ECOMMERCE_GC' => _x( 'DHL eCommerce Greater China', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_GLOBAL_MAIL_API' => _x( 'DHL eCommerce Solutions', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DE_DHL_EXPRESS' => _x( 'DHL Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_SFTP' => _x( 'DHL Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_FR' => _x( 'DHL France (www.dhl.com)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_FREIGHT' => _x( 'DHL Freight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL' => _x( 'dhl Global', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_GLOBAL_FORWARDING_API' => _x( 'DHL Global Forwarding API', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_GT_API' => _x( 'DHL Global Forwarding Guatemala', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_PA_API' => _x( 'DHL GLOBAL FORWARDING PANAMÁ', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IT_DHL_ECOMMERCE' => _x( 'DHL International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_JP' => _x( 'DHL Japan', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_PARCEL_NL' => _x( 'DHL Parcel NL', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_SG' => _x( 'dhl Singapore', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_ES_SFTP' => _x( 'DHL Spain Domestic', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_SUPPLYCHAIN_IN' => _x( 'DHL supply chain India', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_SUPPLYCHAIN_ID' => _x( 'DHL Supply Chain Indonesia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_AT' => _x( 'dhl_at', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_GLOBAL_MAIL' => _x( 'dhl_global_mail', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_IT' => _x( 'dhl_it', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_PIECEID' => _x( 'dhl_pieceid', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_SUPPLY_CHAIN_AU' => _x( 'dhl_supply_chain_au', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHLPARCEL_UK' => _x( 'dhlparcel_uk', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DIALOGO_LOGISTICA_API' => _x( 'Dialogo Logistica', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DIALOGO_LOGISTICA' => _x( 'Dialogo Logistica', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DIRECTFREIGHT_AU_REF' => _x( 'Direct Freight Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DIREX' => _x( 'Direx', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DKSH' => _x( 'DKSH', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DMFGROUP' => _x( 'DMF', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DNJ_EXPRESS' => _x( 'DNJ Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DOTZOT' => _x( 'DOTZOT', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD' => _x( 'DPD', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_AT_SFTP' => _x( 'DPD Austria', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_DELISTRACK' => _x( 'DPD delistrack', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_NL' => _x( 'DPD Netherlands', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_RU_API' => _x( 'DPD Russia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_SK_SFTP' => _x( 'DPD Slovakia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_CH_SFTP' => _x( 'DPD Switzerland', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_UK_SFTP' => _x( 'DPD UK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_DE' => _x( 'dpd_de', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_FR_REFERENCE' => _x( 'dpd_fr_reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_UK' => _x( 'dpd_uk', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_DPEX' => _x( 'DPEX', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPEX' => _x( 'DPEX (www.dpex.com)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DSV' => _x( 'DSV courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DSV_REFERENCE' => _x( 'DSV Futurewave', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DX' => _x( 'DX', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DX_B2B_CONNUM' => _x( 'DX (B2B)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DX_FREIGHT' => _x( 'DX Freight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DYNALOGIC' => _x( 'Dynamic Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EASTWESTCOURIER_FTP' => _x( 'East West Courier Pte Ltd', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EC_CN' => _x( 'EC_CN', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ECARGO' => _x( 'ECARGO', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ECEXPRESS' => _x( 'ECexpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ECMS' => _x( 'ECMS International Logistics Co.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ECOFREIGHT' => _x( 'Eco Freight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ECOURIER' => _x( 'ecourier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ECOUTIER' => _x( 'eCoutier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EFS' => _x( 'EFS (E-commerce Fulfillment Service)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ELITE_CO' => _x( 'Elite Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ELOGISTICA' => _x( 'elogistica', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ELTA_GR' => _x( 'elta_gr', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ARE_EMIRATES_POST' => _x( 'Emirates Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EMS' => _x( 'EMS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EMS_CN' => _x( 'ems_cn', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ENSENDA' => _x( 'ENSENDA', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EFWNOW_API' => _x( 'Estes Forwarding Worldwide', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ETOMARS' => _x( 'Etomars', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ETOTAL' => _x( 'eTotal Solution Limited', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EDF_FTP' => _x( 'Eurodifarm', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EURODIS' => _x( 'eurodis', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EUROPAKET_API' => _x( 'Europacket+', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MYHERMES_UK_API' => _x( 'EVRi', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EWE' => _x( 'EWE Global Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EXELOT_FTP' => _x( 'Exelot Ltd.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EXPEDITORS' => _x( 'Expeditors', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EXPEDITORS_API_REF' => _x( 'Expeditors API Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EZSHIP' => _x( 'EZship', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FAIRSENDEN_API' => _x( 'fairsenden', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FXTRAN' => _x( 'Falcon Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FAN' => _x( 'FAN COURIER EXPRESS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FNF_ZA' => _x( 'Fast & Furious', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FASTDESPATCH' => _x( 'Fast Despatch Logistics Limited', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FASTBOX' => _x( 'Fastbox', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FASTSHIP' => _x( 'Fastship Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FASTTRACK' => _x( 'fasttrack', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FASTWAY_AU' => _x( 'fastway_au', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FASTWAY_UK' => _x( 'FASTWAY_UK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FASTWAY_US' => _x( 'FASTWAY_US', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FASTWAY_ZA' => _x( 'fastway_za', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FAXECARGO' => _x( 'Faxe Cargo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FEDEX_FR' => _x( 'FedEx® Freight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FEDEX_API' => _x( 'FedEx®', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FERCAM_IT' => _x( 'fercam_it', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FETCHR' => _x( 'Fetchr', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FIRST_LOGISTICS_API' => _x( 'First Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FIRST_LOGISITCS' => _x( 'first_logisitcs', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FITZMARK_API' => _x( 'FitzMark', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FLASHEXPRESS_WEBHOOK' => _x( 'Flash Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FLIGHTLG' => _x( 'Flight Logistics Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FLIPXP' => _x( 'FlipXpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FLYTEXPRESS' => _x( 'FLYTEXPRESS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FORWARDAIR' => _x( 'Forward Air', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FOUR_PX_EXPRESS' => _x( 'FOUR PX EXPRESS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FR_COLISSIMO' => _x( 'fr_colissimo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FR_MONDIAL' => _x( 'fr_mondial', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FRAGILEPAK_SFTP' => _x( 'FragilePAK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FRONTDOORCORP' => _x( 'FRONTdoor Collective', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FUJEXP' => _x( 'FUJIE EXPRESS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GWLOGIS_API' => _x( 'G.I.G', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GAC' => _x( 'GAC', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GATI_KWE_API' => _x( 'Gati-KWE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GDPHARM' => _x( 'GDPharm Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GW_WORLD' => _x( 'Gebrüder Weiss', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GEODIS' => _x( 'GEODIS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GEODIS_API' => _x( 'GEODIS - Distribution & Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GPOST' => _x( 'Georgian Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GIAO_HANG' => _x( 'Giao hàng nhanh', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GIO_ECOURIER_API' => _x( 'GIO Express Ecourier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GIO_ECOURIER' => _x( 'GIO Express Inc', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GOGLOBALPOST' => _x( 'Global Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLOBEGISTICS' => _x( 'GLOBEGISTICS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLOVO' => _x( 'Glovo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLS' => _x( 'GLS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLS_SPAIN_API' => _x( 'GLS Spain', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLS_DE' => _x( 'GLS_DE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLS_ES' => _x( 'GLS_ES', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLS_FR' => _x( 'GLS_FR', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLS_ITALY_FTP' => _x( 'gls_italy_ftp', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLS_SPAIN' => _x( 'gls_spain', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GOLS' => _x( 'GO Logistics & Storage', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GOPEOPLE' => _x( 'Go People', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GORUSH' => _x( 'Go Rush', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GOJEK' => _x( 'Gojek', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GREYHOUND' => _x( 'GREYHOUND', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MAZET' => _x( 'Groupe Mazet', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HANJIN' => _x( 'HanJin', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HELLENIC_POST' => _x( 'Hellenic (Greece) Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HELLMANN' => _x( 'Hellmann Worldwide Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HELTHJEM_API' => _x( 'Helthjem', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HERMES_DE_FTP' => _x( 'Hermes Germany', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HERMES_UK_SFTP' => _x( 'Hermes UK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HERMESWORLD_UK' => _x( 'hermesworld_uk', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HEROEXPRESS' => _x( 'Hero Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HFD' => _x( 'HFD', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HK_RPX' => _x( 'hk_rpx', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HOMELOGISTICS' => _x( 'Home Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HOMERUNNER' => _x( 'HomeRunner', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HERMES_IT' => _x( 'HR Parcel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HSDEXPRESS' => _x( 'HSDEXPRESS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HSM_GLOBAL' => _x( 'HSM Global', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HUANTONG' => _x( 'HuanTong', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HUBBED' => _x( 'HUBBED', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HUNTER_EXPRESS_SFTP' => _x( 'Hunter Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IBVENTURE_WEBHOOK' => _x( 'IB Venture', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTUR_IS' => _x( 'Iceland Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ICSCOURIER' => _x( 'ICS COURIER', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IDEXPRESS_ID' => _x( 'iDexpress Indonesia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IDN_POS' => _x( 'idn_pos', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IDS_LOGISTICS' => _x( 'ids_logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ILYANGLOGIS' => _x( 'Ilyang logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IMEXGLOBALSOLUTIONS' => _x( 'imexglobalsolutions', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IMILE_API' => _x( 'iMile', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IML' => _x( 'IML courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IMX' => _x( 'IMX', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INDIA_POST' => _x( 'India Post Domestic', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INDIA_POST_INT' => _x( 'India Post International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INEXPOST' => _x( 'Inexpost', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INNTRALOG_SFTP' => _x( 'Inntralog GmbH', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INPOST_UK' => _x( 'InPost', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INSTABOX_WEBHOOK' => _x( 'Instabox', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INTERNATIONAL_SEUR_API' => _x( 'International Seur API', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INTERSMARTTRANS' => _x( 'INTERSMARTTRANS & SOLUTIONS SL', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INTEX_DE' => _x( 'INTEX Paketdienst GmbH', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INTIME_FTP' => _x( 'InTime', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ITHINKLOGISTICS' => _x( 'iThink Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JTCARGO' => _x( 'J&T CARGO', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JTEXPRESS_PH' => _x( 'J&T Express Philippines', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JTEXPRESS_SG_API' => _x( 'J&T Express Singapore', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JT_LOGISTICS' => _x( 'J&T International logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JAVIT' => _x( 'Javit', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_JCEX' => _x( 'JCEX courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JD_EXPRESS' => _x( 'JD Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JD_WORLDWIDE' => _x( 'JD Worldwide', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JETSHIP_MY' => _x( 'jetship_my', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JNE_API' => _x( 'JNE (API)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IDN_JNE' => _x( 'JNE Express (Jalur Nugraha Ekakurir)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JOYINGBOX' => _x( 'joyingbox', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KARGOMKOLAY' => _x( 'KargomKolay (CargoMini)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KEDAEX' => _x( 'KedaEX', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HK_TGX' => _x( 'Kerry Express Hong Kong', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KERRY_EXPRESS_TW_API' => _x( 'Kerry Express TaiWan', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'THA_KERRY' => _x( 'Kerry Express Thailand', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KERRY_EXPRESS_TH_WEBHOOK' => _x( 'Kerry Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KNG' => _x( 'Keuhne + Nagel Global', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BE_KIALA' => _x( 'Kiala', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOGISYSTEMS_SFTP' => _x( 'Kiitääjät', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KOMON_EXPRESS' => _x( 'Komon Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KRONOS_WEBHOOK' => _x( 'Kronos Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KRONOS' => _x( 'Kronos Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KUEHNE' => _x( 'Kuehne + Nagel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LALAMOVE_API' => _x( 'Lalamove', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LBCEXPRESS_FTP' => _x( 'LBC EXPRESS INC.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LBCEXPRESS_API' => _x( 'LBC EXPRESS INC.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LCTBR_API' => _x( 'LCT do Brasil', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LTU_LIETUVOS' => _x( 'Lietuvos pastas', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LINKBRIDGE' => _x( 'Link Bridge(BeiJing)international logistics co.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LION_PARCEL' => _x( 'LION PARCEL', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LIVRAPIDE' => _x( 'Livrapide', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOGGI' => _x( 'Loggi', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOGISTICSWORLDWIDE_KR' => _x( 'LOGISTICSWORLDWIDE KR', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOGISTICSWORLDWIDE_MY' => _x( 'LOGISTICSWORLDWIDE MY', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOGWIN_LOGISTICS' => _x( 'Logwin Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOGYSTO' => _x( 'Logysto', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LUWJISTIK' => _x( 'Luwjistik', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MX_CARGO' => _x( 'M&X cargo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'M3LOGISTICS' => _x( 'M3 Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'REIMAGINEDELIVERY' => _x( 'maergo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MAGYAR_POSTA_API' => _x( 'Magyar Posta', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MAIL_BOX_ETC' => _x( 'Mail Boxes Etc.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MYS_EMS' => _x( 'Malaysia Post EMS / Pos Laju', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MALCA_AMIT_API' => _x( 'Malca Amit', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MALCA_AMIT' => _x( 'Malca-Amit', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MARKEN' => _x( 'Marken', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MEDAFRICA' => _x( 'Med Africa Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MEEST' => _x( 'Meest', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MEGASAVE' => _x( 'megasave', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MENSAJEROSURBANOS_API' => _x( 'Mensajeros Urbanos', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MWD' => _x( 'Metropolitan Warehouse & Delivery', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MWD_API' => _x( 'Metropolitan Warehouse & Delivery', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MHI' => _x( 'Mhi', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MIKROPAKKET' => _x( 'Mikropakket', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MISUMI_CN' => _x( 'MISUMI Group Inc.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MNX' => _x( 'MNX', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MOBI_BR' => _x( 'Mobi Logistica', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MONDIALRELAY_FR' => _x( 'Mondial Relay France', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MONDIALRELAY_ES' => _x( 'Mondial Relay Spain(Punto Pack)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MONDIAL_BE' => _x( 'MONDIAL_BE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MOOVA' => _x( 'Moova', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MORNINGLOBAL' => _x( 'Morning Global', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MOTHERSHIP_API' => _x( 'Mothership', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MOVIANTO' => _x( 'Movianto', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MUDITA' => _x( 'MUDITA', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MYDYNALOGIC' => _x( 'My DynaLogic', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MYSENDLE_API' => _x( 'mySendle', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NMTRANSFER' => _x( 'N&M Transfer Co., Inc.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NACEX_SPAIN_REFERENCE' => _x( 'nacex_spain_reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NAEKO_FTP' => _x( 'Naeko Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NAQEL_EXPRESS' => _x( 'Naqel Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NEWZEALAND_COURIERS' => _x( 'NEW ZEALAND COURIERS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NEWGISTICS' => _x( 'Newgistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NEWGISTICSAPI' => _x( 'Newgistics API', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NIGHTLINE_UK' => _x( 'nightline_uk', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NIMBUSPOST' => _x( 'NimbusPost', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NIPPON_EXPRESS_FTP' => _x( 'Nippon Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NIPPON_EXPRESS' => _x( 'Nippon Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NORTHLINE' => _x( 'Northline', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NOVA_POSHTA_API' => _x( 'Nova Poshta API', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NOVOFARMA_WEBHOOK' => _x( 'Novofarma', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NTL' => _x( 'NTL logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NYTLOGISTICS' => _x( 'NYT SUPPLY CHAIN LOGISTICS Co., LTD', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'OHI_WEBHOOK' => _x( 'Ohi', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHOPOLIVE' => _x( 'Olive', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'OMLOGISTICS_API' => _x( 'OM LOGISTICS LTD', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'OMNIRPS_WEBHOOK' => _x( 'Omni Returns', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ONTRAC' => _x( 'ONTRAC', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ORANGECONNEX' => _x( 'orangeconnex', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ORANGE_DS' => _x( 'OrangeDS (Orange Distribution Solutions Inc)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'OSM_WORLDWIDE_SFTP' => _x( 'OSM Worldwide', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'OZEPARTS_SHIPPING' => _x( 'Ozeparts Shipping', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'P2P_TRC' => _x( 'P2P TrakPak', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PACKETA' => _x( 'Packeta', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PACKFLEET' => _x( 'PACKFLEET', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PACKS' => _x( 'Packs', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PAKAJO' => _x( 'Pakajo World', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PANDAGO_API' => _x( 'Pandago', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PANDION' => _x( 'Pandion', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PANDU' => _x( 'PANDU', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PANTHER_REFERENCE_API' => _x( 'Panther Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PANTHER_ORDER_NUMBER' => _x( 'panther_order_number', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PAPA_WEBHOOK' => _x( 'Papa', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARCELRIGHT' => _x( 'Parcel Right', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARCEL_2_POST' => _x( 'Parcel To Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARCELFORCE' => _x( 'PARCELFORCE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARCELSTARS_WEBHOOK' => _x( 'Parcelstars', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARCLL' => _x( 'PARCLL', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PASSPORTSHIPPING' => _x( 'Passport Shipping', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PATHEON' => _x( 'Patheon Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PAYO' => _x( 'Payo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PCHOME_API' => _x( 'Pchome Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PGEON_API' => _x( 'Pgeon', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PHSE_API' => _x( 'PHSE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PICKUPP_VNM' => _x( 'pickupp_vnm', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PIDGE' => _x( 'Pidge', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PIL_LOGISTICS' => _x( 'PIL Logistics (China) Co.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PLYCONGROUP' => _x( 'Plycon Transportation Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POLARSPEED' => _x( 'PolarSpeed Inc', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTONE' => _x( 'Post ONE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTAPLUS' => _x( 'Posta Plus', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTE_ITALIANE_PACCOCELERE' => _x( 'Poste Italiane Paccocelere', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTEN_NORGE' => _x( 'Posten Norge (www.posten.no)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTI_API' => _x( 'Posti API', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTNL_INT_3_S' => _x( 'PostNL International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NLD_POSTNL' => _x( 'PostNL International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTNL_INTERNATIONAL' => _x( 'PostNL International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SWE_POSTNORD' => _x( 'Postnord sweden', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTPLUS' => _x( 'PostPlus', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PROCARRIER' => _x( 'Pro Carrier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PRODUCTCAREGROUP_SFTP' => _x( 'Product Care Services Limited', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PROFESSIONAL_COURIERS' => _x( 'PROFESSIONAL COURIERS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PPL' => _x( 'Professional Parcel Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PROMEDDELIVERY' => _x( 'ProMed Delivery', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PUROLATOR' => _x( 'purolator', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PUROLATOR_INTERNATIONAL' => _x( 'Purolator International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'QTRACK' => _x( 'QTrack', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'QUALITYPOST' => _x( 'qualitypost', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'QINTL_API' => _x( 'Quickstat Courier LLC', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'QUIQUP' => _x( 'Quiqup', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RANSA_WEBHOOK' => _x( 'Ransa', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'REDJEPAKKETJE' => _x( 'Red je Pakketje', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RELAISCOLIS' => _x( 'Relais Colis', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RHENUS_GROUP' => _x( 'Rhenus Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RHENUS_UK_API' => _x( 'Rhenus Logistics UK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AIR_CANADA' => _x( 'Rivo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RIXONHK_API' => _x( 'Rixon Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ROCHE_INTERNAL_SFTP' => _x( 'Roche Internal Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ROYAL_MAIL_FTP' => _x( 'Royal Mail', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ROYALSHIPMENTS' => _x( 'royalshipments', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RRDONNELLEY' => _x( 'rrdonnelley', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RUSSIAN_POST' => _x( 'Russian post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SAEE' => _x( 'saee', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SAGAWA' => _x( 'SAGAWA', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SAGAWA_API' => _x( 'Sagawa', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SBERLOGISTICS_RU' => _x( 'Sber Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SECRETLAB_WEBHOOK' => _x( 'Secretlab', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SEINO_API' => _x( 'Seino', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SEKO_SFTP' => _x( 'SEKO Worldwide', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SENDING' => _x( 'Sending Transporte Urgente y Comunicacion', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHOWL' => _x( 'SENHONG INTERNATIONAL LOGISTICS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NOWLOG_API' => _x( 'Sequoialog', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SERVIENTREGA' => _x( 'Servientrega', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SERVIP_WEBHOOK' => _x( 'SerVIP', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SETEL' => _x( 'Setel Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SF_EX' => _x( 'SF Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SF_EXPRESS_CN' => _x( 'SF Express China', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SGT_IT' => _x( 'SGT_IT', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHADOWFAX' => _x( 'Shadowfax', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHENZHEN' => _x( 'shenzhen 1st International Logistics(Group)Co', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HOTSIN_CARGO' => _x( 'SHENZHEN HOTSIN CARGO INTL FORWARDING CO., LTD', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KWT' => _x( 'Shenzhen Jinghuada Logistics Co.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHERPA' => _x( 'Sherpa', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHIPA' => _x( 'SHIPA', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHIPPIE' => _x( 'Shippie', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHIPPIFY' => _x( 'Shippify, Inc', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHIPROCKET' => _x( 'Shiprocket X', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHIPX' => _x( 'ShipX', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHIPXPRES' => _x( 'SHIPXPRESS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPX' => _x( 'Shopee Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPX_TH' => _x( 'Shopee Xpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHUNBANG_EXPRESS' => _x( 'ShunBang Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHYPLITE' => _x( 'Shypmax', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SIMPLETIRE_WEBHOOK' => _x( 'SimpleTire', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SIMSGLOBAL' => _x( 'Sims Global', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SIODEMKA' => _x( 'SIODEMKA', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SKynet_WORLDWIDE' => _x( 'SkyNet Worldwide Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SKY_POSTAL' => _x( 'SkyPostal', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SK_POSTA' => _x( 'Slovenska pošta', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SMARTCAT' => _x( 'SMARTCAT', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SMARTKARGO' => _x( 'SmartKargo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SMG_EXPRESS' => _x( 'SMG Direct', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SMSA_EXPRESS_WEBHOOK' => _x( 'SMSA Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SNTGLOBAL_API' => _x( 'Snt Global Etrax', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SOLISTICA_API' => _x( 'solistica', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPANISH_SEUR_FTP' => _x( 'Spanish Seur', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPECTRAN' => _x( 'Spectran', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPEEDEX' => _x( 'speedex', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPEEDY' => _x( 'Speedy', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPREETAIL_API' => _x( 'Spreetail', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPRINT_PACK' => _x( 'SPRINT PACK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SRT_TRANSPORT' => _x( 'SRT Transport', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'STAR_TRACK_NEXT_FLIGHT' => _x( 'Star Track Next Flight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'STARLINKS_API' => _x( 'Starlinks Global', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'STARTRACK' => _x( 'startrack', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'STAR_TRACK_WEBHOOK' => _x( 'StarTrack', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'STARTRACK_EXPRESS' => _x( 'startrack_express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'STATOVERNIGHT' => _x( 'Stat Overnight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_STO' => _x( 'STO Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SWISHIP' => _x( 'Swiship', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SWISS_POST' => _x( 'SWISS POST', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'T_CAT' => _x( 'T-cat', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'T_CAT_API' => _x( 'T-cat', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOGINEXT_WEBHOOK' => _x( 'T&W Delivery', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TW_TAIWAN_POST' => _x( 'Taiwan Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TAMERGROUP_WEBHOOK' => _x( 'Tamer Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TAQBIN_HK' => _x( 'TAQBIN Hong Kong', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TAQBIN_SG' => _x( 'taqbin_sg', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TCS_API' => _x( 'TCS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TECOR' => _x( 'tecor', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TELEPORT_WEBHOOK' => _x( 'Teleport', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SIC_TELIWAY' => _x( 'Teliway SIC Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TESTING_COURIER_WEBHOOK' => _x( 'Testing Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TESTING_COURIER' => _x( 'Testing Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TH_CJ' => _x( 'TH_CJ', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'THIJS_NL' => _x( 'Thijs Logistiek', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'THUNDEREXPRESS' => _x( 'Thunder Express Australia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TIPSA_API' => _x( 'Tipsa API', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TIPSA_REF' => _x( 'Tipsa Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_FR_REFERENCE' => _x( 'TNT France Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_REFR' => _x( 'TNT Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_AU' => _x( 'tnt_au', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_CN' => _x( 'TNT_CN', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_DE' => _x( 'TNT_DE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_ES' => _x( 'TNT_ES', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_IT' => _x( 'tnt_it', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_JP' => _x( 'TNT_JP', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_PL' => _x( 'TNT_PL', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOLL_WEBHOOK' => _x( 'Toll Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOLL_IPEC' => _x( 'TOLL IPEC', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOLL_PRIORITY' => _x( 'Toll Priority', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOMYDOOR' => _x( 'Tomydoor', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TONAMI_FTP' => _x( 'Tonami', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ESDEX' => _x( 'Top Ideal Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOTAL_EXPRESS_API' => _x( 'Total Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOURLINE_REFERENCE' => _x( 'Tourline Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'THAIPARCELS' => _x( 'TP Logistic', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TRANS2U' => _x( 'Trans2u', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TRANSMISSION' => _x( 'TRANSMISSION', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TANET' => _x( 'Transport Ambientales', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TRANSVIRTUAL' => _x( 'TransVirtual', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TRUNKRS' => _x( 'Trunkrs', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TRUSK' => _x( 'Trusk France', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TUSKLOGISTICS' => _x( 'Tusk Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TYP' => _x( 'TYP', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'U_ENVIOS' => _x( 'U-ENVIOS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UBER_WEBHOOK' => _x( 'Uber', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UCS' => _x( 'UCS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UDS' => _x( 'United Delivery Service', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UPS' => _x( 'United Parcel Service', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UP_EXPRESS' => _x( 'up_express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UPARCEL' => _x( 'uParcel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UPS_API' => _x( 'UPS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UPS_FREIGHT' => _x( 'UPS Freight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UPS_REFERENCE' => _x( 'UPS Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'URGENT_CARGUS' => _x( 'Urgent Cargus', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'US_APC' => _x( 'us_apc', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'USPS_API' => _x( 'USPS API', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PB_USPSFLATS_FTP' => _x( 'USPS Flats (Pitney Bowes)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'USPS_WEBHOOK' => _x( 'USPS Informed Visibility - Webhook', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VALUE_WEBHOOK' => _x( 'Value Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VIAXPRESS' => _x( 'ViaXpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VNPOST_API' => _x( 'Vietnam Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VIRTRANSPORT_SFTP' => _x( 'Vir Transport', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VNPOST_EMS' => _x( 'vnpost_ems', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VOX' => _x( 'VOX SOLUCION EMPRESARIAL SRL', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WATKINS_SHEPARD' => _x( 'watkins_shepard', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WEWORLDEXPRESS' => _x( 'We World Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WESHIP_API' => _x( 'WeShip', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WESHIP' => _x( 'WeShip', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WHISTL_SFTP' => _x( 'Whistl', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WINESHIPPING_WEBHOOK' => _x( 'Wineshipping', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WISH_EMAIL_PUSH' => _x( 'Wish', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WOOYOUNG_LOGISTICS_SFTP' => _x( 'WOO YOUNG LOGISTICS CO., LTD.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WORLDCOURIER' => _x( 'World Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WORLDNET' => _x( 'Worldnet Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WSPEXPRESS' => _x( 'WSP Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'XYY' => _x( 'Xingyunyi Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'XPEDIGO' => _x( 'Xpedigo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'XPRESSBEES' => _x( 'XPRESSBEES', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YAMATO' => _x( 'YAMATO', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TAQBIN_SG_API' => _x( 'Yamato Singapore', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YIFAN' => _x( 'YiFan Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YODEL' => _x( 'yodel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YODEL_API' => _x( 'Yodel API', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YODEL_DIR' => _x( 'Yodel Direct', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YODEL_INTNL' => _x( 'Yodel International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YUSEN' => _x( 'Yusen Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YUSEN_SFTP' => _x( 'Yusen Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YYCOM' => _x( 'yycom', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YYEXPRESS' => _x( 'YYEXPRESS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZTO_DOMESTIC' => _x( 'ZTO Express China', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZUELLIGPHARMA_SFTP' => _x( 'Zuellig Pharma Korea', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), - return array( - 'global' => array( - 'name' => 'Global', - 'items' => array( - 'B_TWO_C_EUROPE' => _x( 'B2C Europe', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CJ_LOGISTICS' => _x( 'CJ Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CORREOS_EXPRESS' => _x( 'Correos Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_ACTIVE_TRACING' => _x( 'DHL Active Tracing', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_BENELUX' => _x( 'DHL Benelux', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_GLOBAL_MAIL' => _x( 'DHL ecCommerce US', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_GLOBAL_MAIL_ASIA' => _x( 'DHL eCommerce Asia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL' => _x( 'DHL Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_GLOBAL_ECOMMERCE' => _x( 'DHL Global eCommerce', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_PACKET' => _x( 'DHL Packet', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DPD' => _x( 'DPD Global', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DPD_LOCAL' => _x( 'DPD Local', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DPD_LOCAL_REF' => _x( 'DPD Local Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DPE_EXPRESS' => _x( 'DPE Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DPEX' => _x( 'DPEX Hong Kong', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DTDC_EXPRESS' => _x( 'DTDC Express Global', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ESHOPWORLD' => _x( 'EShopWorld', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'FEDEX' => _x( 'FedEx', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'FLYT_EXPRESS' => _x( 'FLYT Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'GLS' => _x( 'GLS', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'IMX' => _x( 'IMX France', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'INT_SUER' => _x( 'International SEUR', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'LANDMARK_GLOBAL' => _x( 'Landmark Global', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'MATKAHUOLTO' => _x( 'Matkahuoloto', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'OMNIPARCEL' => _x( 'Omni Parcel', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ONE_WORLD' => _x( 'One World', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POSTI' => _x( 'Posti', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'RABEN_GROUP' => _x( 'Raben Group', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SF_EXPRESS' => _x( 'SF EXPRESS', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SKYNET_Worldwide' => _x( 'SkyNet Worldwide Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SPREADEL' => _x( 'Spreadel', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TNT' => _x( 'TNT Global', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'UPS' => _x( 'UPS', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'UPS_MI' => _x( 'UPS Mail Innovations', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'WEBINTERPRET' => _x( 'WebInterpret', 'Name of carrier', 'woocommerce-paypal-payments' ), ), + 'AG' => array( + 'name' => _x( 'Argentina', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'FASTRACK' => _x( 'Fasttrack', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ANDREANI' => _x( 'Grupo logistico Andreani', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ARG_OCA' => _x( 'OCA Argentina', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + 'AU' => array( + 'name' => _x( 'Australia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'ADSONE' => _x( 'Adsone', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ALLIEDEXPRESS' => _x( 'Allied Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ARAMEX_AU' => _x( 'Aramex Australia (formerly Fastway AU)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AU_AU_POST' => _x( 'Australia Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BLUESTAR' => _x( 'Blue Star', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BONDSCOURIERS' => _x( 'Bonds Courier Service (bondscouriers.com.au)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BORDEREXPRESS' => _x( 'Border Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COPE' => _x( 'Cope Sensitive Freight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COURIERS_PLEASE' => _x( 'CouriersPlease (couriersplease.com.au)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DELIVERE' => _x( 'deliverE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DESIGNERTRANSPORT_WEBHOOK' => _x( 'Designer Transport', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_AU' => _x( 'DHL Supply Chain Australia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DIRECTCOURIERS' => _x( 'Direct Couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DTDC_AU' => _x( 'DTDC Australia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ENDEAVOUR_DELIVERY' => _x( 'Endeavour Delivery', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HUNTER_EXPRESS' => _x( 'Hunter Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ICUMULUS' => _x( 'iCumulus', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INTERPARCEL_AU' => _x( 'Interparcel Australia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NEWAY' => _x( 'Neway Transport', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARCELPOINT' => _x( 'Parcelpoint', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PFLOGISTICS' => _x( 'PFL', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SENDLE' => _x( 'Sendle', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHIPPIT' => _x( 'Shippit', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'THENILE_WEBHOOK' => _x( 'SortHub courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'STAR_TRACK_EXPRESS' => _x( 'Star Track Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AUS_STARTRACK' => _x( 'StarTrack (startrack.com.au)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TFM' => _x( 'TFM Xpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TIGFREIGHT' => _x( 'TIG Freight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOLL' => _x( 'Toll IPEC', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UBI_LOGISTICS' => _x( 'UBI Smart Parcel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'XL_EXPRESS' => _x( 'XL Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + 'AT' => array( + 'name' => _x( 'Austria', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'AUSTRIAN_POST_EXPRESS' => _x( 'Austrian Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AU_AUSTRIAN_POST' => _x( 'Austrian Post (Registered)', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + 'BGD' => array( + 'name' => _x( 'Bangladesh', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'PAPERFLY' => _x( 'Paperfly Private Limited', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + 'BE' => array( + 'name' => _x( 'Belgium', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'B_TWO_C_EUROPE' => _x( 'B2C courier Europe', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_BENELUX' => _x( 'dhl benelux', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BEL_DHL' => _x( 'DHL Benelux', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LANDMARK_GLOBAL' => _x( 'Landmark Global', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LANDMARK_GLOBAL_REFERENCE' => _x( 'Landmark Global Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MIKROPAKKET_BE' => _x( 'Mikropakket Belgium', 'Name of carrier', 'woocommerce-paypal-payments' ), - ), - 'AG' => array( - 'name' => _x( 'Antigua and Barbuda', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'CORREOS_AG' => _x( 'Correos Antigua and Barbuda', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'AR' => array( - 'name' => _x( 'Argentina', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'EMIRATES_POST' => _x( 'Emirates Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'OCA_AR ' => _x( 'OCA Argentina', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BIH' => array( + 'name' => _x( 'Bosnia and Herzegovina', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'BH_POSTA' => _x( 'BH Posta (www.posta.ba)', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'AU' => array( - 'name' => _x( 'Australia', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'ADSONE' => _x( 'Adsone', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'AUSTRALIA_POST' => _x( 'Australia Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TOLL_AU' => _x( 'Australia Toll', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'BONDS_COURIERS' => _x( 'Bonds Couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'COURIERS_PLEASE' => _x( 'Couriers Please', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_AU' => _x( 'DHL Australia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DTDC_AU' => _x( 'DTDC Australia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'FASTWAY_AU' => _x( 'Fastway Australia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'HUNTER_EXPRESS ' => _x( 'Hunter Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SENDLE' => _x( 'Sendle', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'STARTRACK' => _x( 'Star Track', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'STARTRACK_EXPRESS' => _x( 'Star Track Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TNT_AU ' => _x( 'TNT Australia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TOLL' => _x( 'Toll', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'UBI_LOGISTICS' => _x( 'UBI Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BR' => array( + 'name' => _x( 'Brazil', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'BRA_CORREIOS' => _x( 'Correios Brazil', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DIRECTLOG' => _x( 'Directlog', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FRETERAPIDO' => _x( 'Frete Rapido', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INTELIPOST' => _x( 'Intelipost', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOTAL_EXPRESS' => _x( 'Total Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'AT' => array( - 'name' => _x( 'Austria', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'AUSTRIAN_POST_EXPRESS' => _x( 'Austrian Post Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'AUSTRIAN_POST' => _x( 'Austrian Post Registered', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_AT' => _x( 'DHL Austria', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BG' => array( + 'name' => _x( 'Bulgaria', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'A1POST' => _x( 'A1Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BG_BULGARIAN_POST' => _x( 'Bulgarian Posts', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'BE' => array( - 'name' => _x( 'Belgium', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'BPOST' => _x( 'bpost', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'BPOST_INT' => _x( 'bpost International', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'MONDIAL_BE' => _x( 'Mondial Belgium', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TAXIPOST' => _x( 'TaxiPost', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KHM' => array( + 'name' => _x( 'Cambodia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'AFLLOG_FTP' => _x( 'AFL LOGISTICS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KHM_CAMBODIA_POST' => _x( 'Cambodia Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ROADRUNNER_FREIGHT' => _x( 'Roadbull Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + + ), ), - ), - 'BR' => array( - 'name' => _x( 'Brazil', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'CORREOS_BR' => _x( 'Correos Brazil', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DIRECTLOG_BR' => _x( 'Directlog', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CA' => array( + 'name' => _x( 'Canada', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'CA_CANADA_POST' => _x( 'Canada Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CHITCHATS' => _x( 'Chit Chats', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CORPORATECOURIERS_WEBHOOK' => _x( 'Corporate Couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COURANT_PLUS' => _x( 'Courant Plus', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLOBAL_ESTES' => _x( 'Estes Express Lines', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DICOM' => _x( 'GLS Logistic Systems Canada Ltd./Dicom', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOCUS_WEBHOOK' => _x( 'Locus courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOOMIS_EXPRESS' => _x( 'Loomis Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MBW' => _x( 'MBW Courier Inc.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NATIONEX' => _x( 'Nationex courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARCELPAL_WEBHOOK' => _x( 'ParcelPal', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AIR_CANADA_GLOBAL' => _x( 'Rivo (Air canada)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ROUTIFIC_WEBHOOK' => _x( 'Routific', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RPXLOGISTICS' => _x( 'RPX Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'STALLIONEXPRESS' => _x( 'Stallion Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZIINGFINALMILE' => _x( 'Ziing Final Mile Inc', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'BG' => array( - 'name' => _x( 'Bulgaria', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'BULGARIAN_POST' => _x( 'Bulgarian Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CL' => array( + 'name' => _x( 'Chile', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'BLUEX' => _x( 'Blue Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'STARKEN' => _x( 'STARKEN couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'CA' => array( - 'name' => _x( 'Canada', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'CANADA_POST' => _x( 'Canada Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CANPAR' => _x( 'Canpar', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'GREYHOUND' => _x( 'Greyhound', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'LOOMIS' => _x( 'Loomis', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'PUROLATOR' => _x( 'Purolator', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN' => array( + 'name' => _x( 'China', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'CN_17POST' => _x( '17 Post Service', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ACSWORLDWIDE' => _x( 'ACS Worldwide Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CAINIAO' => _x( 'AliExpress Standard Shipping', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ANJUN' => _x( 'Anjun couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ANSERX' => _x( 'ANSERX courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AUPOST_CN' => _x( 'AuPost China', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BEL_RS' => _x( 'BEL North Russia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_BESTEXPRESS' => _x( 'Best Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_BOXC' => _x( 'BoxC courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BUYLOGIC' => _x( 'buylogic', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CPEX' => _x( 'Captain Express International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CGS_EXPRESS' => _x( 'CGS Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_CHINA_POST_EMS' => _x( 'China Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CHUKOU1' => _x( 'Chukou1', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CJPACKET' => _x( 'CJ Packet', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CLEVY_LINKS' => _x( 'Clevy Links', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CNDEXPRESS' => _x( 'CND Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CNEXPS' => _x( 'CNE Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COMET_TECH' => _x( 'CometTech', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CPACKET' => _x( 'Cpacket couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CUCKOOEXPRESS' => _x( 'Cuckoo Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DEX_I' => _x( 'DEX-I courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DIDADI' => _x( 'DIDADI Logistics tech', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPE_EXPRESS' => _x( 'DPE Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DTD_EXPR' => _x( 'DTD Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EMPS_CN' => _x( 'EMPS Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_EQUICK' => _x( 'Equick China', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ESHIPPING' => _x( 'Eshipping', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZES_EXPRESS' => _x( 'Eshun international Logistic', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FAR_INTERNATIONAL' => _x( 'Far international', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FARGOOD' => _x( 'FarGood', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FULFILLME' => _x( 'Fulfillme', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GANGBAO' => _x( 'GANGBAO Supplychain', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GESWL' => _x( 'GESWL Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_GOFLY' => _x( 'GoFly', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HDB' => _x( 'Haidaibao', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HDB_BOX' => _x( 'Haidaibao (BOX)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HH_EXP' => _x( 'Hua Han Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HUAHAN_EXPRESS' => _x( 'HUAHANG EXPRESS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HUODULL' => _x( 'Huodull', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HX_EXPRESS' => _x( 'HX Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IDEXPRESS' => _x( 'IDEX courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INTEL_VALLEY' => _x( 'Intel-Valley Supply chain (ShenZhen) Co. Ltd', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'J_NET' => _x( 'J-Net', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JINDOUYUN' => _x( 'jindouyun courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JOOM_LOGIS' => _x( 'Joom Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JOYING_BOX' => _x( 'Joying Box', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'K1_EXPRESS' => _x( 'K1 Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KY_EXPRESS' => _x( 'Kua Yue Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LALAMOVE' => _x( 'Lalamove', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LEADER' => _x( 'leader', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SDH_SCM' => _x( 'lightning monkey', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOGISTERS' => _x( 'Logisters', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LTIANEXP' => _x( 'LTIAN EXP', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LTL' => _x( 'LTL COURIER', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MORE_LINK' => _x( 'Morelink', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MXE' => _x( 'MXE Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NANJINGWOYUAN' => _x( 'Nanjing Woyuan', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ONEWORLDEXPRESS' => _x( 'One World Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PADTF' => _x( 'padtf.com', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PAGO' => _x( 'Pago Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PAN_ASIA' => _x( 'Pan-Asia International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_PAYPAL_PACKAGE' => _x( 'PayPal Package', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PFCEXPRESS' => _x( 'PFC Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_POST56' => _x( 'Post56', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HKD' => _x( 'Qingdao HKD International Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ETS_EXPRESS' => _x( 'RETS express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RUSTON' => _x( 'Ruston', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_SF_EXPRESS' => _x( 'SF Express (www.sf-express.com)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SFB2C' => _x( 'SF International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SFC_LOGISTICS' => _x( 'SFC', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SFCSERVICE' => _x( 'SFC Service', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DAJIN' => _x( 'Shanghai Aqrum Chemical Logistics Co.Ltd', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SINOTRANS' => _x( 'Sinotrans', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'STONE3PL' => _x( 'STONE3PL', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SYPOST' => _x( 'Sunyou Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TARRIVE' => _x( 'TONDA GLOBAL', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOPHATTEREXPRESS' => _x( 'Tophatter Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOPYOU' => _x( 'TopYou', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UC_EXPRE' => _x( 'ucexpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VIWO' => _x( 'VIWO IoT', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WANBEXPRESS' => _x( 'WanbExpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WEASHIP' => _x( 'Weaship', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_WEDO' => _x( 'WeDo Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WINIT' => _x( 'WinIt', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WISE_EXPRESS' => _x( 'Wise Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_WISHPOST' => _x( 'WishPost', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'XQ_EXPRESS' => _x( 'XQ Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YANWEN' => _x( 'Yanwen Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YDH_EXPRESS' => _x( 'YDH express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ELIAN_POST' => _x( 'Yilian (Elian) Supply Chain', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YINGNUO_LOGISTICS' => _x( 'yingnuo logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YTO' => _x( 'YTO Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CN_YUNDA' => _x( 'Yunda Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YUNEXPRESS' => _x( 'YunExpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZJS_EXPRESS' => _x( 'ZJS International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZTO_EXPRESS' => _x( 'ZTO Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'CL' => array( - 'name' => _x( 'Chile', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'CORREOS_CL' => _x( 'Correos Chile', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COL' => array( + 'name' => _x( 'Colombia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'COORDINADORA' => _x( 'Coordinadora', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'CN' => array( - 'name' => _x( 'China', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'FOUR_PX_EXPRESS' => _x( 'Correos', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'AUPOST_CN' => _x( 'AUPOST CHINA', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'BQC_EXPRESS' => _x( 'BQC Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'BUYLOGIC' => _x( 'Buylogic', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CHINA_POST' => _x( 'China Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CNEXPS' => _x( 'CN Exps', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'EC_CN' => _x( 'EC China', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'EFS' => _x( 'EFS', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'EMPS_CN' => _x( 'EMPS China', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'EMS_CN' => _x( 'EMS China', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'HUAHAN_EXPRESS' => _x( 'Huahan Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SFC_EXPRESS' => _x( 'SFC Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TNT_CN' => _x( 'TNT China', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'WINIT' => _x( 'WinIt', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'YANWEN_CN' => _x( 'Yanwen', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HRV' => array( + 'name' => _x( 'Croatia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'GLS_CROTIA' => _x( 'GLS Croatia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HRV_HRVATSKA' => _x( 'Hrvatska posta', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'OVERSE_EXP' => _x( 'Overseas Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'CR' => array( - 'name' => _x( 'Costa Rica', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'CORREOS_CR' => _x( 'Correos De Costa Rica', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CY' => array( + 'name' => _x( 'Cyprus', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'CYPRUS_POST_CYP' => _x( 'Cyprus Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'HR' => array( - 'name' => _x( 'Croatia', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'HRVATSKA_HR' => _x( 'Hrvatska', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CZ' => array( + 'name' => _x( 'Czech Republic', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'CESKA_CZ' => _x( 'Ceska Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLS_CZ' => _x( 'GLS Czech Republic', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'CY' => array( - 'name' => _x( 'Cyprus', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'CYPRUS_POST_CYP' => _x( 'Cyprus Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DNK' => array( + 'name' => _x( 'Denmark', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'BUDBEE_WEBHOOK' => _x( 'Budbee courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DANSKE_FRAGT' => _x( 'Danske Fragtaend', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTNORD_LOGISTICS_DK' => _x( 'ostnord denmark', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTNORD_LOGISTICS' => _x( 'PostNord Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'XPRESSEN_DK' => _x( 'Xpressen courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'CZ' => array( - 'name' => _x( 'Czech Republic', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'CESKA_CZ' => _x( 'Ceska', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'GLS_CZ' => _x( 'GLS Czech Republic', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EST' => array( + 'name' => _x( 'Estonia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'OMNIVA' => _x( 'Omniva', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'FR' => array( - 'name' => _x( 'France', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'BERT' => _x( 'BERT TRANSPORT', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CHRONOPOST_FR' => _x( 'Chronopost France', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'COLIPOSTE' => _x( 'Coliposte', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'COLIS' => _x( 'Colis France', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_FR' => _x( 'DHL France', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DPD_FR' => _x( 'DPD France', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'GEODIS' => _x( 'GEODIS - Distribution & Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'GLS_FR' => _x( 'GLS France', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'LAPOSTE' => _x( 'LA Poste', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'MONDIAL' => _x( 'Mondial Relay', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'RELAIS_COLIS_FR' => _x( 'Relais Colis', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TELIWAY' => _x( 'Teliway', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TNT_FR' => _x( 'TNT France', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FIN' => array( + 'name' => _x( 'Finland', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'MATKAHUOLTO' => _x( 'Matkahuolto', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTI' => _x( 'Posti courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'DE' => array( - 'name' => _x( 'Germany', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'ASENDIA_DE' => _x( 'Asendia Germany', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DELTEC_DE' => _x( 'Deltec Germany', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DEUTSCHE_DE' => _x( 'Deutsche', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_DEUTSCHE_POST' => _x( 'DHL Deutsche Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DPD_DE' => _x( 'DPD Germany', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'GLS_DE' => _x( 'GLS Germany', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'HERMES_DE' => _x( 'Hermes Germany', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TNT_DE' => _x( 'TNT Germany', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FR' => array( + 'name' => _x( 'France', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'CHRONOPOST_FR' => _x( 'Chronopost france (www.chronopost.fr)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COLIS_PRIVE' => _x( 'Colis Privé', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FR_COLIS' => _x( 'Colissimo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CUBYN' => _x( 'Cubyn', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_FR' => _x( 'DPD France', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FR_EXAPAQ' => _x( 'DPD France (formerly exapaq)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GEODIS_ESPACE' => _x( 'Geodis E-space', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HEPPNER_FR' => _x( 'Heppner France', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LA_POSTE_SUIVI' => _x( 'La Poste', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_FR' => _x( 'TNT France', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VIRTRANSPORT' => _x( 'VIR Transport', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'GR' => array( - 'name' => _x( 'Greece', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'ELTA_GR' => _x( 'ELTA Greece', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'GENIKI_GR' => _x( 'Geniki Greece', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ACS_GR' => _x( 'GRC Greece', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DE' => array( + 'name' => _x( 'Germany', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'HERMES_DE' => _x( 'Hermes Germany', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AO_DEUTSCHLAND' => _x( 'AO Deutschland', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DE_DPD_DELISTRACK' => _x( 'DPD Germany', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FIEGE' => _x( 'Fiege Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GEIS' => _x( 'Geis CZ', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GEL_EXPRESS' => _x( 'Gel Express Logistik', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GENERAL_OVERNIGHT' => _x( 'Go!Express and logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HEPPNER' => _x( 'Heppner Internationale Spedition GmbH & Co.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HERMES_2MANN_HANDLING' => _x( 'Hermes Einrichtungs Service GmbH & Co. KG', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NOX_NACHTEXPRESS' => _x( 'Innight Express Germany GmbH (nox NachtExpress)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LIEFERY' => _x( 'liefery', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NOX_NIGHT_TIME_EXPRESS' => _x( 'NOX NightTimeExpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARCELONE' => _x( 'PARCEL ONE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PRESSIODE' => _x( 'Pressio', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RABEN_GROUP' => _x( 'Raben Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'STRECK_TRANSPORT' => _x( 'Streck Transport', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SWISHIP_DE' => _x( 'Swiship DE', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'HK' => array( - 'name' => _x( 'Hong Kong', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'ASENDIA_HK' => _x( 'Asendia Hong Kong', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_HK' => _x( 'DHL Hong Kong', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DPD_HK' => _x( 'DPD Hong Kong', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'HK_POST' => _x( 'Hong Kong Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'KERRY_EXPRESS_HK' => _x( 'Kerry Express Hong Kong', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'LOGISTICSWORLDWIDE_HK' => _x( 'Logistics Worldwide Hong Kong', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'QUANTIUM' => _x( 'Quantium', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SEKOLOGISTICS' => _x( 'Seko Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TAQBIN_HK' => _x( 'TA-Q-BIN Parcel Hong Kong', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GR' => array( + 'name' => _x( 'Greece', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'ACS_GR' => _x( 'ACS Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EASY_MAIL' => _x( 'Easy Mail', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GENIKI_GR' => _x( 'Geniki Taxydromiki', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPEEDCOURIERS_GR' => _x( 'Speed Couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPEEDEXCOURIER' => _x( 'SPEEDEX couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'HU' => array( - 'name' => _x( 'Hungary', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'MAGYAR_HU' => _x( 'Magyar', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HK' => array( + 'name' => _x( 'Hong Kong', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'CFL_LOGISTICS' => _x( 'CFL Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CJ_HK_INTERNATIONAL' => _x( 'CJ Logistics International(Hong Kong)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CLE_LOGISTICS' => _x( 'CL E-Logistics Solutions Limited', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CONTINENTAL' => _x( 'Continental', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COSTMETICSNOW' => _x( 'Cosmetics Now', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DEALERSEND' => _x( 'DealerSend', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_ECOMERCE_ASA' => _x( 'DHL eCommerce Asia (API)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_GLOBAL_MAIL_ASIA' => _x( 'DHL Global Mail Asia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_HK' => _x( 'DHL Hong Kong', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_HK' => _x( 'DPD Hong Kong', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DTDC_EXPRESS' => _x( 'DTDC express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLOBAVEND' => _x( 'Globavend', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HK_POST' => _x( 'Hongkong Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JANCO' => _x( 'Janco Ecommerce', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JS_EXPRESS' => _x( 'JS EXPRESS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KEC' => _x( 'KEC courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KERRY_ECOMMERCE' => _x( 'Kerry eCommerce', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LHT_EXPRESS' => _x( 'LHT Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOGISTICSWORLDWIDE_HK' => _x( 'Logistic Worldwide Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MAINWAY' => _x( 'Mainway', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MORNING_EXPRESS' => _x( 'Morning Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'OKAYPARCEL' => _x( 'OkayParcel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'OMNIPARCEL' => _x( 'Omni Parcel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PALEXPRESS' => _x( 'PAL Express Limited', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PICKUP' => _x( 'Pickupp', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'QUANTIUM' => _x( 'Quantium', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RPX' => _x( 'RPX Online', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SEKOLOGISTICS' => _x( 'SEKO Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHIP_IT_ASIA' => _x( 'Ship It Asia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SMOOTH' => _x( 'Smooth Couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'STEPFORWARDFS' => _x( 'STEP FORWARD FREIGHT SERVICE CO LTD', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SFPLUS_WEBHOOK' => _x( 'Zeek courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZEEK_2_DOOR' => _x( 'Zeek2Door', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'IS' => array( - 'name' => _x( 'Iceland', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'POSTUR_IS' => _x( 'Postur', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HU' => array( + 'name' => _x( 'Hungary', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'DPD_HGRY' => _x( 'DPD Hungary', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MAGYAR_HU' => _x( 'Magyar Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'IN' => array( - 'name' => _x( 'India', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'BLUEDART' => _x( 'Bluedart', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DELHIVERY_IN' => _x( 'Delhivery', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DOTZOT' => _x( 'DotZot', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DTDC_IN' => _x( 'DTDC India', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'EKART' => _x( 'Ekart', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'INDIA_POST' => _x( 'India Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'PROFESSIONAL_COURIERS' => _x( 'Professional Couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'REDEXPRESS' => _x( 'Red Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SWIFTAIR' => _x( 'Swift Air', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'XPRESSBEES' => _x( 'Xpress Bees', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IN' => array( + 'name' => _x( 'India', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'BOMBINOEXP' => _x( 'Bombino Express Pvt', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IND_DELHIVERY' => _x( 'Delhivery India', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DELIVERYONTIME' => _x( 'DELIVERYONTIME LOGISTICS PVT LTD', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DTDC_IN' => _x( 'DTDC India', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IND_ECOM' => _x( 'Ecom Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EKART' => _x( 'Ekart logistics (ekartlogistics.com)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IND_FIRSTFLIGHT' => _x( 'First Flight Couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IND_GATI' => _x( 'Gati-KWE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IND_GOJAVAS' => _x( 'GoJavas', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HOLISOL' => _x( 'Holisol', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LEXSHIP' => _x( 'LexShip', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'OCS' => _x( 'OCS ANA Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARCELLED_IN' => _x( 'Parcelled.in', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PICKRR' => _x( 'Pickrr', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IND_SAFEEXPRESS' => _x( 'Safexpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SCUDEX_EXPRESS' => _x( 'Scudex Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHREE_ANJANI_COURIER' => _x( 'Shree Anjani Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHREE_MARUTI' => _x( 'Shree Maruti Courier Services Pvt Ltd', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHREENANDANCOURIER' => _x( 'SHREE NANDAN COURIER', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHREETIRUPATI' => _x( 'SHREE TIRUPATI COURIER SERVICES PVT. LTD.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPOTON' => _x( 'SPOTON Logistics Pvt Ltd', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TRACKON' => _x( 'Trackon Couriers Pvt. Ltd', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'ID' => array( - 'name' => _x( 'Indonesia', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'FIRST_LOGISITCS' => _x( 'First Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'JNE_IDN' => _x( 'JNE Indonesia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'LION_PARCEL' => _x( 'Lion Parcel', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'NINJAVAN_ID' => _x( 'Ninjavan Indonesia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'PANDU' => _x( 'Pandu Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POS_ID' => _x( 'Pos Indonesia Domestic', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POS_INT' => _x( 'Pos Indonesia International', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'RPX_ID' => _x( 'RPX Indonesia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'RPX' => _x( 'RPX International', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TIKI_ID' => _x( 'Tiki', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'WAHANA_ID' => _x( 'Wahana', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ID' => array( + 'name' => _x( 'Indonesia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'ALFATREX' => _x( 'AlfaTrex', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CHOIR_EXP' => _x( 'Choir Express Indonesia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INDOPAKET' => _x( 'INDOPAKET', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JX' => _x( 'JX courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KURASI' => _x( 'KURASI', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NINJAVAN_ID' => _x( 'Ninja Van Indonesia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NINJAVAN_WB' => _x( 'Ninjavan Webhook', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MGLOBAL' => _x( 'PT MGLOBAL LOGISTICS INDONESIA', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PRIMAMULTICIPTA' => _x( 'PT Prima Multi Cipta', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RCL' => _x( 'Red Carpet Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RPX_ID' => _x( 'RPX Indonesia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SAP_EXPRESS' => _x( 'SAP EXPRESS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SIN_GLBL' => _x( 'Sin Global Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TIKI_ID' => _x( 'Tiki shipment', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TRANS_KARGO' => _x( 'Trans Kargo Internasional', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WAHANA_ID' => _x( 'Wahana express (www.wahana.com)', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'IE' => array( - 'name' => _x( 'Ireland', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'AN_POST' => _x( 'AN POST Ireland', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DPD_IR' => _x( 'DPD Ireland', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'MASTERLINK' => _x( 'Masterlink', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TPG' => _x( 'TPG', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'WISELOADS' => _x( 'Wiseloads', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IE' => array( + 'name' => _x( 'Ireland', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'AN_POST' => _x( 'An Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_IR' => _x( 'DPD Ireland', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FASTWAY_IR' => _x( 'Fastway Ireland', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WISELOADS' => _x( 'Wiseloads', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'IL' => array( - 'name' => _x( 'Israel', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'ISRAEL_POST' => _x( 'Israel Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IL' => array( + 'name' => _x( 'Israel', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'ISRAEL_POST' => _x( 'Israel Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ISR_POST_DOMESTIC' => _x( 'Israel Post Domestic', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'IT' => array( - 'name' => _x( 'Italy', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'BRT_IT' => _x( 'BRT Bartolini', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_IT' => _x( 'DHL Italy', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DMM_NETWORK' => _x( 'DMM Network', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'FERCAM_IT' => _x( 'FERCAM Logistics & Transport', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'GLS_IT' => _x( 'GLS Italy', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'HERMES_IT' => _x( 'Hermes Italy', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POSTE_ITALIANE' => _x( 'Poste Italiane', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'REGISTER_MAIL_IT' => _x( 'Register Mail IT', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SDA_IT' => _x( 'SDA Italy', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SGT_IT' => _x( 'SGT Corriere Espresso', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TNT_CLICK_IT' => _x( 'TNT Click Italy', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TNT_IT' => _x( 'TNT Italy', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IT' => array( + 'name' => _x( 'Italy', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'BRT_IT_PARCELID' => _x( 'BRT Bartolini (Parcel ID)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BRT_IT' => _x( 'BRT Couriers Italy', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ARCO_SPEDIZIONI' => _x( 'Arco Spedizioni SP', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BLINKLASTMILE' => _x( 'Blink', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BRT_IT_SENDER_REF' => _x( 'BRT Bartolini (Sender Reference)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DMM_NETWORK' => _x( 'DMM Network', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLS_IT' => _x( 'GLS Italy', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HRPARCEL' => _x( 'HR Parcel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'I_DIKA' => _x( 'i-dika', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LICCARDI_EXPRESS' => _x( 'LICCARDI EXPRESS COURIER', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MILKMAN' => _x( 'Milkman Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IT_NEXIVE' => _x( 'Nexive (TNT Post Italy)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IT_POSTE_ITALIA' => _x( 'Poste Italiane', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SAILPOST' => _x( 'SAILPOST', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SDA_IT' => _x( 'SDA Italy', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_CLICK_IT' => _x( 'TNT-Click Italy', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'JP' => array( - 'name' => _x( 'Japan', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'DHL_JP' => _x( 'DHL Japan', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'JP_POST' => _x( 'JP Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'JAPAN_POST' => _x( 'Japan Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POCZTEX' => _x( 'Pocztex', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SAGAWA' => _x( 'Sagawa', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SAGAWA_JP' => _x( 'Sagawa JP', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TNT_JP' => _x( 'TNT Japan', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'YAMATO' => _x( 'Yamato Japan', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JP' => array( + 'name' => _x( 'Japan', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'EFEX' => _x( 'eFEx (E-Commerce Fulfillment & Express)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JPN_JAPAN_POST' => _x( 'Japan Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KWE_GLOBAL' => _x( 'KWE Global', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MAIL_PLUS' => _x( 'MailPlus', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MAILPLUS_JPN' => _x( 'MailPlus (Japan)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SEINO' => _x( 'Seino', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'KR' => array( - 'name' => _x( 'Korea', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'ECARGO' => _x( 'Ecargo', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'EPARCEL_KR' => _x( 'eParcel Korea', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'KOREA_POST' => _x( 'Korea Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'KOR_KOREA_POST' => _x( 'KOR Korea Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CJ_KR' => _x( 'Korea Thai CJ', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'LOGISTICSWORLDWIDE_KR' => _x( 'Logistics Worldwide Korea', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'PANTOS' => _x( 'Pantos', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'RINCOS' => _x( 'Rincos', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ROCKET_PARCEL' => _x( 'Rocket Parcel International', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SRE_KOREA' => _x( 'SRE Korea', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JEY' => array( + 'name' => _x( 'Jersey', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'JERSEY_POST' => _x( 'Jersey Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'LT' => array( - 'name' => _x( 'Lithuania', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'LIETUVOS_LT' => _x( 'Lietuvos Pastas', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KR' => array( + 'name' => _x( 'Korea', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'CELLO_SQUARE' => _x( 'Cello Square', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CROSHOT' => _x( 'Croshot', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DOORA' => _x( 'Doora Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EPARCEL_KR' => _x( 'eParcel Korea', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KPOST' => _x( 'Korea Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KR_KOREA_POST' => _x( 'Koreapost (www.koreapost.go.kr)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KYUNGDONG_PARCEL' => _x( 'Kyungdong Parcel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOTTE' => _x( 'Lotte Global Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RINCOS' => _x( 'Rincos', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ROCKET_PARCEL' => _x( 'Rocket Parcel International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHIP_GATE' => _x( 'ShipGate', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHIPTER' => _x( 'SHIPTER', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SRE_KOREA' => _x( 'SRE Korea (www.srekorea.co.kr)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOLOS' => _x( 'Tolos courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'MY' => array( - 'name' => _x( 'Malaysia', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'AIRPAK_MY' => _x( 'Airpak', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CITYLINK_MY' => _x( 'CityLink Malaysia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CJ_MY' => _x( 'CJ Malaysia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CJ_INT_MY' => _x( 'CJ Malaysia International', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CUCKOOEXPRESS' => _x( 'Cuckoo Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'JETSHIP_MY' => _x( 'Jet Ship Malaysia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'KANGAROO_MY' => _x( 'Kangaroo Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'LOGISTICSWORLDWIDE_MY' => _x( 'Logistics Worldwide Malaysia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'MALAYSIA_POST' => _x( 'Malaysia Post EMS / Pos Laju', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'NATIONWIDE' => _x( 'Nationwide', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'NINJAVAN_MY' => _x( 'Ninjavan Malaysia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SKYNET_MY' => _x( 'Skynet Malaysia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TAQBIN_MY' => _x( 'TA-Q-BIN Parcel Malaysia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KWT' => array( + 'name' => _x( 'Kuwait', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'POSTA_PLUS' => _x( 'Posta Plus', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'MX' => array( - 'name' => _x( 'Mexico', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'CORREOS_MX' => _x( 'Correos De Mexico', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ESTAFETA' => _x( 'Estafeta', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'AEROFLASH' => _x( 'Mexico Aeroflash', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'REDPACK' => _x( 'Mexico Redpack', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SENDA_MX' => _x( 'Mexico Senda Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LAO' => array( + 'name' => _x( "Lao People's Democratic Republic (the)", 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'LAO_POST' => _x( 'Lao Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'NL' => array( - 'name' => _x( 'Netherlands', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'DHL_NL' => _x( 'DHL Netherlands', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_PARCEL_NL' => _x( 'DHL Parcel Netherlands', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'GLS_NL' => _x( 'GLS Netherlands', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'KIALA' => _x( 'Kiala', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POSTNL' => _x( 'PostNL', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POSTNL_INT' => _x( 'PostNl International', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POSTNL_INT_3_S' => _x( 'PostNL International 3S', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TNT_NL' => _x( 'TNT Netherlands', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TRANSMISSION' => _x( 'Transmission Netherlands', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LVA' => array( + 'name' => _x( 'Latvia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'CDEK' => _x( 'CDEK courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LATVIJAS_PASTS' => _x( 'Latvijas Pasts', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'NZ' => array( - 'name' => _x( 'New Zealand', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'COURIER_POST' => _x( 'Courier Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'FASTWAY_NZ' => _x( 'Fastway New Zealand', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'NZ_POST' => _x( 'New Zealand Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TOLL_IPEC' => _x( 'Toll IPEC', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LT' => array( + 'name' => _x( 'Lithuania', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'VENIPAK' => _x( 'Venipak', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'NG' => array( - 'name' => _x( 'Nigeria', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'COURIERPLUS' => _x( 'Courier Plus', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'NIPOST_NG' => _x( 'NiPost', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MY' => array( + 'name' => _x( 'Malaysia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'ABXEXPRESS_MY' => _x( 'ABX Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MYS_AIRPAK' => _x( 'Airpak Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CITYLINK_MY' => _x( 'City-Link Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CJ_CENTURY' => _x( 'CJ Century', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CJ_INT_MY' => _x( 'CJ International Malaysia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COLLECTCO' => _x( 'CollectCo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FMX' => _x( 'FMX', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MYS_GDEX' => _x( 'GDEX Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JTEXPRESS' => _x( 'J&T EXPRESS MALAYSIA', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JINSUNG' => _x( 'JINSUNG TRADING', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JOCOM' => _x( 'Jocom', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KANGAROO_MY' => _x( 'Kangaroo Worldwide Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LINE' => _x( 'Line Clear Express & Logistics Sdn Bhd', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOGISTIKA' => _x( 'Logistika', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'M_XPRESS' => _x( 'M Xpress Sdn Bhd', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MYS_MYS_POST' => _x( 'Malaysia Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MATDESPATCH' => _x( 'Matdespatch', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MYS_MYPOST_ONLINE' => _x( 'Mypostonline', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NATIONWIDE_MY' => _x( 'Nationwide Express Courier Services Bhd (www.nationwide.com.my)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NINJAVAN_MY' => _x( 'Ninja Van (www.ninjavan.co)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PICKUPP_MYS' => _x( 'PICK UPP', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MYS_SKYNET' => _x( 'Skynet Malaysia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TAQBIN_MY' => _x( 'TAQBIN Malaysia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WEPOST' => _x( 'WePost Sdn Bhd', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WYNGS' => _x( 'Wyngs', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZEPTO_EXPRESS' => _x( 'ZeptoExpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'NO' => array( - 'name' => _x( 'Norway', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'POSTEN_NORGE' => _x( 'Posten Norge', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MX' => array( + 'name' => _x( 'Mexico', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'CORREOS_DE_MEXICO' => _x( 'Correos Mexico', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MEX_ESTAFETA' => _x( 'Estafeta (www.estafeta.com)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GRUPO' => _x( 'Grupo ampm', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HOUNDEXPRESS' => _x( 'Hound Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IVOY_WEBHOOK' => _x( 'Ivoy courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MEX_SENDA' => _x( 'Mexico Senda Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PAQUETEXPRESS' => _x( 'Paquetexpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MEX_REDPACK' => _x( 'Redpack', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'PH' => array( - 'name' => _x( 'Philippines', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'TWO_GO' => _x( '2GO', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'AIR_21' => _x( 'Air 21', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'AIRSPEED' => _x( 'Airspeed', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'JAMEXPRESS_PH' => _x( 'Jam Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'LBC_PH' => _x( 'LBC Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'NINJAVAN_PH' => _x( 'Ninjavan Philippines', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'RAF_PH' => _x( 'RAF Philippines', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'XEND_EXPRESS_PH' => _x( 'Xend Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NL' => array( + 'name' => _x( 'Netherlands', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'BROUWER_TRANSPORT' => _x( 'Brouwer Transport en Logistiek', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NLD_DHL' => _x( 'DHL Netherlands', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FIEGE_NL' => _x( 'Fiege Netherlands', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NLD_GLS' => _x( 'GLS Netherlands', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HAPPY2POINT' => _x( 'Happy 2ThePoint', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PAPER_EXPRESS' => _x( 'Paper Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTNL_INTL_3S' => _x( 'PostNL International 3S', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TRUNKRS_WEBHOOK' => _x( 'Trunkrs courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'PL' => array( - 'name' => _x( 'Poland', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'DHL_PL' => _x( 'DHL Poland', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DPD_PL' => _x( 'DPD Poland', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'INPOST_PACZKOMATY' => _x( 'InPost Paczkomaty', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POCZTA_POLSKA' => _x( 'Poczta Polska', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SIODEMKA' => _x( 'Siodemka', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TNT_PL' => _x( 'TNT Poland', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NZ' => array( + 'name' => _x( 'New Zealand', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'FASTWAY_NZ' => _x( 'Fastway New Zealand', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INTERPARCEL_NZ' => _x( 'Interparcel New Zealand', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MAINFREIGHT' => _x( 'Mainfreight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NZ_NZ_POST' => _x( 'New Zealand Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOLL_NZ' => _x( 'Toll New Zealand', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'PT' => array( - 'name' => _x( 'Portugal', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'ADICIONAL_PT' => _x( 'Adicional Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CHRONOPOST_PT' => _x( 'Chronopost Portugal', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CTT_PT' => _x( 'Portugal PTT', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SEUR_PT' => _x( 'Portugal Seur', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NG' => array( + 'name' => _x( 'Nigeria', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'NIPOST_NG' => _x( 'NIpost (www.nipost.gov.ng)', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'RO' => array( - 'name' => _x( 'Romania', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'DPD_RO' => _x( 'DPD Romania', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POSTA_RO' => _x( 'Postaromana', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NO' => array( + 'name' => _x( 'Norway', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'HELTHJEM' => _x( 'Helthjem', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'RU' => array( - 'name' => _x( 'Russia', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'DPD_RU' => _x( 'DPD Russia', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'RUSSIAN_POST' => _x( 'Russian Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PAK' => array( + 'name' => _x( 'Pakistan', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'FORRUN' => _x( 'forrun Pvt Ltd (Arpatech Venture)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TCS' => _x( 'TCS courier ', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'SA' => array( - 'name' => _x( 'Saudi Arabia', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'DAWN_WING' => _x( 'Dawn Wing', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'RAM' => _x( 'Ram', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'THE_COURIER_GUY' => _x( 'The Courier Guy', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PRY' => array( + 'name' => _x( 'Paraguay', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'AEX' => _x( 'AEX Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'CS' => array( - 'name' => _x( 'Serbia', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'POST_SERBIA_CS' => _x( 'Serbia Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PH' => array( + 'name' => _x( 'Philippines', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'TWO_GO' => _x( '2GO Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PHL_JAMEXPRESS' => _x( 'Jam Express Philippines', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PIXSELL' => _x( 'PIXSELL LOGISTICS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RAF_PH' => _x( 'RAF Philippines', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'XDE_WEBHOOK' => _x( 'Ximex Delivery Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'XPOST' => _x( 'Xpost.ph', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'SG' => array( - 'name' => _x( 'Singapore', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'DHL_SG' => _x( 'DHL Singapore', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'JETSHIP_SG' => _x( 'JetShip Singapore', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'NINJAVAN_SG' => _x( 'Ninjavan Singapore', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'PARCELPOST_SG' => _x( 'Parcel Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SINGPOST' => _x( 'Singapore Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TAQBIN_SG' => _x( 'TA-Q-BIN Parcel Singapore', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PL' => array( + 'name' => _x( 'Poland', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'DHL_PL' => _x( 'DHL Poland', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_POLAND' => _x( 'DPD Poland', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FEDEX_POLAND' => _x( 'FedEx® Poland Domestic', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INPOST_PACZKOMATY' => _x( 'InPost Paczkomaty', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PL_POCZTA_POLSKA' => _x( 'Poczta Polska', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ROYAL_MAIL' => _x( 'Royal Mail', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'ZA' => array( - 'name' => _x( 'South Africa', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'FASTWAY_ZA' => _x( 'Fastway South Africa', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PT' => array( + 'name' => _x( 'Portugal', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'ADICIONAL' => _x( 'Adicional Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BNEED' => _x( 'Bneed courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CARRIERS' => _x( 'Carriers courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PRT_CHRONOPOST' => _x( 'Chronopost Portugal', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PRT_CTT' => _x( 'CTT Portugal', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DELNEXT' => _x( 'Delnext', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'ES' => array( - 'name' => _x( 'Spain', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'ASM_ES' => _x( 'ASM', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CBL_LOGISTICA' => _x( 'CBL Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CORREOS_ES' => _x( 'Correos De Spain', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_ES ' => _x( 'DHL Spain', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_PARCEL_ES' => _x( 'DHL Parcel Spain', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'GLS_ES' => _x( 'GLS Spain', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'INT_SEUR' => _x( 'International Suer', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ITIS' => _x( 'ITIS', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'NACEX_ES' => _x( 'Nacex Spain', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'REDUR_ES' => _x( 'Redur Spain', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SEUR_ES' => _x( 'Spanish Seur', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TNT_ES' => _x( 'TNT Spain', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RO' => array( + 'name' => _x( 'Romania', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'DPD_RO' => _x( 'DPD Romania', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTA_RO' => _x( 'Post Roman (www.posta-romana.ro)', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'SE' => array( - 'name' => _x( 'Sweden', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'DBSCHENKER_SE' => _x( 'DB Schenker Sweden', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DIRECTLINK_SE' => _x( 'DirectLink Sweden', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POSTNORD_LOGISTICS_GLOBAL' => _x( 'PostNord Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POSTNORD_LOGISTICS_DK' => _x( 'PostNord Logistics Denmark', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'POSTNORD_LOGISTICS_SE' => _x( 'PostNord Logistics Sweden', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RUS' => array( + 'name' => _x( 'Russia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'BOX_BERRY' => _x( 'Boxberry courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CSE' => _x( 'CSE courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_PARCEL_RU' => _x( 'DHL Parcel Russia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DOBROPOST' => _x( 'DobroPost', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_RU' => _x( 'DPD Russia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EXPRESSSALE' => _x( 'Expresssale', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GBS_BROKER' => _x( 'GBS-Broker', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PONY_EXPRESS' => _x( 'Pony express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHOPFANS' => _x( 'ShopfansRU LLC', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'CH' => array( - 'name' => _x( 'Switzerland', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'SWISS_POST' => _x( 'Swiss Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SAU' => array( + 'name' => _x( 'Saudi Arabia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'SAU_SAUDI_POST' => _x( 'Saudi Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SMSA_EXPRESS' => _x( 'SMSA Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'THABIT_LOGISTICS' => _x( 'Thabit Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZAJIL_EXPRESS' => _x( 'Zajil Express Company', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'TW' => array( - 'name' => _x( 'Taiwan', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'CHUNGHWA_POST' => _x( 'Chunghwa Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TAIWAN_POST_TW' => _x( 'Taiwan Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SRB' => array( + 'name' => _x( 'Serbia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'POST_SERBIA' => _x( 'Posta Serbia', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'TH' => array( - 'name' => _x( 'Thailand', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'ACOMMMERCE' => _x( 'Acommerce', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ALPHAFAST' => _x( 'Alphafast', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CJ_TH' => _x( 'CJ Thailand', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'FASTRACK' => _x( 'FastTrack Thailand', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'KERRY_EXPRESS_TH' => _x( 'Kerry Express Thailand', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'NIM_EXPRESS' => _x( 'NIM Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'NINJAVAN_THAI' => _x( 'Ninjavan Thailand', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SENDIT' => _x( 'SendIt', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'THAILAND_POST' => _x( 'Thailand Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SG' => array( + 'name' => _x( 'Singapore', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'CLOUDWISH_ASIA' => _x( 'Cloudwish Asia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SG_DETRACK' => _x( 'Detrack', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FONSEN' => _x( 'Fonsen Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GRAB_WEBHOOK' => _x( 'Grab courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SIMPLYPOST' => _x( 'J&T Express Singapore', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JANIO' => _x( 'Janio Asia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'IND_JAYONEXPRESS' => _x( 'Jayon Express (JEX)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'JET_SHIP' => _x( 'Jet-Ship Worldwide', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KGMHUB' => _x( 'KGM Hub', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LEGION_EXPRESS' => _x( 'Legion Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NHANS_SOLUTIONS' => _x( 'Nhans Solutions', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NINJAVAN_SG' => _x( 'Ninja van Singapore', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARCELPOST_SG' => _x( 'Parcel Post Singapore', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARKNPARCEL' => _x( 'Park N Parcel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PICKUPP_SGP' => _x( 'PICK UPP (Singapore)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SG_QXPRESS' => _x( 'Qxpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RAIDEREX' => _x( 'RaidereX', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ROADBULL' => _x( 'Red Carpet Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RZYEXPRESS' => _x( 'RZY Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SG_SG_POST' => _x( 'Singapore Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SG_SPEEDPOST' => _x( 'Singapore Speedpost', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TCK_EXPRESS' => _x( 'TCK Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COUREX' => _x( 'Urbanfox', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WMG' => _x( 'WMG Delivery', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZYLLEM' => _x( 'Zyllem', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'TR' => array( - 'name' => _x( 'Turkey', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'PTT_POST' => _x( 'PTT Posta', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SVK' => array( + 'name' => _x( 'Slovakia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'GLS_SLOV' => _x( 'GLS General Logistics Systems Slovakia s.r.o.', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'UA' => array( - 'name' => _x( 'Ukraine', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'NOVA_POSHTA' => _x( 'Nova Poshta', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'NOVA_POSHTA_INT' => _x( 'Nova Poshta International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SVN' => array( + 'name' => _x( 'Slovenia', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'GLS_SLOVEN' => _x( 'GLS Slovenia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POST_SLOVENIA' => _x( 'Post of Slovenia', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'AE' => array( - 'name' => _x( 'United Arab Emirates', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'AXL' => _x( 'AXL Express & Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CONTINENTAL' => _x( 'Continental', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SKYNET_UAE' => _x( 'Skynet Worldwide Express UAE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZA' => array( + 'name' => _x( 'South Africa', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'ZA_COURIERIT' => _x( 'Courier IT', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DAWN_WING' => _x( 'Dawn Wing', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPE_SOUTH_AFRC' => _x( 'DPE South Africa', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INTEXPRESS' => _x( 'Internet Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'COLLIVERY' => _x( 'MDS Collivery Pty (Ltd)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RAM' => _x( 'RAM courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SKYNET_ZA' => _x( 'Skynet World Wide Express South Africa', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SOUTH_AFRICAN_POST_OFFICE' => _x( 'South African Post Office', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZA_SPECIALISED_FREIGHT' => _x( 'Specialised Freight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'THECOURIERGUY' => _x( 'The Courier Guy', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'GB' => array( - 'name' => _x( 'United Kingdom', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'AIRBORNE_EXPRESS_UK' => _x( 'Airborne Express UK', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'AIRSURE' => _x( 'Airsure', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'APC_OVERNIGHT' => _x( 'APC Overnight', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ASENDIA_UK' => _x( 'Asendia UK', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'COLLECTPLUS' => _x( 'CollectPlus', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DELTEC_UK' => _x( 'Deltec UK', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DHL_UK' => _x( 'DHL UK', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DPD_DELISTRACK' => _x( 'DPD Delistrack', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'DPD_UK' => _x( 'DPD UK', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'FASTWAY_UK' => _x( 'Fastway UK', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'HERMESWORLD_UK' => _x( 'HermesWorld', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'INTERLINK' => _x( 'Interlink Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'MYHERMES' => _x( 'MyHermes UK', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'NIGHTLINE_UK' => _x( 'Nightline UK', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'PARCELFORCE' => _x( 'Parcel Force', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ROYAL_MAIL' => _x( 'Royal Mail', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'RPD_2_MAN' => _x( 'RPD2man Deliveries', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'SKYNET_UK' => _x( 'Skynet Worldwide Express UK', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'TNT_UK' => _x( 'TNT UK', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'UK_MAIL' => _x( 'UK Mail', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'YODEL' => _x( 'Yodel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ES' => array( + 'name' => _x( 'Spain', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'ABCUSTOM' => _x( 'AB Custom Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ADERONLINE' => _x( 'Ader couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ASIGNA' => _x( 'ASIGNA courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ESP_ASM' => _x( 'ASM(GLS Spain)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CBL_LOGISTICA' => _x( 'CBL Logistica', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CORREOS_EXPRESS' => _x( 'Correos Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_PARCEL_ES' => _x( 'DHL parcel Spain(www.dhl.com)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_ES' => _x( 'DHL Spain(www.dhl.com)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ECOSCOOTING' => _x( 'ECOSCOOTING', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ESP_ENVIALIA' => _x( 'Envialia', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ENVIALIA_REFERENCE' => _x( 'Envialia Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INTEGRA2_FTP' => _x( 'Integra2', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MRW_FTP' => _x( 'MRW courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ESP_MRW' => _x( 'MRW spain', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NACEX' => _x( 'NACEX', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NACEX_ES' => _x( 'NACEX Spain', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ESP_NACEX' => _x( 'NACEX Spain', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PAACK_WEBHOOK' => _x( 'Paack courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ESP_PACKLINK' => _x( 'Packlink', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ESP_REDUR' => _x( 'Redur Spain', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PRT_INT_SEUR' => _x( 'SEUR International', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PRT_SEUR' => _x( 'SEUR portugal', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SEUR_ES' => _x( 'Seur Spain', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SEUR_SP_API' => _x( 'Spanish Seur API', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPRING_GDS' => _x( 'Spring GDS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SZENDEX' => _x( 'SZENDEX', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_NL' => _x( 'THT Netherland', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TIPSA' => _x( 'TIPSA courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT' => _x( 'TNT Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLOBAL_TNT' => _x( 'TNT global', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TOURLINE' => _x( 'tourline', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VAMOX' => _x( 'VAMOX', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VIA_EXPRESS' => _x( 'Viaxpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZELERIS' => _x( 'Zeleris', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'US' => array( - 'name' => _x( 'United States', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'ABC_PACKAGE' => _x( 'ABC Package Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'AIRBORNE_EXPRESS' => _x( 'Airborne Express', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ASENDIA_US' => _x( 'Asendia USA', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'CPACKET' => _x( 'Cpacket', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ENSENDA' => _x( 'Ensenda USA', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ESTES' => _x( 'Estes', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'FASTWAY_US' => _x( 'Fastway USA', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'GLOBEGISTICS' => _x( 'Globegistics USA', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'INTERNATIONAL_BRIDGE' => _x( 'International Bridge', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'ONTRAC' => _x( 'OnTrac', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'RL_US' => _x( 'RL Carriers', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'RRDONNELLEY' => _x( 'RR Donnelley', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'USPS' => _x( 'USPS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SE' => array( + 'name' => _x( 'Sweden', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'AIRMEE_WEBHOOK' => _x( 'Airmee couriers', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BRING' => _x( 'Bring', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DBSCHENKER_SE' => _x( 'DB Schenker (www.dbschenker.com)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DBSCHENKER_SV' => _x( 'DB Schenker Sweden', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), - 'VN' => array( - 'name' => _x( 'Vietnam', 'Name of carrier country', 'woocommerce-paypal-payments' ), - 'items' => array( - 'KERRY_EXPRESS_VN' => _x( 'Kerry Express Vietnam', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'VIETNAM_POST' => _x( 'Vietnam Post', 'Name of carrier', 'woocommerce-paypal-payments' ), - 'VNPOST_EMS' => _x( 'Vietnam Post EMS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CH' => array( + 'name' => _x( 'Switzerland', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'ASENDIA_HK' => _x( 'Asendia HonKong', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PLANZER' => _x( 'Planzer Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SWISS_POST_FTP' => _x( 'Swiss Post FTP', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VIAEUROPE' => _x( 'ViaEurope', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), ), - ), + 'TW' => array( + 'name' => _x( 'Taiwan', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'CNWANGTONG' => _x( 'cnwangtong', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CTC_EXPRESS' => _x( 'CTC Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DIMERCO' => _x( 'Dimerco Express Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HCT_LOGISTICS' => _x( 'HCT LOGISTICS CO.LTD.', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KERRYTJ' => _x( 'Kerry TJ Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PRESIDENT_TRANS' => _x( 'PRESIDENT TRANSNET CORP', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLOBAL_EXPRESS' => _x( 'Tai Wan Global Business', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + 'TH' => array( + 'name' => _x( 'Thailand', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'ALPHAFAST' => _x( 'Alphafast', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CJ_KR' => _x( 'CJ Korea Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'THA_DYNAMIC_LOGISTICS' => _x( 'Dynamic Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FASTRK_SERV' => _x( 'Fastrak Services', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FLASHEXPRESS' => _x( 'Flash Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NIM_EXPRESS' => _x( 'Nim Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NINJAVAN_THAI' => _x( 'Ninja van Thai', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SENDIT' => _x( 'Sendit', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SKYBOX' => _x( 'SKYBOX', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'THA_THAILAND_POST' => _x( 'Thailand Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + 'TR' => array( + 'name' => _x( 'Turkey', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'ASE' => _x( 'ASE KARGO', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CDEK_TR' => _x( 'CDEK TR', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PTS' => _x( 'PTS courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PTT_POST' => _x( 'PTT Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHIPENTEGRA' => _x( 'ShipEntegra', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YURTICI_KARGO' => _x( 'Yurtici Kargo', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + 'UA' => array( + 'name' => _x( 'Ukraine', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'NOVA_POSHTA_INT' => _x( 'Nova Poshta (International)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NOVA_POSHTA' => _x( 'Nova Poshta (novaposhta.ua)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'POSTA_UKR' => _x( 'UkrPoshta', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + 'AE' => array( + 'name' => _x( 'United Arab Emirates', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'IBEONE' => _x( 'Beone Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MARA_XPRESS' => _x( 'Mara Xpress', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FETCHR_WEBHOOK' => _x( 'Mena 360 (Fetchr)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ONECLICK' => _x( 'One click delivery services', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SKYNET_UAE' => _x( 'SKYNET UAE', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + 'GB' => array( + 'name' => _x( 'United Kingdom', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'AMAZON' => _x( 'Amazon Shipping', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AO_COURIER' => _x( 'AO Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'APC_OVERNIGHT' => _x( 'APC overnight (apc-overnight.com)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'APC_OVERNIGHT_CONNUM' => _x( 'APC Overnight Consignment', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'APG' => _x( 'APG eCommerce Solutions', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ARK_LOGISTICS' => _x( 'ARK Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GB_ARROW' => _x( 'Arrow XL', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ASENDIA_UK' => _x( 'Asendia UK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BH_WORLDWIDE' => _x( 'B&H Worldwide', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BIRDSYSTEM' => _x( 'BirdSystem', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'BLUECARE' => _x( 'Bluecare Express Ltd', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CAE_DELIVERS' => _x( 'CAE Delivers', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'CARIBOU' => _x( 'Caribou', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DAIGLOBALTRACK' => _x( 'DAI Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DELTEC_UK' => _x( 'Deltec Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_REFR' => _x( 'DHl (Reference number)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DHL_UK' => _x( 'dhl UK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DIAMOND_EUROGISTICS' => _x( 'Diamond Eurogistics Limited', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DIRECTPARCELS' => _x( 'Direct Parcels', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DMS_MATRIX' => _x( 'DMSMatrix', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_LOCAL' => _x( 'DPD Local', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DPD_LOCAL_REF' => _x( 'DPD Local reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DX_SFTP' => _x( 'DX (SFTP)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'EU_FLEET_SOLUTIONS' => _x( 'EU Fleet Solutions', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FEDEX_UK' => _x( 'FedEx® UK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'FURDECO' => _x( 'Furdeco', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GBA' => _x( 'GBA Services Ltd', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GEMWORLDWIDE' => _x( 'GEM Worldwide', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HERMES' => _x( 'HermesWorld UK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HOME_DELIVERY_SOLUTIONS' => _x( 'Home Delivery Solutions Ltd', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'INTERPARCEL_UK' => _x( 'Interparcel UK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MYHERMES' => _x( 'MyHermes UK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NATIONAL_SAMEDAY' => _x( 'National Sameday', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GB_NORSK' => _x( 'Norsk Global', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'OCS_WORLDWIDE' => _x( 'OCS WORLDWIDE', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PALLETWAYS' => _x( 'Palletways', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GB_PANTHER' => _x( 'Panther', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PANTHER_REFERENCE' => _x( 'Panther Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARCEL2GO' => _x( 'Parcel2Go', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PARCELINKLOGISTICS' => _x( 'Parcelink Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PLUS_LOG_UK' => _x( 'Plus UK Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RPD2MAN' => _x( 'RPD2man Deliveries', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SKYNET_UK' => _x( 'Skynet UK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'AMAZON_FBA_SWISHIP' => _x( 'Swiship UK', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'THEDELIVERYGROUP' => _x( 'TDG – The Delivery Group', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PALLET_NETWORK' => _x( 'The Pallet Network', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_UK' => _x( 'TNT UK Limited (www.tnt.com)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TNT_UK_REFR' => _x( 'TNT UK Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GB_TUFFNELLS' => _x( 'Tuffnells Parcels Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TUFFNELLS_REFERENCE' => _x( 'Tuffnells Parcels Express- Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UK_UK_MAIL' => _x( 'UK mail (ukmail.com)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WHISTL' => _x( 'Whistl', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WNDIRECT' => _x( 'wnDirect', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UK_XDP' => _x( 'XDP Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'XDP_UK_REFERENCE' => _x( 'XDP Express Reference', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'XPERT_DELIVERY' => _x( 'Xpert Delivery', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UK_YODEL' => _x( 'Yodel (www.yodel.co.uk)', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + 'US' => array( + 'name' => _x( 'United States', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'GIO_EXPRESS' => _x( 'Gio Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLOBALTRANZ' => _x( 'GlobalTranz', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GSI_EXPRESS' => _x( 'GSI EXPRESS', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GSO' => _x( 'GSO (GLS-USA)', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'HIPSHIPPER' => _x( 'Hipshipper', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'GLOBAL_IPARCEL' => _x( 'i-parcel', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'DESCARTES' => _x( 'Innovel courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'US_LASERSHIP' => _x( 'LaserShip', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LONESTAR' => _x( 'Lone Star Overnight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'MAILAMERICAS' => _x( 'MailAmericas', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NEWEGGEXPRESS' => _x( 'Newegg Express', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'US_OLD_DOMINION' => _x( 'Old Dominion Freight Line', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'OSM_WORLDWIDE' => _x( 'OSM Worldwide', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PCFCORP' => _x( 'PCF Final Mile', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PILOT_FREIGHT' => _x( 'Pilot Freight Services', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PITNEY_BOWES' => _x( 'Pitney Bowes', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'PITTOHIO' => _x( 'PITT OHIO', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'QWINTRY' => _x( 'Qwintry Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'RL_US' => _x( 'RL Carriers', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SAIA_FREIGHT' => _x( 'Saia LTL Freight', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SHIPTOR' => _x( 'Shiptor', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SONICTL' => _x( 'Sonic Transportation & Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SEFL' => _x( 'Southeastern Freight Lines', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SPEEDEE' => _x( 'Spee-Dee Delivery', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'SUTTON' => _x( 'Sutton Transport', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TAZMANIAN_FREIGHT' => _x( 'Tazmanian Freight Systems', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TFORCE_FINALMILE' => _x( 'TForce Final Mile', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'LOGISTYX_TRANSGROUP' => _x( 'Transgroup courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'TRUMPCARD' => _x( 'TRUMPCARD LLC', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'USPS' => _x( 'United States Postal Service', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'UPS_MAIL_INNOVATIONS' => _x( 'UPS Mail Innovations', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'USF_REDDAWAY' => _x( 'USF Reddaway', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'USHIP' => _x( 'uShip courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WESTBANK_COURIER' => _x( 'West Bank Courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WESTGATE_GL' => _x( 'Westgate Global', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'WIZMO' => _x( 'Wizmo', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'XPO_LOGISTICS' => _x( 'XPO logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'YAKIT' => _x( 'Yakit courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'US_YRC' => _x( 'YRC courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'ZINC' => _x( 'Zinc courier', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + 'URY' => array( + 'name' => _x( 'Uruguay', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'CORREO_UY' => _x( 'Correo Uruguayo', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + 'VN' => array( + 'name' => _x( 'Vietnam', 'Name of carrier country', 'woocommerce-paypal-payments' ), + 'items' => array( + 'JTEXPRESS_VN' => _x( 'J&T Express Vietnam', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'KERRYTTC_VN' => _x( 'Kerry Express (Vietnam) Co Ltd', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'NTLOGISTICS_VN' => _x( 'Nhat Tin Logistics', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VNM_VIETNAM_POST' => _x( 'Vietnam Post', 'Name of carrier', 'woocommerce-paypal-payments' ), + 'VNM_VIETTELPOST' => _x( 'ViettelPost', 'Name of carrier', 'woocommerce-paypal-payments' ), + ), + ), + ) ); 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 a7ad9a62e..3184061ef 100644 --- a/modules/ppcp-order-tracking/resources/css/order-edit-page.scss +++ b/modules/ppcp-order-tracking/resources/css/order-edit-page.scss @@ -13,8 +13,132 @@ font-weight: bold; } - input,select { + input:not([type="checkbox"]),select { width: 100%; } + + #items-select-container { + display:none + } + + .ppcp-tracking-columns-wrapper { + display: grid; + grid-template-columns: 1fr 2fr; + grid-gap: 60px; + + .ppcp-tracking-column + .ppcp-tracking-column { + border-left: 1px solid #c3c4c7; + padding-left: 20px; + } + } + + .ppcp-shipment { + + border: 1px solid #ccc; + border-radius: 4px; + background: #fff; + margin: 20px 0px; + + .wc-order-item-sku { + font-size: .92em!important; + color: #888; + } + + .ppcp-shipment-header { + + padding: 0px 10px; + cursor: pointer; + background: #f0f0f1; + + h4 { + display: inline-block; + margin: 10px 0px; + } + + button { + float: right; + border: 0; + background: 0 0; + cursor: pointer; + height: 38px; + } + } + + .ppcp-shipment-info { + padding: 0px 10px 20px 10px; + } + + .ppcp-shipment-info.hidden { + display: none; + } + + .select2-container--default .select2-selection__rendered { + font-weight: bold; + } + + .active .ppcp-shipment-header { + background-color: #e0e0e0; + border-bottom: 1px solid #ccc; + } + + select { + width: auto; + } + } + + .ppcp-shipment.closed { + .ppcp-shipment-header .shipment-toggle-indicator .toggle-indicator:before { + content: "\f140"; + } + } + + .ppcp-tracking-loader { + z-index: 1000; + border: none; + margin: 0px; + padding: 0px; + width: 100%; + height: 100%; + top: 0px; + left: 0px; + background: rgb(255, 255, 255); + opacity: 0.6; + cursor: wait; + position: absolute; + display: none; + } + + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } } +.ppcp-remove-tracking-item { + color: #b32d2e; + font-size: 14px; + cursor: pointer; +} + +#side-sortables #ppcp_order-tracking { + .ppcp-tracking-columns-wrapper { + display: block; + } + + .ppcp-tracking-columns-wrapper .ppcp-tracking-column+.ppcp-tracking-column { + border-top: 1px solid #c3c4c7; + padding-left: 0px; + margin-top: 20px; + border-left: none; + } + + .update_shipment { + margin-top: 20px; + } + + .select2-container { + width: 100% !important; + } +} + + diff --git a/modules/ppcp-order-tracking/resources/js/order-edit-page.js b/modules/ppcp-order-tracking/resources/js/order-edit-page.js index 745ca529d..3db9167af 100644 --- a/modules/ppcp-order-tracking/resources/js/order-edit-page.js +++ b/modules/ppcp-order-tracking/resources/js/order-edit-page.js @@ -7,48 +7,161 @@ document.addEventListener( return; } - jQuery(document).on('click', '.submit_tracking_info', function () { - const transactionId = document.querySelector('.ppcp-tracking-transaction_id'); - const trackingNumber = document.querySelector('.ppcp-tracking-tracking_number'); - const status = document.querySelector('.ppcp-tracking-status'); - const carrier = document.querySelector('.ppcp-tracking-carrier'); - const orderId = document.querySelector('.ppcp-order_id'); - const submitButton = document.querySelector('.submit_tracking_info'); + const includeAllItemsCheckbox = document.getElementById('include-all-items'); + const shipmentsWrapper = '#ppcp_order-tracking .ppcp-tracking-column.shipments'; + const transactionId = document.querySelector('.ppcp-tracking-transaction_id'); + const orderId = document.querySelector('.ppcp-tracking-order_id'); + const carrier = document.querySelector('.ppcp-tracking-carrier'); + const carrierNameOther = document.querySelector('.ppcp-tracking-carrier_name_other'); - submitButton.setAttribute('disabled', 'disabled'); - fetch(config.ajax.tracking_info.endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - credentials: 'same-origin', - body: JSON.stringify({ - nonce: config.ajax.tracking_info.nonce, - transaction_id: transactionId ? transactionId.value : null, - tracking_number: trackingNumber ? trackingNumber.value : null, - status: status ? status.value : null, - carrier: carrier ? carrier.value : null, - order_id: orderId ? orderId.value : null, - action: submitButton ? submitButton.dataset.action : null, - }) - }).then(function (res) { - return res.json(); - }).then(function (data) { - if (!data.success) { - jQuery( "" + data.data.message + "" ).insertAfter(submitButton); - setTimeout(()=> jQuery('.tracking-info-message').remove(),3000); - submitButton.removeAttribute('disabled'); - console.error(data); - throw Error(data.data.message); - } + function toggleLineItemsSelectbox() { + const selectContainer = document.getElementById('items-select-container'); + includeAllItemsCheckbox?.addEventListener('change', function(){ + selectContainer.style.display = includeAllItemsCheckbox.checked ? 'none' : 'block'; + }) + } - jQuery( "" + data.data.message + "" ).insertAfter(submitButton); - setTimeout(()=> jQuery('.tracking-info-message').remove(),3000); + function toggleShipment() { + jQuery(document).on('click', '.ppcp-shipment-header', function(event) { + const shipmentContainer = event.target.closest('.ppcp-shipment'); + const shipmentInfo = shipmentContainer.querySelector('.ppcp-shipment-info'); - submitButton.dataset.action = 'update'; - submitButton.textContent = 'update'; - submitButton.removeAttribute('disabled'); + shipmentContainer.classList.toggle('active'); + shipmentContainer.classList.toggle('closed'); + shipmentInfo.classList.toggle('hidden'); }); - }) + } + + function toggleShipmentUpdateButtonDisabled() { + jQuery(document).on('change', '.ppcp-shipment-status', function(event) { + const shipmentSelectbox = event.target; + const shipment = shipmentSelectbox.closest('.ppcp-shipment'); + const updateShipmentButton = shipment.querySelector('.update_shipment'); + const selectedValue = shipmentSelectbox.value; + + updateShipmentButton.classList.remove('button-disabled'); + }); + } + + function toggleLoaderVisibility() { + const loader = document.querySelector('.ppcp-tracking-loader'); + if (loader) { + if (loader.style.display === 'none' || loader.style.display === '') { + loader.style.display = 'block'; + } else { + loader.style.display = 'none'; + } + } + } + + function toggleOtherCarrierName() { + jQuery(carrier).on('change', function() { + const hiddenHtml = carrierNameOther.parentNode; + if (carrier.value === 'OTHER') { + hiddenHtml.classList.remove('hidden'); + } else { + if (!hiddenHtml.classList.contains('hidden')) { + hiddenHtml.classList.add('hidden'); + } + } + }) + } + + function handleAddShipment() { + jQuery(document).on('click', '.submit_tracking_info', function () { + const trackingNumber = document.querySelector('.ppcp-tracking-tracking_number'); + const status = document.querySelector('.ppcp-tracking-status'); + const submitButton = document.querySelector('.submit_tracking_info'); + const items = document.querySelector('.ppcp-tracking-items'); + + let checkedItems = includeAllItemsCheckbox?.checked || !items ? 0 : Array.from(items.selectedOptions).map(option => option.value) + + toggleLoaderVisibility() + fetch(config.ajax.tracking_info.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + credentials: 'same-origin', + body: JSON.stringify({ + nonce: config.ajax.tracking_info.nonce, + transaction_id: transactionId ? transactionId.value : null, + tracking_number: trackingNumber ? trackingNumber.value : null, + status: status ? status.value : null, + carrier: carrier ? carrier.value : null, + carrier_name_other: carrierNameOther ? carrierNameOther.value : null, + order_id: orderId ? orderId.value : null, + items: checkedItems + }) + }).then(function (res) { + return res.json(); + }).then(function (data) { + toggleLoaderVisibility() + + if (!data.success || ! data.data.shipment) { + jQuery( "" + data.data.message + "" ).insertAfter(submitButton); + setTimeout(()=> jQuery('.tracking-info-message').remove(),3000); + submitButton.removeAttribute('disabled'); + console.error(data); + throw Error(data.data.message); + } + + jQuery( "" + data.data.message + "" ).insertAfter(submitButton); + setTimeout(()=> jQuery('.tracking-info-message').remove(),3000); + jQuery(data.data.shipment).appendTo(shipmentsWrapper); + }); + }) + } + + function handleUpdateShipment() { + jQuery(document).on('click', '.update_shipment', function (event) { + const updateShipment = event.target; + const parentElement = updateShipment.parentNode.parentNode; + const shipmentStatus = parentElement.querySelector('.ppcp-shipment-status'); + const shipmentTrackingNumber = parentElement.querySelector('.ppcp-shipment-tacking_number'); + const shipmentCarrier = parentElement.querySelector('.ppcp-shipment-carrier'); + const shipmentCarrierNameOther = parentElement.querySelector('.ppcp-shipment-carrier-other'); + + toggleLoaderVisibility() + fetch(config.ajax.tracking_info.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + credentials: 'same-origin', + body: JSON.stringify({ + nonce: config.ajax.tracking_info.nonce, + transaction_id: transactionId ? transactionId.value : null, + tracking_number: shipmentTrackingNumber ? shipmentTrackingNumber.value : null, + status: shipmentStatus ? shipmentStatus.value : null, + carrier: shipmentCarrier ? shipmentCarrier.value : null, + carrier_name_other: shipmentCarrierNameOther ? shipmentCarrierNameOther.value : null, + order_id: orderId ? orderId.value : null, + action: 'update' + }) + }).then(function (res) { + return res.json(); + }).then(function (data) { + toggleLoaderVisibility() + + if (!data.success) { + jQuery( "" + data.data.message + "" ).insertAfter(updateShipment); + setTimeout(()=> jQuery('.tracking-info-message').remove(),3000); + console.error(data); + throw Error(data.data.message); + } + + jQuery( "" + data.data.message + "" ).insertAfter(updateShipment); + setTimeout(()=> jQuery('.tracking-info-message').remove(),3000); + }); + }) + } + + handleAddShipment(); + handleUpdateShipment(); + toggleLineItemsSelectbox(); + toggleShipment(); + toggleShipmentUpdateButtonDisabled(); + toggleOtherCarrierName(); }, ); diff --git a/modules/ppcp-order-tracking/services.php b/modules/ppcp-order-tracking/services.php index 26b34594c..722edcba4 100644 --- a/modules/ppcp-order-tracking/services.php +++ b/modules/ppcp-order-tracking/services.php @@ -9,9 +9,14 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\OrderTracking; +use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; +use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; +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 { @@ -20,12 +25,18 @@ return array( $container->get( 'ppcp.asset-version' ) ); }, + 'order-tracking.shipment.factory' => static function ( ContainerInterface $container ) : ShipmentFactoryInterface { + return new ShipmentFactory(); + }, 'order-tracking.endpoint.controller' => static function ( ContainerInterface $container ) : OrderTrackingEndpoint { return new OrderTrackingEndpoint( $container->get( 'api.host' ), $container->get( 'api.bearer' ), $container->get( 'woocommerce.logger.woocommerce' ), - $container->get( 'button.request-data' ) + $container->get( 'button.request-data' ), + $container->get( 'order-tracking.shipment.factory' ), + $container->get( 'order-tracking.allowed-shipping-statuses' ), + $container->get( 'order-tracking.is-merchant-country-us' ) ); }, 'order-tracking.module.url' => static function ( ContainerInterface $container ): string { @@ -41,17 +52,21 @@ return array( }, 'order-tracking.meta-box.renderer' => static function ( ContainerInterface $container ): MetaBoxRenderer { return new MetaBoxRenderer( - $container->get( 'order-tracking.endpoint.controller' ), $container->get( 'order-tracking.allowed-shipping-statuses' ), - $container->get( 'order-tracking.available-carriers' ) + $container->get( 'order-tracking.available-carriers' ), + $container->get( 'order-tracking.endpoint.controller' ), + $container->get( 'order-tracking.is-merchant-country-us' ) ); }, 'order-tracking.allowed-shipping-statuses' => static function ( ContainerInterface $container ): array { - return array( - 'SHIPPED' => 'SHIPPED', - 'ON_HOLD' => 'ON_HOLD', - 'DELIVERED' => 'DELIVERED', - 'CANCELLED' => 'CANCELLED', + return (array) apply_filters( + 'woocommerce_paypal_payments_tracking_statuses', + array( + 'SHIPPED' => 'Shipped', + 'ON_HOLD' => 'On Hold', + 'DELIVERED' => 'Delivered', + 'CANCELLED' => 'Cancelled', + ) ); }, 'order-tracking.allowed-carriers' => static function ( ContainerInterface $container ): array { @@ -73,4 +88,35 @@ return array( ), ); }, + 'order-tracking.is-tracking-available' => static function ( ContainerInterface $container ): bool { + try { + $bearer = $container->get( 'api.bearer' ); + assert( $bearer instanceof Bearer ); + + $token = $bearer->bearer(); + return $token->is_tracking_available(); + } catch ( RuntimeException $exception ) { + return false; + } + }, + 'order-tracking.is-module-enabled' => static function ( ContainerInterface $container ): bool { + $order_id = isset( $_GET['post'] ) ? (int) $_GET['post'] : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( empty( $order_id ) ) { + return false; + } + + $meta = get_post_meta( $order_id, PayPalGateway::ORDER_ID_META_KEY, true ); + + if ( empty( $meta ) ) { + return false; + } + + $is_tracking_available = $container->get( 'order-tracking.is-tracking-available' ); + + return $is_tracking_available && apply_filters( 'woocommerce_paypal_payments_shipment_tracking_enabled', true ); + }, + + 'order-tracking.is-merchant-country-us' => static function ( ContainerInterface $container ): bool { + return $container->get( 'api.shop.country' ) === 'US'; + }, ); diff --git a/modules/ppcp-order-tracking/src/Assets/OrderEditPageAssets.php b/modules/ppcp-order-tracking/src/Assets/OrderEditPageAssets.php index cf9fab613..39706832b 100644 --- a/modules/ppcp-order-tracking/src/Assets/OrderEditPageAssets.php +++ b/modules/ppcp-order-tracking/src/Assets/OrderEditPageAssets.php @@ -78,11 +78,13 @@ class OrderEditPageAssets { * @return array a map of script data. */ public function get_script_data(): array { + return array( 'ajax' => array( 'tracking_info' => array( 'endpoint' => \WC_AJAX::get_endpoint( OrderTrackingEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( OrderTrackingEndpoint::nonce() ), + 'url' => admin_url( 'admin-ajax.php' ), ), ), ); diff --git a/modules/ppcp-order-tracking/src/Endpoint/OrderTrackingEndpoint.php b/modules/ppcp-order-tracking/src/Endpoint/OrderTrackingEndpoint.php index 065149288..73728c58b 100644 --- a/modules/ppcp-order-tracking/src/Endpoint/OrderTrackingEndpoint.php +++ b/modules/ppcp-order-tracking/src/Endpoint/OrderTrackingEndpoint.php @@ -11,20 +11,31 @@ namespace WooCommerce\PayPalCommerce\OrderTracking\Endpoint; use Exception; use Psr\Log\LoggerInterface; +use stdClass; use WC_Order; use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData; +use WooCommerce\PayPalCommerce\OrderTracking\OrderTrackingModule; +use WooCommerce\PayPalCommerce\OrderTracking\Shipment\ShipmentFactoryInterface; +use WooCommerce\PayPalCommerce\OrderTracking\Shipment\ShipmentInterface; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait; /** * The OrderTrackingEndpoint. * * @psalm-type SupportedStatuses = 'SHIPPED'|'ON_HOLD'|'DELIVERED'|'CANCELLED' - * @psalm-type TrackingInfo = array{transaction_id: string, status: SupportedStatuses, tracking_number?: string, carrier?: string} - * @psalm-type RequestValues = array{transaction_id: string, status: SupportedStatuses, order_id: int, action: 'create'|'update', tracking_number?: string, carrier?: string} + * @psalm-type TrackingInfo = array{ + * transaction_id: string, + * status: SupportedStatuses, + * tracking_number: string, + * carrier: string, + * items?: list, + * carrier_name_other?: string, + * } * Class OrderTrackingEndpoint */ class OrderTrackingEndpoint { @@ -45,40 +56,70 @@ class OrderTrackingEndpoint { * * @var string */ - private $host; + protected $host; /** * The bearer. * * @var Bearer */ - private $bearer; + protected $bearer; /** * The logger. * * @var LoggerInterface */ - private $logger; + protected $logger; + + /** + * The ShipmentFactory. + * + * @var ShipmentFactoryInterface + */ + protected $shipment_factory; + + /** + * Allowed shipping statuses. + * + * @var string[] + */ + protected $allowed_statuses; + + /** + * Whether new API should be used. + * + * @var bool + */ + protected $should_use_new_api; /** * PartnersEndpoint constructor. * - * @param string $host The host. - * @param Bearer $bearer The bearer. - * @param LoggerInterface $logger The logger. - * @param RequestData $request_data The Request data. + * @param string $host The host. + * @param Bearer $bearer The bearer. + * @param LoggerInterface $logger The logger. + * @param RequestData $request_data The Request data. + * @param ShipmentFactoryInterface $shipment_factory The ShipmentFactory. + * @param string[] $allowed_statuses Allowed shipping statuses. + * @param bool $should_use_new_api Whether new API should be used. */ public function __construct( string $host, Bearer $bearer, LoggerInterface $logger, - RequestData $request_data + RequestData $request_data, + ShipmentFactoryInterface $shipment_factory, + array $allowed_statuses, + bool $should_use_new_api ) { - $this->host = $host; - $this->bearer = $bearer; - $this->logger = $logger; - $this->request_data = $request_data; + $this->host = $host; + $this->bearer = $bearer; + $this->logger = $logger; + $this->request_data = $request_data; + $this->shipment_factory = $shipment_factory; + $this->allowed_statuses = $allowed_statuses; + $this->should_use_new_api = $should_use_new_api; } /** @@ -91,20 +132,30 @@ class OrderTrackingEndpoint { } try { - $data = $this->request_data->read_request( $this->nonce() ); - $action = $data['action']; - $request_body = $this->extract_tracking_information( $data ); - $order_id = (int) $data['order_id']; - $action === 'create' ? $this->add_tracking_information( $request_body, $order_id ) : $this->update_tracking_information( $request_body, $order_id ); + $data = $this->request_data->read_request( $this->nonce() ); + $order_id = (int) $data['order_id']; + $action = $data['action'] ?? ''; - $action_message = $action === 'create' ? 'created' : 'updated'; - $message = sprintf( - // translators: %1$s is the action message (created or updated). - _x( 'successfully %1$s', 'tracking info success message', 'woocommerce-paypal-payments' ), - esc_html( $action_message ) + $shipment = $this->create_shipment( $order_id, $data ); + + $action === 'update' + ? $this->update_tracking_information( $shipment, $order_id ) + : $this->add_tracking_information( $shipment, $order_id ); + + $message = $action === 'update' + ? _x( 'successfully updated', 'tracking info success message', 'woocommerce-paypal-payments' ) + : _x( 'successfully created', 'tracking info success message', 'woocommerce-paypal-payments' ); + + ob_start(); + $shipment->render( $this->allowed_statuses ); + $shipment_html = ob_get_clean(); + + wp_send_json_success( + array( + 'message' => $message, + 'shipment' => $shipment_html, + ) ); - - wp_send_json_success( array( 'message' => $message ) ); } catch ( Exception $error ) { wp_send_json_error( array( 'message' => $error->getMessage() ), 500 ); } @@ -113,99 +164,106 @@ class OrderTrackingEndpoint { /** * Creates the tracking information of a given order with the given data. * - * @param array $data The tracking information to add. - * @psalm-param TrackingInfo $data - * @param int $order_id The order ID. - * @throws RuntimeException If problem creating. + * @param ShipmentInterface $shipment The shipment. + * @param int $order_id The order ID. + * + * @throws RuntimeException If problem adding. */ - public function add_tracking_information( array $data, int $order_id ) : void { - $url = trailingslashit( $this->host ) . 'v1/shipping/trackers-batch'; + public function add_tracking_information( ShipmentInterface $shipment, int $order_id ) : void { + $wc_order = wc_get_order( $order_id ); + if ( ! $wc_order instanceof WC_Order ) { + return; + } - $body = array( - 'trackers' => array( (array) apply_filters( 'woocommerce_paypal_payments_tracking_data_before_sending', $data, $order_id ) ), - ); + $shipment_request_data = $this->generate_request_data( $wc_order, $shipment ); - $args = array( - 'method' => 'POST', - 'headers' => $this->request_headers(), - 'body' => wp_json_encode( $body ), - ); + $url = $shipment_request_data['url'] ?? ''; + $args = $shipment_request_data['args'] ?? array(); - do_action( 'woocommerce_paypal_payments_before_tracking_is_added', $order_id, $data ); + if ( ! $url || empty( $args ) ) { + $this->throw_runtime_exception( $shipment_request_data, 'create' ); + } + + do_action( 'woocommerce_paypal_payments_before_tracking_is_added', $order_id, $shipment_request_data ); $response = $this->request( $url, $args ); if ( is_wp_error( $response ) ) { - $error = new RuntimeException( - 'Could not create order tracking information.' + $args = array( + 'args' => $args, + 'response' => $response, ); - $this->logger->log( - 'warning', - $error->getMessage(), - array( - 'args' => $args, - 'response' => $response, - ) - ); - throw $error; + $this->throw_runtime_exception( $args, 'create' ); } - /** - * Need to ignore Method WP_Error::offsetGet does not exist - * - * @psalm-suppress UndefinedMethod - */ - $json = json_decode( $response['body'] ); $status_code = (int) wp_remote_retrieve_response_code( $response ); - if ( 200 !== $status_code ) { - $error = new PayPalApiException( - $json, - $status_code - ); - $this->logger->log( - 'warning', - sprintf( - 'Failed to create order tracking information. PayPal API response: %1$s', - $error->getMessage() - ), - array( - 'args' => $args, - 'response' => $response, - ) - ); - throw $error; + if ( 201 !== $status_code && ! is_wp_error( $response ) ) { + $this->throw_paypal_api_exception( $status_code, $args, $response, 'create' ); } - $wc_order = wc_get_order( $order_id ); - if ( is_a( $wc_order, WC_Order::class ) ) { - $wc_order->update_meta_data( '_ppcp_paypal_tracking_number', $data['tracking_number'] ?? '' ); - $wc_order->save(); - } + $this->save_tracking_metadata( $wc_order, $shipment->tracking_number(), array_keys( $shipment->line_items() ) ); do_action( 'woocommerce_paypal_payments_after_tracking_is_added', $order_id, $response ); } /** - * Gets the tracking information of a given order. + * Updates the tracking information of a given order with the given shipment. * - * @param int $wc_order_id The order ID. - * @return array|null The tracking information. - * @psalm-return TrackingInfo|null - * @throws RuntimeException If problem getting. + * @param ShipmentInterface $shipment The shipment. + * @param int $order_id The order ID. + * + * @throws RuntimeException If problem updating. */ - public function get_tracking_information( int $wc_order_id ) : ?array { - $wc_order = wc_get_order( $wc_order_id ); - if ( ! is_a( $wc_order, WC_Order::class ) ) { - throw new RuntimeException( 'wrong order ID' ); + public function update_tracking_information( ShipmentInterface $shipment, int $order_id ) : void { + $host = trailingslashit( $this->host ); + $tracker_id = $this->find_tracker_id( $shipment->transaction_id(), $shipment->tracking_number() ); + $url = "{$host}v1/shipping/trackers/{$tracker_id}"; + $shipment_data = $shipment->to_array(); + + $args = array( + 'method' => 'PUT', + 'headers' => $this->request_headers(), + 'body' => wp_json_encode( (array) apply_filters( 'woocommerce_paypal_payments_tracking_data_before_update', $shipment_data, $order_id ) ), + ); + + do_action( 'woocommerce_paypal_payments_before_tracking_is_updated', $order_id, $shipment_data ); + + $response = $this->request( $url, $args ); + + if ( is_wp_error( $response ) ) { + $args = array( + 'args' => $args, + 'response' => $response, + ); + $this->throw_runtime_exception( $args, 'update' ); } - if ( ! $wc_order->meta_exists( '_ppcp_paypal_tracking_number' ) ) { + $status_code = (int) wp_remote_retrieve_response_code( $response ); + if ( 204 !== $status_code && ! is_wp_error( $response ) ) { + $this->throw_paypal_api_exception( $status_code, $args, $response, 'update' ); + } + + do_action( 'woocommerce_paypal_payments_after_tracking_is_updated', $order_id, $response ); + } + + /** + * Gets the tracking information of a given order. + * + * @param int $wc_order_id The order ID. + * @param string $tracking_number The tracking number. + * + * @return ShipmentInterface|null The tracking information. + * @throws RuntimeException If problem getting. + */ + public function get_tracking_information( int $wc_order_id, string $tracking_number ) : ?ShipmentInterface { + $wc_order = wc_get_order( $wc_order_id ); + if ( ! $wc_order instanceof WC_Order ) { return null; } - $transaction_id = $wc_order->get_transaction_id(); - $tracking_number = $wc_order->get_meta( '_ppcp_paypal_tracking_number', true ); - $url = trailingslashit( $this->host ) . 'v1/shipping/trackers/' . $this->find_tracker_id( $transaction_id, $tracking_number ); + $host = trailingslashit( $this->host ); + $tracker_id = $this->find_tracker_id( $wc_order->get_transaction_id(), $tracking_number ); + $url = "{$host}v1/shipping/trackers/{$tracker_id}"; $args = array( 'method' => 'GET', @@ -215,18 +273,11 @@ class OrderTrackingEndpoint { $response = $this->request( $url, $args ); if ( is_wp_error( $response ) ) { - $error = new RuntimeException( - 'Could not fetch the tracking information.' + $args = array( + 'args' => $args, + 'response' => $response, ); - $this->logger->log( - 'warning', - $error->getMessage(), - array( - 'args' => $args, - 'response' => $response, - ) - ); - throw $error; + $this->throw_runtime_exception( $args, 'fetch' ); } /** @@ -241,46 +292,39 @@ class OrderTrackingEndpoint { return null; } - return $this->extract_tracking_information( (array) $data ); + return $this->create_shipment( $wc_order_id, (array) $data ); } /** - * Updates the tracking information of a given order with the given data. + * Gets the list of shipments of a given order. * - * @param array $data The tracking information to update. - * @psalm-param TrackingInfo $data - * @param int $order_id The order ID. - * @throws RuntimeException If problem updating. + * @param int $wc_order_id The order ID. + * @return ShipmentInterface[] The list of shipments. + * @throws RuntimeException If problem getting. */ - public function update_tracking_information( array $data, int $order_id ) : void { - $tracking_info = $this->get_tracking_information( $order_id ); - $transaction_id = $tracking_info['transaction_id'] ?? ''; - $tracking_number = $tracking_info['tracking_number'] ?? ''; - $url = trailingslashit( $this->host ) . 'v1/shipping/trackers/' . $this->find_tracker_id( $transaction_id, $tracking_number ); + public function list_tracking_information( int $wc_order_id ) : ?array { + $wc_order = wc_get_order( $wc_order_id ); + if ( ! $wc_order instanceof WC_Order ) { + return array(); + } + + $host = trailingslashit( $this->host ); + $transaction_id = $wc_order->get_transaction_id(); + $url = "{$host}v1/shipping/trackers?transaction_id={$transaction_id}"; $args = array( - 'method' => 'PUT', + 'method' => 'GET', 'headers' => $this->request_headers(), - 'body' => wp_json_encode( (array) apply_filters( 'woocommerce_paypal_payments_tracking_data_before_update', $data, $order_id ) ), ); - do_action( 'woocommerce_paypal_payments_before_tracking_is_updated', $order_id, $data ); - $response = $this->request( $url, $args ); if ( is_wp_error( $response ) ) { - $error = new RuntimeException( - 'Could not update order tracking information.' + $args = array( + 'args' => $args, + 'response' => $response, ); - $this->logger->log( - 'warning', - $error->getMessage(), - array( - 'args' => $args, - 'response' => $response, - ) - ); - throw $error; + $this->throw_runtime_exception( $args, 'fetch' ); } /** @@ -288,34 +332,20 @@ class OrderTrackingEndpoint { * * @psalm-suppress UndefinedMethod */ - $json = json_decode( $response['body'] ); + $data = json_decode( $response['body'] ); $status_code = (int) wp_remote_retrieve_response_code( $response ); - if ( 204 !== $status_code ) { - $error = new PayPalApiException( - $json, - $status_code - ); - $this->logger->log( - 'warning', - sprintf( - 'Failed to update the order tracking information. PayPal API response: %1$s', - $error->getMessage() - ), - array( - 'args' => $args, - 'response' => $response, - ) - ); - throw $error; + + if ( 200 !== $status_code ) { + return null; } - $wc_order = wc_get_order( $order_id ); - if ( is_a( $wc_order, WC_Order::class ) ) { - $wc_order->update_meta_data( '_ppcp_paypal_tracking_number', $data['tracking_number'] ?? '' ); - $wc_order->save(); + $shipments = array(); + + foreach ( $data->trackers as $shipment ) { + $shipments[] = $this->create_shipment( $wc_order_id, (array) $shipment ); } - do_action( 'woocommerce_paypal_payments_after_tracking_is_updated', $order_id, $response ); + return $shipments; } /** @@ -328,33 +358,72 @@ class OrderTrackingEndpoint { } /** - * Extracts the needed tracking information from given data. + * Creates the shipment based on requested data. * + * @param int $wc_order_id The WC order ID. * @param array $data The request data map. - * @psalm-param RequestValues $data - * @return array A map of tracking information keys to values. - * @psalm-return TrackingInfo - * @throws RuntimeException If problem extracting. + * @psalm-param TrackingInfo $data + * + * @return ShipmentInterface The shipment. + * @throws RuntimeException If problem creating. */ - protected function extract_tracking_information( array $data ): array { - if ( empty( $data['transaction_id'] ) || empty( $data['status'] ) ) { - $this->logger->log( 'warning', 'Missing transaction_id or status.' ); - throw new RuntimeException( 'Missing transaction_id or status.' ); - } + protected function create_shipment( int $wc_order_id, array $data ): ShipmentInterface { + $carrier = $data['carrier'] ?? ''; $tracking_info = array( - 'transaction_id' => $data['transaction_id'], - 'status' => $data['status'], + 'transaction_id' => $data['transaction_id'] ?? '', + 'status' => $data['status'] ?? '', + 'tracking_number' => $data['tracking_number'] ?? '', + 'carrier' => $carrier, ); - if ( ! empty( $data['tracking_number'] ) ) { - $tracking_info['tracking_number'] = $data['tracking_number']; + if ( ! empty( $data['items'] ) ) { + $tracking_info['items'] = array_map( 'intval', $data['items'] ); } - if ( ! empty( $data['carrier'] ) ) { - $tracking_info['carrier'] = $data['carrier']; + if ( $carrier === 'OTHER' ) { + $tracking_info['carrier_name_other'] = $data['carrier_name_other'] ?? ''; } - return $tracking_info; + + $this->validate_tracking_info( $tracking_info ); + + return $this->shipment_factory->create_shipment( + $wc_order_id, + $tracking_info['transaction_id'], + $tracking_info['tracking_number'], + $tracking_info['status'], + $tracking_info['carrier'], + $tracking_info['carrier_name_other'] ?? '', + $tracking_info['items'] ?? array() + ); + } + + /** + * Validates the requested tracking info. + * + * @param array $tracking_info A map of tracking information keys to values. + * @return void + * @throws RuntimeException If validation failed. + */ + protected function validate_tracking_info( array $tracking_info ): void { + $error_message = __( 'Missing required information:', 'woocommerce-paypal-payments' ); + $empty_keys = array(); + + foreach ( $tracking_info as $key => $value ) { + if ( ! empty( $value ) ) { + continue; + } + + $empty_keys[] = $key; + } + + if ( empty( $empty_keys ) ) { + return; + } + + $error_message .= implode( ' ,', $empty_keys ); + + throw new RuntimeException( $error_message ); } /** @@ -379,4 +448,111 @@ class OrderTrackingEndpoint { protected function find_tracker_id( string $transaction_id, string $tracking_number ): string { return ! empty( $tracking_number ) ? "{$transaction_id}-{$tracking_number}" : "{$transaction_id}-NOTRACKER"; } + + /** + * Saves the tracking metadata for given line items. + * + * @param WC_Order $wc_order The WooCommerce order. + * @param string $tracking_number The tracking number. + * @param int[] $line_items The list of shipment line items. + * @return void + */ + protected function save_tracking_metadata( WC_Order $wc_order, string $tracking_number, array $line_items ): void { + $tracking_meta = $wc_order->get_meta( OrderTrackingModule::PPCP_TRACKING_INFO_META_NAME ); + + if ( ! is_array( $tracking_meta ) ) { + $tracking_meta = array(); + } + + foreach ( $line_items as $item ) { + $tracking_meta[ $tracking_number ][] = $item; + } + + $wc_order->update_meta_data( OrderTrackingModule::PPCP_TRACKING_INFO_META_NAME, $tracking_meta ); + $wc_order->save(); + } + + /** + * Generates the request data. + * + * @param WC_Order $wc_order The WC order. + * @param ShipmentInterface $shipment The shipment. + * @return array + */ + protected function generate_request_data( WC_Order $wc_order, ShipmentInterface $shipment ): array { + $paypal_order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY ); + $host = trailingslashit( $this->host ); + $shipment_data = $shipment->to_array(); + + $old_api_data = $shipment_data; + unset( $old_api_data['items'] ); + $request_shipment_data = array( 'trackers' => array( $old_api_data ) ); + + if ( $this->should_use_new_api ) { + unset( $shipment_data['transaction_id'] ); + $shipment_data['capture_id'] = $shipment->transaction_id(); + $request_shipment_data = $shipment_data; + } + + $url = $this->should_use_new_api ? "{$host}v2/checkout/orders/{$paypal_order_id}/track" : "{$host}v1/shipping/trackers"; + $args = array( + 'method' => 'POST', + 'headers' => $this->request_headers(), + 'body' => wp_json_encode( (array) apply_filters( 'woocommerce_paypal_payments_tracking_data_before_sending', $request_shipment_data, $wc_order->get_id() ) ), + ); + + return array( + 'url' => $url, + 'args' => $args, + ); + } + + /** + * Throws PayPal APi exception and logs the error message with given arguments. + * + * @param int $status_code The response status code. + * @param array $args The arguments. + * @param array $response The request response. + * @param string $message_part The part of the message. + * @return void + * + * @throws PayPalApiException PayPal APi exception. + */ + protected function throw_paypal_api_exception( int $status_code, array $args, array $response, string $message_part ): void { + $error = new PayPalApiException( + json_decode( $response['body'] ), + $status_code + ); + $this->logger->log( + 'warning', + sprintf( + "Failed to {$message_part} order tracking information. PayPal API response: %s", + $error->getMessage() + ), + array( + 'args' => $args, + 'response' => $response, + ) + ); + throw $error; + } + + /** + * Throws the exception && logs the error message with given arguments. + * + * @param array $args The arguments. + * @param string $message_part The part of the message. + * @return void + * + * @throws RuntimeException The exception. + */ + protected function throw_runtime_exception( array $args, string $message_part ): void { + $error = new RuntimeException( "Could not {$message_part} the order tracking information." ); + $this->logger->log( + 'warning', + $error->getMessage(), + $args + ); + throw $error; + } } diff --git a/modules/ppcp-order-tracking/src/MetaBoxRenderer.php b/modules/ppcp-order-tracking/src/MetaBoxRenderer.php index b04f0cff4..d14fa8cc6 100644 --- a/modules/ppcp-order-tracking/src/MetaBoxRenderer.php +++ b/modules/ppcp-order-tracking/src/MetaBoxRenderer.php @@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\OrderTracking; use WC_Order; use WooCommerce\PayPalCommerce\OrderTracking\Endpoint\OrderTrackingEndpoint; +use WooCommerce\PayPalCommerce\OrderTracking\Shipment\ShipmentInterface; use WP_Post; /** @@ -24,15 +25,6 @@ use WP_Post; */ class MetaBoxRenderer { - public const NAME_PREFIX = 'ppcp-tracking'; - - /** - * The OrderTrackingEndpoint. - * - * @var OrderTrackingEndpoint - */ - protected $order_tracking_endpoint; - /** * Allowed shipping statuses. * @@ -48,85 +40,136 @@ class MetaBoxRenderer { */ protected $carriers; + /** + * The order tracking endpoint. + * + * @var OrderTrackingEndpoint + */ + protected $order_tracking_endpoint; + + /** + * Whether new API should be used. + * + * @var bool + */ + protected $should_use_new_api; + /** * MetaBoxRenderer constructor. * - * @param OrderTrackingEndpoint $order_tracking_endpoint The OrderTrackingEndpoint. * @param string[] $allowed_statuses Allowed shipping statuses. * @param array $carriers Available shipping carriers. * @psalm-param Carriers $carriers + * @param OrderTrackingEndpoint $order_tracking_endpoint The order tracking endpoint. + * @param bool $should_use_new_api Whether new API should be used. */ public function __construct( - OrderTrackingEndpoint $order_tracking_endpoint, array $allowed_statuses, - array $carriers + array $carriers, + OrderTrackingEndpoint $order_tracking_endpoint, + bool $should_use_new_api ) { - $this->order_tracking_endpoint = $order_tracking_endpoint; $this->allowed_statuses = $allowed_statuses; $this->carriers = $carriers; + $this->order_tracking_endpoint = $order_tracking_endpoint; + $this->should_use_new_api = $should_use_new_api; } /** * Renders the order tracking MetaBox. * * @param mixed $post_or_order_object Either WP_Post or WC_Order when COT is data source. - * - * @return void */ public function render( $post_or_order_object ): void { $wc_order = ( $post_or_order_object instanceof WP_Post ) ? wc_get_order( $post_or_order_object->ID ) : $post_or_order_object; - if ( ! is_a( $wc_order, WC_Order::class ) ) { + if ( ! $wc_order instanceof WC_Order ) { return; } - $tracking_info = $this->order_tracking_endpoint->get_tracking_information( $wc_order->get_id() ); + $transaction_id = $wc_order->get_transaction_id() ?: ''; + $order_items = $wc_order->get_items(); + $order_item_count = ! empty( $order_items ) ? count( $order_items ) : 0; - $transaction_id = $tracking_info['transaction_id'] ?? $wc_order->get_transaction_id() ?: ''; - $tracking_number = $tracking_info['tracking_number'] ?? ''; - $status_value = $tracking_info['status'] ?? 'SHIPPED'; - $carrier_value = $tracking_info['carrier'] ?? ''; - - $carriers = (array) apply_filters( 'woocommerce_paypal_payments_tracking_carriers', $this->carriers, $wc_order->get_id() ); - $statuses = (array) apply_filters( 'woocommerce_paypal_payments_tracking_statuses', $this->allowed_statuses, $wc_order->get_id() ); - $tracking_number = (string) apply_filters( 'woocommerce_paypal_payments_tracking_number', $tracking_number, $wc_order->get_id() ); - - $action = ! $tracking_info ? 'create' : 'update'; + /** + * The shipments + * + * @var ShipmentInterface[] $shipments + */ + $shipments = $this->order_tracking_endpoint->list_tracking_information( $wc_order->get_id() ) ?? array(); ?> -

- -

-

- -

-

- - -

-

- - +

+ 1 && $this->should_use_new_api ) : ?> +

+ + +

+ + +
+

+ +

+ + +

+

+ + -

- -

-

+ +

+

+ + +

+ + +

+ +
+

+ render( $this->allowed_statuses ); + } + ?> + +

+ +
+
+ get( 'wcgateway.settings' ); - assert( $settings instanceof Settings ); - - $pui_helper = $c->get( 'wcgateway.pay-upon-invoice-helper' ); - assert( $pui_helper instanceof PayUponInvoiceHelper ); - - if ( $pui_helper->is_pui_gateway_enabled() ) { - $settings->set( 'tracking_enabled', true ); - $settings->persist(); - } - - $tracking_enabled = $settings->has( 'tracking_enabled' ) && $settings->get( 'tracking_enabled' ); - if ( ! $tracking_enabled ) { - return; - } + $tracking_enabled = $c->get( 'order-tracking.is-module-enabled' ); $endpoint = $c->get( 'order-tracking.endpoint.controller' ); assert( $endpoint instanceof OrderTrackingEndpoint ); + add_action( 'wc_ajax_' . OrderTrackingEndpoint::ENDPOINT, array( $endpoint, 'handle_request' ) ); + + if ( ! $tracking_enabled ) { + return; + } + + $asset_loader = $c->get( 'order-tracking.assets' ); + assert( $asset_loader instanceof OrderEditPageAssets ); + $logger = $c->get( 'woocommerce.logger.woocommerce' ); assert( $logger instanceof LoggerInterface ); - add_action( - 'admin_enqueue_scripts', - /** - * Param types removed to avoid third-party issues. - * - * @psalm-suppress MissingClosureParamType - */ - function ( $hook ) use ( $c ): void { - if ( $hook !== 'post.php' || ! $this->is_paypal_order_edit_page() ) { - return; - } - - $asset_loader = $c->get( 'order-tracking.assets' ); - assert( $asset_loader instanceof OrderEditPageAssets ); - - $asset_loader->register(); - $asset_loader->enqueue(); - } - ); - - add_action( - 'wc_ajax_' . OrderTrackingEndpoint::ENDPOINT, - array( $endpoint, 'handle_request' ) - ); + add_action( 'init', array( $asset_loader, 'register' ) ); + add_action( 'admin_enqueue_scripts', array( $asset_loader, 'enqueue' ) ); + $meta_box_renderer = $c->get( 'order-tracking.meta-box.renderer' ); add_action( 'add_meta_boxes', - /** - * Param types removed to avoid third-party issues. - * - * @psalm-suppress MissingClosureParamType - */ - function( $post_type ) use ( $c ) { + static function() use ( $meta_box_renderer ) { /** * Class and function exist in WooCommerce. * @@ -112,54 +81,17 @@ class OrderTrackingModule implements ModuleInterface { $screen = class_exists( CustomOrdersTableController::class ) && wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ? wc_get_page_screen_id( 'shop-order' ) : 'shop_order'; - if ( $post_type !== $screen || ! $this->is_paypal_order_edit_page() ) { - return; - } - $meta_box_renderer = $c->get( 'order-tracking.meta-box.renderer' ); add_meta_box( 'ppcp_order-tracking', - __( 'Tracking Information', 'woocommerce-paypal-payments' ), + __( 'PayPal Shipment Tracking', 'woocommerce-paypal-payments' ), array( $meta_box_renderer, 'render' ), $screen, - 'side' + 'normal' ); }, 10, - 1 - ); - - add_action( - 'woocommerce_order_status_completed', - static function( int $order_id ) use ( $endpoint, $logger ) { - $tracking_information = $endpoint->get_tracking_information( $order_id ); - - if ( $tracking_information ) { - return; - } - - $wc_order = wc_get_order( $order_id ); - if ( ! is_a( $wc_order, WC_Order::class ) ) { - return; - } - - $transaction_id = $wc_order->get_transaction_id(); - if ( empty( $transaction_id ) ) { - return; - } - - $tracking_data = array( - 'transaction_id' => $transaction_id, - 'status' => 'SHIPPED', - ); - - try { - $endpoint->add_tracking_information( $tracking_data, $order_id ); - } catch ( Exception $exception ) { - $logger->error( "Couldn't create tracking information: " . $exception->getMessage() ); - throw $exception; - } - } + 2 ); } } diff --git a/modules/ppcp-order-tracking/src/Shipment/Shipment.php b/modules/ppcp-order-tracking/src/Shipment/Shipment.php new file mode 100644 index 000000000..361989c72 --- /dev/null +++ b/modules/ppcp-order-tracking/src/Shipment/Shipment.php @@ -0,0 +1,285 @@ +tracking_number = $tracking_number; + $this->status = $status; + $this->carrier = $carrier; + $this->carrier_name_other = $carrier_name_other; + $this->line_items = $line_items; + $this->transaction_id = $transaction_id; + $this->wc_order_id = $wc_order_id; + } + + /** + * {@inheritDoc} + */ + public function transaction_id(): string { + return $this->transaction_id; + } + + /** + * {@inheritDoc} + */ + public function tracking_number(): string { + return $this->tracking_number; + } + + /** + * {@inheritDoc} + */ + public function status(): string { + return $this->status; + } + + /** + * {@inheritDoc} + */ + public function carrier(): string { + return $this->carrier; + } + + /** + * {@inheritDoc} + */ + public function carrier_name_other(): string { + return $this->carrier_name_other; + } + + /** + * {@inheritDoc} + */ + public function line_items(): array { + $wc_order = wc_get_order( $this->wc_order_id ); + if ( ! $wc_order instanceof WC_Order ) { + return array(); + } + + $wc_order_items = $wc_order->get_items(); + $tracking_meta = $wc_order->get_meta( OrderTrackingModule::PPCP_TRACKING_INFO_META_NAME ); + $saved_line_items = $tracking_meta[ $this->tracking_number() ] ?? array(); + $line_items = $this->line_items ?: $saved_line_items; + + $tracking_items = array(); + foreach ( $wc_order_items as $item ) { + assert( $item instanceof WC_Order_Item_Product ); + if ( ! empty( $line_items ) && ! in_array( $item->get_id(), $line_items, true ) ) { + continue; + } + + $product = $item->get_product(); + $currency = $wc_order->get_currency(); + $quantity = (int) $item->get_quantity(); + $price_without_tax = (float) $wc_order->get_item_subtotal( $item, false ); + $price_without_tax_rounded = round( $price_without_tax, 2 ); + + $ppcp_order_item = new Item( + mb_substr( $item->get_name(), 0, 127 ), + new Money( $price_without_tax_rounded, $currency ), + $quantity, + $product instanceof WC_Product ? $this->prepare_description( $product->get_description() ) : '', + null, + $product instanceof WC_Product ? $product->get_sku() : '', + ( $product instanceof WC_Product && $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS + ); + + $tracking_items[ $item->get_id() ] = $ppcp_order_item->to_array(); + } + + return $tracking_items; + } + + /** + * {@inheritDoc} + */ + public function render( array $allowed_statuses ): void { + $carrier = $this->carrier(); + $tracking_number = $this->tracking_number(); + $carrier_name_other = $this->carrier_name_other(); + ?> +
+
+

+ +
+ +
+ $this->transaction_id(), + 'tracking_number' => $this->tracking_number(), + 'status' => $this->status(), + 'carrier' => $this->carrier(), + 'items' => array_values( $this->line_items() ), + ); + + if ( ! empty( $this->carrier_name_other() ) ) { + $shipment['carrier_name_other'] = $this->carrier_name_other(); + } + + return $shipment; + } + + /** + * Cleanups the description and prepares it for sending to PayPal. + * + * @param string $description Item description. + * @return string + */ + protected function prepare_description( string $description ): string { + $description = strip_shortcodes( wp_strip_all_tags( $description ) ); + return substr( $description, 0, 127 ) ?: ''; + } + + /** + * Renders the shipment line items info. + * + * @return void + */ + protected function render_shipment_line_item_info(): void { + $line_items = $this->line_items(); + if ( empty( $line_items ) ) { + return; + } + + $format = '

%1$s %2$s

'; + $order_items_info = array(); + + foreach ( $this->line_items() as $shipment_line_item ) { + $sku = $shipment_line_item['sku'] ?? ''; + $name = $shipment_line_item['name'] ?? ''; + + $sku_markup = sprintf( + '#%1$s', + esc_html( $sku ) + ); + + $order_items_info_markup = sprintf( + '%1$s%2$s', + esc_html( $name ), + $sku ? $sku_markup : '' + ); + + $order_items_info[] = $order_items_info_markup; + } + + printf( + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + $format, + esc_html__( 'Shipped Products:', 'woocommerce-paypal-payments' ), + wp_kses_post( implode( ', ', $order_items_info ) ) + ); + } +} diff --git a/modules/ppcp-order-tracking/src/Shipment/ShipmentFactory.php b/modules/ppcp-order-tracking/src/Shipment/ShipmentFactory.php new file mode 100644 index 000000000..cddae0b2b --- /dev/null +++ b/modules/ppcp-order-tracking/src/Shipment/ShipmentFactory.php @@ -0,0 +1,31 @@ +, + * carrier_name_other?: string, + * } + */ +interface ShipmentInterface { + + /** + * The transaction ID. + * + * @return string + */ + public function transaction_id(): string; + + /** + * The tracking number. + * + * @return string + */ + public function tracking_number(): string; + + /** + * The shipment status. + * + * @return string + */ + public function status(): string; + + /** + * The shipment carrier. + * + * @return string + */ + public function carrier(): string; + + /** + * The shipment carrier name for "OTHER". + * + * @return string + */ + public function carrier_name_other(): string; + + /** + * The list of shipment line items. + * + * @return array> The map of shipment line item ID to line item map. + * @psalm-return array + */ + public function line_items(): array; + + /** + * Renders the shipment. + * + * @param string[] $allowed_statuses Allowed shipping statuses. + * + * @return void + */ + public function render( array $allowed_statuses ): void; + + /** + * Returns the object as array. + * + * @return array The map of shipment object. + * @psalm-return shipmentMap + */ + public function to_array(): array; +} diff --git a/modules/ppcp-status-report/src/StatusReportModule.php b/modules/ppcp-status-report/src/StatusReportModule.php index 636bb35c8..09f67618e 100644 --- a/modules/ppcp-status-report/src/StatusReportModule.php +++ b/modules/ppcp-status-report/src/StatusReportModule.php @@ -78,8 +78,6 @@ class StatusReportModule implements ModuleInterface { $had_ppec_plugin = PPECHelper::is_plugin_configured(); - $is_tracking_available = $c->get( 'order-tracking.is-tracking-available' ); - $items = array( array( 'label' => esc_html__( 'Onboarded', 'woocommerce-paypal-payments' ), @@ -165,12 +163,6 @@ class StatusReportModule implements ModuleInterface { $had_ppec_plugin ), ), - array( - 'label' => esc_html__( 'Tracking enabled', 'woocommerce-paypal-payments' ), - 'exported_label' => 'Tracking enabled', - 'description' => esc_html__( 'Whether tracking is enabled on PayPal account or not.', 'woocommerce-paypal-payments' ), - 'value' => $this->bool_to_html( $is_tracking_available ), - ), ); // For now only show this status if PPCP_FLAG_SUBSCRIPTIONS_API is true. diff --git a/modules/ppcp-subscription/src/RenewalHandler.php b/modules/ppcp-subscription/src/RenewalHandler.php index d4b3943f9..f4a9a6cbd 100644 --- a/modules/ppcp-subscription/src/RenewalHandler.php +++ b/modules/ppcp-subscription/src/RenewalHandler.php @@ -241,7 +241,7 @@ class RenewalHandler { * @param \WC_Customer $customer The customer. * @param \WC_Order $wc_order The current WooCommerce order we want to process. * - * @return PaymentToken|null + * @return PaymentToken|null|false */ private function get_token_for_customer( \WC_Customer $customer, \WC_Order $wc_order ) { /** diff --git a/modules/ppcp-uninstall/services.php b/modules/ppcp-uninstall/services.php index 6f7cda722..ca67dc71b 100644 --- a/modules/ppcp-uninstall/services.php +++ b/modules/ppcp-uninstall/services.php @@ -45,6 +45,12 @@ return array( ); }, + 'uninstall.ppcp-all-action-names' => function( ContainerInterface $container ) : array { + return array( + 'woocommerce_paypal_payments_uninstall', + ); + }, + 'uninstall.clear-db-endpoint' => function( ContainerInterface $container ) : string { return 'ppcp-clear-db'; }, diff --git a/modules/ppcp-uninstall/src/ClearDatabase.php b/modules/ppcp-uninstall/src/ClearDatabase.php index fc09519a5..ed5d73bb6 100644 --- a/modules/ppcp-uninstall/src/ClearDatabase.php +++ b/modules/ppcp-uninstall/src/ClearDatabase.php @@ -31,4 +31,13 @@ class ClearDatabase implements ClearDatabaseInterface { as_unschedule_action( $action_name ); } } + + /** + * {@inheritDoc} + */ + public function clear_actions( array $action_names ): void { + foreach ( $action_names as $action_name ) { + do_action( $action_name ); + } + } } diff --git a/modules/ppcp-uninstall/src/ClearDatabaseInterface.php b/modules/ppcp-uninstall/src/ClearDatabaseInterface.php index 6d6aedb77..34d9b1469 100644 --- a/modules/ppcp-uninstall/src/ClearDatabaseInterface.php +++ b/modules/ppcp-uninstall/src/ClearDatabaseInterface.php @@ -29,4 +29,12 @@ interface ClearDatabaseInterface { */ public function clear_scheduled_actions( array $action_names ): void; + /** + * Clears the given actions. + * + * @param string[] $action_names The list of action names. + * @throws RuntimeException If problem clearing. + */ + public function clear_actions( array $action_names ): void; + } diff --git a/modules/ppcp-uninstall/src/UninstallModule.php b/modules/ppcp-uninstall/src/UninstallModule.php index 649be5c49..25d65ae0f 100644 --- a/modules/ppcp-uninstall/src/UninstallModule.php +++ b/modules/ppcp-uninstall/src/UninstallModule.php @@ -47,8 +47,9 @@ class UninstallModule implements ModuleInterface { $clear_db_endpoint = $container->get( 'uninstall.clear-db-endpoint' ); $option_names = $container->get( 'uninstall.ppcp-all-option-names' ); $scheduled_action_names = $container->get( 'uninstall.ppcp-all-scheduled-action-names' ); + $action_names = $container->get( 'uninstall.ppcp-all-action-names' ); - $this->handleClearDbAjaxRequest( $request_data, $clear_db, $clear_db_endpoint, $option_names, $scheduled_action_names ); + $this->handleClearDbAjaxRequest( $request_data, $clear_db, $clear_db_endpoint, $option_names, $scheduled_action_names, $action_names ); } /** @@ -69,17 +70,19 @@ class UninstallModule implements ModuleInterface { * @param string $nonce The nonce. * @param string[] $option_names The list of option names. * @param string[] $scheduled_action_names The list of scheduled action names. + * @param string[] $action_names The list of action names. */ protected function handleClearDbAjaxRequest( RequestData $request_data, ClearDatabaseInterface $clear_db, string $nonce, array $option_names, - array $scheduled_action_names + array $scheduled_action_names, + array $action_names ): void { add_action( "wc_ajax_{$nonce}", - static function () use ( $request_data, $clear_db, $nonce, $option_names, $scheduled_action_names ) { + static function () use ( $request_data, $clear_db, $nonce, $option_names, $scheduled_action_names, $action_names ) { try { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_send_json_error( 'Not admin.', 403 ); @@ -91,6 +94,7 @@ class UninstallModule implements ModuleInterface { $clear_db->delete_options( $option_names ); $clear_db->clear_scheduled_actions( $scheduled_action_names ); + $clear_db->clear_actions( $action_names ); wp_send_json_success(); return true; diff --git a/modules/ppcp-vaulting/services.php b/modules/ppcp-vaulting/services.php index c274c4549..d18768ea2 100644 --- a/modules/ppcp-vaulting/services.php +++ b/modules/ppcp-vaulting/services.php @@ -56,10 +56,14 @@ return array( 'vaulting.payment-token-factory' => function( ContainerInterface $container ): PaymentTokenFactory { return new PaymentTokenFactory(); }, + 'vaulting.payment-token-helper' => function( ContainerInterface $container ): PaymentTokenHelper { + return new PaymentTokenHelper(); + }, 'vaulting.payment-tokens-migration' => function( ContainerInterface $container ): PaymentTokensMigration { return new PaymentTokensMigration( $container->get( 'vaulting.payment-token-factory' ), $container->get( 'vaulting.repository.payment-token' ), + $container->get( 'vaulting.payment-token-helper' ), $container->get( 'woocommerce.logger.woocommerce' ) ); }, diff --git a/modules/ppcp-vaulting/src/PaymentTokenHelper.php b/modules/ppcp-vaulting/src/PaymentTokenHelper.php new file mode 100644 index 000000000..de940ccf3 --- /dev/null +++ b/modules/ppcp-vaulting/src/PaymentTokenHelper.php @@ -0,0 +1,35 @@ +get_token() === $token_id ) { + return true; + } + } + + return false; + } +} diff --git a/modules/ppcp-vaulting/src/PaymentTokensMigration.php b/modules/ppcp-vaulting/src/PaymentTokensMigration.php index 69b6c6fda..a7d3511cc 100644 --- a/modules/ppcp-vaulting/src/PaymentTokensMigration.php +++ b/modules/ppcp-vaulting/src/PaymentTokensMigration.php @@ -35,6 +35,13 @@ class PaymentTokensMigration { */ private $payment_token_repository; + /** + * The payment token helper. + * + * @var PaymentTokenHelper + */ + private $payment_token_helper; + /** * The logger. * @@ -47,16 +54,19 @@ class PaymentTokensMigration { * * @param PaymentTokenFactory $payment_token_factory The payment token factory. * @param PaymentTokenRepository $payment_token_repository The payment token repository. + * @param PaymentTokenHelper $payment_token_helper The payment token helper. * @param LoggerInterface $logger The logger. */ public function __construct( PaymentTokenFactory $payment_token_factory, PaymentTokenRepository $payment_token_repository, + PaymentTokenHelper $payment_token_helper, LoggerInterface $logger ) { $this->payment_token_factory = $payment_token_factory; $this->payment_token_repository = $payment_token_repository; $this->logger = $logger; + $this->payment_token_helper = $payment_token_helper; } /** @@ -72,7 +82,7 @@ class PaymentTokensMigration { foreach ( $tokens as $token ) { if ( isset( $token->source()->card ) ) { $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $id, CreditCardGateway::ID ); - if ( $this->token_exist( $wc_tokens, $token ) ) { + if ( $this->payment_token_helper->token_exist( $wc_tokens, $token->id() ) ) { $this->logger->info( 'Token already exist for user ' . (string) $id ); continue; } @@ -97,7 +107,7 @@ class PaymentTokensMigration { } } elseif ( $token->source()->paypal ) { $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $id, PayPalGateway::ID ); - if ( $this->token_exist( $wc_tokens, $token ) ) { + if ( $this->payment_token_helper->token_exist( $wc_tokens, $token->id() ) ) { $this->logger->info( 'Token already exist for user ' . (string) $id ); continue; } @@ -126,21 +136,4 @@ class PaymentTokensMigration { } } } - - /** - * Checks if given PayPal token exist as WC Payment Token. - * - * @param array $wc_tokens WC Payment Tokens. - * @param PaymentToken $token PayPal Token ID. - * @return bool - */ - private function token_exist( array $wc_tokens, PaymentToken $token ): bool { - foreach ( $wc_tokens as $wc_token ) { - if ( $wc_token->get_token() === $token->id() ) { - return true; - } - } - - return false; - } } diff --git a/modules/ppcp-wc-gateway/resources/js/SettingsHandler/SubElementsHandler.js b/modules/ppcp-wc-gateway/resources/js/SettingsHandler/SubElementsHandler.js new file mode 100644 index 000000000..cb0b30252 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/SettingsHandler/SubElementsHandler.js @@ -0,0 +1,51 @@ + +class SubElementsHandler { + constructor(element, options) { + const fieldSelector = 'input, select, textarea'; + + this.element = element; + this.values = options.values; + this.elements = options.elements; + + this.elementsSelector = this.elements.join(','); + + this.input = jQuery(this.element).is(fieldSelector) + ? this.element + : jQuery(this.element).find(fieldSelector).get(0); + + this.updateElementsVisibility(); + + jQuery(this.input).change(() => { + this.updateElementsVisibility(); + }); + } + + updateElementsVisibility() { + const $elements = jQuery(this.elementsSelector); + + let value = this.getValue(this.input); + value = (value !== null ? value.toString() : value); + + if (this.values.indexOf(value) !== -1) { + $elements.show(); + } else { + $elements.hide(); + } + } + + getValue(element) { + const $el = jQuery(element); + + if ($el.is(':checkbox') || $el.is(':radio')) { + if ($el.is(':checked')) { + return $el.val(); + } else { + return null; + } + } else { + return $el.val(); + } + } +} + +export default SubElementsHandler; diff --git a/modules/ppcp-wc-gateway/resources/js/common.js b/modules/ppcp-wc-gateway/resources/js/common.js new file mode 100644 index 000000000..e017594a7 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common.js @@ -0,0 +1,10 @@ +import moveWrappedElements from "./common/wrapped-elements"; +document.addEventListener( + 'DOMContentLoaded', + () => { + // Wait for current execution context to end. + setTimeout(function () { + moveWrappedElements(); + }, 0); + } +); diff --git a/modules/ppcp-wc-gateway/resources/js/common/wrapped-elements.js b/modules/ppcp-wc-gateway/resources/js/common/wrapped-elements.js new file mode 100644 index 000000000..827e50586 --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common/wrapped-elements.js @@ -0,0 +1,14 @@ + +// This function is needed because WordPress moves our custom notices to the global placeholder. +function moveWrappedElements() { + (($) => { + $('*[data-ppcp-wrapper]').each(function() { + let $wrapper = $('.' + $(this).data('ppcpWrapper')); + if ($wrapper.length) { + $wrapper.append(this); + } + }); + })(jQuery) +} + +export default moveWrappedElements; diff --git a/modules/ppcp-wc-gateway/resources/js/gateway-settings.js b/modules/ppcp-wc-gateway/resources/js/gateway-settings.js index c20432d48..bbb455be9 100644 --- a/modules/ppcp-wc-gateway/resources/js/gateway-settings.js +++ b/modules/ppcp-wc-gateway/resources/js/gateway-settings.js @@ -4,6 +4,7 @@ import Renderer from '../../../ppcp-button/resources/js/modules/Renderer/Rendere import MessageRenderer from "../../../ppcp-button/resources/js/modules/Renderer/MessageRenderer"; import {setVisibleByClass, isVisible} from "../../../ppcp-button/resources/js/modules/Helper/Hiding"; import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder"; +import SubElementsHandler from "./SettingsHandler/SubElementsHandler"; document.addEventListener( 'DOMContentLoaded', @@ -307,5 +308,16 @@ document.addEventListener( createButtonPreview(() => getButtonDefaultSettings('#ppcpPayLaterButtonPreview')); }); } + + // Generic behaviours, can be moved to common.js once it's on trunk branch. + jQuery( '*[data-ppcp-handlers]' ).each( (index, el) => { + const handlers = jQuery(el).data('ppcpHandlers'); + for (const handlerConfig of handlers) { + new { + SubElementsHandler: SubElementsHandler + }[handlerConfig.handler](el, handlerConfig.options) + } + }); + } ); diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index a95d7e0ef..63665dd4e 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -212,10 +212,18 @@ return array( return new ConnectAdminNotice( $state, $settings ); }, 'wcgateway.notice.currency-unsupported' => static function ( ContainerInterface $container ): UnsupportedCurrencyAdminNotice { - $state = $container->get( 'onboarding.state' ); - $shop_currency = $container->get( 'api.shop.currency' ); - $supported_currencies = $container->get( 'api.supported-currencies' ); - return new UnsupportedCurrencyAdminNotice( $state, $shop_currency, $supported_currencies ); + $state = $container->get( 'onboarding.state' ); + $shop_currency = $container->get( 'api.shop.currency' ); + $supported_currencies = $container->get( 'api.supported-currencies' ); + $is_wc_gateways_list_page = $container->get( 'wcgateway.is-wc-gateways-list-page' ); + $is_ppcp_settings_page = $container->get( 'wcgateway.is-ppcp-settings-page' ); + return new UnsupportedCurrencyAdminNotice( + $state, + $shop_currency, + $supported_currencies, + $is_wc_gateways_list_page, + $is_ppcp_settings_page + ); }, 'wcgateway.notice.dcc-without-paypal' => static function ( ContainerInterface $container ): GatewayWithoutPayPalAdminNotice { return new GatewayWithoutPayPalAdminNotice( @@ -1200,24 +1208,11 @@ return array( 'wcgateway.settings.has_enabled_separate_button_gateways' => static function ( ContainerInterface $container ): bool { return (bool) $container->get( 'wcgateway.settings.allow_card_button_gateway' ); }, - - 'order-tracking.is-tracking-available' => static function ( ContainerInterface $container ): bool { - try { - $bearer = $container->get( 'api.bearer' ); - assert( $bearer instanceof Bearer ); - - $token = $bearer->bearer(); - return $token->is_tracking_available(); - } catch ( RuntimeException $exception ) { - return false; - } - }, - 'wcgateway.settings.should-disable-tracking-checkbox' => static function ( ContainerInterface $container ): bool { $pui_helper = $container->get( 'wcgateway.pay-upon-invoice-helper' ); assert( $pui_helper instanceof PayUponInvoiceHelper ); - $is_tracking_available = $container->get( 'order-tracking.is-tracking-available' ); + $is_tracking_available = $container->get( 'order-tracking.is-module-enabled' ); if ( ! $is_tracking_available ) { return true; @@ -1258,44 +1253,6 @@ return array( return $label; }, - 'wcgateway.settings.tracking-label' => static function ( ContainerInterface $container ): string { - $tracking_label = sprintf( - // translators: %1$s and %2$s are the opening and closing of HTML tag. - __( 'Enable %1$sshipment tracking information%2$s to be sent to PayPal for seller protection features.', 'woocommerce-paypal-payments' ), - '', - '' - ); - - if ( 'DE' === $container->get( 'api.shop.country' ) ) { - $tracking_label .= '
' . sprintf( - // translators: %1$s and %2$s are the opening and closing of HTML tag. - __( 'Required when %1$sPay upon Invoice%2$s is used.', 'woocommerce-paypal-payments' ), - '', - '' - ); - } - - $is_tracking_available = $container->get( 'order-tracking.is-tracking-available' ); - - if ( $is_tracking_available ) { - return $tracking_label; - } - - $tracking_label .= '
' . sprintf( - // translators: %1$s and %2$s are the opening and closing of HTML tag. - __( - ' To use tracking features, you must %1$senable tracking on your account%2$s.', - 'woocommerce-paypal-payments' - ), - '', - '' - ); - - return $tracking_label; - }, 'wcgateway.enable-dcc-url-sandbox' => static function ( ContainerInterface $container ): string { return 'https://www.sandbox.paypal.com/bizsignup/entry/product/ppcp'; }, diff --git a/modules/ppcp-wc-gateway/src/Assets/SettingsPageAssets.php b/modules/ppcp-wc-gateway/src/Assets/SettingsPageAssets.php index 57e1af650..14c07e90f 100644 --- a/modules/ppcp-wc-gateway/src/Assets/SettingsPageAssets.php +++ b/modules/ppcp-wc-gateway/src/Assets/SettingsPageAssets.php @@ -87,6 +87,13 @@ class SettingsPageAssets { */ protected $all_funding_sources; + /** + * Whether it's a settings page of this plugin. + * + * @var bool + */ + private $is_settings_page; + /** * Assets constructor. * @@ -100,6 +107,7 @@ class SettingsPageAssets { * @param bool $is_pay_later_button_enabled Whether Pay Later button is enabled either for checkout, cart or product page. * @param array $disabled_sources The list of disabled funding sources. * @param array $all_funding_sources The list of all existing funding sources. + * @param bool $is_settings_page Whether it's a settings page of this plugin. */ public function __construct( string $module_url, @@ -111,7 +119,8 @@ class SettingsPageAssets { Environment $environment, bool $is_pay_later_button_enabled, array $disabled_sources, - array $all_funding_sources + array $all_funding_sources, + bool $is_settings_page ) { $this->module_url = $module_url; $this->version = $version; @@ -123,6 +132,7 @@ class SettingsPageAssets { $this->is_pay_later_button_enabled = $is_pay_later_button_enabled; $this->disabled_sources = $disabled_sources; $this->all_funding_sources = $all_funding_sources; + $this->is_settings_page = $is_settings_page; } /** @@ -138,11 +148,13 @@ class SettingsPageAssets { return; } - if ( ! $this->is_paypal_payment_method_page() ) { - return; + if ( $this->is_settings_page ) { + $this->register_admin_assets(); } - $this->register_admin_assets(); + if ( $this->is_paypal_payment_method_page() ) { + $this->register_paypal_admin_assets(); + } } ); @@ -173,9 +185,9 @@ class SettingsPageAssets { } /** - * Register assets for admin pages. + * Register assets for PayPal admin pages. */ - private function register_admin_assets(): void { + private function register_paypal_admin_assets(): void { wp_enqueue_style( 'ppcp-gateway-settings', trailingslashit( $this->module_url ) . 'assets/css/gateway-settings.css', @@ -212,4 +224,18 @@ class SettingsPageAssets { ) ); } + + /** + * Register assets for PayPal admin pages. + */ + private function register_admin_assets(): void { + wp_enqueue_script( + 'ppcp-admin-common', + trailingslashit( $this->module_url ) . 'assets/js/common.js', + array(), + $this->version, + true + ); + } + } diff --git a/modules/ppcp-wc-gateway/src/Gateway/GatewayRepository.php b/modules/ppcp-wc-gateway/src/Gateway/GatewayRepository.php index 250b1143f..6362fa260 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/GatewayRepository.php +++ b/modules/ppcp-wc-gateway/src/Gateway/GatewayRepository.php @@ -44,4 +44,14 @@ class GatewayRepository { } ); } + + /** + * Indicates if a given gateway ID is registered. + * + * @param string $gateway_id The gateway ID. + * @return bool + */ + public function exists( string $gateway_id ): bool { + return in_array( $gateway_id, $this->ppcp_gateway_ids, true ); + } } diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 0b01b4750..c703de904 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -290,9 +290,11 @@ class PayPalGateway extends \WC_Payment_Gateway { // in the constructor, so must do it here. global $theorder; if ( $theorder instanceof WC_Order ) { - $payment_method_title = $theorder->get_payment_method_title(); - if ( $payment_method_title ) { - $this->title = $payment_method_title; + if ( $theorder->get_payment_method() === self::ID ) { + $payment_method_title = $theorder->get_payment_method_title(); + if ( $payment_method_title ) { + $this->title = $payment_method_title; + } } } } diff --git a/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php b/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php index 065f486be..f8d717f08 100644 --- a/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php +++ b/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php @@ -135,12 +135,12 @@ class DCCProductStatus { $this->settings->set( 'products_dcc_enabled', true ); $this->settings->persist(); $this->current_status_cache = true; - $this->cache->set( self::DCC_STATUS_CACHE_KEY, 'true', 3 * MONTH_IN_SECONDS ); + $this->cache->set( self::DCC_STATUS_CACHE_KEY, 'true', MONTH_IN_SECONDS ); return true; } } - $expiration = 3 * MONTH_IN_SECONDS; + $expiration = MONTH_IN_SECONDS; if ( $this->dcc_applies->for_country_currency() ) { $expiration = 3 * HOUR_IN_SECONDS; } diff --git a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php index 4f0e4ba6c..df70c0779 100644 --- a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php +++ b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php @@ -127,11 +127,11 @@ class PayUponInvoiceProductStatus { $this->settings->set( 'products_pui_enabled', true ); $this->settings->persist(); $this->current_status_cache = true; - $this->cache->set( self::PUI_STATUS_CACHE_KEY, 'true', 3 * MONTH_IN_SECONDS ); + $this->cache->set( self::PUI_STATUS_CACHE_KEY, 'true', MONTH_IN_SECONDS ); return true; } } - $this->cache->set( self::PUI_STATUS_CACHE_KEY, 'false', 3 * MONTH_IN_SECONDS ); + $this->cache->set( self::PUI_STATUS_CACHE_KEY, 'false', MONTH_IN_SECONDS ); $this->current_status_cache = false; return false; diff --git a/modules/ppcp-wc-gateway/src/Notice/UnsupportedCurrencyAdminNotice.php b/modules/ppcp-wc-gateway/src/Notice/UnsupportedCurrencyAdminNotice.php index 27ef14f79..88f02cffe 100644 --- a/modules/ppcp-wc-gateway/src/Notice/UnsupportedCurrencyAdminNotice.php +++ b/modules/ppcp-wc-gateway/src/Notice/UnsupportedCurrencyAdminNotice.php @@ -40,17 +40,42 @@ class UnsupportedCurrencyAdminNotice { */ private $shop_currency; + /** + * Indicates if we're on the WooCommerce gateways list page. + * + * @var bool + */ + private $is_wc_gateways_list_page; + + /** + * Indicates if we're on a PPCP Settings page. + * + * @var bool + */ + private $is_ppcp_settings_page; + /** * UnsupportedCurrencyAdminNotice constructor. * * @param State $state The state. * @param string $shop_currency The shop currency. * @param array $supported_currencies The supported currencies. + * @param bool $is_wc_gateways_list_page Indicates if we're on the WooCommerce gateways list page. + * @param bool $is_ppcp_settings_page Indicates if we're on a PPCP Settings page. */ - public function __construct( State $state, string $shop_currency, array $supported_currencies ) { - $this->state = $state; - $this->shop_currency = $shop_currency; - $this->supported_currencies = $supported_currencies; + public function __construct( + State $state, + string $shop_currency, + array $supported_currencies, + bool $is_wc_gateways_list_page, + bool $is_ppcp_settings_page + ) { + $this->state = $state; + $this->shop_currency = $shop_currency; + $this->supported_currencies = $supported_currencies; + $this->is_wc_gateways_list_page = $is_wc_gateways_list_page; + $this->is_ppcp_settings_page = $is_ppcp_settings_page; + } /** @@ -63,16 +88,19 @@ class UnsupportedCurrencyAdminNotice { return null; } + $paypal_currency_support_url = 'https://developer.paypal.com/api/rest/reference/currency-codes/'; + $message = sprintf( - /* translators: %1$s the shop currency, 2$s the gateway name. */ + /* translators: %1$s the shop currency, %2$s the PayPal currency support page link opening HTML tag, %3$s the link ending HTML tag. */ __( - 'Attention: Your current WooCommerce store currency (%1$s) is not supported by PayPal. Please update your store currency to one that is supported by PayPal to ensure smooth transactions. Visit the PayPal currency support page for more information on supported currencies.', + 'Attention: Your current WooCommerce store currency (%1$s) is not supported by PayPal. Please update your store currency to one that is supported by PayPal to ensure smooth transactions. Visit the %2$sPayPal currency support page%3$s for more information on supported currencies.', 'woocommerce-paypal-payments' ), $this->shop_currency, - 'https://developer.paypal.com/api/rest/reference/currency-codes/' + '', + '' ); - return new Message( $message, 'warning' ); + return new Message( $message, 'warning', true, 'ppcp-notice-wrapper' ); } /** @@ -81,7 +109,9 @@ class UnsupportedCurrencyAdminNotice { * @return bool */ protected function should_display(): bool { - return $this->state->current_state() === State::STATE_ONBOARDED && ! $this->currency_supported(); + return $this->state->current_state() === State::STATE_ONBOARDED + && ! $this->currency_supported() + && ( $this->is_wc_gateways_list_page || $this->is_ppcp_settings_page ); } /** diff --git a/modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php b/modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php index 815f8d73e..47348eca3 100644 --- a/modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/OrderMetaTrait.php @@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Processor; use WC_Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; +use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; @@ -22,14 +23,16 @@ trait OrderMetaTrait { /** * Adds common metadata to the order. * - * @param WC_Order $wc_order The WC order to which metadata will be added. - * @param Order $order The PayPal order. - * @param Environment $environment The environment. + * @param WC_Order $wc_order The WC order to which metadata will be added. + * @param Order $order The PayPal order. + * @param Environment $environment The environment. + * @param OrderTransient|null $order_transient The order transient helper. */ protected function add_paypal_meta( WC_Order $wc_order, Order $order, - Environment $environment + Environment $environment, + OrderTransient $order_transient = null ): void { $wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() ); $wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); @@ -43,6 +46,8 @@ trait OrderMetaTrait { } $wc_order->save(); + + do_action( 'woocommerce_paypal_payments_woocommerce_order_created', $wc_order, $order ); } /** diff --git a/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php b/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php index 443fd1cbd..1fb10ed8c 100644 --- a/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php +++ b/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php @@ -116,6 +116,13 @@ class OrderProcessor { */ private $order_helper; + /** + * Array to store temporary order data changes to restore after processing. + * + * @var array + */ + private $restore_order_data = array(); + /** * OrderProcessor constructor. * @@ -292,8 +299,12 @@ class OrderProcessor { * @return Order */ public function patch_order( \WC_Order $wc_order, Order $order ): Order { + $this->apply_outbound_order_filters( $wc_order ); $updated_order = $this->order_factory->from_wc_order( $wc_order, $order ); - $order = $this->order_endpoint->patch_order_with( $order, $updated_order ); + $this->restore_order_from_filters( $wc_order ); + + $order = $this->order_endpoint->patch_order_with( $order, $updated_order ); + return $order; } @@ -323,4 +334,48 @@ class OrderProcessor { true ); } + + /** + * Applies filters to the WC_Order, so they are reflected only on PayPal Order. + * + * @param WC_Order $wc_order The WoocOmmerce Order. + * @return void + */ + private function apply_outbound_order_filters( WC_Order $wc_order ): void { + $items = $wc_order->get_items(); + + $this->restore_order_data['names'] = array(); + + foreach ( $items as $item ) { + if ( ! $item instanceof \WC_Order_Item ) { + continue; + } + + $original_name = $item->get_name(); + $new_name = apply_filters( 'woocommerce_paypal_payments_order_line_item_name', $original_name, $item->get_id(), $wc_order->get_id() ); + + if ( $new_name !== $original_name ) { + $this->restore_order_data['names'][ $item->get_id() ] = $original_name; + $item->set_name( $new_name ); + } + } + } + + /** + * Restores the WC_Order to it's state before filters. + * + * @param WC_Order $wc_order The WooCommerce Order. + * @return void + */ + private function restore_order_from_filters( WC_Order $wc_order ): void { + if ( is_array( $this->restore_order_data['names'] ?? null ) ) { + foreach ( $this->restore_order_data['names'] as $wc_item_id => $original_name ) { + $wc_item = $wc_order->get_item( $wc_item_id, false ); + + if ( $wc_item ) { + $wc_item->set_name( $original_name ); + } + } + } + } } diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php index 9d42b1240..19e38b99f 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway\Settings; +use WooCommerce\PayPalCommerce\ApiClient\Helper\PurchaseUnitSanitizer; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; use WooCommerce\PayPalCommerce\Onboarding\Environment; @@ -401,20 +402,6 @@ return function ( ContainerInterface $container, array $fields ): array { 'requirements' => array( 'pui_ready' ), 'gateway' => Settings::CONNECTION_TAB_ID, ), - 'tracking_enabled' => array( - 'title' => __( 'Shipment Tracking', 'woocommerce-paypal-payments' ), - 'type' => 'checkbox', - 'desc_tip' => true, - 'label' => $container->get( 'wcgateway.settings.tracking-label' ), - 'description' => __( 'Allows to send shipment tracking numbers to PayPal for PayPal transactions.', 'woocommerce-paypal-payments' ), - 'default' => false, - 'screens' => array( - State::STATE_ONBOARDED, - ), - 'requirements' => array(), - 'gateway' => Settings::CONNECTION_TAB_ID, - 'input_class' => $container->get( 'wcgateway.settings.should-disable-tracking-checkbox' ) ? array( 'ppcp-disabled-checkbox' ) : array(), - ), 'fraudnet_enabled' => array( 'title' => __( 'FraudNet', 'woocommerce-paypal-payments' ), 'type' => 'checkbox', @@ -496,6 +483,55 @@ return function ( ContainerInterface $container, array $fields ): array { 'requirements' => array(), 'gateway' => Settings::CONNECTION_TAB_ID, ), + 'subtotal_mismatch_behavior' => array( + 'title' => __( 'Subtotal mismatch behavior', 'woocommerce-paypal-payments' ), + 'type' => 'select', + 'input_class' => array( 'wc-enhanced-select' ), + 'default' => 'vertical', + 'desc_tip' => true, + 'description' => __( + 'Differences between WooCommerce and PayPal roundings may cause mismatch in order items subtotal calculations. If not handled, these mismatches will cause the PayPal transaction to fail.', + 'woocommerce-paypal-payments' + ), + 'options' => array( + PurchaseUnitSanitizer::MODE_DITCH => __( 'Do not send line items to PayPal', 'woocommerce-paypal-payments' ), + PurchaseUnitSanitizer::MODE_EXTRA_LINE => __( 'Add another line item', 'woocommerce-paypal-payments' ), + ), + 'screens' => array( + State::STATE_START, + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => Settings::CONNECTION_TAB_ID, + 'custom_attributes' => array( + 'data-ppcp-handlers' => wp_json_encode( + array( + array( + 'handler' => 'SubElementsHandler', + 'options' => array( + 'values' => array( PurchaseUnitSanitizer::MODE_EXTRA_LINE ), + 'elements' => array( '#field-subtotal_mismatch_line_name' ), + ), + ), + ) + ), + ), + ), + 'subtotal_mismatch_line_name' => array( + 'title' => __( 'Subtotal mismatch line name', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'desc_tip' => true, + 'description' => __( 'The name of the extra line that will be sent to PayPal to correct the subtotal mismatch.', 'woocommerce-paypal-payments' ), + 'maxlength' => 22, + 'default' => '', + 'screens' => array( + State::STATE_START, + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'placeholder' => PurchaseUnitSanitizer::EXTRA_LINE_NAME, + 'gateway' => Settings::CONNECTION_TAB_ID, + ), ); return array_merge( $fields, $connection_fields ); diff --git a/modules/ppcp-wc-gateway/src/Settings/HeaderRenderer.php b/modules/ppcp-wc-gateway/src/Settings/HeaderRenderer.php index a4302bc37..5f9b79280 100644 --- a/modules/ppcp-wc-gateway/src/Settings/HeaderRenderer.php +++ b/modules/ppcp-wc-gateway/src/Settings/HeaderRenderer.php @@ -77,6 +77,8 @@ class HeaderRenderer { ' + +
'; } } diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index b1d057537..bd36ca5d4 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -401,9 +401,7 @@ class SettingsListener { $this->webhook_registrar->unregister(); foreach ( $this->signup_link_ids as $key ) { - if ( $this->signup_link_cache->has( $key ) ) { - $this->signup_link_cache->delete( $key ); - } + ( new OnboardingUrl( $this->signup_link_cache, $key, get_current_user_id() ) )->delete(); } } } @@ -638,4 +636,15 @@ class SettingsListener { throw $exception; } } + + /** + * Handles onboarding URLs deletion + */ + public function listen_for_uninstall(): void { + // Clear onboarding links from cache. + foreach ( $this->signup_link_ids as $key ) { + ( new OnboardingUrl( $this->signup_link_cache, $key, get_current_user_id() ) )->delete(); + } + } + } diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 28c610b7e..c4fea76b3 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -33,6 +33,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways; use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint; use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\GatewayRepository; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus; use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus; @@ -183,7 +184,8 @@ class WCGatewayModule implements ModuleInterface { $c->get( 'onboarding.environment' ), $settings_status->is_pay_later_button_enabled(), $settings->has( 'disable_funding' ) ? $settings->get( 'disable_funding' ) : array(), - $c->get( 'wcgateway.settings.funding-sources' ) + $c->get( 'wcgateway.settings.funding-sources' ), + $c->get( 'wcgateway.is-ppcp-settings-page' ) ); $assets->register_assets(); } @@ -355,6 +357,14 @@ class WCGatewayModule implements ModuleInterface { return; } + $gateway_repository = $c->get( 'wcgateway.gateway-repository' ); + assert( $gateway_repository instanceof GatewayRepository ); + + // Only allow to proceed if the payment method is one of our Gateways. + if ( ! $gateway_repository->exists( $wc_order->get_payment_method() ) ) { + return; + } + $intent = strtoupper( (string) $wc_order->get_meta( PayPalGateway::INTENT_META_KEY ) ); $captured = wc_string_to_bool( $wc_order->get_meta( AuthorizedPaymentsProcessor::CAPTURED_META_KEY ) ); if ( $intent !== 'AUTHORIZE' || $captured ) { @@ -391,6 +401,16 @@ class WCGatewayModule implements ModuleInterface { 3 ); + add_action( + 'woocommerce_paypal_payments_uninstall', + static function () use ( $c ) { + $listener = $c->get( 'wcgateway.settings.listener' ); + assert( $listener instanceof SettingsListener ); + + $listener->listen_for_uninstall(); + } + ); + if ( defined( 'WP_CLI' ) && WP_CLI ) { \WP_CLI::add_command( 'pcp settings', @@ -496,7 +516,6 @@ class WCGatewayModule implements ModuleInterface { try { $listener->listen_for_vaulting_enabled(); - $listener->listen_for_tracking_enabled(); } catch ( RuntimeException $exception ) { add_action( 'admin_notices', diff --git a/modules/ppcp-wc-gateway/webpack.config.js b/modules/ppcp-wc-gateway/webpack.config.js index bd4a22fdc..a8a78a8bc 100644 --- a/modules/ppcp-wc-gateway/webpack.config.js +++ b/modules/ppcp-wc-gateway/webpack.config.js @@ -6,6 +6,7 @@ module.exports = { mode: isProduction ? 'production' : 'development', target: 'web', entry: { + 'common': path.resolve('./resources/js/common.js'), 'gateway-settings': path.resolve('./resources/js/gateway-settings.js'), 'fraudnet': path.resolve('./resources/js/fraudnet.js'), 'oxxo': path.resolve('./resources/js/oxxo.js'), diff --git a/modules/ppcp-webhooks/services.php b/modules/ppcp-webhooks/services.php index 9223a7348..4d585206a 100644 --- a/modules/ppcp-webhooks/services.php +++ b/modules/ppcp-webhooks/services.php @@ -80,6 +80,7 @@ return array( $order_endpoint = $container->get( 'api.endpoint.order' ); $authorized_payments_processor = $container->get( 'wcgateway.processor.authorized-payments' ); $payment_token_factory = $container->get( 'vaulting.payment-token-factory' ); + $payment_token_helper = $container->get( 'vaulting.payment-token-helper' ); $refund_fees_updater = $container->get( 'wcgateway.helper.refund-fees-updater' ); return array( @@ -95,7 +96,7 @@ return array( new PaymentCaptureRefunded( $logger, $refund_fees_updater ), new PaymentCaptureReversed( $logger ), new PaymentCaptureCompleted( $logger, $order_endpoint ), - new VaultPaymentTokenCreated( $logger, $prefix, $authorized_payments_processor, $payment_token_factory ), + new VaultPaymentTokenCreated( $logger, $prefix, $authorized_payments_processor, $payment_token_factory, $payment_token_helper ), new VaultPaymentTokenDeleted( $logger ), new PaymentCapturePending( $logger ), new PaymentSaleCompleted( $logger ), diff --git a/modules/ppcp-webhooks/src/Handler/PaymentCaptureRefunded.php b/modules/ppcp-webhooks/src/Handler/PaymentCaptureRefunded.php index 053cd619a..eab1defc0 100644 --- a/modules/ppcp-webhooks/src/Handler/PaymentCaptureRefunded.php +++ b/modules/ppcp-webhooks/src/Handler/PaymentCaptureRefunded.php @@ -112,8 +112,7 @@ class PaymentCaptureRefunded implements RequestHandler { 'amount' => $request['resource']['amount']['value'], ) ); - if ( is_wp_error( $refund ) ) { - assert( $refund instanceof WP_Error ); + if ( $refund instanceof WP_Error ) { $message = sprintf( 'Order %1$s could not be refunded. %2$s', (string) $wc_order->get_id(), diff --git a/modules/ppcp-webhooks/src/Handler/VaultPaymentTokenCreated.php b/modules/ppcp-webhooks/src/Handler/VaultPaymentTokenCreated.php index 152932556..893305c75 100644 --- a/modules/ppcp-webhooks/src/Handler/VaultPaymentTokenCreated.php +++ b/modules/ppcp-webhooks/src/Handler/VaultPaymentTokenCreated.php @@ -13,6 +13,7 @@ use Psr\Log\LoggerInterface; use WC_Payment_Token_CC; use WC_Payment_Tokens; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenFactory; +use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenHelper; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenPayPal; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; @@ -54,6 +55,13 @@ class VaultPaymentTokenCreated implements RequestHandler { */ protected $payment_token_factory; + /** + * The payment token helper. + * + * @var PaymentTokenHelper + */ + private $payment_token_helper; + /** * VaultPaymentTokenCreated constructor. * @@ -61,17 +69,20 @@ class VaultPaymentTokenCreated implements RequestHandler { * @param string $prefix The prefix. * @param AuthorizedPaymentsProcessor $authorized_payments_processor The authorized payment processor. * @param PaymentTokenFactory $payment_token_factory The payment token factory. + * @param PaymentTokenHelper $payment_token_helper The payment token helper. */ public function __construct( LoggerInterface $logger, string $prefix, AuthorizedPaymentsProcessor $authorized_payments_processor, - PaymentTokenFactory $payment_token_factory + PaymentTokenFactory $payment_token_factory, + PaymentTokenHelper $payment_token_helper ) { $this->logger = $logger; $this->prefix = $prefix; $this->authorized_payments_processor = $authorized_payments_processor; $this->payment_token_factory = $payment_token_factory; + $this->payment_token_helper = $payment_token_helper; } /** @@ -123,33 +134,39 @@ class VaultPaymentTokenCreated implements RequestHandler { if ( ! is_null( $request['resource'] ) && isset( $request['resource']['id'] ) ) { if ( ! is_null( $request['resource']['source'] ) && isset( $request['resource']['source']['card'] ) ) { - $token = new WC_Payment_Token_CC(); - $token->set_token( $request['resource']['id'] ); - $token->set_user_id( $wc_customer_id ); - $token->set_gateway_id( CreditCardGateway::ID ); + $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $wc_customer_id, CreditCardGateway::ID ); + if ( ! $this->payment_token_helper->token_exist( $wc_tokens, $request['resource']['id'] ) ) { + $token = new WC_Payment_Token_CC(); + $token->set_token( $request['resource']['id'] ); + $token->set_user_id( $wc_customer_id ); + $token->set_gateway_id( CreditCardGateway::ID ); - $token->set_last4( $request['resource']['source']['card']['last_digits'] ?? '' ); - $expiry = explode( '-', $request['resource']['source']['card']['expiry'] ?? '' ); - $token->set_expiry_year( $expiry[0] ?? '' ); - $token->set_expiry_month( $expiry[1] ?? '' ); - $token->set_card_type( $request['resource']['source']['card']['brand'] ?? '' ); - $token->save(); - WC_Payment_Tokens::set_users_default( $wc_customer_id, $token->get_id() ); - } elseif ( isset( $request['resource']['source']['paypal'] ) ) { - $payment_token_paypal = $this->payment_token_factory->create( 'paypal' ); - assert( $payment_token_paypal instanceof PaymentTokenPayPal ); - - $payment_token_paypal->set_token( $request['resource']['id'] ); - $payment_token_paypal->set_user_id( $wc_customer_id ); - $payment_token_paypal->set_gateway_id( PayPalGateway::ID ); - - $email = $request['resource']['source']['paypal']['payer']['email_address'] ?? ''; - if ( $email && is_email( $email ) ) { - $payment_token_paypal->set_email( $email ); + $token->set_last4( $request['resource']['source']['card']['last_digits'] ?? '' ); + $expiry = explode( '-', $request['resource']['source']['card']['expiry'] ?? '' ); + $token->set_expiry_year( $expiry[0] ?? '' ); + $token->set_expiry_month( $expiry[1] ?? '' ); + $token->set_card_type( $request['resource']['source']['card']['brand'] ?? '' ); + $token->save(); + WC_Payment_Tokens::set_users_default( $wc_customer_id, $token->get_id() ); } + } elseif ( isset( $request['resource']['source']['paypal'] ) ) { + $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $wc_customer_id, PayPalGateway::ID ); + if ( ! $this->payment_token_helper->token_exist( $wc_tokens, $request['resource']['id'] ) ) { + $payment_token_paypal = $this->payment_token_factory->create( 'paypal' ); + assert( $payment_token_paypal instanceof PaymentTokenPayPal ); - $payment_token_paypal->save(); - WC_Payment_Tokens::set_users_default( $wc_customer_id, $payment_token_paypal->get_id() ); + $payment_token_paypal->set_token( $request['resource']['id'] ); + $payment_token_paypal->set_user_id( $wc_customer_id ); + $payment_token_paypal->set_gateway_id( PayPalGateway::ID ); + + $email = $request['resource']['source']['paypal']['payer']['email_address'] ?? ''; + if ( $email && is_email( $email ) ) { + $payment_token_paypal->set_email( $email ); + } + + $payment_token_paypal->save(); + WC_Payment_Tokens::set_users_default( $wc_customer_id, $payment_token_paypal->get_id() ); + } } } diff --git a/psalm-baseline.xml b/psalm-baseline.xml index fb66e57af..1c735db2f 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -259,6 +259,11 @@ DAY_IN_SECONDS + + + DAY_IN_SECONDS + + realpath( __FILE__ ) diff --git a/tests/PHPUnit/ApiClient/Entity/ItemTest.php b/tests/PHPUnit/ApiClient/Entity/ItemTest.php index 2028000e3..a12d26101 100644 --- a/tests/PHPUnit/ApiClient/Entity/ItemTest.php +++ b/tests/PHPUnit/ApiClient/Entity/ItemTest.php @@ -66,7 +66,9 @@ class ItemTest extends TestCase 'description', $tax, 'sku', - 'PHYSICAL_GOODS' + 'PHYSICAL_GOODS', + 'url', + 'image_url' ); $expected = [ @@ -76,6 +78,8 @@ class ItemTest extends TestCase 'description' => 'description', 'sku' => 'sku', 'category' => 'PHYSICAL_GOODS', + 'url' => 'url', + 'image_url' => 'image_url', 'tax' => [2], ]; diff --git a/tests/PHPUnit/ApiClient/Entity/PurchaseUnitTest.php b/tests/PHPUnit/ApiClient/Entity/PurchaseUnitTest.php index dbb18c4de..c9d81f8aa 100644 --- a/tests/PHPUnit/ApiClient/Entity/PurchaseUnitTest.php +++ b/tests/PHPUnit/ApiClient/Entity/PurchaseUnitTest.php @@ -3,6 +3,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient\Entity; +use WooCommerce\PayPalCommerce\ApiClient\Helper\PurchaseUnitSanitizer; use WooCommerce\PayPalCommerce\TestCase; use Mockery; @@ -75,24 +76,43 @@ class PurchaseUnitTest extends TestCase $this->assertEquals($expected, $testee->to_array()); } - /** - * @dataProvider dataForDitchTests - * @param array $items - * @param Amount $amount - * @param bool $doDitch - */ - public function testDitchMethod(array $items, Amount $amount, bool $doDitch, string $message) + /** + * @dataProvider dataForDitchTests + * @param array $items + * @param Amount $amount + * @param bool|array $doDitch + * @param string $message + */ + public function testDitchMethod(array $items, Amount $amount, $doDitch, string $message) { + if (is_array($doDitch)) { + $doDitchItems = $doDitch['items']; + $doDitchBreakdown = $doDitch['breakdown']; + $doDitchTax = $doDitch['tax']; + } else { + $doDitchItems = $doDitch; + $doDitchBreakdown = $doDitch; + $doDitchTax = $doDitch; + } + $testee = new PurchaseUnit( $amount, $items ); + $testee->set_sanitizer(new PurchaseUnitSanitizer(PurchaseUnitSanitizer::MODE_DITCH)); + $array = $testee->to_array(); - $resultItems = $doDitch === ! array_key_exists('items', $array); - $resultBreakdown = $doDitch === ! array_key_exists('breakdown', $array['amount']); + $resultItems = $doDitchItems === ! array_key_exists('items', $array); + + $resultBreakdown = $doDitchBreakdown === ! array_key_exists('breakdown', $array['amount']); $this->assertTrue($resultItems, $message); $this->assertTrue($resultBreakdown, $message); + + foreach ($array['items'] ?? [] as $item) { + $resultTax = $doDitchTax === ! array_key_exists('tax', $item); + $this->assertTrue($resultTax, $message); + } } public function dataForDitchTests() : array @@ -406,6 +426,58 @@ class PurchaseUnitTest extends TestCase 'insurance' => null, ], ], + 'ditch_items_total_but_not_breakdown' => [ + 'message' => 'Items should be ditched because the item total does not add up. But not breakdown because it adds up.', + 'ditch' => [ + 'items' => true, + 'breakdown' => false, + 'tax' => true, + ], + 'items' => [ + [ + 'value' => 11, + 'quantity' => 2, + 'tax' => 3, + 'category' => Item::PHYSICAL_GOODS, + ], + ], + 'amount' => 26, + 'breakdown' => [ + 'item_total' => 20, + 'tax_total' => 6, + 'shipping' => null, + 'discount' => null, + 'shipping_discount' => null, + 'handling' => null, + 'insurance' => null, + ], + ], + 'ditch_items_tax_with_incorrect_tax_total' => [ + 'message' => 'Ditch tax from items. Items should not be ditched because the mismatch is on the tax.', + 'ditch' => [ + 'items' => false, + 'breakdown' => false, + 'tax' => true, + ], + 'items' => [ + [ + 'value' => 10, + 'quantity' => 2, + 'tax' => 4, + 'category' => Item::PHYSICAL_GOODS, + ], + ], + 'amount' => 26, + 'breakdown' => [ + 'item_total' => 20, + 'tax_total' => 6, + 'shipping' => null, + 'discount' => null, + 'shipping_discount' => null, + 'handling' => null, + 'insurance' => null, + ], + ], ]; $values = []; @@ -421,10 +493,16 @@ class PurchaseUnitTest extends TestCase 'tax' => $tax, 'quantity'=> $item['quantity'], 'category' => $item['category'], - 'to_array' => [], + 'to_array' => [ + 'unit_amount' => $unitAmount->to_array(), + 'tax' => $tax->to_array(), + 'quantity'=> $item['quantity'], + 'category' => $item['category'], + ], ] ); } + $breakdown = null; if ($test['breakdown']) { $breakdown = Mockery::mock(AmountBreakdown::class); @@ -438,10 +516,29 @@ class PurchaseUnitTest extends TestCase return $money; }); } + + $breakdown + ->shouldReceive('to_array') + ->andReturn( + array_map( + function ($value) { + return $value ? (new Money($value, 'EUR'))->to_array() : null; + }, + $test['breakdown'] + ) + ); } + + $amountMoney = new Money($test['amount'], 'EUR'); $amount = Mockery::mock(Amount::class); - $amount->shouldReceive('to_array')->andReturn(['value' => number_format( $test['amount'], 2, '.', '' ), 'breakdown' => []]); - $amount->shouldReceive('value_str')->andReturn(number_format( $test['amount'], 2, '.', '' )); + $amount + ->shouldReceive('to_array') + ->andReturn([ + 'value' => $amountMoney->value_str(), + 'currency_code' => $amountMoney->currency_code(), + 'breakdown' => $breakdown ? $breakdown->to_array() : [], + ]); + $amount->shouldReceive('value_str')->andReturn($amountMoney->value_str()); $amount->shouldReceive('currency_code')->andReturn('EUR'); $amount->shouldReceive('breakdown')->andReturn($breakdown); @@ -456,6 +553,262 @@ class PurchaseUnitTest extends TestCase return $values; } + /** + * @dataProvider dataForExtraLineTests + * @param array $items + * @param Amount $amount + * @param array $expected + * @param string $message + */ + public function testExtraLineMethod(array $items, Amount $amount, array $expected, string $message) + { + $testee = new PurchaseUnit( + $amount, + $items + ); + + $testee->set_sanitizer(new PurchaseUnitSanitizer(PurchaseUnitSanitizer::MODE_EXTRA_LINE, $expected['extra_line_name'] ?? null)); + + $countItemsBefore = count($items); + $array = $testee->to_array(); + $countItemsAfter = count($array['items']); + $extraItem = array_pop($array['items']); + + $this->assertEquals($countItemsBefore + 1, $countItemsAfter, $message); + $this->assertEquals($expected['extra_line_value'], $extraItem['unit_amount']['value'], $message); + $this->assertEquals($expected['extra_line_name'] ?? PurchaseUnitSanitizer::EXTRA_LINE_NAME, $extraItem['name'], $message); + + foreach ($array['items'] as $i => $item) { + $this->assertEquals($expected['item_value'][$i], $item['unit_amount']['value'], $message); + } + } + + public function dataForExtraLineTests() : array + { + $data = [ + 'default' => [ + 'message' => 'Extra line should be added with price 0.01 and line amount 10.', + 'expected' => [ + 'item_value' => [10], + 'extra_line_value' => 0.01, + ], + 'items' => [ + [ + 'value' => 10, + 'quantity' => 2, + 'tax' => 3, + 'category' => Item::PHYSICAL_GOODS, + ], + ], + 'amount' => 26.01, + 'breakdown' => [ + 'item_total' => 20.01, + 'tax_total' => 6, + 'shipping' => null, + 'discount' => null, + 'shipping_discount' => null, + 'handling' => null, + 'insurance' => null, + ], + ], + 'with_custom_name' => [ + 'message' => 'Extra line should be added with price 0.01 and line amount 10.', + 'expected' => [ + 'item_value' => [10], + 'extra_line_value' => 0.01, + 'extra_line_name' => 'My custom line name', + ], + 'items' => [ + [ + 'value' => 10, + 'quantity' => 2, + 'tax' => 3, + 'category' => Item::PHYSICAL_GOODS, + ], + ], + 'amount' => 26.01, + 'breakdown' => [ + 'item_total' => 20.01, + 'tax_total' => 6, + 'shipping' => null, + 'discount' => null, + 'shipping_discount' => null, + 'handling' => null, + 'insurance' => null, + ], + ], + 'with_rounding_down' => [ + 'message' => 'Extra line should be added with price 0.01 and line amount 10.00.', + 'expected' => [ + 'item_value' => [10.00], + 'extra_line_value' => 0.01 + ], + 'items' => [ + [ + 'value' => 10.005, + 'quantity' => 2, + 'tax' => 3, + 'category' => Item::PHYSICAL_GOODS, + ], + ], + 'amount' => 26.01, + 'breakdown' => [ + 'item_total' => 20.01, + 'tax_total' => 6, + 'shipping' => null, + 'discount' => null, + 'shipping_discount' => null, + 'handling' => null, + 'insurance' => null, + ], + ], + 'with_two_rounding_down' => [ + 'message' => 'Extra line should be added with price 0.03 and lines amount 10.00 and 4.99.', + 'expected' => [ + 'item_value' => [10.00, 4.99], + 'extra_line_value' => 0.03 + ], + 'items' => [ + [ + 'value' => 10.005, + 'quantity' => 2, + 'tax' => 3, + 'category' => Item::PHYSICAL_GOODS, + ], + [ + 'value' => 5, + 'quantity' => 2, + 'tax' => 3, + 'category' => Item::PHYSICAL_GOODS, + ], + ], + 'amount' => 36.01, + 'breakdown' => [ + 'item_total' => 30.01, + 'tax_total' => 6, + 'shipping' => null, + 'discount' => null, + 'shipping_discount' => null, + 'handling' => null, + 'insurance' => null, + ], + ], + 'with_many_roundings_down' => [ + 'message' => 'Extra line should be added with price 0.01 and lines amount 10.00, 5.00 and 6.66.', + 'expected' => [ + 'item_value' => [10.00, 4.99, 6.66], + 'extra_line_value' => 0.02 + ], + 'items' => [ + [ + 'value' => 10.005, + 'quantity' => 1, + 'tax' => 3, + 'category' => Item::PHYSICAL_GOODS, + ], + [ + 'value' => 5.001, + 'quantity' => 1, + 'tax' => 3, + 'category' => Item::PHYSICAL_GOODS, + ], + [ + 'value' => 6.666, + 'quantity' => 1, + 'tax' => 3, + 'category' => Item::PHYSICAL_GOODS, + ], + ], + 'amount' => 27.67, + 'breakdown' => [ + 'item_total' => 21.67, + 'tax_total' => 6, + 'shipping' => null, + 'discount' => null, + 'shipping_discount' => null, + 'handling' => null, + 'insurance' => null, + ], + ] + ]; + + $values = []; + foreach ($data as $testKey => $test) { + $items = []; + foreach ($test['items'] as $key => $item) { + $unitAmount = new Money($item['value'], 'EUR'); + $tax = new Money($item['tax'], 'EUR'); + $items[$key] = Mockery::mock( + Item::class, + [ + 'unit_amount' => $unitAmount, + 'tax' => $tax, + 'quantity'=> $item['quantity'], + 'category' => $item['category'], + ] + ); + + $items[$key]->shouldReceive('to_array')->andReturnUsing(function (bool $roundToFloor = false) use ($unitAmount, $tax, $item) { + return [ + 'unit_amount' => $unitAmount->to_array($roundToFloor), + 'tax' => $tax->to_array(), + 'quantity'=> $item['quantity'], + 'category' => $item['category'], + ]; + }); + + } + + $breakdown = null; + if ($test['breakdown']) { + $breakdown = Mockery::mock(AmountBreakdown::class); + foreach ($test['breakdown'] as $method => $value) { + $breakdown->shouldReceive($method)->andReturnUsing(function () use ($value) { + if (! is_numeric($value)) { + return null; + } + + $money = new Money($value, 'EUR'); + return $money; + }); + } + + $breakdown + ->shouldReceive('to_array') + ->andReturn( + array_map( + function ($value) { + return $value ? (new Money($value, 'EUR'))->to_array() : null; + }, + $test['breakdown'] + ) + ); + } + + $amountMoney = new Money($test['amount'], 'EUR'); + $amount = Mockery::mock(Amount::class); + $amount + ->shouldReceive('to_array') + ->andReturn([ + 'value' => $amountMoney->value_str(), + 'currency_code' => $amountMoney->currency_code(), + 'breakdown' => $breakdown ? $breakdown->to_array() : [], + ]); + $amount->shouldReceive('value_str')->andReturn($amountMoney->value_str()); + $amount->shouldReceive('currency_code')->andReturn('EUR'); + $amount->shouldReceive('breakdown')->andReturn($breakdown); + + $values[$testKey] = [ + $items, + $amount, + $test['expected'], + $test['message'], + ]; + } + + return $values; + } + public function testPayee() { $amount = Mockery::mock(Amount::class); diff --git a/tests/PHPUnit/ApiClient/Factory/ItemFactoryTest.php b/tests/PHPUnit/ApiClient/Factory/ItemFactoryTest.php index 3f72dab07..f8eedb3bb 100644 --- a/tests/PHPUnit/ApiClient/Factory/ItemFactoryTest.php +++ b/tests/PHPUnit/ApiClient/Factory/ItemFactoryTest.php @@ -52,6 +52,14 @@ class ItemFactoryTest extends TestCase $woocommerce->session = $session; $session->shouldReceive('get')->andReturn([]); + when('wp_get_attachment_image_src')->justReturn('image_url'); + $product + ->expects('get_image_id') + ->andReturn(1); + $product + ->expects('get_permalink') + ->andReturn('url'); + $result = $testee->from_wc_cart($cart); $this->assertCount(1, $result); @@ -107,6 +115,13 @@ class ItemFactoryTest extends TestCase $woocommerce->session = $session; $session->shouldReceive('get')->andReturn([]); + when('wp_get_attachment_image_src')->justReturn('image_url'); + $product + ->expects('get_image_id') + ->andReturn(1); + $product + ->expects('get_permalink') + ->andReturn('url'); $result = $testee->from_wc_cart($cart); @@ -132,6 +147,14 @@ class ItemFactoryTest extends TestCase expect('wp_strip_all_tags')->andReturnFirstArg(); expect('strip_shortcodes')->andReturnFirstArg(); + when('wp_get_attachment_image_src')->justReturn('image_url'); + $product + ->expects('get_image_id') + ->andReturn(1); + $product + ->expects('get_permalink') + ->andReturn('url'); + $item = Mockery::mock(\WC_Order_Item_Product::class); $item ->expects('get_product') @@ -217,6 +240,14 @@ class ItemFactoryTest extends TestCase ->expects('get_fees') ->andReturn([]); + when('wp_get_attachment_image_src')->justReturn('image_url'); + $product + ->expects('get_image_id') + ->andReturn(1); + $product + ->expects('get_permalink') + ->andReturn('url'); + $result = $testee->from_wc_order($order); $item = current($result); /** @@ -271,6 +302,14 @@ class ItemFactoryTest extends TestCase ->expects('get_fees') ->andReturn([]); + when('wp_get_attachment_image_src')->justReturn('image_url'); + $product + ->expects('get_image_id') + ->andReturn(1); + $product + ->expects('get_permalink') + ->andReturn('url'); + $result = $testee->from_wc_order($order); $item = current($result); /** diff --git a/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php b/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php index db50265d5..ea8b0ab0e 100644 --- a/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php +++ b/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Onboarding\Helper; -use PHPUnit\Framework\TestCase; +use WooCommerce\PayPalCommerce\TestCase; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use RuntimeException; use function Brain\Monkey\Functions\when; @@ -15,7 +15,7 @@ class OnboardingUrlTest extends TestCase private $user_id = 123; private $onboardingUrl; - protected function setUp(): void + public function setUp(): void { parent::setUp(); diff --git a/tests/PHPUnit/WcGateway/Assets/SettingsPagesAssetsTest.php b/tests/PHPUnit/WcGateway/Assets/SettingsPagesAssetsTest.php index 6bd2ca14f..95aa53e0c 100644 --- a/tests/PHPUnit/WcGateway/Assets/SettingsPagesAssetsTest.php +++ b/tests/PHPUnit/WcGateway/Assets/SettingsPagesAssetsTest.php @@ -27,8 +27,9 @@ class SettingsPagesAssetsTest extends TestCase Mockery::mock(Environment::class), true, array(), - array() - ); + array(), + true + ); when('is_admin') ->justReturn(true); diff --git a/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php b/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php index 32a2366ca..97ceca9b5 100644 --- a/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php +++ b/tests/PHPUnit/WcGateway/Processor/OrderProcessorTest.php @@ -57,6 +57,7 @@ class OrderProcessorTest extends TestCase ->andReturn($payments); $wcOrder = Mockery::mock(\WC_Order::class); + $wcOrder->expects('get_items')->andReturn([]); $wcOrder->expects('update_meta_data') ->with(PayPalGateway::ORDER_PAYMENT_MODE_META_KEY, 'live'); $wcOrder->shouldReceive('get_id')->andReturn(1); @@ -193,7 +194,8 @@ class OrderProcessorTest extends TestCase ->andReturn($payments); $wcOrder = Mockery::mock(\WC_Order::class); - $orderStatus = Mockery::mock(OrderStatus::class); + $wcOrder->expects('get_items')->andReturn([]); + $orderStatus = Mockery::mock(OrderStatus::class); $orderStatus ->shouldReceive('is') ->with(OrderStatus::APPROVED) diff --git a/tests/e2e/PHPUnit/Order/PurchaseUnitTest.php b/tests/e2e/PHPUnit/Order/PurchaseUnitTest.php index 23edee911..6a72c4255 100644 --- a/tests/e2e/PHPUnit/Order/PurchaseUnitTest.php +++ b/tests/e2e/PHPUnit/Order/PurchaseUnitTest.php @@ -364,6 +364,11 @@ class PurchaseUnitTest extends TestCase ], self::adaptAmountFormat([ 'value' => 10.69, + 'breakdown' => [ + 'item_total' => 10.69, + 'tax_total' => 0, + 'shipping' => 0, + ], ]), ]; } @@ -432,6 +437,11 @@ class PurchaseUnitTest extends TestCase ], self::adaptAmountFormat([ 'value' => 10.69, + 'breakdown' => [ + 'item_total' => 10.69, + 'tax_total' => 0, + 'shipping' => 0, + ], ], get_woocommerce_currency()), ]; }