Merge branch 'PCP-591-save-and-display-vaulted-payment-methods-in-woo-commerce-native-endpoint' into PCP-991-detach-vaulting-from-wc-subscriptions-support

This commit is contained in:
emilicastells 2022-12-20 11:30:32 +01:00
commit 2008b9d9a8
No known key found for this signature in database
GPG key ID: 1520C07081754570
49 changed files with 2091 additions and 875 deletions

View file

@ -8,3 +8,23 @@ if (!defined('MONTH_IN_SECONDS')) {
if (!defined('HOUR_IN_SECONDS')) {
define('HOUR_IN_SECONDS', 60 * MINUTE_IN_SECONDS);
}
/**
* Cancel the next occurrence of a scheduled action.
*
* While only the next instance of a recurring or cron action is unscheduled by this method, that will also prevent
* all future instances of that recurring or cron action from being run. Recurring and cron actions are scheduled in
* a sequence instead of all being scheduled at once. Each successive occurrence of a recurring action is scheduled
* only after the former action is run. If the next instance is never run, because it's unscheduled by this function,
* then the following instance will never be scheduled (or exist), which is effectively the same as being unscheduled
* by this method also.
*
* @param string $hook The hook that the job will trigger.
* @param array $args Args that would have been passed to the job.
* @param string $group The group the job is assigned to.
*
* @return string|null The scheduled action ID if a scheduled action was found, or null if no matching action found.
*/
function as_unschedule_action($hook, $args = array(), $group = '')
{
}

412
composer.lock generated
View file

@ -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",
@ -778,6 +834,10 @@
"runkit",
"testing"
],
"support": {
"issues": "https://github.com/antecedent/patchwork/issues",
"source": "https://github.com/antecedent/patchwork/tree/2.1.21"
},
"time": "2022-02-07T07:28:34+00:00"
},
{
@ -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",
@ -917,16 +985,16 @@
},
{
"name": "composer/pcre",
"version": "2.0.2",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
"reference": "36d3086c793f32a9a9e853a0abe8e19ec9d936fa"
"reference": "3fdb2807b31a78a40ad89570e30ec77466c98717"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/36d3086c793f32a9a9e853a0abe8e19ec9d936fa",
"reference": "36d3086c793f32a9a9e853a0abe8e19ec9d936fa",
"url": "https://api.github.com/repos/composer/pcre/zipball/3fdb2807b31a78a40ad89570e30ec77466c98717",
"reference": "3fdb2807b31a78a40ad89570e30ec77466c98717",
"shasum": ""
},
"require": {
@ -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",
@ -980,7 +1052,7 @@
"type": "tidelift"
}
],
"time": "2022-11-03T20:20:28+00:00"
"time": "2022-11-16T18:32:04+00:00"
},
{
"name": "composer/semver",
@ -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.4.1"
},
"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,6 +1593,10 @@
"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"
},
{
@ -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.0"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
@ -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,6 +1775,10 @@
"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"
},
{
@ -1706,6 +1828,10 @@
"object",
"object graph"
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.0"
},
"funding": [
{
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
@ -1716,16 +1842,16 @@
},
{
"name": "netresearch/jsonmapper",
"version": "v4.0.0",
"version": "v4.1.0",
"source": {
"type": "git",
"url": "https://github.com/cweiske/jsonmapper.git",
"reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d"
"reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d",
"reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f",
"reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f",
"shasum": ""
},
"require": {
@ -1758,7 +1884,12 @@
}
],
"description": "Map nested JSON structures onto PHP classes",
"time": "2020-12-01T19:48:11+00:00"
"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",
@ -1810,6 +1941,10 @@
"parser",
"php"
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2"
},
"time": "2022-11-12T15:38:23+00:00"
},
{
@ -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,6 +2530,10 @@
}
],
"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"
},
{
@ -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.0"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
@ -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,6 +2895,16 @@
"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"
},
@ -2770,6 +2985,10 @@
"testing",
"xunit"
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.31"
},
"funding": [
{
"url": "https://phpunit.de/sponsors.html",
@ -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,6 +3728,10 @@
],
"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"
},
{
@ -3474,20 +3783,25 @@
"phpcs",
"standards"
],
"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": "2022-06-18T07:21:10+00:00"
},
{
"name": "symfony/console",
"version": "v5.4.15",
"version": "v5.4.16",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "ea59bb0edfaf9f28d18d8791410ee0355f317669"
"reference": "8e9b9c8dfb33af6057c94e1b44846bee700dc5ef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/ea59bb0edfaf9f28d18d8791410ee0355f317669",
"reference": "ea59bb0edfaf9f28d18d8791410ee0355f317669",
"url": "https://api.github.com/repos/symfony/console/zipball/8e9b9c8dfb33af6057c94e1b44846bee700dc5ef",
"reference": "8e9b9c8dfb33af6057c94e1b44846bee700dc5ef",
"shasum": ""
},
"require": {
@ -3556,6 +3870,9 @@
"console",
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v5.4.16"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@ -3570,7 +3887,7 @@
"type": "tidelift"
}
],
"time": "2022-10-26T21:41:52+00:00"
"time": "2022-11-25T14:09:27+00:00"
},
{
"name": "symfony/deprecation-contracts",
@ -3620,6 +3937,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 +4019,9 @@
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@ -3777,6 +4100,9 @@
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@ -3858,6 +4184,9 @@
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@ -3938,6 +4267,9 @@
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@ -4014,6 +4346,9 @@
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@ -4094,6 +4429,9 @@
"interoperability",
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v2.5.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@ -4177,6 +4515,9 @@
"utf-8",
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v5.4.15"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@ -4231,6 +4572,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 +4686,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 +4760,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 +4828,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 +4878,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 +4923,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 +4973,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 +4990,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",
"php": "^7.2 | ^8.0",
"ext-json": "*"
},
"platform-dev": [],
"plugin-api-version": "1.1.0"
"plugin-api-version": "2.3.0"
}

View file

@ -25,6 +25,7 @@ return function ( string $root_dir ): iterable {
( require "$modules_dir/ppcp-webhooks/module.php" )(),
( require "$modules_dir/ppcp-vaulting/module.php" )(),
( require "$modules_dir/ppcp-order-tracking/module.php" )(),
( require "$modules_dir/ppcp-uninstall/module.php" )(),
);
return $modules;

View file

@ -198,7 +198,9 @@ return array(
$logger,
$application_context_repository,
$paypal_request_id,
$subscription_helper
$subscription_helper,
$container->get( 'wcgateway.is-fraudnet-enabled' ),
$container->get( 'wcgateway.fraudnet' )
);
},
'api.endpoint.billing-agreements' => static function ( ContainerInterface $container ): BillingAgreementsEndpoint {

View file

@ -29,6 +29,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository
use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
use WP_Error;
/**
@ -94,6 +95,20 @@ class OrderEndpoint {
*/
private $application_context_repository;
/**
* True if FraudNet support is enabled in settings, otherwise false.
*
* @var bool
*/
protected $is_fraudnet_enabled;
/**
* The FraudNet entity.
*
* @var FraudNet
*/
protected $fraudnet;
/**
* The BN Code.
*
@ -120,6 +135,8 @@ class OrderEndpoint {
* @param ApplicationContextRepository $application_context_repository The application context repository.
* @param PayPalRequestIdRepository $paypal_request_id_repository The paypal request id repository.
* @param SubscriptionHelper $subscription_helper The subscription helper.
* @param bool $is_fraudnet_enabled true if FraudNet support is enabled in settings, otherwise false.
* @param FraudNet $fraudnet The FraudNet entity.
* @param string $bn_code The BN Code.
*/
public function __construct(
@ -132,6 +149,8 @@ class OrderEndpoint {
ApplicationContextRepository $application_context_repository,
PayPalRequestIdRepository $paypal_request_id_repository,
SubscriptionHelper $subscription_helper,
bool $is_fraudnet_enabled,
FraudNet $fraudnet,
string $bn_code = ''
) {
@ -144,7 +163,9 @@ class OrderEndpoint {
$this->application_context_repository = $application_context_repository;
$this->bn_code = $bn_code;
$this->paypal_request_id_repository = $paypal_request_id_repository;
$this->is_fraudnet_enabled = $is_fraudnet_enabled;
$this->subscription_helper = $subscription_helper;
$this->fraudnet = $fraudnet;
}
/**
@ -224,6 +245,11 @@ class OrderEndpoint {
if ( $this->bn_code ) {
$args['headers']['PayPal-Partner-Attribution-Id'] = $this->bn_code;
}
if ( $this->is_fraudnet_enabled ) {
$args['headers']['PayPal-Client-Metadata-Id'] = $this->fraudnet->session_id();
}
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(

View file

@ -26,7 +26,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNet;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PaymentSource;
use WP_Error;

View file

@ -208,6 +208,40 @@ class PaymentTokenEndpoint {
return wp_remote_retrieve_response_code( $response ) === 204;
}
/**
* Deletes payment token by the given id.
*
* @param string $token_id Token id.
* @return bool
*
* @throws RuntimeException If something goes wrong while deleting the token.
*/
public function delete_token_by_id( string $token_id ): bool {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/vault/payment-tokens/' . $token_id;
$args = array(
'method' => 'DELETE',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not delete payment token.', 'woocommerce-paypal-payments' )
);
$this->logger->warning( $error->getMessage() );
throw $error;
}
return wp_remote_retrieve_response_code( $response ) === 204;
}
/**
* Starts the process of PayPal account vaulting (without payment), returns the links for further actions.
*

View file

@ -0,0 +1,17 @@
{
"name": "woocommerce/ppcp-uninstall",
"type": "dhii-mod",
"description": "Uninstall module for PPCP",
"license": "GPL-2.0",
"require": {
"php": "^7.2 | ^8.0",
"dhii/module-interface": "^0.3.0-alpha1"
},
"autoload": {
"psr-4": {
"WooCommerce\\PayPalCommerce\\Uninstall\\": "src"
}
},
"minimum-stability": "dev",
"prefer-stable": true
}

View file

@ -0,0 +1,56 @@
<?php
/**
* The uninstall module extensions.
*
* @package WooCommerce\PayPalCommerce\Uninstall
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Uninstall;
use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
return array(
'wcgateway.settings.fields' => static function ( ContainerInterface $container, array $fields ): array {
$uninstall_fields = array(
'uninstall_heading' => array(
'heading' => __( 'Uninstall/Clear Database', 'woocommerce-paypal-payments' ),
'type' => 'ppcp-heading',
'screens' => array(
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => Settings::CONNECTION_TAB_ID,
'description' => __( 'Manage plugin data and scheduled actions stored in database.', 'woocommerce-paypal-payments' ),
),
'uninstall_clear_db_on_uninstall' => array(
'title' => __( 'Remove PayPal Payments data from Database on uninstall', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
'label' => __( 'Remove options and scheduled actions from database when uninstalling the plugin.', 'woocommerce-paypal-payments' ),
'default' => false,
'screens' => array(
State::STATE_START,
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => Settings::CONNECTION_TAB_ID,
),
'uninstall_clear_db_now' => array(
'title' => __( 'Remove PayPal Payments data from Database.', 'woocommerce-paypal-payments' ),
'type' => 'ppcp-text',
'text' => '<button type="button" class="button ppcp-clear_db_now">' . esc_html__( 'Clear now', 'woocommerce-paypal-payments' ) . '</button>',
'screens' => array(
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => Settings::CONNECTION_TAB_ID,
'description' => __( 'Click to remove options and scheduled actions from database now.', 'woocommerce-paypal-payments' ),
),
);
return array_merge( $fields, $uninstall_fields );
},
);

View file

@ -0,0 +1,16 @@
<?php
/**
* The uninstall module.
*
* @package WooCommerce\PayPalCommerce\Uninstall
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Uninstall;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
return function (): ModuleInterface {
return new UninstallModule();
};

View file

@ -1,8 +1,8 @@
{
"name": "ppcp-vaulting",
"name": "ppcp-uninstall",
"version": "1.0.0",
"license": "GPL-3.0-or-later",
"main": "resources/js/myaccount-payments.js",
"main": "resources/js/ppcp-clear-db.js",
"browserslist": [
"> 0.5%",
"Safari >= 8",

View file

@ -0,0 +1,43 @@
document.addEventListener(
'DOMContentLoaded',
() => {
const config = PayPalCommerceGatewayClearDb;
if (!typeof (config)) {
return;
}
const clearDbConfig = config.clearDb;
document.querySelector(clearDbConfig.button)?.addEventListener('click', function () {
const isConfirmed = confirm(clearDbConfig.confirmationMessage);
if (!isConfirmed) {
return;
}
const clearButton = document.querySelector(clearDbConfig.button);
clearButton.setAttribute('disabled', 'disabled');
fetch(clearDbConfig.endpoint, {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({
nonce: clearDbConfig.nonce,
})
}).then((res)=>{
return res.json();
}).then((data)=>{
if (!data.success) {
jQuery(clearDbConfig.failureMessage).insertAfter(clearButton);
setTimeout(()=> jQuery(clearDbConfig.messageSelector).remove(),3000);
clearButton.removeAttribute('disabled');
throw Error(data.data.message);
}
jQuery(clearDbConfig.successMessage).insertAfter(clearButton);
setTimeout(()=> jQuery(clearDbConfig.messageSelector).remove(),3000);
clearButton.removeAttribute('disabled');
window.location.replace(clearDbConfig.redirectUrl);
});
})
},
);

View file

@ -0,0 +1,96 @@
<?php
/**
* The uninstall module services.
*
* @package WooCommerce\PayPalCommerce\Uninstall
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Uninstall;
use WooCommerce\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository;
use WooCommerce\PayPalCommerce\Uninstall\Assets\ClearDatabaseAssets;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\Webhooks\Status\WebhookSimulation;
use WooCommerce\PayPalCommerce\Webhooks\WebhookRegistrar;
return array(
'uninstall.ppcp-all-option-names' => function( ContainerInterface $container ) : array {
return array(
$container->get( 'webhook.last-webhook-storage.key' ),
PayPalRequestIdRepository::KEY,
'woocommerce_ppcp-is_pay_later_settings_migrated',
'woocommerce_' . PayPalGateway::ID . '_settings',
'woocommerce_' . CreditCardGateway::ID . '_settings',
'woocommerce_' . PayUponInvoiceGateway::ID . '_settings',
'woocommerce_' . CardButtonGateway::ID . '_settings',
Settings::KEY,
'woocommerce-ppcp-version',
WebhookSimulation::OPTION_ID,
WebhookRegistrar::KEY,
);
},
'uninstall.ppcp-all-scheduled-action-names' => function( ContainerInterface $container ) : array {
return array(
'woocommerce_paypal_payments_check_pui_payment_captured',
'woocommerce_paypal_payments_check_saved_payment',
);
},
'uninstall.clear-db-endpoint' => function( ContainerInterface $container ) : string {
return 'ppcp-clear-db';
},
'uninstall.clear-database-script-data' => function( ContainerInterface $container ) : array {
return array(
'clearDb' => array(
'endpoint' => \WC_AJAX::get_endpoint( $container->get( 'uninstall.clear-db-endpoint' ) ),
'nonce' => wp_create_nonce( $container->get( 'uninstall.clear-db-endpoint' ) ),
'button' => '.ppcp-clear_db_now',
'messageSelector' => '.clear-db-info-message',
'confirmationMessage' => __( 'Are you sure? the operation will remove all plugin data.', 'woocommerce-paypal-payments' ),
'successMessage' => sprintf(
'<div class="updated clear-db-info-message"><p><strong>%1$s</strong></p></div>',
esc_html__( 'The plugin data is successfully cleared.', 'woocommerce-paypal-payments' )
),
'failureMessage' => sprintf(
'<div class="error clear-db-info-message"><p><strong>%1$s</strong></p></div>',
esc_html__( 'Operation failed. Check WooCommerce logs for more details.', 'woocommerce-paypal-payments' )
),
'redirectUrl' => admin_url( 'admin.php?page=wc-settings&tab=checkout&section=ppcp-gateway' ),
),
);
},
'uninstall.module-url' => static function ( ContainerInterface $container ): string {
/**
* The path cannot be false.
*
* @psalm-suppress PossiblyFalseArgument
*/
return plugins_url(
'/modules/ppcp-uninstall/',
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
);
},
'uninstall.clear-db-assets' => function( ContainerInterface $container ) : ClearDatabaseAssets {
return new ClearDatabaseAssets(
$container->get( 'uninstall.module-url' ),
$container->get( 'ppcp.asset-version' ),
'ppcp-clear-db',
$container->get( 'uninstall.clear-database-script-data' )
);
},
'uninstall.clear-db' => function( ContainerInterface $container ) : ClearDatabaseInterface {
return new ClearDatabase();
},
);

View file

@ -0,0 +1,96 @@
<?php
/**
* Register and configure assets for uninstall module.
*
* @package WooCommerce\PayPalCommerce\Uninstall\Assets
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Uninstall\Assets;
/**
* Class ClearDatabaseAssets
*/
class ClearDatabaseAssets {
/**
* The URL to the module.
*
* @var string
*/
private $module_url;
/**
* The assets version.
*
* @var string
*/
private $version;
/**
* The script name.
*
* @var string
*/
protected $script_name;
/**
* A map of script data.
*
* @var array
*/
protected $script_data;
/**
* ClearDatabaseAssets constructor.
*
* @param string $module_url The URL to the module.
* @param string $version The assets version.
* @param string $script_name The script name.
* @param array $script_data A map of script data.
*/
public function __construct(
string $module_url,
string $version,
string $script_name,
array $script_data
) {
$this->module_url = $module_url;
$this->version = $version;
$this->script_data = $script_data;
$this->script_name = $script_name;
}
/**
* Registers the scripts and styles.
*
* @return void
*/
public function register(): void {
$module_url = untrailingslashit( $this->module_url );
wp_register_script(
$this->script_name,
"{$module_url}/assets/js/{$this->script_name}.js",
array( 'jquery' ),
$this->version,
true
);
wp_localize_script(
$this->script_name,
'PayPalCommerceGatewayClearDb',
$this->script_data
);
}
/**
* Enqueues the necessary scripts.
*
* @return void
*/
public function enqueue(): void {
wp_enqueue_script( $this->script_name );
}
}

View file

@ -0,0 +1,34 @@
<?php
/**
* Clears the plugin related data from DB.
*
* @package WooCommerce\PayPalCommerce\Uninstall
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Uninstall;
/**
* Class ClearDatabase
*/
class ClearDatabase implements ClearDatabaseInterface {
/**
* {@inheritDoc}
*/
public function delete_options( array $option_names ):void {
foreach ( $option_names as $option_name ) {
delete_option( $option_name );
}
}
/**
* {@inheritDoc}
*/
public function clear_scheduled_actions( array $action_names ):void {
foreach ( $action_names as $action_name ) {
as_unschedule_action( $action_name );
}
}
}

View file

@ -0,0 +1,32 @@
<?php
/**
* Can delete the options and clear scheduled actions from database.
*
* @package WooCommerce\PayPalCommerce\Uninstall
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Uninstall;
use RuntimeException;
interface ClearDatabaseInterface {
/**
* Deletes the given options from database.
*
* @param string[] $option_names The list of option names.
* @throws RuntimeException If problem deleting.
*/
public function delete_options( array $option_names ): void;
/**
* Clears the given scheduled actions.
*
* @param string[] $action_names The list of scheduled action names.
* @throws RuntimeException If problem clearing.
*/
public function clear_scheduled_actions( array $action_names ): void;
}

View file

@ -0,0 +1,98 @@
<?php
/**
* The uninstall module.
*
* @package WooCommerce\PayPalCommerce\Uninstall
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Uninstall;
use Exception;
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
use WooCommerce\PayPalCommerce\Uninstall\Assets\ClearDatabaseAssets;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
/**
* Class UninstallModule
*/
class UninstallModule implements ModuleInterface {
/**
* {@inheritDoc}
*/
public function setup(): ServiceProviderInterface {
return new ServiceProvider(
require __DIR__ . '/../services.php',
require __DIR__ . '/../extensions.php'
);
}
/**
* {@inheritDoc}
*/
public function run( ContainerInterface $container ): void {
$page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' );
if ( Settings::CONNECTION_TAB_ID === $page_id ) {
$this->registerClearDatabaseAssets( $container->get( 'uninstall.clear-db-assets' ) );
}
$request_data = $container->get( 'button.request-data' );
$clear_db = $container->get( 'uninstall.clear-db' );
$clear_db_endpoint = $container->get( 'uninstall.clear-db-endpoint' );
$option_names = $container->get( 'uninstall.ppcp-all-option-names' );
$scheduled_action_names = $container->get( 'uninstall.ppcp-all-scheduled-action-names' );
$this->handleClearDbAjaxRequest( $request_data, $clear_db, $clear_db_endpoint, $option_names, $scheduled_action_names );
}
/**
* Registers the assets for clear database functionality.
*
* @param ClearDatabaseAssets $asset_loader The clear database functionality asset loader.
*/
protected function registerClearDatabaseAssets( ClearDatabaseAssets $asset_loader ): void {
add_action( 'init', array( $asset_loader, 'register' ) );
add_action( 'admin_enqueue_scripts', array( $asset_loader, 'enqueue' ) );
}
/**
* Handles the AJAX request to clear the database.
*
* @param RequestData $request_data The request data helper.
* @param ClearDatabaseInterface $clear_db Can delete the options and clear scheduled actions from database.
* @param string $nonce The nonce.
* @param string[] $option_names The list of option names.
* @param string[] $scheduled_action_names The list of scheduled action names.
*/
protected function handleClearDbAjaxRequest(
RequestData $request_data,
ClearDatabaseInterface $clear_db,
string $nonce,
array $option_names,
array $scheduled_action_names
): void {
add_action(
"wc_ajax_{$nonce}",
static function () use ( $request_data, $clear_db, $nonce, $option_names, $scheduled_action_names ) {
try {
// Validate nonce.
$request_data->read_request( $nonce );
$clear_db->delete_options( $option_names );
$clear_db->clear_scheduled_actions( $scheduled_action_names );
wp_send_json_success();
return true;
} catch ( Exception $error ) {
wp_send_json_error( $error->getMessage(), 403 );
return false;
}
}
);
}
}

View file

@ -6,7 +6,7 @@ module.exports = {
mode: isProduction ? 'production' : 'development',
target: 'web',
entry: {
'myaccount-payments': path.resolve('./resources/js/myaccount-payments.js'),
'ppcp-clear-db': path.resolve('./resources/js/ppcp-clear-db.js'),
},
output: {
path: path.resolve(__dirname, 'assets/'),
@ -17,6 +17,19 @@ module.exports = {
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel-loader',
}]
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
{
loader: 'file-loader',
options: {
name: 'css/[name].css',
}
},
{loader:'sass-loader'}
]
}]
}
};

View file

@ -1,42 +0,0 @@
document.addEventListener(
'DOMContentLoaded',
() => {
jQuery('.ppcp-delete-payment-button').click(async (event) => {
event.preventDefault();
jQuery(this).prop('disabled', true);
const token = event.target.id;
const response = await fetch(
PayPalCommerceGatewayVaulting.delete.endpoint,
{
method: 'POST',
credentials: 'same-origin',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(
{
nonce: PayPalCommerceGatewayVaulting.delete.nonce,
token,
}
)
}
);
const reportError = error => {
alert(error);
}
if (!response.ok) {
try {
const result = await response.json();
reportError(result.data);
} catch (exc) {
console.error(exc);
reportError(response.status);
}
}
window.location.reload();
});
});

View file

@ -20,27 +20,11 @@ return array(
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
);
},
'vaulting.assets.myaccount-payments' => function( ContainerInterface $container ) : MyAccountPaymentsAssets {
return new MyAccountPaymentsAssets(
$container->get( 'vaulting.module-url' ),
$container->get( 'ppcp.asset-version' )
);
},
'vaulting.payment-tokens-renderer' => static function (): PaymentTokensRenderer {
return new PaymentTokensRenderer();
},
'vaulting.repository.payment-token' => static function ( ContainerInterface $container ): PaymentTokenRepository {
$factory = $container->get( 'api.factory.payment-token' );
$endpoint = $container->get( 'api.endpoint.payment-token' );
return new PaymentTokenRepository( $factory, $endpoint );
},
'vaulting.endpoint.delete' => function( ContainerInterface $container ) : DeletePaymentTokenEndpoint {
return new DeletePaymentTokenEndpoint(
$container->get( 'vaulting.repository.payment-token' ),
$container->get( 'button.request-data' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'vaulting.payment-token-checker' => function( ContainerInterface $container ) : PaymentTokenChecker {
return new PaymentTokenChecker(
$container->get( 'vaulting.repository.payment-token' ),
@ -70,4 +54,13 @@ return array(
$container->get( 'wcgateway.settings' )
);
},
'vaulting.payment-token-paypal' => function( ContainerInterface $container ): PaymentTokenPayPal {
return new PaymentTokenPayPal();
},
'vaulting.payment-tokens-migration' => function( ContainerInterface $container ): PaymentTokensMigration {
return new PaymentTokensMigration(
$container->get( 'vaulting.payment-token-paypal' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},
);

View file

@ -1,77 +0,0 @@
<?php
/**
* Register and configure assets for My account PayPal payments page.
*
* @package WooCommerce\PayPalCommerce\Vaulting\Assets
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Vaulting\Assets;
use WooCommerce\PayPalCommerce\Vaulting\Endpoint\DeletePaymentTokenEndpoint;
/**
* Class MyAccountPaymentsAssets
*/
class MyAccountPaymentsAssets {
/**
* The URL to the module.
*
* @var string
*/
private $module_url;
/**
* The assets version.
*
* @var string
*/
private $version;
/**
* MyAccountPaymentsAssets constructor.
*
* @param string $module_url The URL to the module.
* @param string $version The assets version.
*/
public function __construct(
string $module_url,
string $version
) {
$this->module_url = untrailingslashit( $module_url );
$this->version = $version;
}
/**
* Enqueues the necessary scripts.
*
* @return void
*/
public function enqueue(): void {
wp_enqueue_script(
'ppcp-vaulting-myaccount-payments',
untrailingslashit( $this->module_url ) . '/assets/js/myaccount-payments.js',
array( 'jquery' ),
$this->version,
true
);
}
/**
* Localize script.
*/
public function localize() {
wp_localize_script(
'ppcp-vaulting-myaccount-payments',
'PayPalCommerceGatewayVaulting',
array(
'delete' => array(
'endpoint' => \WC_AJAX::get_endpoint( DeletePaymentTokenEndpoint::ENDPOINT ),
'nonce' => wp_create_nonce( DeletePaymentTokenEndpoint::nonce() ),
),
)
);
}
}

View file

@ -1,95 +0,0 @@
<?php
/**
* The endpoint for deleting payment tokens.
*
* @package WooCommerce\PayPalCommerce\Vaulting\Endpoint
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Vaulting\Endpoint;
use Exception;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
/**
* Class DeletePayment
*/
class DeletePaymentTokenEndpoint {
const ENDPOINT = 'ppc-vaulting-delete';
/**
* The repository.
*
* @var PaymentTokenRepository
*/
protected $repository;
/**
* The request data.
*
* @var RequestData
*/
protected $request_data;
/**
* The logger.
*
* @var LoggerInterface
*/
protected $logger;
/**
* DeletePaymentTokenEndpoint constructor.
*
* @param PaymentTokenRepository $repository The repository.
* @param RequestData $request_data The request data.
* @param LoggerInterface $logger The logger.
*/
public function __construct( PaymentTokenRepository $repository, RequestData $request_data, LoggerInterface $logger ) {
$this->repository = $repository;
$this->request_data = $request_data;
$this->logger = $logger;
}
/**
* Returns the nonce for the endpoint.
*
* @return string
*/
public static function nonce(): string {
return self::ENDPOINT;
}
/**
* Handles the incoming request.
*/
public function handle_request() {
try {
$data = $this->request_data->read_request( $this->nonce() );
$tokens = $this->repository->all_for_user_id( get_current_user_id() );
if ( $tokens ) {
foreach ( $tokens as $token ) {
if ( isset( $data['token'] ) && $token->id() === $data['token'] ) {
if ( $this->repository->delete_token( get_current_user_id(), $token ) ) {
wp_send_json_success();
return true;
}
wp_send_json_error( 'Could not delete payment token.' );
return false;
}
}
}
} catch ( Exception $error ) {
$this->logger->error( 'Failed to delete payment: ' . $error->getMessage() );
wp_send_json_error( $error->getMessage(), 403 );
return false;
}
}
}

View file

@ -0,0 +1,32 @@
<?php
/**
* WooCommerce Payment token for PayPal.
*
* @package WooCommerce\PayPalCommerce\Vaulting
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Vaulting;
use WC_Payment_Token;
/**
* Class PaymentTokenPayPal
*/
class PaymentTokenPayPal extends WC_Payment_Token {
/**
* Token Type String.
*
* @var string
*/
protected $type = 'PayPal';
public function get_email() {
return $this->get_meta( 'email' );
}
public function set_email( $email ) {
$this->add_meta_data( 'email', $email, true );
}
}

View file

@ -0,0 +1,110 @@
<?php
/**
* The payment tokens migration handler.
*
* @package WooCommerce\PayPalCommerce\Vaulting
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Vaulting;
use Exception;
use Psr\Log\LoggerInterface;
use WC_Payment_Token_CC;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
/**
* Class PaymentTokensMigration
*/
class PaymentTokensMigration {
/**
* WC Payment token PayPal.
*
* @var PaymentTokenPayPal
*/
private $payment_token_paypal;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* PaymentTokensMigration constructor.
*
* @param PaymentTokenPayPal $payment_token_paypal WC Payment token PayPal.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
PaymentTokenPayPal $payment_token_paypal,
LoggerInterface $logger
) {
$this->payment_token_paypal = $payment_token_paypal;
$this->logger = $logger;
}
/**
* Migrates user existing vaulted tokens into WC payment tokens API.
*
* @param int $id WooCommerce customer id.
*/
public function migrate_payment_tokens_for_user( int $id ):void {
$tokens = (array) get_user_meta( $id, PaymentTokenRepository::USER_META, true );
$tokens_migrated = 0;
foreach ( $tokens as $token ) {
if ( isset( $token->source()->card ) ) {
$payment_token = new WC_Payment_Token_CC();
$payment_token->set_token( $token->id() );
$payment_token->set_user_id( $id );
$payment_token->set_gateway_id( CreditCardGateway::ID );
$payment_token->set_last4( $token->source()->card->last_digits );
$payment_token->set_card_type( $token->source()->card->brand );
try {
$payment_token->save();
} catch ( Exception $exception ) {
$this->logger->error(
"Could not save WC payment token credit card {$token->id()} for user {$id}. "
. $exception->getMessage()
);
continue;
}
$tokens_migrated++;
} elseif ( $token->source()->paypal ) {
$this->payment_token_paypal->set_token( $token->id() );
$this->payment_token_paypal->set_user_id( $id );
$this->payment_token_paypal->set_gateway_id( PayPalGateway::ID );
$email = $token->source()->paypal->payer->email_address ?? '';
if ( $email && is_email( $email ) ) {
$this->payment_token_paypal->set_email( $email );
}
try {
$this->payment_token_paypal->save();
} catch ( Exception $exception ) {
$this->logger->error(
"Could not save WC payment token PayPal {$token->id()} for user {$id}. "
. $exception->getMessage()
);
continue;
}
$tokens_migrated++;
}
}
if ( $tokens_migrated > 0 && count( $tokens ) === $tokens_migrated ) {
update_user_meta( $id, 'ppcp_tokens_migrated', true );
}
}
}

View file

@ -1,83 +0,0 @@
<?php
/**
* The payment tokens renderer.
*
* @package WooCommerce\PayPalCommerce\Vaulting
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Vaulting;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
/**
* Class PaymentTokensRendered
*/
class PaymentTokensRenderer {
/**
* Render payment tokens.
*
* @param PaymentToken[] $tokens The tokens.
* @return false|string
*/
public function render( array $tokens ) {
ob_start();
?>
<table class="shop_table shop_table_responsive">
<thead>
<tr>
<th><?php echo esc_html__( 'Payment sources', 'woocommerce-paypal-payments' ); ?></th>
<th></th>
</tr>
</thead>
<tbody>
<?php
foreach ( $tokens as $token ) {
$source = $token->source() ?? null;
if ( $source && isset( $source->card ) ) {
?>
<tr>
<td><?php echo esc_attr( $source->card->brand ) . ' ...' . esc_attr( $source->card->last_digits ); ?></td>
<td>
<a class="ppcp-delete-payment-button" id="<?php echo esc_attr( $token->id() ); ?>" href=""><?php echo esc_html__( 'Delete', 'woocommerce-paypal-payments' ); ?></a>
</td>
</tr>
<?php
}
if ( $source && isset( $source->paypal ) ) {
?>
<tr>
<td><?php echo esc_attr( $source->paypal->payer->email_address ); ?></td>
<td>
<a class="ppcp-delete-payment-button" id="<?php echo esc_attr( $token->id() ); ?>" href=""><?php echo esc_html__( 'Delete', 'woocommerce-paypal-payments' ); ?></a>
</td>
</tr>
<?php
}
?>
<?php
}
?>
</tbody>
</table>
<?php
return ob_get_clean();
}
/**
* Render no payments message.
*
* @return false|string
*/
public function render_no_tokens() {
ob_start();
?>
<div class="woocommerce-Message woocommerce-Message--info woocommerce-info">
<?php echo esc_html__( 'No payments available yet.', 'woocommerce-paypal-payments' ); ?>
</div>
<?php
return ob_get_clean();
}
}

View file

@ -9,6 +9,9 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Vaulting;
use RuntimeException;
use WC_Payment_Token;
use WC_Payment_Tokens;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
@ -16,9 +19,10 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use WC_Order;
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
use WooCommerce\PayPalCommerce\Vaulting\Endpoint\DeletePaymentTokenEndpoint;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WP_User_Query;
/**
* Class StatusReportModule
@ -43,7 +47,6 @@ class VaultingModule implements ModuleInterface {
* @throws NotFoundException When service could not be found.
*/
public function run( ContainerInterface $container ): void {
$settings = $container->get( 'wcgateway.settings' );
if ( ! $settings->has( 'vault_enabled' ) || ! $settings->get( 'vault_enabled' ) ) {
return;
@ -54,61 +57,6 @@ class VaultingModule implements ModuleInterface {
$listener->listen();
add_filter(
'woocommerce_account_menu_items',
function( $menu_links ) {
$menu_links = array_slice( $menu_links, 0, 5, true )
+ array( 'ppcp-paypal-payment-tokens' => __( 'PayPal payments', 'woocommerce-paypal-payments' ) )
+ array_slice( $menu_links, 5, null, true );
return $menu_links;
},
40
);
add_action(
'init',
function () {
add_rewrite_endpoint( 'ppcp-paypal-payment-tokens', EP_PAGES );
}
);
add_action(
'woocommerce_paypal_payments_gateway_migrate',
function () {
add_action(
'init',
function () {
add_rewrite_endpoint( 'ppcp-paypal-payment-tokens', EP_PAGES );
flush_rewrite_rules();
}
);
}
);
add_action(
'woocommerce_paypal_payments_gateway_activate',
function () {
add_rewrite_endpoint( 'ppcp-paypal-payment-tokens', EP_PAGES );
flush_rewrite_rules();
}
);
add_action(
'woocommerce_account_ppcp-paypal-payment-tokens_endpoint',
function () use ( $container ) {
$payment_token_repository = $container->get( 'vaulting.repository.payment-token' );
$renderer = $container->get( 'vaulting.payment-tokens-renderer' );
$tokens = $payment_token_repository->all_for_user_id( get_current_user_id() );
if ( $tokens ) {
echo wp_kses_post( $renderer->render( $tokens ) );
} else {
echo wp_kses_post( $renderer->render_no_tokens() );
}
}
);
$subscription_helper = $container->get( 'subscription.helper' );
add_action(
'woocommerce_created_customer',
@ -124,27 +72,6 @@ class VaultingModule implements ModuleInterface {
}
);
$asset_loader = $container->get( 'vaulting.assets.myaccount-payments' );
add_action(
'wp_enqueue_scripts',
function () use ( $asset_loader ) {
if ( is_account_page() && $this->is_payments_page() ) {
$asset_loader->enqueue();
$asset_loader->localize();
}
}
);
add_action(
'wc_ajax_' . DeletePaymentTokenEndpoint::ENDPOINT,
static function () use ( $container ) {
$endpoint = $container->get( 'vaulting.endpoint.delete' );
assert( $endpoint instanceof DeletePaymentTokenEndpoint );
$endpoint->handle_request();
}
);
add_action(
'woocommerce_paypal_payments_check_saved_payment',
function ( int $order_id, int $customer_id, string $intent ) use ( $container ) {
@ -156,6 +83,125 @@ class VaultingModule implements ModuleInterface {
);
$this->filterFailedVaultingEmailsForSubscriptionOrders( $container );
add_filter(
'woocommerce_payment_token_class',
/**
* Param types removed to avoid third-party issues.
*
* @psalm-suppress MissingClosureParamType
*/
function ( $type ) {
if ( $type === 'WC_Payment_Token_PayPal' ) {
return PaymentTokenPayPal::class;
}
return $type;
}
);
add_filter(
'woocommerce_payment_methods_list_item',
/**
* Param types removed to avoid third-party issues.
*
* @psalm-suppress MissingClosureParamType
*/
function( $item, $payment_token ) {
if ( ! is_array( $item ) || ! is_a( $payment_token, WC_Payment_Token::class ) ) {
return $item;
}
if ( strtolower( $payment_token->get_type() ) !== 'paypal' ) {
return $item;
}
assert( $payment_token instanceof PaymentTokenPayPal );
$item['method']['brand'] = $payment_token->get_email() ?? 'PayPal';
return $item;
},
10,
2
);
add_action(
'wp',
function() use ( $container ) {
global $wp;
if ( isset( $wp->query_vars['delete-payment-method'] ) ) {
$token_id = absint( $wp->query_vars['delete-payment-method'] );
$token = WC_Payment_Tokens::get( $token_id );
if (
is_null( $token )
|| ( $token->get_gateway_id() !== PayPalGateway::ID && $token->get_gateway_id() !== CreditCardGateway::ID )
) {
return;
}
$wpnonce = wc_clean( wp_unslash( $_REQUEST['_wpnonce'] ?? '' ) );
$token_id_string = (string) $token_id;
$action = 'delete-payment-method-' . $token_id_string;
if (
$token->get_user_id() !== get_current_user_id()
|| ! isset( $wpnonce ) || ! is_string( $wpnonce )
|| wp_verify_nonce( $wpnonce, $action ) === false
) {
wc_add_notice( __( 'Invalid payment method.', 'woocommerce-paypal-payments' ), 'error' );
wp_safe_redirect( wc_get_account_endpoint_url( 'payment-methods' ) );
exit();
}
try {
$payment_token_endpoint = $container->get( 'api.endpoint.payment-token' );
$payment_token_endpoint->delete_token_by_id( $token->get_token() );
} catch ( RuntimeException $exception ) {
wc_add_notice( __( 'Could not delete payment token. ', 'woocommerce-paypal-payments' ) . $exception->getMessage(), 'error' );
return;
}
}
}
);
add_action(
'woocommerce_paypal_payments_gateway_migrate_on_update',
function () use ( $container ) {
$customers = new WP_User_Query(
array(
'fields' => 'ID',
'limit' => -1,
'meta_key' => 'ppcp-vault-token',
'meta_query' => array(
array(
'key' => 'ppcp_tokens_migrated',
'compare' => 'NOT EXISTS',
),
),
)
);
$migrate = $container->get( 'vaulting.payment-tokens-migration' );
assert( $migrate instanceof PaymentTokensMigration );
foreach ( $customers->get_results() as $id ) {
$migrate->migrate_payment_tokens_for_user( (int) $id );
}
}
);
add_filter(
'woocommerce_available_payment_gateways',
function( array $methods ): array {
global $wp;
if ( isset( $wp->query_vars['add-payment-method'] ) ) {
unset( $methods[ PayPalGateway::ID ] );
}
return $methods;
}
);
}
/**

View file

@ -395,6 +395,24 @@ return function ( ContainerInterface $container, array $fields ): 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',
'desc_tip' => true,
'label' => sprintf(
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
__( 'Manage online risk with %1$sFraudNet%2$s.', 'woocommerce-paypal-payments' ),
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#FraudNet" target="_blank">',
'</a>'
),
'description' => __( 'FraudNet is a JavaScript library developed by PayPal and embedded into a merchants web page to collect browser-based data to help reduce fraud.', 'woocommerce-paypal-payments' ),
'default' => false,
'screens' => array(
State::STATE_ONBOARDED,
),
'requirements' => array(),
'gateway' => Settings::CONNECTION_TAB_ID,
),
'credentials_integration_configuration_heading' => array(
'heading' => __( 'General integration configuration', 'woocommerce-paypal-payments' ),

View file

@ -84,6 +84,8 @@ return array(
$application_context_repository,
$pay_pal_request_id_repository,
$subscription_helper,
$container->get( 'wcgateway.is-fraudnet-enabled' ),
$container->get( 'wcgateway.fraudnet' ),
$bn_code
);
},

View file

@ -11,6 +11,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PayUponInvoiceOrderEndpoint;
@ -28,6 +29,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn;
use WooCommerce\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail;
use WooCommerce\PayPalCommerce\WcGateway\Admin\RenderAuthorizeAction;
use WooCommerce\PayPalCommerce\WcGateway\Assets\FraudNetAssets;
use WooCommerce\PayPalCommerce\WcGateway\Checkout\CheckoutPayPalAddressPreset;
use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways;
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
@ -38,9 +40,9 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXO;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOEndpoint;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNet;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNetSessionId;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNetSourceWebsiteId;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNetSessionId;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNetSourceWebsiteId;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PaymentSourceFactory;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoice;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
@ -1568,7 +1570,7 @@ return array(
$container->get( 'api.host' ),
$container->get( 'api.bearer' ),
$container->get( 'api.factory.order' ),
$container->get( 'wcgateway.pay-upon-invoice-fraudnet' ),
$container->get( 'wcgateway.fraudnet' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},
@ -1589,15 +1591,15 @@ return array(
$container->get( 'wcgateway.processor.refunds' )
);
},
'wcgateway.pay-upon-invoice-fraudnet-session-id' => static function ( ContainerInterface $container ): FraudNetSessionId {
'wcgateway.fraudnet-session-id' => static function ( ContainerInterface $container ): FraudNetSessionId {
return new FraudNetSessionId();
},
'wcgateway.pay-upon-invoice-fraudnet-source-website-id' => static function ( ContainerInterface $container ): FraudNetSourceWebsiteId {
'wcgateway.fraudnet-source-website-id' => static function ( ContainerInterface $container ): FraudNetSourceWebsiteId {
return new FraudNetSourceWebsiteId( $container->get( 'api.merchant_id' ) );
},
'wcgateway.pay-upon-invoice-fraudnet' => static function ( ContainerInterface $container ): FraudNet {
$session_id = $container->get( 'wcgateway.pay-upon-invoice-fraudnet-session-id' );
$source_website_id = $container->get( 'wcgateway.pay-upon-invoice-fraudnet-source-website-id' );
'wcgateway.fraudnet' => static function ( ContainerInterface $container ): FraudNet {
$session_id = $container->get( 'wcgateway.fraudnet-session-id' );
$source_website_id = $container->get( 'wcgateway.fraudnet-source-website-id' );
return new FraudNet(
(string) $session_id(),
(string) $source_website_id()
@ -1618,21 +1620,15 @@ return array(
},
'wcgateway.pay-upon-invoice' => static function ( ContainerInterface $container ): PayUponInvoice {
return new PayUponInvoice(
$container->get( 'wcgateway.url' ),
$container->get( 'wcgateway.pay-upon-invoice-fraudnet' ),
$container->get( 'wcgateway.pay-upon-invoice-order-endpoint' ),
$container->get( 'woocommerce.logger.woocommerce' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'onboarding.environment' ),
$container->get( 'ppcp.asset-version' ),
$container->get( 'onboarding.state' ),
$container->get( 'wcgateway.is-ppcp-settings-page' ),
$container->get( 'wcgateway.current-ppcp-settings-page-id' ),
$container->get( 'wcgateway.pay-upon-invoice-product-status' ),
$container->get( 'wcgateway.pay-upon-invoice-helper' ),
$container->get( 'wcgateway.checkout-helper' ),
$container->get( 'api.factory.capture' ),
$container->get( 'session.handler' )
$container->get( 'api.factory.capture' )
);
},
'wcgateway.oxxo' => static function( ContainerInterface $container ): OXXO {
@ -1926,4 +1922,78 @@ return array(
return $button_locations;
},
'wcgateway.ppcp-gateways' => static function ( ContainerInterface $container ): array {
return array(
PayPalGateway::ID,
CreditCardGateway::ID,
PayUponInvoiceGateway::ID,
CardButtonGateway::ID,
OXXOGateway::ID,
);
},
'wcgateway.enabled-ppcp-gateways' => static function ( ContainerInterface $container ): array {
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
$ppcp_gateways = $container->get( 'wcgateway.ppcp-gateways' );
$enabled_ppcp_gateways = array();
foreach ( $ppcp_gateways as $gateway ) {
if ( ! isset( $available_gateways[ $gateway ] ) ) {
continue;
}
$enabled_ppcp_gateways[] = $gateway;
}
return $enabled_ppcp_gateways;
},
'wcgateway.is-paypal-continuation' => static function ( ContainerInterface $container ): bool {
$session_handler = $container->get( 'session.handler' );
assert( $session_handler instanceof SessionHandler );
$order = $session_handler->order();
if ( ! $order ) {
return false;
}
$source = $order->payment_source();
if ( $source && $source->card() ) {
return false; // Ignore for DCC.
}
if ( 'card' === $session_handler->funding_source() ) {
return false; // Ignore for card buttons.
}
return true;
},
'wcgateway.current-context' => static function ( ContainerInterface $container ): string {
$context = 'mini-cart';
if ( is_product() || wc_post_content_has_shortcode( 'product_page' ) ) {
$context = 'product';
}
if ( is_cart() ) {
$context = 'cart';
}
if ( is_checkout() && ! $container->get( 'wcgateway.is-paypal-continuation' ) ) {
$context = 'checkout';
}
if ( is_checkout_pay_page() ) {
$context = 'pay-now';
}
return $context;
},
'wcgateway.is-fraudnet-enabled' => static function ( ContainerInterface $container ): bool {
$settings = $container->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
return $settings->has( 'fraudnet_enabled' ) && $settings->get( 'fraudnet_enabled' );
},
'wcgateway.fraudnet-assets' => function( ContainerInterface $container ) : FraudNetAssets {
return new FraudNetAssets(
$container->get( 'wcgateway.url' ),
$container->get( 'ppcp.asset-version' ),
$container->get( 'wcgateway.fraudnet' ),
$container->get( 'onboarding.environment' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'wcgateway.enabled-ppcp-gateways' ),
$container->get( 'wcgateway.current-context' ),
$container->get( 'wcgateway.is-fraudnet-enabled' )
);
},
);

View file

@ -0,0 +1,186 @@
<?php
/**
* Register and configure FraudNet assets
*
* @package WooCommerce\PayPalCommerce\WcGateway\Assets
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Assets;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
/**
* Class FraudNetAssets
*/
class FraudNetAssets {
/**
* The URL of this module.
*
* @var string
*/
protected $module_url;
/**
* The assets version.
*
* @var string
*/
protected $version;
/**
* The FraudNet entity.
*
* @var FraudNet
*/
protected $fraud_net;
/**
* The environment.
*
* @var Environment
*/
protected $environment;
/**
* The Settings.
*
* @var Settings
*/
protected $settings;
/**
* The list of enabled PayPal gateways.
*
* @var string[]
*/
protected $enabled_ppcp_gateways;
/**
* The current context.
*
* @var string
*/
protected $context;
/**
* True if FraudNet support is enabled in settings, otherwise false.
*
* @var bool
*/
protected $is_fraudnet_enabled;
/**
* Assets constructor.
*
* @param string $module_url The url of this module.
* @param string $version The assets version.
* @param FraudNet $fraud_net The FraudNet entity.
* @param Environment $environment The environment.
* @param Settings $settings The Settings.
* @param string[] $enabled_ppcp_gateways The list of enabled PayPal gateways.
* @param string $context The current context.
* @param bool $is_fraudnet_enabled true if FraudNet support is enabled in settings, otherwise false.
*/
public function __construct(
string $module_url,
string $version,
FraudNet $fraud_net,
Environment $environment,
Settings $settings,
array $enabled_ppcp_gateways,
string $context,
bool $is_fraudnet_enabled
) {
$this->module_url = $module_url;
$this->version = $version;
$this->fraud_net = $fraud_net;
$this->environment = $environment;
$this->settings = $settings;
$this->enabled_ppcp_gateways = $enabled_ppcp_gateways;
$this->context = $context;
$this->is_fraudnet_enabled = $is_fraudnet_enabled;
}
/**
* Registers FraudNet assets.
*/
public function register_assets(): void {
add_action(
'wp_enqueue_scripts',
function() {
if ( $this->should_load_fraudnet_script() ) {
wp_enqueue_script(
'ppcp-fraudnet',
trailingslashit( $this->module_url ) . 'assets/js/fraudnet.js',
array(),
$this->version,
true
);
wp_localize_script(
'ppcp-fraudnet',
'FraudNetConfig',
array(
'f' => $this->fraud_net->session_id(),
's' => $this->fraud_net->source_website_id(),
'sandbox' => $this->environment->current_environment_is( Environment::SANDBOX ),
)
);
}
}
);
}
/**
* Checks if FraudNet script should be loaded.
*
* @return bool true if FraudNet script should be loaded, otherwise false.
*/
protected function should_load_fraudnet_script(): bool {
if ( empty( $this->enabled_ppcp_gateways ) ) {
return false;
}
$is_pui_gateway_enabled = in_array( PayUponInvoiceGateway::ID, $this->enabled_ppcp_gateways, true );
$is_only_standard_gateway_enabled = $this->enabled_ppcp_gateways === array( PayPalGateway::ID );
if ( $this->context !== 'checkout' || $is_only_standard_gateway_enabled ) {
return $this->is_fraudnet_enabled && $this->are_buttons_enabled_for_context();
}
return $is_pui_gateway_enabled ? true : $this->is_fraudnet_enabled;
}
/**
* Checks if buttons are enabled for current context.
*
* @return bool true if enabled, otherwise false.
*/
protected function are_buttons_enabled_for_context() : bool {
if ( ! in_array( PayPalGateway::ID, $this->enabled_ppcp_gateways, true ) ) {
return false;
}
$location_prefix = $this->context === 'checkout' ? '' : "{$this->context}_";
$setting_name = "button_{$location_prefix}enabled";
$buttons_enabled_for_context = $this->settings->has( $setting_name ) && $this->settings->get( $setting_name );
if ( $this->context === 'product' ) {
return $buttons_enabled_for_context || $this->settings->has( 'mini-cart' ) && $this->settings->get( 'mini-cart' );
}
if ( $this->context === 'pay-now' ) {
return true;
}
return $buttons_enabled_for_context;
}
}

View file

@ -7,7 +7,7 @@
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice;
namespace WooCommerce\PayPalCommerce\WcGateway\FraudNet;
/**
* Class FraudNet

View file

@ -7,7 +7,7 @@
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice;
namespace WooCommerce\PayPalCommerce\WcGateway\FraudNet;
use Exception;

View file

@ -7,7 +7,7 @@
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice;
namespace WooCommerce\PayPalCommerce\WcGateway\FraudNet;
/**
* Class FraudNetSourceWebsiteId.

View file

@ -202,7 +202,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
$this->api_shop_country = $api_shop_country;
if ( $this->onboarded ) {
$this->supports = array( 'refunds' );
$this->supports = array( 'refunds', 'tokenization' );
}
if ($this->gateways_enabled()) {
$this->supports = array(
@ -218,6 +218,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
'subscription_payment_method_change_customer',
'subscription_payment_method_change_admin',
'multiple_subscriptions',
'tokenization',
);
}

View file

@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\CaptureFactory;
use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Helper\CheckoutHelper;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceHelper;
@ -34,20 +35,6 @@ class PayUponInvoice {
use TransactionIdHandlingTrait;
/**
* The module URL.
*
* @var string
*/
protected $module_url;
/**
* The FraudNet entity.
*
* @var FraudNet
*/
protected $fraud_net;
/**
* The pui order endpoint.
*
@ -69,20 +56,6 @@ class PayUponInvoice {
*/
protected $settings;
/**
* The environment.
*
* @var Environment
*/
protected $environment;
/**
* The asset version.
*
* @var string
*/
protected $asset_version;
/**
* The PUI helper.
*
@ -97,13 +70,6 @@ class PayUponInvoice {
*/
protected $state;
/**
* Whether the current page is the PPCP settings page.
*
* @var bool
*/
protected $is_ppcp_settings_page;
/**
* Current PayPal settings page id.
*
@ -132,64 +98,39 @@ class PayUponInvoice {
*/
protected $capture_factory;
/**
* The session handler
*
* @var SessionHandler
*/
protected $session_handler;
/**
* PayUponInvoice constructor.
*
* @param string $module_url The module URL.
* @param FraudNet $fraud_net The FraudNet entity.
* @param PayUponInvoiceOrderEndpoint $pui_order_endpoint The PUI order endpoint.
* @param LoggerInterface $logger The logger.
* @param Settings $settings The settings.
* @param Environment $environment The environment.
* @param string $asset_version The asset version.
* @param State $state The onboarding state.
* @param bool $is_ppcp_settings_page Whether page is PayPal settings poge.
* @param string $current_ppcp_settings_page_id Current PayPal settings page id.
* @param PayUponInvoiceProductStatus $pui_product_status The PUI product status.
* @param PayUponInvoiceHelper $pui_helper The PUI helper.
* @param CheckoutHelper $checkout_helper The checkout helper.
* @param CaptureFactory $capture_factory The capture factory.
* @param SessionHandler $session_handler The session handler.
*/
public function __construct(
string $module_url,
FraudNet $fraud_net,
PayUponInvoiceOrderEndpoint $pui_order_endpoint,
LoggerInterface $logger,
Settings $settings,
Environment $environment,
string $asset_version,
State $state,
bool $is_ppcp_settings_page,
string $current_ppcp_settings_page_id,
PayUponInvoiceProductStatus $pui_product_status,
PayUponInvoiceHelper $pui_helper,
CheckoutHelper $checkout_helper,
CaptureFactory $capture_factory,
SessionHandler $session_handler
CaptureFactory $capture_factory
) {
$this->module_url = $module_url;
$this->fraud_net = $fraud_net;
$this->pui_order_endpoint = $pui_order_endpoint;
$this->logger = $logger;
$this->settings = $settings;
$this->environment = $environment;
$this->asset_version = $asset_version;
$this->state = $state;
$this->is_ppcp_settings_page = $is_ppcp_settings_page;
$this->current_ppcp_settings_page_id = $current_ppcp_settings_page_id;
$this->pui_product_status = $pui_product_status;
$this->pui_helper = $pui_helper;
$this->checkout_helper = $checkout_helper;
$this->capture_factory = $capture_factory;
$this->session_handler = $session_handler;
}
/**
@ -236,11 +177,6 @@ class PayUponInvoice {
}
);
add_action(
'wp_enqueue_scripts',
array( $this, 'register_assets' )
);
add_action(
'ppcp_payment_capture_completed_webhook_handler',
function ( WC_Order $wc_order, string $order_id ) {
@ -610,31 +546,4 @@ class PayUponInvoice {
}
);
}
/**
* Registers PUI assets.
*/
public function register_assets(): void {
$gateway_settings = get_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings' );
$gateway_enabled = $gateway_settings['enabled'] ?? '';
if ( $gateway_enabled === 'yes' && ! $this->session_handler->order() && ( is_checkout() || is_checkout_pay_page() ) ) {
wp_enqueue_script(
'ppcp-pay-upon-invoice',
trailingslashit( $this->module_url ) . 'assets/js/pay-upon-invoice.js',
array(),
$this->asset_version,
true
);
wp_localize_script(
'ppcp-pay-upon-invoice',
'FraudNetConfig',
array(
'f' => $this->fraud_net->session_id(),
's' => $this->fraud_net->source_website_id(),
'sandbox' => $this->environment->current_environment_is( Environment::SANDBOX ),
)
);
}
}
}

View file

@ -22,6 +22,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn;
use WooCommerce\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail;
use WooCommerce\PayPalCommerce\WcGateway\Admin\RenderAuthorizeAction;
use WooCommerce\PayPalCommerce\WcGateway\Assets\FraudNetAssets;
use WooCommerce\PayPalCommerce\WcGateway\Assets\SettingsPageAssets;
use WooCommerce\PayPalCommerce\WcGateway\Checkout\CheckoutPayPalAddressPreset;
use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways;
@ -258,6 +259,10 @@ class WCGatewayModule implements ModuleInterface {
}
( $c->get( 'wcgateway.oxxo' ) )->init();
$fraudnet_assets = $c->get( 'wcgateway.fraudnet-assets' );
assert( $fraudnet_assets instanceof FraudNetAssets );
$fraudnet_assets->register_assets();
}
);

View file

@ -7,7 +7,7 @@ module.exports = {
target: 'web',
entry: {
'gateway-settings': path.resolve('./resources/js/gateway-settings.js'),
'pay-upon-invoice': path.resolve('./resources/js/pay-upon-invoice.js'),
'fraudnet': path.resolve('./resources/js/fraudnet.js'),
'oxxo': path.resolve('./resources/js/oxxo.js'),
'gateway-settings-style': path.resolve('./resources/css/gateway-settings.scss'),
},

View file

@ -28,6 +28,7 @@ use WooCommerce\PayPalCommerce\Webhooks\Handler\PaymentCaptureReversed;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\Webhooks\Handler\VaultCreditCardCreated;
use WooCommerce\PayPalCommerce\Webhooks\Handler\VaultPaymentTokenCreated;
use WooCommerce\PayPalCommerce\Webhooks\Handler\VaultPaymentTokenDeleted;
use WooCommerce\PayPalCommerce\Webhooks\Status\Assets\WebhooksStatusPageAssets;
use WooCommerce\PayPalCommerce\Webhooks\Status\WebhookSimulation;
@ -73,6 +74,8 @@ return array(
$prefix = $container->get( 'api.prefix' );
$order_endpoint = $container->get( 'api.endpoint.order' );
$authorized_payments_processor = $container->get( 'wcgateway.processor.authorized-payments' );
$payment_token_paypal = $container->get( 'vaulting.payment-token-paypal' );
return array(
new CheckoutOrderApproved( $logger, $prefix, $order_endpoint ),
new CheckoutOrderCompleted( $logger, $prefix ),
@ -80,8 +83,8 @@ return array(
new PaymentCaptureRefunded( $logger, $prefix ),
new PaymentCaptureReversed( $logger, $prefix ),
new PaymentCaptureCompleted( $logger, $prefix, $order_endpoint ),
new VaultPaymentTokenCreated( $logger, $prefix, $authorized_payments_processor ),
new VaultCreditCardCreated( $logger, $prefix ),
new VaultPaymentTokenCreated( $logger, $prefix, $authorized_payments_processor, $payment_token_paypal ),
new VaultPaymentTokenDeleted( $logger ),
new PaymentCapturePending( $logger ),
);
},

View file

@ -10,6 +10,11 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
use Psr\Log\LoggerInterface;
use WC_Payment_Token_CC;
use WC_Payment_Tokens;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenPayPal;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
use WP_REST_Request;
use WP_REST_Response;
@ -40,17 +45,31 @@ class VaultPaymentTokenCreated implements RequestHandler {
*/
protected $authorized_payments_processor;
/**
* WooCommerce Payment token PayPal.
*
* @var PaymentTokenPayPal
*/
private $payment_token_paypal;
/**
* VaultPaymentTokenCreated constructor.
*
* @param LoggerInterface $logger The logger.
* @param string $prefix The prefix.
* @param AuthorizedPaymentsProcessor $authorized_payments_processor The authorized payment processor.
* @param PaymentTokenPayPal $payment_token_paypal WooCommerce Payment token PayPal.
*/
public function __construct( LoggerInterface $logger, string $prefix, AuthorizedPaymentsProcessor $authorized_payments_processor ) {
public function __construct(
LoggerInterface $logger,
string $prefix,
AuthorizedPaymentsProcessor $authorized_payments_processor,
PaymentTokenPayPal $payment_token_paypal
) {
$this->logger = $logger;
$this->prefix = $prefix;
$this->authorized_payments_processor = $authorized_payments_processor;
$this->payment_token_paypal = $payment_token_paypal;
}
/**
@ -98,6 +117,35 @@ class VaultPaymentTokenCreated implements RequestHandler {
$wc_customer_id = (int) str_replace( $this->prefix, '', $customer_id );
$this->authorized_payments_processor->capture_authorized_payments_for_customer( $wc_customer_id );
if ( ! is_null( $request['resource'] ) && isset( $request['resource']['id'] ) ) {
if ( ! is_null( $request['resource']['source'] ) && isset( $request['resource']['source']['card'] ) ) {
$token = new WC_Payment_Token_CC();
$token->set_token( $request['resource']['id'] );
$token->set_user_id( $wc_customer_id );
$token->set_gateway_id( CreditCardGateway::ID );
$token->set_last4( $request['resource']['source']['card']['last_digits'] ?? '' );
$expiry = explode( '-', $request['resource']['source']['card']['expiry'] ?? '' );
$token->set_expiry_year( $expiry[0] ?? '' );
$token->set_expiry_month( $expiry[1] ?? '' );
$token->set_card_type( $request['resource']['source']['card']['brand'] ?? '' );
$token->save();
WC_Payment_Tokens::set_users_default( $wc_customer_id, $token->get_id() );
} elseif ( isset( $request['resource']['source']['paypal'] ) ) {
$this->payment_token_paypal->set_token( $request['resource']['id'] );
$this->payment_token_paypal->set_user_id( $wc_customer_id );
$this->payment_token_paypal->set_gateway_id( PayPalGateway::ID );
$email = $request['resource']['source']['paypal']['payer']['email_address'] ?? '';
if ( $email && is_email( $email ) ) {
$this->payment_token_paypal->set_email( $email );
}
$this->payment_token_paypal->save();
WC_Payment_Tokens::set_users_default( $wc_customer_id, $this->payment_token_paypal->get_id() );
}
}
$response['success'] = true;
return new WP_REST_Response( $response );
}

View file

@ -1,6 +1,6 @@
<?php
/**
* Handles the Webhook VAULT.CREDIT-CARD.CREATED
* Handles the Webhook VAULT.PAYMENT-TOKEN.DELETED
*
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
*/
@ -10,37 +10,29 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
use Psr\Log\LoggerInterface;
use WC_Payment_Tokens;
use WP_REST_Request;
use WP_REST_Response;
/**
* Class VaultCreditCardCreated
* Class VaultPaymentTokenDeleted
*/
class VaultCreditCardCreated implements RequestHandler {
class VaultPaymentTokenDeleted implements RequestHandler {
/**
* The logger.
*
* @var LoggerInterface
*/
protected $logger;
private $logger;
/**
* The prefix.
*
* @var string
*/
protected $prefix;
/**
* VaultCreditCardCreated constructor.
* VaultPaymentTokenDeleted constructor.
*
* @param LoggerInterface $logger The logger.
* @param string $prefix The prefix.
*/
public function __construct( LoggerInterface $logger, string $prefix ) {
public function __construct( LoggerInterface $logger ) {
$this->logger = $logger;
$this->prefix = $prefix;
}
/**
@ -50,7 +42,7 @@ class VaultCreditCardCreated implements RequestHandler {
*/
public function event_types(): array {
return array(
'VAULT.CREDIT-CARD.CREATED',
'VAULT.PAYMENT-TOKEN.DELETED',
);
}
@ -73,15 +65,31 @@ class VaultCreditCardCreated implements RequestHandler {
* @return WP_REST_Response
*/
public function handle_request( WP_REST_Request $request ): WP_REST_Response {
// TODO currently this webhook is not triggered from PayPal, implement it once is available.
$response = array( 'success' => false );
$message = 'VAULT.CREDIT-CARD.CREATED received.';
$this->logger->log( 'info', $message );
$response = array(
'success' => true,
'message' => $message,
);
if ( ! is_null( $request['resource'] ) && isset( $request['resource']['id'] ) ) {
$token_id = wc_clean( wp_unslash( $request['resource']['id'] ?? '' ) );
/**
* Needed for database query.
*
* @psalm-suppress InvalidGlobal
*/
global $wpdb;
$token = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token=%s",
$token_id
)
);
if ( isset( $token->token_id ) ) {
WC_Payment_Tokens::delete( $token->token_id );
}
}
$response['success'] = true;
return new WP_REST_Response( $response );
}
}

View file

@ -23,7 +23,7 @@ class WebhookSimulation {
public const STATE_WAITING = 'waiting';
public const STATE_RECEIVED = 'received';
private const OPTION_ID = 'ppcp-webhook-simulation';
public const OPTION_ID = 'ppcp-webhook-simulation';
/**
* The webhooks endpoint.

View file

@ -11,27 +11,27 @@
"install:modules:ppcp-button": "cd modules/ppcp-button && yarn install",
"install:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn install",
"install:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn install",
"install:modules:ppcp-vaulting": "cd modules/ppcp-vaulting && yarn install",
"install:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn install",
"install:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn install",
"install:modules:ppcp-compat": "cd modules/ppcp-compat && yarn install",
"install:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn install",
"build:modules:ppcp-button": "cd modules/ppcp-button && yarn run build",
"build:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn run build",
"build:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn run build",
"build:modules:ppcp-vaulting": "cd modules/ppcp-vaulting && yarn run build",
"build:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn run build",
"build:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn run build",
"build:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run build",
"build:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run build",
"build:modules": "run-p build:modules:*",
"watch:modules:ppcp-button": "cd modules/ppcp-button && yarn run watch",
"watch:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn run watch",
"watch:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn run watch",
"watch:modules:ppcp-vaulting": "cd modules/ppcp-vaulting && yarn run watch",
"watch:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn run watch",
"watch:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn run watch",
"watch:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run watch",
"watch:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run watch",
"watch:modules": "run-p watch:modules:*",
"ddev:setup": "ddev start && ddev orchestrate",

View file

@ -29,6 +29,7 @@ use Mockery;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
use WooCommerce\PayPalCommerce\TestCase;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
use function Brain\Monkey\Functions\expect;
use function Brain\Monkey\Functions\when;
@ -74,6 +75,8 @@ class OrderEndpointTest extends TestCase
$headers->shouldReceive('getAll');
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -83,7 +86,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
$rawResponse = [
@ -135,6 +140,8 @@ class OrderEndpointTest extends TestCase
$headers->shouldReceive('getAll');
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -144,7 +151,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
$rawResponse = [
@ -187,6 +196,8 @@ class OrderEndpointTest extends TestCase
->expects('get_for_order_id')->with($orderId)->andReturn('uniqueRequestId');
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -196,7 +207,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
expect('wp_remote_get')->andReturn($rawResponse);
@ -253,6 +266,8 @@ class OrderEndpointTest extends TestCase
->expects('get_for_order')->with($orderToCapture)->andReturn('uniqueRequestId');
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -262,7 +277,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
expect('wp_remote_get')
@ -320,6 +337,8 @@ class OrderEndpointTest extends TestCase
$paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class);
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -329,7 +348,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
$result = $testee->capture($orderToCapture);
@ -365,6 +386,8 @@ class OrderEndpointTest extends TestCase
->expects('get_for_order')->with($orderToCapture)->andReturn('uniqueRequestId');
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -374,7 +397,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
$headers->shouldReceive('getAll');
@ -417,6 +442,8 @@ class OrderEndpointTest extends TestCase
->expects('get_for_order')->with($orderToCapture)->andReturn('uniqueRequestId');
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -426,7 +453,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
@ -471,6 +500,8 @@ class OrderEndpointTest extends TestCase
->expects('get_for_order')->with($orderToCapture)->andReturn('uniqueRequestId');
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = Mockery::mock(
OrderEndpoint::class,
[
@ -483,6 +514,8 @@ class OrderEndpointTest extends TestCase
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper,
false,
$fraudnet
]
)->makePartial();
$orderToExpect = Mockery::mock(Order::class);
@ -551,6 +584,8 @@ class OrderEndpointTest extends TestCase
->expects('get_for_order')->with($orderToUpdate)->andReturn('uniqueRequestId');
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = Mockery::mock(
OrderEndpoint::class,
[
@ -563,6 +598,8 @@ class OrderEndpointTest extends TestCase
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper,
false,
$fraudnet
]
)->makePartial();
$testee
@ -656,6 +693,8 @@ class OrderEndpointTest extends TestCase
->expects('get_for_order')->with($orderToUpdate)->andReturn('uniqueRequestId');
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -665,7 +704,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
expect('wp_remote_get')
@ -755,6 +796,8 @@ class OrderEndpointTest extends TestCase
->expects('get_for_order')->with($orderToUpdate)->andReturn('uniqueRequestId');
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = Mockery::mock(
OrderEndpoint::class,
[
@ -766,7 +809,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
]
)->makePartial();
@ -831,6 +876,8 @@ class OrderEndpointTest extends TestCase
$paypalRequestIdRepository = Mockery::mock(PayPalRequestIdRepository::class);
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -840,7 +887,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
$result = $testee->patch_order_with($orderToUpdate, $orderToCompare);
@ -901,6 +950,8 @@ class OrderEndpointTest extends TestCase
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$subscription_helper->shouldReceive('cart_contains_subscription')->andReturn(true);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -910,7 +961,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
$purchaseUnit = Mockery::mock(PurchaseUnit::class, ['contains_physical_goods' => false]);
@ -1010,6 +1063,8 @@ class OrderEndpointTest extends TestCase
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$subscription_helper->shouldReceive('cart_contains_subscription')->andReturn(true);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -1019,7 +1074,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
$purchaseUnit = Mockery::mock(PurchaseUnit::class, ['contains_physical_goods' => true]);
@ -1088,6 +1145,8 @@ class OrderEndpointTest extends TestCase
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$subscription_helper->shouldReceive('cart_contains_subscription')->andReturn(true);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -1097,7 +1156,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
$purchaseUnit = Mockery::mock(PurchaseUnit::class, ['contains_physical_goods' => false]);
@ -1177,6 +1238,8 @@ class OrderEndpointTest extends TestCase
$subscription_helper = Mockery::mock(SubscriptionHelper::class);
$subscription_helper->shouldReceive('cart_contains_subscription')->andReturn(true);
$fraudnet = Mockery::mock(FraudNet::class);
$testee = new OrderEndpoint(
$host,
$bearer,
@ -1186,7 +1249,9 @@ class OrderEndpointTest extends TestCase
$logger,
$applicationContextRepository,
$paypalRequestIdRepository,
$subscription_helper
$subscription_helper,
false,
$fraudnet
);
$purchaseUnit = Mockery::mock(PurchaseUnit::class, ['contains_physical_goods' => true]);

View file

@ -14,7 +14,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Token;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
use WooCommerce\PayPalCommerce\TestCase;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNet;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PaymentSource;
use function Brain\Monkey\Functions\expect;
use function Brain\Monkey\Functions\when;

75
uninstall.php Normal file
View file

@ -0,0 +1,75 @@
<?php
/**
* Uninstalls the plugin.
*
* @package WooCommerce\PayPalCommerce
*/
declare(strict_types=1);
use WooCommerce\PayPalCommerce\Uninstall\ClearDatabaseInterface;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\CachingContainer;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
die( 'Direct access not allowed.' );
}
$root_dir = __DIR__;
$main_plugin_file = "{$root_dir}/woocommerce-paypal-payments.php";
if ( !file_exists( $main_plugin_file ) ) {
return;
}
require $main_plugin_file;
( static function (string $root_dir): void {
$autoload_filepath = "{$root_dir}/vendor/autoload.php";
if ( file_exists( $autoload_filepath ) && ! class_exists( '\WooCommerce\PayPalCommerce\PluginModule' ) ) {
require $autoload_filepath;
}
try {
$bootstrap = require "{$root_dir}/bootstrap.php";
$app_container = $bootstrap( $root_dir );
assert( $app_container instanceof CachingContainer );
$settings = $app_container->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$should_clear_db = $settings->has( 'uninstall_clear_db_on_uninstall' ) && $settings->get( 'uninstall_clear_db_on_uninstall' );
if ( ! $should_clear_db ) {
return;
}
$clear_db = $app_container->get( 'uninstall.clear-db' );
assert( $clear_db instanceof ClearDatabaseInterface );
$option_names = $app_container->get( 'uninstall.ppcp-all-option-names' );
$scheduled_action_names = $app_container->get( 'uninstall.ppcp-all-scheduled-action-names' );
$clear_db->delete_options( $option_names );
$clear_db->clear_scheduled_actions( $scheduled_action_names );
} catch ( Throwable $throwable ) {
$message = sprintf(
'<strong>Error:</strong> %s <br><pre>%s</pre>',
$throwable->getMessage(),
$throwable->getTraceAsString()
);
add_action(
'all_admin_notices',
static function () use ( $message ) {
$class = 'notice notice-error';
printf(
'<div class="%1$s"><p>%2$s</p></div>',
esc_attr( $class ),
wp_kses_post( $message )
);
}
);
}
} )($root_dir);