diff --git a/.env.e2e.example b/.env.e2e.example index 079cdb856..c8023a9cb 100644 --- a/.env.e2e.example +++ b/.env.e2e.example @@ -2,6 +2,8 @@ PPCP_E2E_WP_DIR=${ROOT_DIR}/.ddev/wordpress BASEURL="https://woocommerce-paypal-payments.ddev.site" +PRODUCT_URL="/product/prod" + CUSTOMER_EMAIL="customer@example.com" CUSTOMER_PASSWORD="password" diff --git a/.psalm/stubs.php b/.psalm/stubs.php index bdf5e45c5..8b4990022 100644 --- a/.psalm/stubs.php +++ b/.psalm/stubs.php @@ -9,6 +9,10 @@ if (!defined('HOUR_IN_SECONDS')) { define('HOUR_IN_SECONDS', 60 * MINUTE_IN_SECONDS); } +if (!defined('ABSPATH')) { + define('ABSPATH', ''); +} + /** * Cancel the next occurrence of a scheduled action. * diff --git a/README.md b/README.md index 676d60ffe..d640960c9 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ Enable xdebug via `$ ddev xdebug`, and press `Start Listening for PHP Debug Conn After creating the server in the PHPStorm dialog, you need to set the local project path for the server plugin path. It should look [like this](https://i.imgur.com/ofsF1Mc.png). +See [tests/playwright](tests/playwright) for e2e (browser-based) tests. + ## Test account setup You will need a PayPal sandbox merchant and customer accounts to configure the plugin and make test purchases with it. diff --git a/api/order-functions.php b/api/order-functions.php new file mode 100644 index 000000000..f477651fd --- /dev/null +++ b/api/order-functions.php @@ -0,0 +1,109 @@ +get_meta( PayPalGateway::ORDER_ID_META_KEY ); + if ( ! $paypal_id_or_wc_order ) { + throw new InvalidArgumentException( 'PayPal order ID not found in meta.' ); + } + } + if ( ! is_string( $paypal_id_or_wc_order ) ) { + throw new InvalidArgumentException( 'Invalid PayPal order ID, string expected.' ); + } + + $order_endpoint = PPCP::container()->get( 'api.endpoint.order' ); + assert( $order_endpoint instanceof OrderEndpoint ); + + return $order_endpoint->order( $paypal_id_or_wc_order ); +} + +/** + * Captures the PayPal order. + * + * @param WC_Order $wc_order The WC order. + * @throws InvalidArgumentException When the order cannot be captured. + * @throws Exception When the operation fails. + */ +function ppcp_capture_order( WC_Order $wc_order ): void { + $intent = strtoupper( (string) $wc_order->get_meta( PayPalGateway::INTENT_META_KEY ) ); + + if ( $intent !== 'AUTHORIZE' ) { + throw new InvalidArgumentException( 'Only orders with "authorize" intent can be captured.' ); + } + $captured = wc_string_to_bool( $wc_order->get_meta( AuthorizedPaymentsProcessor::CAPTURED_META_KEY ) ); + if ( $captured ) { + throw new InvalidArgumentException( 'The order is already captured.' ); + } + + $authorized_payment_processor = PPCP::container()->get( 'wcgateway.processor.authorized-payments' ); + assert( $authorized_payment_processor instanceof AuthorizedPaymentsProcessor ); + + if ( ! $authorized_payment_processor->capture_authorized_payment( $wc_order ) ) { + throw new RuntimeException( 'Capture failed.' ); + } +} + +/** + * Refunds the PayPal order. + * Note that you can use wc_refund_payment() to trigger the refund in WC and PayPal. + * + * @param WC_Order $wc_order The WC order. + * @param float $amount The refund amount. + * @param string $reason The reason for the refund. + * @return string The PayPal refund ID. + * @throws InvalidArgumentException When the order cannot be refunded. + * @throws Exception When the operation fails. + */ +function ppcp_refund_order( WC_Order $wc_order, float $amount, string $reason = '' ): string { + $order = ppcp_get_paypal_order( $wc_order ); + + $refund_processor = PPCP::container()->get( 'wcgateway.processor.refunds' ); + assert( $refund_processor instanceof RefundProcessor ); + + return $refund_processor->refund( $order, $wc_order, $amount, $reason ); +} + +/** + * Voids the authorization. + * + * @param WC_Order $wc_order The WC order. + * @throws InvalidArgumentException When the order cannot be voided. + * @throws Exception When the operation fails. + */ +function ppcp_void_order( WC_Order $wc_order ): void { + $order = ppcp_get_paypal_order( $wc_order ); + + $refund_processor = PPCP::container()->get( 'wcgateway.processor.refunds' ); + assert( $refund_processor instanceof RefundProcessor ); + + $refund_processor->void( $order ); +} diff --git a/bootstrap.php b/bootstrap.php index 7b8f7271d..fe4552110 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -19,6 +19,11 @@ return function ( array $additional_containers = array(), array $additional_modules = array() ): ContainerInterface { + /** + * Skip path check. + * + * @psalm-suppress UnresolvableInclude + */ $modules = ( require "$root_dir/modules.php" )( $root_dir ); $modules = array_merge( $modules, $additional_modules ); @@ -41,7 +46,12 @@ return function ( // TODO: caching does not work currently, // may want to consider fixing it later (pass proxy as parent to DelegatingContainer) // for now not fixed since we were using this behavior for long time and fixing it now may break things. - $container = new DelegatingContainer( $provider ); + $container = new DelegatingContainer( $provider ); + /** + * Skip iterable vs array check. + * + * @psalm-suppress PossiblyInvalidArgument + */ $app_container = new CachingContainer( new CompositeContainer( array_merge( diff --git a/changelog.txt b/changelog.txt index 7e19ac016..610e98732 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,16 @@ *** Changelog *** += 2.0.4 - TBD = +* Fix - Allow Pay Later in mini-cart #1221 +* Fix - Duplicated auth error when credentials become wrong #1229 +* Fix - Webhook issues when switching sandbox, and delete all webhooks when unsubscribing #1239 +* Fix - High volume of traffic from merchant-integrations endpoint #1273 +* Enhancement - Remove shortcodes from description #1226 +* Enhancement - Handle price suffix with price for product button check #1234 +* Enhancement - Show funding source as payment method #1220 +* Enhancement - Change "Enabled" to "Available" in status text #1237 +* Enhancement - Programmatically capturing/voiding authorized payments #590 + = 2.0.3 - 2023-03-14 = * Fix - `DEVICE_DATA_NOT_AVAILABLE` error message when FraudNet is enabled #1177 * Fix - Redirect to connection tab after manual credentials input #1201 diff --git a/composer.json b/composer.json index e149ce2ed..4a94eb54b 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,10 @@ "psr-4": { "WooCommerce\\PayPalCommerce\\": "src", "WooCommerce\\PayPalCommerce\\Vendor\\": "lib/packages/" - } + }, + "files": [ + "api/order-functions.php" + ] }, "autoload-dev": { "psr-4": { diff --git a/composer.lock b/composer.lock index ce4259243..295f011ee 100644 --- a/composer.lock +++ b/composer.lock @@ -35,6 +35,10 @@ ], "description": "Promoting container interoperability through standard service providers", "homepage": "https://github.com/container-interop/service-provider", + "support": { + "issues": "https://github.com/container-interop/service-provider/issues", + "source": "https://github.com/container-interop/service-provider/tree/master" + }, "time": "2017-09-20T14:13:36+00:00" }, { @@ -86,6 +90,10 @@ } ], "description": "Interfaces for human readable string interoperation.", + "support": { + "issues": "https://github.com/Dhii/human-readable-interface/issues", + "source": "https://github.com/Dhii/human-readable-interface/tree/v0.2.0-alpha1" + }, "time": "2021-03-05T00:36:01+00:00" }, { @@ -134,6 +142,10 @@ } ], "description": "Interfaces for modules", + "support": { + "issues": "https://github.com/Dhii/module-interface/issues", + "source": "https://github.com/Dhii/module-interface/tree/v0.3.0-alpha2" + }, "time": "2021-08-23T08:23:01+00:00" }, { @@ -177,6 +189,10 @@ } ], "description": "Interfaces for package-related interop", + "support": { + "issues": "https://github.com/Dhii/package-interface/issues", + "source": "https://github.com/Dhii/package-interface/tree/v0.1.0-alpha4" + }, "time": "2021-12-08T15:57:36+00:00" }, { @@ -225,6 +241,10 @@ } ], "description": "A base interface for validators", + "support": { + "issues": "https://github.com/Dhii/validation-interface/issues", + "source": "https://github.com/Dhii/validation-interface/tree/v0.3.0-alpha3" + }, "time": "2021-01-14T16:19:20+00:00" }, { @@ -268,6 +288,10 @@ } ], "description": "Implementation for dealing with SemVer-compliant versions", + "support": { + "issues": "https://github.com/Dhii/versions/issues", + "source": "https://github.com/Dhii/versions/tree/v0.1.0-alpha3" + }, "time": "2021-12-08T16:54:50+00:00" }, { @@ -312,6 +336,10 @@ "container-interop", "psr" ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.1" + }, "time": "2021-03-05T17:36:06+00:00" }, { @@ -359,6 +387,9 @@ "psr", "psr-3" ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, "time": "2021-05-03T11:20:27+00:00" }, { @@ -399,6 +430,10 @@ } ], "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, "time": "2019-03-08T08:55:37+00:00" }, { @@ -465,6 +500,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -528,6 +566,10 @@ } ], "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" + }, "time": "2021-02-24T05:28:06+00:00" }, { @@ -576,6 +618,10 @@ } ], "description": "Interfaces for interop within WordPress", + "support": { + "issues": "https://github.com/wp-oop/wordpress-interface/issues", + "source": "https://github.com/wp-oop/wordpress-interface/tree/v0.1.0-alpha2" + }, "time": "2021-04-30T09:37:37+00:00" } ], @@ -656,6 +702,11 @@ "non-blocking", "promise" ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.2" + }, "funding": [ { "url": "https://github.com/amphp", @@ -697,12 +748,12 @@ } }, "autoload": { - "psr-4": { - "Amp\\ByteStream\\": "lib" - }, "files": [ "lib/functions.php" - ] + ], + "psr-4": { + "Amp\\ByteStream\\": "lib" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -728,6 +779,11 @@ "non-blocking", "stream" ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + }, "funding": [ { "url": "https://github.com/amphp", @@ -738,16 +794,16 @@ }, { "name": "antecedent/patchwork", - "version": "2.1.21", + "version": "2.1.25", "source": { "type": "git", "url": "https://github.com/antecedent/patchwork.git", - "reference": "25c1fa0cd9a6e6d0d13863d8df8f050b6733f16d" + "reference": "17314e042d45e0dacb0a494c2d1ef50e7621136a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antecedent/patchwork/zipball/25c1fa0cd9a6e6d0d13863d8df8f050b6733f16d", - "reference": "25c1fa0cd9a6e6d0d13863d8df8f050b6733f16d", + "url": "https://api.github.com/repos/antecedent/patchwork/zipball/17314e042d45e0dacb0a494c2d1ef50e7621136a", + "reference": "17314e042d45e0dacb0a494c2d1ef50e7621136a", "shasum": "" }, "require": { @@ -778,7 +834,11 @@ "runkit", "testing" ], - "time": "2022-02-07T07:28:34+00:00" + "support": { + "issues": "https://github.com/antecedent/patchwork/issues", + "source": "https://github.com/antecedent/patchwork/tree/2.1.25" + }, + "time": "2023-02-19T12:51:24+00:00" }, { "name": "brain/monkey", @@ -844,6 +904,10 @@ "test", "testing" ], + "support": { + "issues": "https://github.com/Brain-WP/BrainMonkey/issues", + "source": "https://github.com/Brain-WP/BrainMonkey" + }, "time": "2021-11-11T15:53:55+00:00" }, { @@ -899,6 +963,10 @@ } ], "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" + }, "funding": [ { "url": "https://packagist.com", @@ -966,6 +1034,10 @@ "regex", "regular expression" ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/2.1.0" + }, "funding": [ { "url": "https://packagist.com", @@ -1042,6 +1114,11 @@ "validation", "versioning" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" + }, "funding": [ { "url": "https://packagist.com", @@ -1103,6 +1180,11 @@ "Xdebug", "performance" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, "funding": [ { "url": "https://packagist.com", @@ -1188,6 +1270,10 @@ "stylecheck", "tests" ], + "support": { + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, "time": "2022-02-04T12:51:07+00:00" }, { @@ -1239,6 +1325,10 @@ } ], "description": "A highly ISP-compliant collection of interfaces that represent maps and lists.", + "support": { + "issues": "https://github.com/Dhii/collections-interface/issues", + "source": "https://github.com/Dhii/collections-interface/tree/v0.3.0" + }, "time": "2021-10-06T10:56:09+00:00" }, { @@ -1295,6 +1385,10 @@ "PSR-11", "container" ], + "support": { + "issues": "https://github.com/Dhii/containers/issues", + "source": "https://github.com/Dhii/containers/tree/v0.1.4" + }, "time": "2021-10-06T11:13:51+00:00" }, { @@ -1328,6 +1422,10 @@ "MIT" ], "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, "time": "2019-12-04T15:06:13+00:00" }, { @@ -1380,6 +1478,10 @@ "constructor", "instantiate" ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", @@ -1435,6 +1537,10 @@ } ], "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, "time": "2021-06-11T22:34:44+00:00" }, { @@ -1487,28 +1593,32 @@ "php", "server" ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" + }, "time": "2022-03-02T22:36:06+00:00" }, { "name": "graham-campbell/result-type", - "version": "v1.1.0", + "version": "v1.1.1", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "a878d45c1914464426dc94da61c9e1d36ae262a8" + "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/a878d45c1914464426dc94da61c9e1d36ae262a8", - "reference": "a878d45c1914464426dc94da61c9e1d36ae262a8", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", + "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9" + "phpoption/phpoption": "^1.9.1" }, "require-dev": { - "phpunit/phpunit": "^8.5.28 || ^9.5.21" + "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" }, "type": "library", "autoload": { @@ -1535,6 +1645,10 @@ "Result-Type", "result" ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.1" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -1545,7 +1659,7 @@ "type": "tidelift" } ], - "time": "2022-07-30T15:56:11+00:00" + "time": "2023-02-25T20:23:15+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -1592,6 +1706,10 @@ "keywords": [ "test" ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, "time": "2020-07-09T08:09:16+00:00" }, { @@ -1657,20 +1775,24 @@ "test double", "testing" ], + "support": { + "issues": "https://github.com/mockery/mockery/issues", + "source": "https://github.com/mockery/mockery/tree/1.3.6" + }, "time": "2022-09-07T15:05:49+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.11.0", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "shasum": "" }, "require": { @@ -1706,13 +1828,17 @@ "object", "object graph" ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", "type": "tidelift" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2023-03-08T13:26:56+00:00" }, { "name": "netresearch/jsonmapper", @@ -1758,20 +1884,25 @@ } ], "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0" + }, "time": "2022-12-08T20:46:14+00:00" }, { "name": "nikic/php-parser", - "version": "v4.15.3", + "version": "v4.15.4", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", "shasum": "" }, "require": { @@ -1810,7 +1941,11 @@ "parser", "php" ], - "time": "2023-01-16T22:05:37+00:00" + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + }, + "time": "2023-03-05T19:49:14+00:00" }, { "name": "openlss/lib-array2xml", @@ -1859,6 +1994,10 @@ "xml", "xml conversion" ], + "support": { + "issues": "https://github.com/nullivex/lib-array2xml/issues", + "source": "https://github.com/nullivex/lib-array2xml/tree/master" + }, "time": "2019-03-29T20:06:56+00:00" }, { @@ -1915,6 +2054,10 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, "time": "2021-07-20T11:28:43+00:00" }, { @@ -1962,6 +2105,10 @@ } ], "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, "time": "2022-02-21T01:04:05+00:00" }, { @@ -2002,6 +2149,10 @@ "woocommerce", "wordpress" ], + "support": { + "issues": "https://github.com/php-stubs/woocommerce-stubs/issues", + "source": "https://github.com/php-stubs/woocommerce-stubs/tree/v5.9.1" + }, "time": "2022-04-30T06:35:48+00:00" }, { @@ -2045,6 +2196,10 @@ "static analysis", "wordpress" ], + "support": { + "issues": "https://github.com/php-stubs/wordpress-stubs/issues", + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v5.9.5" + }, "time": "2022-11-09T05:32:14+00:00" }, { @@ -2103,6 +2258,10 @@ "phpcs", "standards" ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibility" + }, "time": "2019-12-27T09:44:58+00:00" }, { @@ -2156,6 +2315,10 @@ "standards", "static analysis" ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" + }, "time": "2022-10-25T01:46:02+00:00" }, { @@ -2207,6 +2370,10 @@ "static analysis", "wordpress" ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" + }, "time": "2022-10-24T09:00:36+00:00" }, { @@ -2256,6 +2423,10 @@ "reflection", "static analysis" ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, "time": "2020-06-27T09:03:43+00:00" }, { @@ -2309,6 +2480,10 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, "time": "2021-10-19T17:43:47+00:00" }, { @@ -2355,28 +2530,32 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" + }, "time": "2022-03-15T21:29:03+00:00" }, { "name": "phpoption/phpoption", - "version": "1.9.0", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "dc5ff11e274a90cc1c743f66c9ad700ce50db9ab" + "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dc5ff11e274a90cc1c743f66c9ad700ce50db9ab", - "reference": "dc5ff11e274a90cc1c743f66c9ad700ce50db9ab", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e", + "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8", - "phpunit/phpunit": "^8.5.28 || ^9.5.21" + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" }, "type": "library", "extra": { @@ -2416,6 +2595,10 @@ "php", "type" ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.1" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -2426,7 +2609,7 @@ "type": "tidelift" } ], - "time": "2022-07-30T15:51:26+00:00" + "time": "2023-02-25T19:38:58+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2489,6 +2672,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2545,6 +2732,10 @@ "filesystem", "iterator" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2592,6 +2783,10 @@ "keywords": [ "template" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" + }, "time": "2015-06-21T13:50:34+00:00" }, { @@ -2641,6 +2836,16 @@ "keywords": [ "timer" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-11-30T08:20:02+00:00" }, { @@ -2690,21 +2895,31 @@ "keywords": [ "tokenizer" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", + "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "abandoned": true, "time": "2021-07-26T12:15:06+00:00" }, { "name": "phpunit/phpunit", - "version": "8.5.31", + "version": "8.5.33", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "33c126b09a42de5c99e5e8032b54e8221264a74e" + "reference": "7d1ff0e8c6b35db78ff13e3e05517d7cbf7aa32e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/33c126b09a42de5c99e5e8032b54e8221264a74e", - "reference": "33c126b09a42de5c99e5e8032b54e8221264a74e", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7d1ff0e8c6b35db78ff13e3e05517d7cbf7aa32e", + "reference": "7d1ff0e8c6b35db78ff13e3e05517d7cbf7aa32e", "shasum": "" }, "require": { @@ -2770,6 +2985,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.33" + }, "funding": [ { "url": "https://phpunit.de/sponsors.html", @@ -2784,7 +3003,7 @@ "type": "tidelift" } ], - "time": "2022-10-28T05:57:37+00:00" + "time": "2023-02-27T13:04:50+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -2829,6 +3048,16 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-11-30T08:15:22+00:00" }, { @@ -2893,6 +3122,10 @@ "compare", "equality" ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.5" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2955,6 +3188,16 @@ "unidiff", "unified diff" ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-11-30T07:59:04+00:00" }, { @@ -3008,6 +3251,16 @@ "environment", "hhvm" ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-11-30T07:53:42+00:00" }, { @@ -3075,6 +3328,10 @@ "export", "exporter" ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.5" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3135,6 +3392,10 @@ "keywords": [ "global state" ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3188,6 +3449,16 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-11-30T07:40:27+00:00" }, { @@ -3233,6 +3504,16 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-11-30T07:37:18+00:00" }, { @@ -3286,6 +3567,16 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-11-30T07:34:24+00:00" }, { @@ -3328,6 +3619,16 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-11-30T07:30:19+00:00" }, { @@ -3374,6 +3675,10 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3423,20 +3728,24 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/master" + }, "time": "2016-10-03T07:35:21+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.7.1", + "version": "3.7.2", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619" + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", "shasum": "" }, "require": { @@ -3472,22 +3781,28 @@ "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", "keywords": [ "phpcs", - "standards" + "standards", + "static analysis" ], - "time": "2022-06-18T07:21:10+00:00" + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2023-02-22T23:07:41+00:00" }, { "name": "symfony/console", - "version": "v5.4.17", + "version": "v5.4.21", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "58422fdcb0e715ed05b385f70d3e8b5ed4bbd45f" + "reference": "c77433ddc6cdc689caf48065d9ea22ca0853fbd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/58422fdcb0e715ed05b385f70d3e8b5ed4bbd45f", - "reference": "58422fdcb0e715ed05b385f70d3e8b5ed4bbd45f", + "url": "https://api.github.com/repos/symfony/console/zipball/c77433ddc6cdc689caf48065d9ea22ca0853fbd9", + "reference": "c77433ddc6cdc689caf48065d9ea22ca0853fbd9", "shasum": "" }, "require": { @@ -3556,6 +3871,9 @@ "console", "terminal" ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.4.21" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3570,7 +3888,7 @@ "type": "tidelift" } ], - "time": "2022-12-28T14:15:31+00:00" + "time": "2023-02-25T16:59:41+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3620,6 +3938,9 @@ ], "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3699,6 +4020,9 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3777,6 +4101,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3858,6 +4185,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3938,6 +4268,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4014,6 +4347,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4094,6 +4430,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4112,16 +4451,16 @@ }, { "name": "symfony/string", - "version": "v5.4.17", + "version": "v5.4.21", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "55733a8664b8853b003e70251c58bc8cb2d82a6b" + "reference": "edac10d167b78b1d90f46a80320d632de0bd9f2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/55733a8664b8853b003e70251c58bc8cb2d82a6b", - "reference": "55733a8664b8853b003e70251c58bc8cb2d82a6b", + "url": "https://api.github.com/repos/symfony/string/zipball/edac10d167b78b1d90f46a80320d632de0bd9f2f", + "reference": "edac10d167b78b1d90f46a80320d632de0bd9f2f", "shasum": "" }, "require": { @@ -4177,6 +4516,9 @@ "utf-8", "utf8" ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.4.21" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4191,7 +4533,7 @@ "type": "tidelift" } ], - "time": "2022-12-12T15:54:21+00:00" + "time": "2023-02-22T08:00:55+00:00" }, { "name": "theseer/tokenizer", @@ -4231,6 +4573,10 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, "funding": [ { "url": "https://github.com/theseer", @@ -4341,6 +4687,10 @@ "inspection", "php" ], + "support": { + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm/tree/4.30.0" + }, "time": "2022-11-06T20:37:08+00:00" }, { @@ -4411,6 +4761,10 @@ "env", "environment" ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -4475,6 +4829,10 @@ "check", "validate" ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, "time": "2022-06-03T18:03:27+00:00" }, { @@ -4521,6 +4879,10 @@ } ], "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", + "support": { + "issues": "https://github.com/webmozart/path-util/issues", + "source": "https://github.com/webmozart/path-util/tree/2.3.0" + }, "abandoned": "symfony/filesystem", "time": "2015-12-17T08:42:14+00:00" }, @@ -4562,6 +4924,10 @@ "woocommerce", "wordpress" ], + "support": { + "issues": "https://github.com/woocommerce/woocommerce-sniffs/issues", + "source": "https://github.com/woocommerce/woocommerce-sniffs/tree/0.1.3" + }, "time": "2022-02-17T15:34:51+00:00" }, { @@ -4608,6 +4974,11 @@ "standards", "wordpress" ], + "support": { + "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", + "source": "https://github.com/WordPress/WordPress-Coding-Standards", + "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" + }, "time": "2020-05-13T23:57:56+00:00" } ], @@ -4620,9 +4991,9 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0, ^7.2 | ^8.0", + "php": "^7.2 | ^8.0", "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.3.0" } diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index e2ad9c05d..1b1eb3da0 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient; +use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer; @@ -177,12 +178,13 @@ return array( $patch_collection_factory = $container->get( 'api.factory.patch-collection-factory' ); $logger = $container->get( 'woocommerce.logger.woocommerce' ); - /** - * The settings. - * - * @var Settings $settings - */ - $settings = $container->get( 'wcgateway.settings' ); + $session_handler = $container->get( 'session.handler' ); + assert( $session_handler instanceof SessionHandler ); + $bn_code = $session_handler->bn_code(); + + $settings = $container->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); + $intent = $settings->has( 'intent' ) && strtoupper( (string) $settings->get( 'intent' ) ) === 'AUTHORIZE' ? 'AUTHORIZE' : 'CAPTURE'; $application_context_repository = $container->get( 'api.repository.application-context' ); $subscription_helper = $container->get( 'subscription.helper' ); @@ -196,7 +198,8 @@ return array( $application_context_repository, $subscription_helper, $container->get( 'wcgateway.is-fraudnet-enabled' ), - $container->get( 'wcgateway.fraudnet' ) + $container->get( 'wcgateway.fraudnet' ), + $bn_code ); }, 'api.endpoint.billing-agreements' => static function ( ContainerInterface $container ): BillingAgreementsEndpoint { diff --git a/modules/ppcp-wc-gateway/extensions.php b/modules/ppcp-wc-gateway/extensions.php index 363d12292..7b3c1b127 100644 --- a/modules/ppcp-wc-gateway/extensions.php +++ b/modules/ppcp-wc-gateway/extensions.php @@ -9,10 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway; -use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; use WooCommerce\PayPalCommerce\Onboarding\Environment; -use WooCommerce\PayPalCommerce\Session\SessionHandler; -use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\WooCommerce\Logging\Logger\NullLogger; use WooCommerce\WooCommerce\Logging\Logger\WooCommerceLogger; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; @@ -52,41 +49,6 @@ return array( $settings = $container->get( 'wcgateway.settings' ); return $settings->has( 'prefix' ) ? (string) $settings->get( 'prefix' ) : 'WC-'; }, - 'api.endpoint.order' => static function ( ContainerInterface $container ): OrderEndpoint { - $order_factory = $container->get( 'api.factory.order' ); - $patch_collection_factory = $container->get( 'api.factory.patch-collection-factory' ); - $logger = $container->get( 'woocommerce.logger.woocommerce' ); - /** - * The session handler. - * - * @var SessionHandler $session_handler - */ - $session_handler = $container->get( 'session.handler' ); - $bn_code = $session_handler->bn_code(); - - /** - * The settings. - * - * @var Settings $settings - */ - $settings = $container->get( 'wcgateway.settings' ); - $intent = $settings->has( 'intent' ) && strtoupper( (string) $settings->get( 'intent' ) ) === 'AUTHORIZE' ? 'AUTHORIZE' : 'CAPTURE'; - $application_context_repository = $container->get( 'api.repository.application-context' ); - $subscription_helper = $container->get( 'subscription.helper' ); - return new OrderEndpoint( - $container->get( 'api.host' ), - $container->get( 'api.bearer' ), - $order_factory, - $patch_collection_factory, - $intent, - $logger, - $application_context_repository, - $subscription_helper, - $container->get( 'wcgateway.is-fraudnet-enabled' ), - $container->get( 'wcgateway.fraudnet' ), - $bn_code - ); - }, 'woocommerce.logger.woocommerce' => function ( ContainerInterface $container ): LoggerInterface { if ( ! function_exists( 'wc_get_logger' ) || ! $container->get( 'wcgateway.logging.is-enabled' ) ) { return new NullLogger(); diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 7e3ff7eca..6a95c98a2 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -231,8 +231,11 @@ return array( 'wcgateway.settings.sections-renderer' => static function ( ContainerInterface $container ): SectionsRenderer { return new SectionsRenderer( $container->get( 'wcgateway.current-ppcp-settings-page-id' ), - $container->get( 'wcgateway.settings.sections' ), - $container->get( 'onboarding.state' ) + $container->get( 'onboarding.state' ), + $container->get( 'wcgateway.helper.dcc-product-status' ), + $container->get( 'api.helpers.dccapplies' ), + $container->get( 'button.helper.messages-apply' ), + $container->get( 'wcgateway.pay-upon-invoice-product-status' ) ); }, 'wcgateway.settings.header-renderer' => static function ( ContainerInterface $container ): HeaderRenderer { @@ -241,52 +244,6 @@ return array( $container->get( 'wcgateway.url' ) ); }, - 'wcgateway.settings.sections' => static function ( ContainerInterface $container ): array { - $sections = array( - Settings::CONNECTION_TAB_ID => __( 'Connection', 'woocommerce-paypal-payments' ), - PayPalGateway::ID => __( 'Standard Payments', 'woocommerce-paypal-payments' ), - Settings::PAY_LATER_TAB_ID => __( 'Pay Later', 'woocommerce-paypal-payments' ), - CreditCardGateway::ID => __( 'Advanced Card Processing', 'woocommerce-paypal-payments' ), - CardButtonGateway::ID => __( 'Standard Card Button', 'woocommerce-paypal-payments' ), - OXXOGateway::ID => __( 'OXXO', 'woocommerce-paypal-payments' ), - PayUponInvoiceGateway::ID => __( 'Pay upon Invoice', 'woocommerce-paypal-payments' ), - ); - - // Remove for all not registered in WC gateways that cannot render anything in this case. - $gateways = WC()->payment_gateways->payment_gateways(); - foreach ( array_diff( - array_keys( $sections ), - array( Settings::CONNECTION_TAB_ID, PayPalGateway::ID, CreditCardGateway::ID, Settings::PAY_LATER_TAB_ID ) - ) as $id ) { - if ( ! isset( $gateways[ $id ] ) ) { - unset( $sections[ $id ] ); - } - } - - $dcc_product_status = $container->get( 'wcgateway.helper.dcc-product-status' ); - assert( $dcc_product_status instanceof DCCProductStatus ); - $dcc_applies = $container->get( 'api.helpers.dccapplies' ); - assert( $dcc_applies instanceof DccApplies ); - if ( ! $dcc_product_status->dcc_is_active() || ! $dcc_applies->for_country_currency() ) { - unset( $sections['ppcp-credit-card-gateway'] ); - } - - $messages_apply = $container->get( 'button.helper.messages-apply' ); - assert( $messages_apply instanceof MessagesApply ); - - if ( ! $messages_apply->for_country() ) { - unset( $sections[ Settings::PAY_LATER_TAB_ID ] ); - } - - $pui_product_status = $container->get( 'wcgateway.pay-upon-invoice-product-status' ); - assert( $pui_product_status instanceof PayUponInvoiceProductStatus ); - - if ( ! $pui_product_status->pui_is_active() ) { - unset( $sections[ PayUponInvoiceGateway::ID ] ); - } - - return $sections; - }, 'wcgateway.settings.status' => static function ( ContainerInterface $container ): SettingsStatus { $settings = $container->get( 'wcgateway.settings' ); return new SettingsStatus( $settings ); diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php index a047c511f..1f485a84d 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayUponInvoice/PayUponInvoice.php @@ -423,15 +423,19 @@ class PayUponInvoice { * @psalm-suppress MissingClosureParamType */ function ( $methods ) { - if ( ! is_array( $methods ) || State::STATE_ONBOARDED !== $this->state->current_state() ) { + if ( + ! is_array( $methods ) + || State::STATE_ONBOARDED !== $this->state->current_state() + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + || ! ( is_checkout() || isset( $_GET['pay_for_order'] ) && $_GET['pay_for_order'] === 'true' ) + ) { return $methods; } if ( ! $this->pui_product_status->pui_is_active() || ! $this->pui_helper->is_checkout_ready_for_pui() - // phpcs:ignore WordPress.Security.NonceVerification.Recommended - || ( isset( $_GET['pay_for_order'] ) && $_GET['pay_for_order'] === 'true' && ! $this->pui_helper->is_pay_for_order_ready_for_pui() ) + || ! $this->pui_helper->is_pay_for_order_ready_for_pui() ) { unset( $methods[ PayUponInvoiceGateway::ID ] ); } @@ -440,31 +444,6 @@ class PayUponInvoice { } ); - add_action( - 'woocommerce_settings_checkout', - function () { - if ( - PayUponInvoiceGateway::ID === $this->current_ppcp_settings_page_id - && ! $this->pui_product_status->pui_is_active() - ) { - $gateway_settings = get_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings' ); - $gateway_enabled = $gateway_settings['enabled'] ?? ''; - if ( 'yes' === $gateway_enabled ) { - $gateway_settings['enabled'] = 'no'; - update_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings', $gateway_settings ); - $redirect_url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-pay-upon-invoice-gateway' ); - wp_safe_redirect( $redirect_url ); - exit; - } - - printf( - '

%1$s

', - esc_html__( 'Could not enable gateway because the connected PayPal account is not activated for Pay upon Invoice. Reconnect your account while Onboard with Pay upon Invoice is selected to try again.', 'woocommerce-paypal-payments' ) - ); - } - } - ); - add_action( 'woocommerce_update_options_checkout_ppcp-pay-upon-invoice-gateway', function () { @@ -509,6 +488,19 @@ class PayUponInvoice { current_ppcp_settings_page_id ) { + $pui_gateway = WC()->payment_gateways->payment_gateways()[ PayUponInvoiceGateway::ID ]; + if ( 'yes' === $pui_gateway->get_option( 'enabled' ) ) { + $pui_gateway->update_option( 'enabled', 'no' ); + $redirect_url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-pay-upon-invoice-gateway' ); + wp_safe_redirect( $redirect_url ); + exit; + } + + printf( + '

%1$s

', + esc_html__( 'Could not enable gateway because the connected PayPal account is not activated for Pay upon Invoice. Reconnect your account while Onboard with Pay upon Invoice is selected to try again.', 'woocommerce-paypal-payments' ) + ); } } ); diff --git a/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php b/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php index 02e676dad..dbe279865 100644 --- a/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php +++ b/modules/ppcp-wc-gateway/src/Helper/DCCProductStatus.php @@ -102,11 +102,11 @@ class DCCProductStatus { return (bool) $this->cache->get( self::DCC_STATUS_CACHE_KEY ); } - if ( is_bool( $this->current_status_cache ) ) { + if ( $this->current_status_cache === true ) { return $this->current_status_cache; } - if ( $this->settings->has( 'products_dcc_enabled' ) && $this->settings->get( 'products_dcc_enabled' ) ) { + if ( $this->settings->has( 'products_dcc_enabled' ) && $this->settings->get( 'products_dcc_enabled' ) === true ) { $this->current_status_cache = true; return true; } diff --git a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php index 9b80bddd2..6c7b40c8f 100644 --- a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php +++ b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceProductStatus.php @@ -91,10 +91,10 @@ class PayUponInvoiceProductStatus { return (bool) $this->cache->get( self::PUI_STATUS_CACHE_KEY ); } - if ( is_bool( $this->current_status_cache ) ) { + if ( $this->current_status_cache === true ) { return $this->current_status_cache; } - if ( $this->settings->has( 'products_pui_enabled' ) && $this->settings->get( 'products_pui_enabled' ) ) { + if ( $this->settings->has( 'products_pui_enabled' ) && $this->settings->get( 'products_pui_enabled' ) === true ) { $this->current_status_cache = true; return true; } diff --git a/modules/ppcp-wc-gateway/src/Processor/RefundProcessor.php b/modules/ppcp-wc-gateway/src/Processor/RefundProcessor.php index 16f9b4698..0cfea30ad 100644 --- a/modules/ppcp-wc-gateway/src/Processor/RefundProcessor.php +++ b/modules/ppcp-wc-gateway/src/Processor/RefundProcessor.php @@ -11,12 +11,14 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Processor; use Exception; use Psr\Log\LoggerInterface; +use WC_Order; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\Amount; use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\Money; +use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\Payments; use WooCommerce\PayPalCommerce\ApiClient\Entity\Refund; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; @@ -70,7 +72,7 @@ class RefundProcessor { /** * Processes a refund. * - * @param \WC_Order $wc_order The WooCommerce order. + * @param WC_Order $wc_order The WooCommerce order. * @param float|null $amount The refund amount. * @param string $reason The reason for the refund. * @@ -78,7 +80,7 @@ class RefundProcessor { * * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.Missing */ - public function process( \WC_Order $wc_order, float $amount = null, string $reason = '' ) : bool { + public function process( WC_Order $wc_order, float $amount = null, string $reason = '' ) : bool { try { $order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY ); if ( ! $order_id ) { @@ -87,15 +89,7 @@ class RefundProcessor { $order = $this->order_endpoint->order( $order_id ); - $purchase_units = $order->purchase_units(); - if ( ! $purchase_units ) { - throw new RuntimeException( 'No purchase units.' ); - } - - $payments = $purchase_units[0]->payments(); - if ( ! $payments ) { - throw new RuntimeException( 'No payments.' ); - } + $payments = $this->get_payments( $order ); $this->logger->debug( sprintf( @@ -109,39 +103,13 @@ class RefundProcessor { switch ( $mode ) { case self::REFUND_MODE_REFUND: - $captures = $payments->captures(); - if ( ! $captures ) { - throw new RuntimeException( 'No capture.' ); - } - - $capture = $captures[0]; - $refund = new Refund( - $capture, - $capture->invoice_id(), - $reason, - new Amount( - new Money( $amount, $wc_order->get_currency() ) - ) - ); - $refund_id = $this->payments_endpoint->refund( $refund ); + $refund_id = $this->refund( $order, $wc_order, $amount, $reason ); $this->add_refund_to_meta( $wc_order, $refund_id ); break; case self::REFUND_MODE_VOID: - $voidable_authorizations = array_filter( - $payments->authorizations(), - function ( Authorization $authorization ): bool { - return $authorization->is_voidable(); - } - ); - if ( ! $voidable_authorizations ) { - throw new RuntimeException( 'No voidable authorizations.' ); - } - - foreach ( $voidable_authorizations as $authorization ) { - $this->payments_endpoint->void( $authorization ); - } + $this->void( $order ); $wc_order->set_status( 'refunded' ); $wc_order->save(); @@ -158,6 +126,67 @@ class RefundProcessor { } } + /** + * Adds a refund to the PayPal order. + * + * @param Order $order The PayPal order. + * @param WC_Order $wc_order The WooCommerce order. + * @param float $amount The refund amount. + * @param string $reason The reason for the refund. + * + * @throws RuntimeException When operation fails. + * @return string The PayPal refund ID. + */ + public function refund( + Order $order, + WC_Order $wc_order, + float $amount, + string $reason = '' + ): string { + $payments = $this->get_payments( $order ); + + $captures = $payments->captures(); + if ( ! $captures ) { + throw new RuntimeException( 'No capture.' ); + } + + $capture = $captures[0]; + $refund = new Refund( + $capture, + $capture->invoice_id(), + $reason, + new Amount( + new Money( $amount, $wc_order->get_currency() ) + ) + ); + + return $this->payments_endpoint->refund( $refund ); + } + + /** + * Voids the authorization. + * + * @param Order $order The PayPal order. + * @throws RuntimeException When operation fails. + */ + public function void( Order $order ): void { + $payments = $this->get_payments( $order ); + + $voidable_authorizations = array_filter( + $payments->authorizations(), + function ( Authorization $authorization ): bool { + return $authorization->is_voidable(); + } + ); + if ( ! $voidable_authorizations ) { + throw new RuntimeException( 'No voidable authorizations.' ); + } + + foreach ( $voidable_authorizations as $authorization ) { + $this->payments_endpoint->void( $authorization ); + } + } + /** * Determines the refunding mode. * @@ -181,4 +210,24 @@ class RefundProcessor { return self::REFUND_MODE_UNKNOWN; } + + /** + * Returns the payments object or throws. + * + * @param Order $order The order. + * @throws RuntimeException When payment not available. + */ + protected function get_payments( Order $order ): Payments { + $purchase_units = $order->purchase_units(); + if ( ! $purchase_units ) { + throw new RuntimeException( 'No purchase units.' ); + } + + $payments = $purchase_units[0]->payments(); + if ( ! $payments ) { + throw new RuntimeException( 'No payments.' ); + } + + return $payments; + } } diff --git a/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php b/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php index 753caef1a..bc61c64a9 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php +++ b/modules/ppcp-wc-gateway/src/Settings/SectionsRenderer.php @@ -9,8 +9,16 @@ declare( strict_types=1 ); namespace WooCommerce\PayPalCommerce\WcGateway\Settings; +use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; +use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; use WooCommerce\PayPalCommerce\Onboarding\State; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOGateway; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway; +use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus; +use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus; /** * Class SectionsRenderer @@ -26,13 +34,6 @@ class SectionsRenderer { */ protected $page_id; - /** - * Key - page/gateway ID, value - displayed text. - * - * @var array - */ - protected $sections; - /** * The onboarding state. * @@ -40,17 +41,65 @@ class SectionsRenderer { */ private $state; + /** + * The DCC product status + * + * @var DCCProductStatus + */ + private $dcc_product_status; + + /** + * The DCC applies + * + * @var DccApplies + */ + private $dcc_applies; + + /** + * The messages apply. + * + * @var MessagesApply + */ + private $messages_apply; + + /** + * The PUI product status. + * + * @var PayUponInvoiceProductStatus + */ + private $pui_product_status; + /** * SectionsRenderer constructor. * * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page. - * @param array $sections Key - page/gateway ID, value - displayed text. * @param State $state The onboarding state. */ - public function __construct( string $page_id, array $sections, State $state ) { - $this->page_id = $page_id; - $this->sections = $sections; - $this->state = $state; + + /** + * SectionsRenderer constructor. + * + * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page. + * @param State $state The onboarding state. + * @param DCCProductStatus $dcc_product_status The DCC product status. + * @param DccApplies $dcc_applies The DCC applies. + * @param MessagesApply $messages_apply The Messages apply. + * @param PayUponInvoiceProductStatus $pui_product_status The PUI product status. + */ + public function __construct( + string $page_id, + State $state, + DCCProductStatus $dcc_product_status, + DccApplies $dcc_applies, + MessagesApply $messages_apply, + PayUponInvoiceProductStatus $pui_product_status + ) { + $this->page_id = $page_id; + $this->state = $state; + $this->dcc_product_status = $dcc_product_status; + $this->dcc_applies = $dcc_applies; + $this->messages_apply = $messages_apply; + $this->pui_product_status = $pui_product_status; } /** @@ -66,6 +115,8 @@ class SectionsRenderer { /** * Renders the Sections tab. + * + * @return string */ public function render(): string { if ( ! $this->should_render() ) { @@ -74,8 +125,8 @@ class SectionsRenderer { $html = '