mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-04 08:47:23 +08:00
Merge branch 'trunk' into PCP-1973-wc-payment-token-created-multiple-times-when-webhook-vault-payment-token-created-is-received
This commit is contained in:
commit
090287f0f0
45 changed files with 3510 additions and 1257 deletions
|
@ -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' ) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
101
composer.lock
generated
101
composer.lock
generated
|
@ -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",
|
||||
|
|
|
@ -35,17 +35,26 @@ class Message {
|
|||
*/
|
||||
private $dismissable;
|
||||
|
||||
/**
|
||||
* The wrapper selector that will contain the notice.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $wrapper;
|
||||
|
||||
/**
|
||||
* Message constructor.
|
||||
*
|
||||
* @param string $message The message text.
|
||||
* @param string $type The message type.
|
||||
* @param bool $dismissable Whether the message is dismissable.
|
||||
* @param string $wrapper The wrapper selector that will contain the notice.
|
||||
*/
|
||||
public function __construct( string $message, string $type, bool $dismissable = true ) {
|
||||
public function __construct( string $message, string $type, bool $dismissable = true, string $wrapper = '' ) {
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,9 +41,10 @@ class Renderer implements RendererInterface {
|
|||
$messages = $this->repository->current_message();
|
||||
foreach ( $messages as $message ) {
|
||||
printf(
|
||||
'<div class="notice notice-%s %s"><p>%s</p></div>',
|
||||
'<div class="notice notice-%s %s" %s><p>%s</p></div>',
|
||||
$message->type(),
|
||||
( $message->is_dismissable() ) ? 'is-dismissible' : '',
|
||||
( $message->wrapper() ? sprintf( 'data-ppcp-wrapper="%s"', esc_attr( $message->wrapper() ) ) : '' ),
|
||||
wp_kses_post( $message->message() )
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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' )
|
||||
);
|
||||
|
@ -201,10 +202,7 @@ class WebhookEndpoint {
|
|||
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 204 !== $status_code ) {
|
||||
$json = null;
|
||||
if ( is_array( $response ) ) {
|
||||
$json = json_decode( $response['body'] );
|
||||
}
|
||||
$json = json_decode( $response['body'] ) ?? null;
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
@ -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() ) {
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,6 +174,7 @@ abstract class AbstractCartEndpoint implements EndpointInterface {
|
|||
wc_clear_notices();
|
||||
}
|
||||
|
||||
if ( $send_response ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'name' => '',
|
||||
|
@ -182,6 +184,7 @@ abstract class AbstractCartEndpoint implements EndpointInterface {
|
|||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns product information from request data.
|
||||
|
@ -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 );
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
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 );
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
})
|
||||
}
|
||||
},
|
||||
);
|
49
modules/ppcp-compat/resources/js/tracking-compat.js
Normal file
49
modules/ppcp-compat/resources/js/tracking-compat.js
Normal file
|
@ -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'));
|
||||
})
|
||||
}
|
||||
},
|
||||
);
|
|
@ -11,7 +11,6 @@ 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(
|
||||
|
||||
|
@ -50,7 +49,7 @@ return array(
|
|||
'ppcp-webhooks-status-page',
|
||||
'ppcp-tracking',
|
||||
'ppcp-fraudnet',
|
||||
'ppcp-gzd-compat',
|
||||
'ppcp-tracking-compat',
|
||||
'ppcp-clear-db',
|
||||
);
|
||||
},
|
||||
|
@ -59,6 +58,14 @@ return array(
|
|||
return function_exists( 'wc_gzd_get_shipments_by_order' ); // 3.0+
|
||||
},
|
||||
|
||||
'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.
|
||||
|
@ -75,18 +82,9 @@ return array(
|
|||
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;
|
||||
},
|
||||
|
||||
);
|
||||
|
|
|
@ -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 ) {
|
||||
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_gzd_scripts = $should_enqueue_gzd_scripts;
|
||||
$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' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <a href="https://wordpress.org/plugins/woocommerce-germanized/">Germanized for WooCommerce</a>
|
||||
* 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 <a href="https://woocommerce.com/document/shipment-tracking/">Shipment Tracking</a>
|
||||
* 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 <a href="https://wordpress.org/plugins/yith-woocommerce-order-tracking/">YITH WooCommerce Order & Shipment Tracking</a>
|
||||
* 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;
|
||||
}
|
||||
|
||||
$wc_order = $shipment->get_order();
|
||||
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',
|
||||
);
|
||||
|
||||
$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 );
|
||||
}
|
||||
$shipment_factory = $c->get( 'order-tracking.shipment.factory' );
|
||||
assert( $shipment_factory instanceof ShipmentFactoryInterface );
|
||||
|
||||
try {
|
||||
$tracking_information = $endpoint->get_tracking_information( $wc_order->get_id() );
|
||||
$ppcp_shipment = $shipment_factory->create_shipment(
|
||||
$wc_order_id,
|
||||
$transaction_id,
|
||||
$tracking_number,
|
||||
'SHIPPED',
|
||||
'OTHER',
|
||||
$carrier,
|
||||
$line_items
|
||||
);
|
||||
|
||||
$tracking_data['tracking_number'] = $tracking_information['tracking_number'] ?? '';
|
||||
$tracking_information = $endpoint->get_tracking_information( $wc_order_id, $tracking_number );
|
||||
|
||||
if ( $shipment->get_tracking_id() ) {
|
||||
$tracking_data['tracking_number'] = $shipment->get_tracking_id();
|
||||
}
|
||||
$tracking_information
|
||||
? $endpoint->update_tracking_information( $ppcp_shipment, $wc_order_id )
|
||||
: $endpoint->add_tracking_information( $ppcp_shipment, $wc_order_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
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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/'),
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,15 +7,76 @@ document.addEventListener(
|
|||
return;
|
||||
}
|
||||
|
||||
jQuery(document).on('click', '.submit_tracking_info', function () {
|
||||
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');
|
||||
|
||||
function toggleLineItemsSelectbox() {
|
||||
const selectContainer = document.getElementById('items-select-container');
|
||||
includeAllItemsCheckbox?.addEventListener('change', function(){
|
||||
selectContainer.style.display = includeAllItemsCheckbox.checked ? 'none' : 'block';
|
||||
})
|
||||
}
|
||||
|
||||
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');
|
||||
|
||||
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 carrier = document.querySelector('.ppcp-tracking-carrier');
|
||||
const orderId = document.querySelector('.ppcp-order_id');
|
||||
const submitButton = document.querySelector('.submit_tracking_info');
|
||||
const items = document.querySelector('.ppcp-tracking-items');
|
||||
|
||||
submitButton.setAttribute('disabled', 'disabled');
|
||||
let checkedItems = includeAllItemsCheckbox?.checked || !items ? 0 : Array.from(items.selectedOptions).map(option => option.value)
|
||||
|
||||
toggleLoaderVisibility()
|
||||
fetch(config.ajax.tracking_info.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
@ -28,13 +89,16 @@ document.addEventListener(
|
|||
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,
|
||||
action: submitButton ? submitButton.dataset.action : null,
|
||||
items: checkedItems
|
||||
})
|
||||
}).then(function (res) {
|
||||
return res.json();
|
||||
}).then(function (data) {
|
||||
if (!data.success) {
|
||||
toggleLoaderVisibility()
|
||||
|
||||
if (!data.success || ! data.data.shipment) {
|
||||
jQuery( "<span class='error tracking-info-message'>" + data.data.message + "</span>" ).insertAfter(submitButton);
|
||||
setTimeout(()=> jQuery('.tracking-info-message').remove(),3000);
|
||||
submitButton.removeAttribute('disabled');
|
||||
|
@ -44,11 +108,60 @@ document.addEventListener(
|
|||
|
||||
jQuery( "<span class='success tracking-info-message'>" + data.data.message + "</span>" ).insertAfter(submitButton);
|
||||
setTimeout(()=> jQuery('.tracking-info-message').remove(),3000);
|
||||
|
||||
submitButton.dataset.action = 'update';
|
||||
submitButton.textContent = 'update';
|
||||
submitButton.removeAttribute('disabled');
|
||||
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( "<span class='error tracking-info-message'>" + data.data.message + "</span>" ).insertAfter(updateShipment);
|
||||
setTimeout(()=> jQuery('.tracking-info-message').remove(),3000);
|
||||
console.error(data);
|
||||
throw Error(data.data.message);
|
||||
}
|
||||
|
||||
jQuery( "<span class='success tracking-info-message'>" + data.data.message + "</span>" ).insertAfter(updateShipment);
|
||||
setTimeout(()=> jQuery('.tracking-info-message').remove(),3000);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
handleAddShipment();
|
||||
handleUpdateShipment();
|
||||
toggleLineItemsSelectbox();
|
||||
toggleShipment();
|
||||
toggleShipmentUpdateButtonDisabled();
|
||||
toggleOtherCarrierName();
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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';
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -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<int>,
|
||||
* carrier_name_other?: string,
|
||||
* }
|
||||
* Class OrderTrackingEndpoint
|
||||
*/
|
||||
class OrderTrackingEndpoint {
|
||||
|
@ -45,21 +56,42 @@ 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.
|
||||
|
@ -68,17 +100,26 @@ class OrderTrackingEndpoint {
|
|||
* @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->shipment_factory = $shipment_factory;
|
||||
$this->allowed_statuses = $allowed_statuses;
|
||||
$this->should_use_new_api = $should_use_new_api;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,19 +133,29 @@ 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 );
|
||||
$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 ShipmentInterface $shipment The shipment.
|
||||
* @param int $order_id The order ID.
|
||||
* @throws RuntimeException If problem creating.
|
||||
*
|
||||
* @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.'
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
$error->getMessage(),
|
||||
array(
|
||||
$args = array(
|
||||
'args' => $args,
|
||||
'response' => $response,
|
||||
)
|
||||
);
|
||||
throw $error;
|
||||
$this->throw_runtime_exception( $args, 'create' );
|
||||
}
|
||||
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 201 !== $status_code && ! is_wp_error( $response ) ) {
|
||||
$this->throw_paypal_api_exception( $status_code, $args, $response, 'create' );
|
||||
}
|
||||
|
||||
$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 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Need to ignore Method WP_Error::offsetGet does not exist
|
||||
* Updates the tracking information of a given order with the given shipment.
|
||||
*
|
||||
* @psalm-suppress UndefinedMethod
|
||||
* @param ShipmentInterface $shipment The shipment.
|
||||
* @param int $order_id The order ID.
|
||||
*
|
||||
* @throws RuntimeException If problem updating.
|
||||
*/
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 !== $status_code ) {
|
||||
$error = new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
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 ) ),
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
sprintf(
|
||||
'Failed to create order tracking information. PayPal API response: %1$s',
|
||||
$error->getMessage()
|
||||
),
|
||||
array(
|
||||
|
||||
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,
|
||||
)
|
||||
);
|
||||
throw $error;
|
||||
$this->throw_runtime_exception( $args, 'update' );
|
||||
}
|
||||
|
||||
$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();
|
||||
$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_added', $order_id, $response );
|
||||
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.
|
||||
* @return array|null The tracking information.
|
||||
* @psalm-return TrackingInfo|null
|
||||
* @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 ) : ?array {
|
||||
public function get_tracking_information( int $wc_order_id, string $tracking_number ) : ?ShipmentInterface {
|
||||
$wc_order = wc_get_order( $wc_order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
throw new RuntimeException( 'wrong order ID' );
|
||||
}
|
||||
|
||||
if ( ! $wc_order->meta_exists( '_ppcp_paypal_tracking_number' ) ) {
|
||||
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.'
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
$error->getMessage(),
|
||||
array(
|
||||
$args = 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.'
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
$error->getMessage(),
|
||||
array(
|
||||
$args = 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<string, mixed> $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<string, mixed> $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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
?>
|
||||
<div class="ppcp-tracking-columns-wrapper">
|
||||
<div class="ppcp-tracking-column">
|
||||
<h3><?php echo esc_html__( 'Add New Shipment Tracking to PayPal order', 'woocommerce-paypal-payments' ); ?></h3>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( self::NAME_PREFIX ); ?>-transaction_id"><?php echo esc_html__( 'Transaction ID', 'woocommerce-paypal-payments' ); ?></label>
|
||||
<input type="text" disabled class="<?php echo esc_attr( self::NAME_PREFIX ); ?>-transaction_id" id="<?php echo esc_attr( self::NAME_PREFIX ); ?>-transaction_id" name="<?php echo esc_attr( self::NAME_PREFIX ); ?>[transaction_id]" value="<?php echo esc_html( $transaction_id ); ?>"/></p>
|
||||
<label for="ppcp-tracking-transaction_id"><?php echo esc_html__( 'Transaction ID', 'woocommerce-paypal-payments' ); ?></label>
|
||||
<input type="text" disabled class="ppcp-tracking-transaction_id disabled" id="ppcp-tracking-transaction_id" name="ppcp-tracking[transaction_id]" value="<?php echo esc_attr( $transaction_id ); ?>" />
|
||||
</p>
|
||||
<?php if ( $order_item_count > 1 && $this->should_use_new_api ) : ?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( self::NAME_PREFIX ); ?>-tracking_number"><?php echo esc_html__( 'Tracking Number', 'woocommerce-paypal-payments' ); ?></label>
|
||||
<input type="text" class="<?php echo esc_attr( self::NAME_PREFIX ); ?>-tracking_number" id="<?php echo esc_attr( self::NAME_PREFIX ); ?>-tracking_number" name="<?php echo esc_attr( self::NAME_PREFIX ); ?>[tracking_number]" value="<?php echo esc_html( $tracking_number ); ?>"/></p>
|
||||
<label for="include-all-items"><?php echo esc_html__( 'Include All Products', 'woocommerce-paypal-payments' ); ?></label>
|
||||
<input type="checkbox" id="include-all-items" checked>
|
||||
<div id="items-select-container">
|
||||
<label for="ppcp-tracking-items"><?php echo esc_html__( 'Select items for this shipment', 'woocommerce-paypal-payments' ); ?></label>
|
||||
<select multiple class="wc-enhanced-select ppcp-tracking-items" id="ppcp-tracking-items" name="ppcp-tracking[items]">
|
||||
<?php foreach ( $order_items as $item ) : ?>
|
||||
<option value="<?php echo intval( $item->get_id() ); ?>"><?php echo esc_html( $item->get_name() ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr( self::NAME_PREFIX ); ?>-status"><?php echo esc_html__( 'Status', 'woocommerce-paypal-payments' ); ?></label>
|
||||
<select class="<?php echo esc_attr( self::NAME_PREFIX ); ?>-status" id="<?php echo esc_attr( self::NAME_PREFIX ); ?>-status" name="<?php echo esc_attr( self::NAME_PREFIX ); ?>[status]">
|
||||
<?php foreach ( $statuses as $key => $status ) : ?>
|
||||
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $status_value, $key ); ?>><?php echo esc_html( $status ); ?></option>
|
||||
<label for="ppcp-tracking-tracking_number"><?php echo esc_html__( 'Tracking Number*', 'woocommerce-paypal-payments' ); ?></label>
|
||||
<input type="text" class="ppcp-tracking-tracking_number" id="ppcp-tracking-tracking_number" name="ppcp-tracking[tracking_number]" />
|
||||
</p>
|
||||
<p>
|
||||
<label for="ppcp-tracking-status"><?php echo esc_html__( 'Status', 'woocommerce-paypal-payments' ); ?></label>
|
||||
<select class="wc-enhanced-select ppcp-tracking-status" id="ppcp-tracking-status" name="ppcp-tracking[status]">
|
||||
<?php foreach ( $this->allowed_statuses as $status_key => $status ) : ?>
|
||||
<option value="<?php echo esc_attr( $status_key ); ?>"><?php echo esc_html( $status ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="ppcp-tracking-carrier"><?php echo esc_html__( 'Carrier', 'woocommerce-paypal-payments' ); ?></label>
|
||||
<select class="ppcp-tracking-carrier" id="ppcp-tracking-carrier" name="ppcp-tracking[carrier]">
|
||||
<option value=""><?php echo esc_html__( 'Select Carrier', 'woocommerce-paypal-payments' ); ?></option>
|
||||
<select class="wc-enhanced-select ppcp-tracking-carrier" id="ppcp-tracking-carrier" name="ppcp-tracking[carrier]">
|
||||
<?php
|
||||
foreach ( $carriers as $carrier ) :
|
||||
foreach ( $this->carriers as $carrier ) :
|
||||
if ( empty( $carrier ) ) {
|
||||
continue;
|
||||
}
|
||||
$country = $carrier['name'] ?? '';
|
||||
$carrier_items = $carrier['items'] ?? array();
|
||||
?>
|
||||
<optgroup label="<?php echo esc_attr( $country ); ?>">
|
||||
<?php foreach ( $carrier_items as $carrier_code => $carrier_name ) : ?>
|
||||
<option value="<?php echo esc_attr( $carrier_code ); ?>" <?php selected( $carrier_value, $carrier_code ); ?>><?php echo esc_html( $carrier_name ); ?></option>
|
||||
<option value="<?php echo esc_attr( $carrier_code ); ?>"><?php echo esc_html( $carrier_name ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</optgroup>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</p>
|
||||
<input type="hidden" class="ppcp-order_id" name="<?php echo esc_attr( self::NAME_PREFIX ); ?>[order_id]" value="<?php echo (int) $wc_order->get_id(); ?>"/>
|
||||
<p>
|
||||
<button type="button" class="button submit_tracking_info" data-action="<?php echo esc_attr( $action ); ?>"><?php echo esc_html( ucfirst( $action ) ); ?></button></p>
|
||||
<p class="hidden">
|
||||
<label for="ppcp-tracking-carrier_name_other"><?php echo esc_html__( 'Carrier Name*', 'woocommerce-paypal-payments' ); ?></label>
|
||||
<input type="text" class="ppcp-tracking-carrier_name_other" id="ppcp-tracking-carrier_name_other" name="ppcp-tracking[carrier_name_other]" />
|
||||
</p>
|
||||
<input type="hidden" class="ppcp-tracking-order_id" name="ppcp-tracking[order_id]" value="<?php echo (int) $wc_order->get_id(); ?>"/>
|
||||
<p><button type="button" class="button submit_tracking_info"><?php echo esc_html__( 'Add Shipment', 'woocommerce-paypal-payments' ); ?></button></p>
|
||||
</div>
|
||||
<div class="ppcp-tracking-column shipments">
|
||||
<h3><?php echo esc_html__( 'Shipments', 'woocommerce-paypal-payments' ); ?></h3>
|
||||
<?php
|
||||
foreach ( $shipments as $shipment ) {
|
||||
$shipment->render( $this->allowed_statuses );
|
||||
}
|
||||
?>
|
||||
<?php if ( empty( $shipments ) ) : ?>
|
||||
<p><?php echo esc_html__( 'No PayPal Shipment Tracking added to this order yet. Add new Shipment Tracking or reload the page to refresh', 'woocommerce-paypal-payments' ); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="blockUI blockOverlay ppcp-tracking-loader"></div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\OrderTracking;
|
||||
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||
use WooCommerce\PayPalCommerce\Compat\AdminContextTrait;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use Exception;
|
||||
|
@ -23,13 +22,14 @@ use WooCommerce\PayPalCommerce\OrderTracking\Endpoint\OrderTrackingEndpoint;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsListener;
|
||||
|
||||
/**
|
||||
* Class OrderTrackingModule
|
||||
*/
|
||||
class OrderTrackingModule implements ModuleInterface {
|
||||
|
||||
use AdminContextTrait;
|
||||
public const PPCP_TRACKING_INFO_META_NAME = '_ppcp_paypal_tracking_info_meta_name';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
|
@ -48,61 +48,30 @@ class OrderTrackingModule implements ModuleInterface {
|
|||
* @throws NotFoundException
|
||||
*/
|
||||
public function run( ContainerInterface $c ): void {
|
||||
$settings = $c->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 );
|
||||
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
assert( $logger instanceof LoggerInterface );
|
||||
add_action( 'wc_ajax_' . OrderTrackingEndpoint::ENDPOINT, array( $endpoint, 'handle_request' ) );
|
||||
|
||||
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() ) {
|
||||
if ( ! $tracking_enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$asset_loader = $c->get( 'order-tracking.assets' );
|
||||
assert( $asset_loader instanceof OrderEditPageAssets );
|
||||
|
||||
$asset_loader->register();
|
||||
$asset_loader->enqueue();
|
||||
}
|
||||
);
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
assert( $logger instanceof LoggerInterface );
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
285
modules/ppcp-order-tracking/src/Shipment/Shipment.php
Normal file
285
modules/ppcp-order-tracking/src/Shipment/Shipment.php
Normal file
|
@ -0,0 +1,285 @@
|
|||
<?php
|
||||
/**
|
||||
* The Shipment.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\OrderTracking\Shipment
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\OrderTracking\Shipment;
|
||||
|
||||
use WC_Order;
|
||||
use WC_Order_Item_Product;
|
||||
use WC_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Item;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\OrderTracking\OrderTrackingModule;
|
||||
|
||||
/**
|
||||
* Class Shipment
|
||||
*/
|
||||
class Shipment implements ShipmentInterface {
|
||||
|
||||
/**
|
||||
* The WC order ID.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $wc_order_id;
|
||||
|
||||
/**
|
||||
* The transaction ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $transaction_id;
|
||||
|
||||
/**
|
||||
* The tracking number.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tracking_number;
|
||||
|
||||
/**
|
||||
* The shipment status.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $status;
|
||||
|
||||
/**
|
||||
* The shipment carrier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $carrier;
|
||||
|
||||
/**
|
||||
* The shipment carrier name for "OTHER".
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $carrier_name_other;
|
||||
/**
|
||||
* The list of shipment line item IDs.
|
||||
*
|
||||
* @var int[]
|
||||
*/
|
||||
protected $line_items;
|
||||
|
||||
/**
|
||||
* Shipment constructor.
|
||||
*
|
||||
* @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 $status The shipment status.
|
||||
* @param string $carrier The shipment carrier.
|
||||
* @param string $carrier_name_other The shipment carrier name for "OTHER".
|
||||
* @param int[] $line_items The list of shipment line item IDs.
|
||||
*/
|
||||
public function __construct(
|
||||
int $wc_order_id,
|
||||
string $transaction_id,
|
||||
string $tracking_number,
|
||||
string $status,
|
||||
string $carrier,
|
||||
string $carrier_name_other,
|
||||
array $line_items
|
||||
) {
|
||||
$this->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();
|
||||
?>
|
||||
<div class="ppcp-shipment closed">
|
||||
<div class="ppcp-shipment-header">
|
||||
<h4><?php echo esc_html__( 'Shipment: ', 'woocommerce-paypal-payments' ); ?><?php echo esc_html( $tracking_number ); ?></h4>
|
||||
<button type="button" class="shipment-toggle-indicator" aria-expanded="false">
|
||||
<span class="toggle-indicator" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="ppcp-shipment-info hidden">
|
||||
<p><strong><?php echo esc_html__( 'Tracking Number:', 'woocommerce-paypal-payments' ); ?></strong> <span><?php echo esc_html( $tracking_number ); ?></span></p>
|
||||
<p><strong><?php echo esc_html__( 'Carrier:', 'woocommerce-paypal-payments' ); ?></strong> <span><?php echo esc_html( $carrier ); ?></span></p>
|
||||
<?php if ( $carrier === 'OTHER' ) : ?>
|
||||
<p><strong><?php echo esc_html__( 'Carrier Name:', 'woocommerce-paypal-payments' ); ?></strong> <span><?php echo esc_html( $carrier_name_other ); ?></span></p>
|
||||
<?php endif; ?>
|
||||
<?php $this->render_shipment_line_item_info(); ?>
|
||||
<label for="ppcp-shipment-status"><?php echo esc_html__( 'Status', 'woocommerce-paypal-payments' ); ?></label>
|
||||
<select class="wc-enhanced-select ppcp-shipment-status" id="ppcp-shipment-status" name="ppcp-shipment-status">
|
||||
<?php foreach ( $allowed_statuses as $status_key => $status ) : ?>
|
||||
<option value="<?php echo esc_attr( $status_key ); ?>" <?php selected( $status_key, $this->status() ); ?>><?php echo esc_html( $status ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<input type="hidden" class="ppcp-shipment-tacking_number" name="ppcp-shipment-tacking_number" value="<?php echo esc_html( $tracking_number ); ?>"/>
|
||||
<input type="hidden" class="ppcp-shipment-carrier" name="ppcp-shipment-carrier" value="<?php echo esc_html( $carrier ); ?>"/>
|
||||
<input type="hidden" class="ppcp-shipment-carrier-other" name="ppcp-shipment-carrier-other" value="<?php echo esc_html( $carrier_name_other ); ?>"/>
|
||||
<button type="button" class="button button-disabled update_shipment"><?php echo esc_html__( 'Update Status', 'woocommerce-paypal-payments' ); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function to_array(): array {
|
||||
$shipment = array(
|
||||
'transaction_id' => $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 = '<p><strong>%1$s</strong> <span>%2$s</span></p>';
|
||||
$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(
|
||||
'#<span class="wc-order-item-sku">%1$s</span>',
|
||||
esc_html( $sku )
|
||||
);
|
||||
|
||||
$order_items_info_markup = sprintf(
|
||||
'<strong>%1$s</strong>%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 ) )
|
||||
);
|
||||
}
|
||||
}
|
31
modules/ppcp-order-tracking/src/Shipment/ShipmentFactory.php
Normal file
31
modules/ppcp-order-tracking/src/Shipment/ShipmentFactory.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
/**
|
||||
* The ShipmentFactory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\OrderTracking\Shipment
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\OrderTracking\Shipment;
|
||||
|
||||
/**
|
||||
* Class ShipmentFactory
|
||||
*/
|
||||
class ShipmentFactory implements ShipmentFactoryInterface {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function create_shipment(
|
||||
int $wc_order_id,
|
||||
string $transaction_id,
|
||||
string $tracking_number,
|
||||
string $status,
|
||||
string $carrier,
|
||||
string $carrier_name_other,
|
||||
array $line_items
|
||||
): ShipmentInterface {
|
||||
return new Shipment( $wc_order_id, $transaction_id, $tracking_number, $status, $carrier, $carrier_name_other, $line_items );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/**
|
||||
* The ShipmentFactory interface.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\OrderTracking\Shipment
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\OrderTracking\Shipment;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Can create order tracking shipment
|
||||
*/
|
||||
interface ShipmentFactoryInterface {
|
||||
|
||||
/**
|
||||
* Returns the new shipment instance.
|
||||
*
|
||||
* @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 $status The shipment status.
|
||||
* @param string $carrier The shipment carrier.
|
||||
* @param string $carrier_name_other The shipment carrier name for "OTHER".
|
||||
* @param int[] $line_items The list of shipment line item IDs.
|
||||
*
|
||||
* @return ShipmentInterface
|
||||
* @throws RuntimeException If problem creating.
|
||||
*/
|
||||
public function create_shipment(
|
||||
int $wc_order_id,
|
||||
string $transaction_id,
|
||||
string $tracking_number,
|
||||
string $status,
|
||||
string $carrier,
|
||||
string $carrier_name_other,
|
||||
array $line_items
|
||||
): ShipmentInterface;
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
/**
|
||||
* The Shipment interface.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\OrderTracking\Shipment
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\OrderTracking\Shipment;
|
||||
|
||||
/**
|
||||
* Represents order tracking shipment
|
||||
*
|
||||
* @psalm-type LineItemId = int
|
||||
* @psalm-type LineItemMap = array{
|
||||
* name: string,
|
||||
* unit_amount: array{currency_code: string, value: string},
|
||||
* quantity: int,
|
||||
* description: string,
|
||||
* sku: string,
|
||||
* category: string,
|
||||
* tax?: array{currency_code: string, value: string},
|
||||
* tax_rate?: string
|
||||
* }
|
||||
* @psalm-type shipmentMap = array{
|
||||
* transaction_id: string,
|
||||
* tracking_number: string,
|
||||
* status: string,
|
||||
* carrier: string,
|
||||
* items: array<LineItemMap>,
|
||||
* 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<int, array<string, scalar>> The map of shipment line item ID to line item map.
|
||||
* @psalm-return array<LineItemId, LineItemMap>
|
||||
*/
|
||||
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<string, scalar> The map of shipment object.
|
||||
* @psalm-return shipmentMap
|
||||
*/
|
||||
public function to_array(): array;
|
||||
}
|
|
@ -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.
|
||||
|
|
10
modules/ppcp-wc-gateway/resources/js/common.js
Normal file
10
modules/ppcp-wc-gateway/resources/js/common.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import moveWrappedElements from "./common/wrapped-elements";
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
() => {
|
||||
// Wait for current execution context to end.
|
||||
setTimeout(function () {
|
||||
moveWrappedElements();
|
||||
}, 0);
|
||||
}
|
||||
);
|
|
@ -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;
|
|
@ -215,7 +215,15 @@ return array(
|
|||
$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 );
|
||||
$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 <a> tag.
|
||||
__( 'Enable %1$sshipment tracking information%2$s to be sent to PayPal for seller protection features.', 'woocommerce-paypal-payments' ),
|
||||
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#shipment-tracking" target="_blank">',
|
||||
'</a>'
|
||||
);
|
||||
|
||||
if ( 'DE' === $container->get( 'api.shop.country' ) ) {
|
||||
$tracking_label .= '<br/>' . sprintf(
|
||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||
__( 'Required when %1$sPay upon Invoice%2$s is used.', 'woocommerce-paypal-payments' ),
|
||||
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#pay-upon-invoice-PUI" target="_blank">',
|
||||
'</a>'
|
||||
);
|
||||
}
|
||||
|
||||
$is_tracking_available = $container->get( 'order-tracking.is-tracking-available' );
|
||||
|
||||
if ( $is_tracking_available ) {
|
||||
return $tracking_label;
|
||||
}
|
||||
|
||||
$tracking_label .= '<br/>' . sprintf(
|
||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||
__(
|
||||
' To use tracking features, you must %1$senable tracking on your account%2$s.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'<a
|
||||
href="https://docs.woocommerce.com/document/woocommerce-paypal-payments/#enable-tracking-on-your-live-account"
|
||||
target="_blank"
|
||||
>',
|
||||
'</a>'
|
||||
);
|
||||
|
||||
return $tracking_label;
|
||||
},
|
||||
'wcgateway.enable-dcc-url-sandbox' => static function ( ContainerInterface $container ): string {
|
||||
return 'https://www.sandbox.paypal.com/bizsignup/entry/product/ppcp';
|
||||
},
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -290,12 +290,14 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
// in the constructor, so must do it here.
|
||||
global $theorder;
|
||||
if ( $theorder instanceof WC_Order ) {
|
||||
if ( $theorder->get_payment_method() === self::ID ) {
|
||||
$payment_method_title = $theorder->get_payment_method_title();
|
||||
if ( $payment_method_title ) {
|
||||
$this->title = $payment_method_title;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::get_title();
|
||||
}
|
||||
|
|
|
@ -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 ) {
|
||||
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 <a href="%2$s">PayPal currency support page</a> 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/'
|
||||
'<a href="' . esc_url( $paypal_currency_support_url ) . '">',
|
||||
'</a>'
|
||||
);
|
||||
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 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -402,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',
|
||||
|
|
|
@ -77,6 +77,8 @@ class HeaderRenderer {
|
|||
'</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="ppcp-notice-wrapper"></div>
|
||||
';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -613,29 +613,4 @@ class SettingsListener {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent enabling tracking if it is not enabled for merchant account.
|
||||
*
|
||||
* @throws RuntimeException When API request fails.
|
||||
*/
|
||||
public function listen_for_tracking_enabled(): void {
|
||||
if ( State::STATE_ONBOARDED !== $this->state->current_state() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$token = $this->bearer->bearer();
|
||||
if ( ! $token->is_tracking_available() ) {
|
||||
$this->settings->set( 'tracking_enabled', false );
|
||||
$this->settings->persist();
|
||||
return;
|
||||
}
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$this->settings->set( 'tracking_enabled', false );
|
||||
$this->settings->persist();
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,7 +183,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();
|
||||
}
|
||||
|
@ -496,7 +497,6 @@ class WCGatewayModule implements ModuleInterface {
|
|||
|
||||
try {
|
||||
$listener->listen_for_vaulting_enabled();
|
||||
$listener->listen_for_tracking_enabled();
|
||||
} catch ( RuntimeException $exception ) {
|
||||
add_action(
|
||||
'admin_notices',
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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],
|
||||
];
|
||||
|
||||
|
|
|
@ -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);
|
||||
/**
|
||||
|
|
|
@ -27,7 +27,8 @@ class SettingsPagesAssetsTest extends TestCase
|
|||
Mockery::mock(Environment::class),
|
||||
true,
|
||||
array(),
|
||||
array()
|
||||
array(),
|
||||
true
|
||||
);
|
||||
|
||||
when('is_admin')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue