Merge trunk (WIP)

This commit is contained in:
dinamiko 2021-10-07 17:36:29 +02:00
commit e36d77fad2
25 changed files with 1052 additions and 195 deletions

314
composer.lock generated
View file

@ -35,20 +35,24 @@
], ],
"description": "Promoting container interoperability through standard service providers", "description": "Promoting container interoperability through standard service providers",
"homepage": "https://github.com/container-interop/service-provider", "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" "time": "2017-09-20T14:13:36+00:00"
}, },
{ {
"name": "dhii/collections-interface", "name": "dhii/collections-interface",
"version": "v0.3.0-alpha4", "version": "v0.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Dhii/collections-interface.git", "url": "https://github.com/Dhii/collections-interface.git",
"reference": "da334f75f6477ef7eecaf28df1d5253fe05684ee" "reference": "74464a969b340d16889eacd9eadc9817f7e7f47a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Dhii/collections-interface/zipball/da334f75f6477ef7eecaf28df1d5253fe05684ee", "url": "https://api.github.com/repos/Dhii/collections-interface/zipball/74464a969b340d16889eacd9eadc9817f7e7f47a",
"reference": "da334f75f6477ef7eecaf28df1d5253fe05684ee", "reference": "74464a969b340d16889eacd9eadc9817f7e7f47a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -58,7 +62,7 @@
"require-dev": { "require-dev": {
"phpunit/phpunit": "^7.0 | ^8.0 | ^9.0", "phpunit/phpunit": "^7.0 | ^8.0 | ^9.0",
"slevomat/coding-standard": "^6.0", "slevomat/coding-standard": "^6.0",
"vimeo/psalm": "^3.11.7 | ^4.0" "vimeo/psalm": "^4.0"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@ -86,7 +90,7 @@
} }
], ],
"description": "A highly ISP-compliant collection of interfaces that represent maps and lists.", "description": "A highly ISP-compliant collection of interfaces that represent maps and lists.",
"time": "2021-03-09T17:36:34+00:00" "time": "2021-10-06T10:56:09+00:00"
}, },
{ {
"name": "dhii/containers", "name": "dhii/containers",
@ -139,6 +143,10 @@
"PSR-11", "PSR-11",
"container" "container"
], ],
"support": {
"issues": "https://github.com/Dhii/containers/issues",
"source": "https://github.com/Dhii/containers/tree/v0.1.0-alpha1"
},
"time": "2020-09-14T18:27:47+00:00" "time": "2020-09-14T18:27:47+00:00"
}, },
{ {
@ -193,6 +201,10 @@
} }
], ],
"description": "Interfaces for working with data containers", "description": "Interfaces for working with data containers",
"support": {
"issues": "https://github.com/Dhii/data-container-interface/issues",
"source": "https://github.com/Dhii/data-container-interface/tree/v0.2.1-alpha1"
},
"abandoned": "dhii/collections-interface", "abandoned": "dhii/collections-interface",
"time": "2019-05-10T14:17:29+00:00" "time": "2019-05-10T14:17:29+00:00"
}, },
@ -242,6 +254,10 @@
} }
], ],
"description": "Interfaces for most common exceptions", "description": "Interfaces for most common exceptions",
"support": {
"issues": "https://github.com/Dhii/exception-interface/issues",
"source": "https://github.com/Dhii/exception-interface/tree/develop"
},
"time": "2018-08-29T10:42:04+00:00" "time": "2018-08-29T10:42:04+00:00"
}, },
{ {
@ -292,6 +308,10 @@
} }
], ],
"description": "Interfaces for working with factories.", "description": "Interfaces for working with factories.",
"support": {
"issues": "https://github.com/Dhii/factory-interface/issues",
"source": "https://github.com/Dhii/factory-interface/tree/master"
},
"abandoned": true, "abandoned": true,
"time": "2018-08-29T11:15:09+00:00" "time": "2018-08-29T11:15:09+00:00"
}, },
@ -341,6 +361,10 @@
} }
], ],
"description": "Interfaces for modules", "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" "time": "2021-08-23T08:23:01+00:00"
}, },
{ {
@ -383,6 +407,10 @@
} }
], ],
"description": "Interoperability interface for objects that can be cast to string", "description": "Interoperability interface for objects that can be cast to string",
"support": {
"issues": "https://github.com/Dhii/stringable-interface/issues",
"source": "https://github.com/Dhii/stringable-interface/tree/master"
},
"abandoned": "symfony/polyfill-php80", "abandoned": "symfony/polyfill-php80",
"time": "2017-01-23T15:08:20+00:00" "time": "2017-01-23T15:08:20+00:00"
}, },
@ -439,6 +467,10 @@
"di", "di",
"wordpress" "wordpress"
], ],
"support": {
"issues": "https://github.com/Dhii/wp-containers/issues",
"source": "https://github.com/Dhii/wp-containers/tree/v0.1.0-alpha1"
},
"abandoned": "wp-oop/containers", "abandoned": "wp-oop/containers",
"time": "2019-05-10T15:04:22+00:00" "time": "2019-05-10T15:04:22+00:00"
}, },
@ -489,6 +521,10 @@
"container-interop", "container-interop",
"psr" "psr"
], ],
"support": {
"issues": "https://github.com/php-fig/container/issues",
"source": "https://github.com/php-fig/container/tree/master"
},
"time": "2017-02-14T16:28:37+00:00" "time": "2017-02-14T16:28:37+00:00"
}, },
{ {
@ -536,6 +572,9 @@
"psr", "psr",
"psr-3" "psr-3"
], ],
"support": {
"source": "https://github.com/php-fig/log/tree/1.1.4"
},
"time": "2021-05-03T11:20:27+00:00" "time": "2021-05-03T11:20:27+00:00"
}, },
{ {
@ -576,6 +615,10 @@
} }
], ],
"description": "A polyfill for getallheaders.", "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" "time": "2019-03-08T08:55:37+00:00"
} }
], ],
@ -622,6 +665,10 @@
"runkit", "runkit",
"testing" "testing"
], ],
"support": {
"issues": "https://github.com/antecedent/patchwork/issues",
"source": "https://github.com/antecedent/patchwork/tree/2.1.15"
},
"time": "2021-08-22T08:00:13+00:00" "time": "2021-08-22T08:00:13+00:00"
}, },
{ {
@ -688,6 +735,10 @@
"test", "test",
"testing" "testing"
], ],
"support": {
"issues": "https://github.com/Brain-WP/BrainMonkey/issues",
"source": "https://github.com/Brain-WP/BrainMonkey"
},
"time": "2020-10-13T17:56:14+00:00" "time": "2020-10-13T17:56:14+00:00"
}, },
{ {
@ -754,6 +805,10 @@
"stylecheck", "stylecheck",
"tests" "tests"
], ],
"support": {
"issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues",
"source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer"
},
"time": "2020-12-07T18:04:37+00:00" "time": "2020-12-07T18:04:37+00:00"
}, },
{ {
@ -805,6 +860,24 @@
"constructor", "constructor",
"instantiate" "instantiate"
], ],
"support": {
"issues": "https://github.com/doctrine/instantiator/issues",
"source": "https://github.com/doctrine/instantiator/tree/1.4.0"
},
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
"type": "tidelift"
}
],
"time": "2020-11-10T18:47:58+00:00" "time": "2020-11-10T18:47:58+00:00"
}, },
{ {
@ -852,6 +925,10 @@
"keywords": [ "keywords": [
"test" "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" "time": "2020-07-09T08:09:16+00:00"
}, },
{ {
@ -965,6 +1042,16 @@
"object", "object",
"object graph" "object graph"
], ],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.10.2"
},
"funding": [
{
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
"type": "tidelift"
}
],
"time": "2020-11-13T09:40:50+00:00" "time": "2020-11-13T09:40:50+00:00"
}, },
{ {
@ -1020,6 +1107,10 @@
} }
], ],
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "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/master"
},
"time": "2018-07-08T19:23:20+00:00" "time": "2018-07-08T19:23:20+00:00"
}, },
{ {
@ -1067,6 +1158,10 @@
} }
], ],
"description": "Library for handling version information and constraints", "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/master"
},
"time": "2018-07-08T19:19:57+00:00" "time": "2018-07-08T19:19:57+00:00"
}, },
{ {
@ -1125,6 +1220,10 @@
"phpcs", "phpcs",
"standards" "standards"
], ],
"support": {
"issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues",
"source": "https://github.com/PHPCompatibility/PHPCompatibility"
},
"time": "2019-12-27T09:44:58+00:00" "time": "2019-12-27T09:44:58+00:00"
}, },
{ {
@ -1177,6 +1276,10 @@
"polyfill", "polyfill",
"standards" "standards"
], ],
"support": {
"issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues",
"source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie"
},
"time": "2021-02-15T10:24:51+00:00" "time": "2021-02-15T10:24:51+00:00"
}, },
{ {
@ -1227,6 +1330,10 @@
"standards", "standards",
"wordpress" "wordpress"
], ],
"support": {
"issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues",
"source": "https://github.com/PHPCompatibility/PHPCompatibilityWP"
},
"time": "2021-07-21T11:09:57+00:00" "time": "2021-07-21T11:09:57+00:00"
}, },
{ {
@ -1276,6 +1383,10 @@
"reflection", "reflection",
"static analysis" "static analysis"
], ],
"support": {
"issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
"source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master"
},
"time": "2020-04-27T09:25:28+00:00" "time": "2020-04-27T09:25:28+00:00"
}, },
{ {
@ -1328,6 +1439,10 @@
} }
], ],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "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/release/4.x"
},
"time": "2019-12-28T18:55:12+00:00" "time": "2019-12-28T18:55:12+00:00"
}, },
{ {
@ -1375,6 +1490,10 @@
} }
], ],
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "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/0.7.2"
},
"time": "2019-08-22T18:11:29+00:00" "time": "2019-08-22T18:11:29+00:00"
}, },
{ {
@ -1438,6 +1557,10 @@
"spy", "spy",
"stub" "stub"
], ],
"support": {
"issues": "https://github.com/phpspec/prophecy/issues",
"source": "https://github.com/phpspec/prophecy/tree/v1.10.3"
},
"time": "2020-03-05T15:02:03+00:00" "time": "2020-03-05T15:02:03+00:00"
}, },
{ {
@ -1501,6 +1624,10 @@
"testing", "testing",
"xunit" "xunit"
], ],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/master"
},
"time": "2018-10-31T16:06:48+00:00" "time": "2018-10-31T16:06:48+00:00"
}, },
{ {
@ -1551,6 +1678,16 @@
"filesystem", "filesystem",
"iterator" "iterator"
], ],
"support": {
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2021-07-19T06:46:01+00:00" "time": "2021-07-19T06:46:01+00:00"
}, },
{ {
@ -1592,6 +1729,10 @@
"keywords": [ "keywords": [
"template" "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" "time": "2015-06-21T13:50:34+00:00"
}, },
{ {
@ -1641,6 +1782,16 @@
"keywords": [ "keywords": [
"timer" "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" "time": "2020-11-30T08:20:02+00:00"
}, },
{ {
@ -1690,6 +1841,16 @@
"keywords": [ "keywords": [
"tokenizer" "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, "abandoned": true,
"time": "2021-07-26T12:15:06+00:00" "time": "2021-07-26T12:15:06+00:00"
}, },
@ -1775,6 +1936,10 @@
"testing", "testing",
"xunit" "xunit"
], ],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/7.5.20"
},
"time": "2020-01-08T08:45:45+00:00" "time": "2020-01-08T08:45:45+00:00"
}, },
{ {
@ -1820,6 +1985,16 @@
], ],
"description": "Looks up which function or method a line of code belongs to", "description": "Looks up which function or method a line of code belongs to",
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "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" "time": "2020-11-30T08:15:22+00:00"
}, },
{ {
@ -1884,6 +2059,16 @@
"compare", "compare",
"equality" "equality"
], ],
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
"source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-11-30T08:04:30+00:00" "time": "2020-11-30T08:04:30+00:00"
}, },
{ {
@ -1940,6 +2125,16 @@
"unidiff", "unidiff",
"unified diff" "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" "time": "2020-11-30T07:59:04+00:00"
}, },
{ {
@ -1993,6 +2188,16 @@
"environment", "environment",
"hhvm" "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" "time": "2020-11-30T07:53:42+00:00"
}, },
{ {
@ -2060,6 +2265,16 @@
"export", "export",
"exporter" "exporter"
], ],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-11-30T07:47:53+00:00" "time": "2020-11-30T07:47:53+00:00"
}, },
{ {
@ -2111,6 +2326,10 @@
"keywords": [ "keywords": [
"global state" "global state"
], ],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
"source": "https://github.com/sebastianbergmann/global-state/tree/2.0.0"
},
"time": "2017-04-27T15:39:26+00:00" "time": "2017-04-27T15:39:26+00:00"
}, },
{ {
@ -2158,6 +2377,16 @@
], ],
"description": "Traverses array structures and object graphs to enumerate all referenced objects", "description": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/", "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" "time": "2020-11-30T07:40:27+00:00"
}, },
{ {
@ -2203,6 +2432,16 @@
], ],
"description": "Allows reflection of object attributes, including inherited and non-public ones", "description": "Allows reflection of object attributes, including inherited and non-public ones",
"homepage": "https://github.com/sebastianbergmann/object-reflector/", "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" "time": "2020-11-30T07:37:18+00:00"
}, },
{ {
@ -2256,6 +2495,16 @@
], ],
"description": "Provides functionality to recursively process PHP variables", "description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context", "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" "time": "2020-11-30T07:34:24+00:00"
}, },
{ {
@ -2298,6 +2547,16 @@
], ],
"description": "Provides a list of PHP built-in functions that operate on resources", "description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations", "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" "time": "2020-11-30T07:30:19+00:00"
}, },
{ {
@ -2341,6 +2600,10 @@
], ],
"description": "Library that helps with managing the version number of Git-hosted PHP projects", "description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version", "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" "time": "2016-10-03T07:35:21+00:00"
}, },
{ {
@ -2392,6 +2655,11 @@
"phpcs", "phpcs",
"standards" "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": "2021-04-09T00:54:41+00:00" "time": "2021-04-09T00:54:41+00:00"
}, },
{ {
@ -2454,6 +2722,23 @@
"polyfill", "polyfill",
"portable" "portable"
], ],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2021-02-19T12:13:01+00:00" "time": "2021-02-19T12:13:01+00:00"
}, },
{ {
@ -2494,6 +2779,10 @@
} }
], ],
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "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/master"
},
"time": "2019-06-13T22:48:21+00:00" "time": "2019-06-13T22:48:21+00:00"
}, },
{ {
@ -2543,6 +2832,10 @@
"check", "check",
"validate" "validate"
], ],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/1.9.1"
},
"time": "2020-07-08T17:02:28+00:00" "time": "2020-07-08T17:02:28+00:00"
}, },
{ {
@ -2583,6 +2876,10 @@
"woocommerce", "woocommerce",
"wordpress" "wordpress"
], ],
"support": {
"issues": "https://github.com/woocommerce/woocommerce-sniffs/issues",
"source": "https://github.com/woocommerce/woocommerce-sniffs/tree/0.1.1"
},
"time": "2021-07-29T17:25:16+00:00" "time": "2021-07-29T17:25:16+00:00"
}, },
{ {
@ -2629,6 +2926,11 @@
"standards", "standards",
"wordpress" "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" "time": "2020-05-13T23:57:56+00:00"
} }
], ],

View file

@ -141,12 +141,14 @@ return array(
}, },
'api.endpoint.payments' => static function ( $container ): PaymentsEndpoint { 'api.endpoint.payments' => static function ( $container ): PaymentsEndpoint {
$authorizations_factory = $container->get( 'api.factory.authorization' ); $authorizations_factory = $container->get( 'api.factory.authorization' );
$logger = $container->get( 'woocommerce.logger.woocommerce' ); $capture_factory = $container->get( 'api.factory.capture' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
return new PaymentsEndpoint( return new PaymentsEndpoint(
$container->get( 'api.host' ), $container->get( 'api.host' ),
$container->get( 'api.bearer' ), $container->get( 'api.bearer' ),
$authorizations_factory, $authorizations_factory,
$capture_factory,
$logger $logger
); );
}, },

View file

@ -11,6 +11,8 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext; use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer; use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
@ -349,8 +351,8 @@ class OrderEndpoint {
$order = $this->order_factory->from_paypal_response( $json ); $order = $this->order_factory->from_paypal_response( $json );
$purchase_units_payments_captures_status = $order->purchase_units()[0]->payments()->captures()[0]->status() ?? ''; $capture_status = $order->purchase_units()[0]->payments()->captures()[0]->status() ?? null;
if ( $purchase_units_payments_captures_status && 'DECLINED' === $purchase_units_payments_captures_status ) { if ( $capture_status && $capture_status->is( CaptureStatus::DECLINED ) ) {
throw new RuntimeException( __( 'Payment provider declined the payment, please use a different payment method.', 'woocommerce-paypal-payments' ) ); throw new RuntimeException( __( 'Payment provider declined the payment, please use a different payment method.', 'woocommerce-paypal-payments' ) );
} }
@ -423,6 +425,12 @@ class OrderEndpoint {
throw $error; throw $error;
} }
$order = $this->order_factory->from_paypal_response( $json ); $order = $this->order_factory->from_paypal_response( $json );
$authorization_status = $order->purchase_units()[0]->payments()->authorizations()[0]->status() ?? null;
if ( $authorization_status && $authorization_status->is( AuthorizationStatus::DENIED ) ) {
throw new RuntimeException( __( 'Payment provider declined the payment, please use a different payment method.', 'woocommerce-paypal-payments' ) );
}
return $order; return $order;
} }

View file

@ -11,11 +11,13 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Refund; use WooCommerce\PayPalCommerce\ApiClient\Entity\Refund;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Factory\CaptureFactory;
/** /**
* Class PaymentsEndpoint * Class PaymentsEndpoint
@ -45,6 +47,13 @@ class PaymentsEndpoint {
*/ */
private $authorizations_factory; private $authorizations_factory;
/**
* The capture factory.
*
* @var CaptureFactory
*/
private $capture_factory;
/** /**
* The logger. * The logger.
* *
@ -58,18 +67,21 @@ class PaymentsEndpoint {
* @param string $host The host. * @param string $host The host.
* @param Bearer $bearer The bearer. * @param Bearer $bearer The bearer.
* @param AuthorizationFactory $authorization_factory The authorization factory. * @param AuthorizationFactory $authorization_factory The authorization factory.
* @param CaptureFactory $capture_factory The capture factory.
* @param LoggerInterface $logger The logger. * @param LoggerInterface $logger The logger.
*/ */
public function __construct( public function __construct(
string $host, string $host,
Bearer $bearer, Bearer $bearer,
AuthorizationFactory $authorization_factory, AuthorizationFactory $authorization_factory,
CaptureFactory $capture_factory,
LoggerInterface $logger LoggerInterface $logger
) { ) {
$this->host = $host; $this->host = $host;
$this->bearer = $bearer; $this->bearer = $bearer;
$this->authorizations_factory = $authorization_factory; $this->authorizations_factory = $authorization_factory;
$this->capture_factory = $capture_factory;
$this->logger = $logger; $this->logger = $logger;
} }
@ -136,11 +148,11 @@ class PaymentsEndpoint {
* *
* @param string $authorization_id The id. * @param string $authorization_id The id.
* *
* @return Authorization * @return Capture
* @throws RuntimeException If the request fails. * @throws RuntimeException If the request fails.
* @throws PayPalApiException If the request fails. * @throws PayPalApiException If the request fails.
*/ */
public function capture( string $authorization_id ): Authorization { public function capture( string $authorization_id ): Capture {
$bearer = $this->bearer->bearer(); $bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/payments/authorizations/' . $authorization_id . '/capture'; $url = trailingslashit( $this->host ) . 'v2/payments/authorizations/' . $authorization_id . '/capture';
$args = array( $args = array(
@ -167,7 +179,7 @@ class PaymentsEndpoint {
); );
} }
return $this->authorizations_factory->from_paypal_response( $json ); return $this->capture_factory->from_paypal_response( $json );
} }
/** /**

View file

@ -44,13 +44,21 @@ class AuthorizationStatus {
*/ */
private $status; private $status;
/**
* The details.
*
* @var AuthorizationStatusDetails|null
*/
private $details;
/** /**
* AuthorizationStatus constructor. * AuthorizationStatus constructor.
* *
* @param string $status The status. * @param string $status The status.
* @param AuthorizationStatusDetails|null $details The details.
* @throws RuntimeException When the status is not valid. * @throws RuntimeException When the status is not valid.
*/ */
public function __construct( string $status ) { public function __construct( string $status, ?AuthorizationStatusDetails $details = null ) {
if ( ! in_array( $status, self::VALID_STATUS, true ) ) { if ( ! in_array( $status, self::VALID_STATUS, true ) ) {
throw new RuntimeException( throw new RuntimeException(
sprintf( sprintf(
@ -60,7 +68,8 @@ class AuthorizationStatus {
) )
); );
} }
$this->status = $status; $this->status = $status;
$this->details = $details;
} }
/** /**
@ -91,4 +100,13 @@ class AuthorizationStatus {
public function name(): string { public function name(): string {
return $this->status; return $this->status;
} }
/**
* Returns the details.
*
* @return AuthorizationStatusDetails|null
*/
public function details(): ?AuthorizationStatusDetails {
return $this->details;
}
} }

View file

@ -0,0 +1,56 @@
<?php
/**
* The AuthorizationStatusDetails object.
*
* @see https://developer.paypal.com/docs/api/payments/v2/#definition-authorization_status_details
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class AuthorizationStatusDetails
*/
class AuthorizationStatusDetails {
const PENDING_REVIEW = 'PENDING_REVIEW';
/**
* The reason.
*
* @var string
*/
private $reason;
/**
* AuthorizationStatusDetails constructor.
*
* @param string $reason The reason explaining authorization status.
*/
public function __construct( string $reason ) {
$this->reason = $reason;
}
/**
* Compares the current reason with a given one.
*
* @param string $reason The reason to compare with.
*
* @return bool
*/
public function is( string $reason ): bool {
return $this->reason === $reason;
}
/**
* Returns the reason explaining authorization status.
*
* @return string
*/
public function reason(): string {
return $this->reason;
}
}

View file

@ -26,17 +26,10 @@ class Capture {
/** /**
* The status. * The status.
* *
* @var string * @var CaptureStatus
*/ */
private $status; private $status;
/**
* The status details.
*
* @var string
*/
private $status_details;
/** /**
* The amount. * The amount.
* *
@ -75,19 +68,17 @@ class Capture {
/** /**
* Capture constructor. * Capture constructor.
* *
* @param string $id The ID. * @param string $id The ID.
* @param string $status The status. * @param CaptureStatus $status The status.
* @param string $status_details The status details. * @param Amount $amount The amount.
* @param Amount $amount The amount. * @param bool $final_capture The final capture.
* @param bool $final_capture The final capture. * @param string $seller_protection The seller protection.
* @param string $seller_protection The seller protection. * @param string $invoice_id The invoice id.
* @param string $invoice_id The invoice id. * @param string $custom_id The custom id.
* @param string $custom_id The custom id.
*/ */
public function __construct( public function __construct(
string $id, string $id,
string $status, CaptureStatus $status,
string $status_details,
Amount $amount, Amount $amount,
bool $final_capture, bool $final_capture,
string $seller_protection, string $seller_protection,
@ -97,7 +88,6 @@ class Capture {
$this->id = $id; $this->id = $id;
$this->status = $status; $this->status = $status;
$this->status_details = $status_details;
$this->amount = $amount; $this->amount = $amount;
$this->final_capture = $final_capture; $this->final_capture = $final_capture;
$this->seller_protection = $seller_protection; $this->seller_protection = $seller_protection;
@ -117,21 +107,12 @@ class Capture {
/** /**
* Returns the status. * Returns the status.
* *
* @return string * @return CaptureStatus
*/ */
public function status() : string { public function status() : CaptureStatus {
return $this->status; return $this->status;
} }
/**
* Returns the status details object.
*
* @return \stdClass
*/
public function status_details() : \stdClass {
return (object) array( 'reason' => $this->status_details );
}
/** /**
* Returns the amount. * Returns the amount.
* *
@ -183,15 +164,18 @@ class Capture {
* @return array * @return array
*/ */
public function to_array() : array { public function to_array() : array {
return array( $data = array(
'id' => $this->id(), 'id' => $this->id(),
'status' => $this->status(), 'status' => $this->status()->name(),
'status_details' => (array) $this->status_details(),
'amount' => $this->amount()->to_array(), 'amount' => $this->amount()->to_array(),
'final_capture' => $this->final_capture(), 'final_capture' => $this->final_capture(),
'seller_protection' => (array) $this->seller_protection(), 'seller_protection' => (array) $this->seller_protection(),
'invoice_id' => $this->invoice_id(), 'invoice_id' => $this->invoice_id(),
'custom_id' => $this->custom_id(), 'custom_id' => $this->custom_id(),
); );
if ( $this->status()->details() ) {
$data['status_details'] = array( 'reason' => $this->status()->details()->reason() );
}
return $data;
} }
} }

View file

@ -0,0 +1,79 @@
<?php
/**
* The CaptureStatus object.
*
* @see https://developer.paypal.com/docs/api/orders/v2/#definition-capture_status
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class CaptureStatus
*/
class CaptureStatus {
const COMPLETED = 'COMPLETED';
const DECLINED = 'DECLINED';
const PARTIALLY_REFUNDED = 'PARTIALLY_REFUNDED';
const REFUNDED = 'REFUNDED';
const FAILED = 'FAILED';
const PENDING = 'PENDING';
/**
* The status.
*
* @var string
*/
private $status;
/**
* The details.
*
* @var CaptureStatusDetails|null
*/
private $details;
/**
* CaptureStatus constructor.
*
* @param string $status The status.
* @param CaptureStatusDetails|null $details The details.
*/
public function __construct( string $status, ?CaptureStatusDetails $details = null ) {
$this->status = $status;
$this->details = $details;
}
/**
* Compares the current status with a given one.
*
* @param string $status The status to compare with.
*
* @return bool
*/
public function is( string $status ): bool {
return $this->status === $status;
}
/**
* Returns the status.
*
* @return string
*/
public function name(): string {
return $this->status;
}
/**
* Returns the details.
*
* @return CaptureStatusDetails|null
*/
public function details(): ?CaptureStatusDetails {
return $this->details;
}
}

View file

@ -0,0 +1,66 @@
<?php
/**
* The CaptureStatusDetails object.
*
* @see https://developer.paypal.com/docs/api/payments/v2/#definition-capture_status_details
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class CaptureStatusDetails
*/
class CaptureStatusDetails {
const BUYER_COMPLAINT = 'BUYER_COMPLAINT';
const CHARGEBACK = 'CHARGEBACK';
const ECHECK = 'ECHECK';
const INTERNATIONAL_WITHDRAWAL = 'INTERNATIONAL_WITHDRAWAL';
const OTHER = 'OTHER';
const PENDING_REVIEW = 'PENDING_REVIEW';
const RECEIVING_PREFERENCE_MANDATES_MANUAL_ACTION = 'RECEIVING_PREFERENCE_MANDATES_MANUAL_ACTION';
const REFUNDED = 'REFUNDED';
const TRANSACTION_APPROVED_AWAITING_FUNDING = 'TRANSACTION_APPROVED_AWAITING_FUNDING';
const UNILATERAL = 'REFUNDED';
const VERIFICATION_REQUIRED = 'VERIFICATION_REQUIRED';
/**
* The reason.
*
* @var string
*/
private $reason;
/**
* CaptureStatusDetails constructor.
*
* @param string $reason The reason explaining capture status.
*/
public function __construct( string $reason ) {
$this->reason = $reason;
}
/**
* Compares the current reason with a given one.
*
* @param string $reason The reason to compare with.
*
* @return bool
*/
public function is( string $reason ): bool {
return $this->reason === $reason;
}
/**
* Returns the reason explaining capture status.
*
* @return string
*/
public function reason(): string {
return $this->reason;
}
}

View file

@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatusDetails;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
/** /**
@ -39,9 +40,14 @@ class AuthorizationFactory {
); );
} }
$reason = $data->status_details->reason ?? null;
return new Authorization( return new Authorization(
$data->id, $data->id,
new AuthorizationStatus( $data->status ) new AuthorizationStatus(
$data->status,
$reason ? new AuthorizationStatusDetails( $reason ) : null
)
); );
} }
} }

View file

@ -10,6 +10,8 @@ declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\ApiClient\Factory; namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture; use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatusDetails;
/** /**
* Class CaptureFactory * Class CaptureFactory
@ -42,11 +44,14 @@ class CaptureFactory {
*/ */
public function from_paypal_response( \stdClass $data ) : Capture { public function from_paypal_response( \stdClass $data ) : Capture {
$reason = isset( $data->status_details->reason ) ? (string) $data->status_details->reason : ''; $reason = $data->status_details->reason ?? null;
return new Capture( return new Capture(
(string) $data->id, (string) $data->id,
(string) $data->status, new CaptureStatus(
$reason, (string) $data->status,
$reason ? new CaptureStatusDetails( $reason ) : null
),
$this->amount_factory->from_paypal_response( $data->amount ), $this->amount_factory->from_paypal_response( $data->amount ),
(bool) $data->final_capture, (bool) $data->final_capture,
(string) $data->seller_protection->status, (string) $data->seller_protection->status,

View file

@ -57,6 +57,7 @@ return array(
$logger = $container->get( 'woocommerce.logger.woocommerce' ); $logger = $container->get( 'woocommerce.logger.woocommerce' );
$payments_endpoint = $container->get( 'api.endpoint.payments' ); $payments_endpoint = $container->get( 'api.endpoint.payments' );
$order_endpoint = $container->get( 'api.endpoint.order' ); $order_endpoint = $container->get( 'api.endpoint.order' );
$environment = $container->get( 'onboarding.environment' );
return new PayPalGateway( return new PayPalGateway(
$settings_renderer, $settings_renderer,
$order_processor, $order_processor,
@ -69,6 +70,7 @@ return array(
$transaction_url_provider, $transaction_url_provider,
$subscription_helper, $subscription_helper,
$page_id, $page_id,
$environment,
$payment_token_repository, $payment_token_repository,
$logger, $logger,
$payments_endpoint, $payments_endpoint,
@ -93,6 +95,8 @@ return array(
$subscription_helper = $container->get( 'subscription.helper' ); $subscription_helper = $container->get( 'subscription.helper' );
$logger = $container->get( 'woocommerce.logger.woocommerce' ); $logger = $container->get( 'woocommerce.logger.woocommerce' );
$payments_endpoint = $container->get( 'api.endpoint.payments' ); $payments_endpoint = $container->get( 'api.endpoint.payments' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
$environment = $container->get( 'onboarding.environment' );
return new CreditCardGateway( return new CreditCardGateway(
$settings_renderer, $settings_renderer,
$order_processor, $order_processor,
@ -110,7 +114,9 @@ return array(
$order_endpoint, $order_endpoint,
$subscription_helper, $subscription_helper,
$logger, $logger,
$payments_endpoint $payments_endpoint,
$logger,
$environment
); );
}, },
'wcgateway.disabler' => static function ( $container ): DisableGateways { 'wcgateway.disabler' => static function ( $container ): DisableGateways {
@ -219,7 +225,7 @@ return array(
$authorized_payments_processor, $authorized_payments_processor,
$settings, $settings,
$logger, $logger,
$environment->current_environment_is( Environment::SANDBOX ) $environment
); );
}, },
'wcgateway.processor.refunds' => static function ( $container ): RefundProcessor { 'wcgateway.processor.refunds' => static function ( $container ): RefundProcessor {

View file

@ -14,6 +14,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
@ -104,6 +105,13 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
*/ */
private $order_endpoint; private $order_endpoint;
/**
* The environment.
*
* @var Environment
*/
protected $environment;
/** /**
* CreditCardGateway constructor. * CreditCardGateway constructor.
* *
@ -124,6 +132,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
* @param SubscriptionHelper $subscription_helper The subscription helper. * @param SubscriptionHelper $subscription_helper The subscription helper.
* @param LoggerInterface $logger The logger. * @param LoggerInterface $logger The logger.
* @param PaymentsEndpoint $payments_endpoint The payments endpoint. * @param PaymentsEndpoint $payments_endpoint The payments endpoint.
* @param Environment $environment The environment.
*/ */
public function __construct( public function __construct(
SettingsRenderer $settings_renderer, SettingsRenderer $settings_renderer,
@ -142,7 +151,8 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
OrderEndpoint $order_endpoint, OrderEndpoint $order_endpoint,
SubscriptionHelper $subscription_helper, SubscriptionHelper $subscription_helper,
LoggerInterface $logger, LoggerInterface $logger,
PaymentsEndpoint $payments_endpoint PaymentsEndpoint $payments_endpoint,
Environment $environment
) { ) {
$this->id = self::ID; $this->id = self::ID;
@ -153,6 +163,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
$this->config = $config; $this->config = $config;
$this->session_handler = $session_handler; $this->session_handler = $session_handler;
$this->refund_processor = $refund_processor; $this->refund_processor = $refund_processor;
$this->environment = $environment;
if ( $state->current_state() === State::STATE_ONBOARDED ) { if ( $state->current_state() === State::STATE_ONBOARDED ) {
$this->supports = array( 'refunds' ); $this->supports = array( 'refunds' );
@ -435,4 +446,13 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
private function is_enabled(): bool { private function is_enabled(): bool {
return $this->config->has( 'dcc_enabled' ) && $this->config->get( 'dcc_enabled' ); return $this->config->has( 'dcc_enabled' ) && $this->config->get( 'dcc_enabled' );
} }
/**
* Returns the environment.
*
* @return Environment
*/
protected function environment(): Environment {
return $this->environment;
}
} }

View file

@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
@ -20,7 +21,6 @@ use WooCommerce\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice;
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor; use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor; use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor; use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Settings\SectionsRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\Webhooks\Status\WebhooksStatusPage; use WooCommerce\PayPalCommerce\Webhooks\Status\WebhooksStatusPage;
@ -143,6 +143,13 @@ class PayPalGateway extends \WC_Payment_Gateway {
*/ */
protected $order_endpoint; protected $order_endpoint;
/**
* The environment.
*
* @var Environment
*/
protected $environment;
/** /**
* PayPalGateway constructor. * PayPalGateway constructor.
* *
@ -157,6 +164,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
* @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order. * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order.
* @param SubscriptionHelper $subscription_helper The subscription helper. * @param SubscriptionHelper $subscription_helper The subscription helper.
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page. * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
* @param Environment $environment The environment.
* @param PaymentTokenRepository $payment_token_repository The payment token repository. * @param PaymentTokenRepository $payment_token_repository The payment token repository.
* @param LoggerInterface $logger The logger. * @param LoggerInterface $logger The logger.
* @param PaymentsEndpoint $payments_endpoint The payments endpoint. * @param PaymentsEndpoint $payments_endpoint The payments endpoint.
@ -174,6 +182,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
TransactionUrlProvider $transaction_url_provider, TransactionUrlProvider $transaction_url_provider,
SubscriptionHelper $subscription_helper, SubscriptionHelper $subscription_helper,
string $page_id, string $page_id,
Environment $environment,
PaymentTokenRepository $payment_token_repository, PaymentTokenRepository $payment_token_repository,
LoggerInterface $logger, LoggerInterface $logger,
PaymentsEndpoint $payments_endpoint, PaymentsEndpoint $payments_endpoint,
@ -190,6 +199,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
$this->refund_processor = $refund_processor; $this->refund_processor = $refund_processor;
$this->transaction_url_provider = $transaction_url_provider; $this->transaction_url_provider = $transaction_url_provider;
$this->page_id = $page_id; $this->page_id = $page_id;
$this->environment = $environment;
$this->onboarded = $state->current_state() === State::STATE_ONBOARDED; $this->onboarded = $state->current_state() === State::STATE_ONBOARDED;
if ( $this->onboarded ) { if ( $this->onboarded ) {
@ -286,16 +296,6 @@ class PayPalGateway extends \WC_Payment_Gateway {
$result_status = $this->authorized_payments->process( $wc_order ); $result_status = $this->authorized_payments->process( $wc_order );
$this->render_authorization_message_for_status( $result_status ); $this->render_authorization_message_for_status( $result_status );
if ( AuthorizedPaymentsProcessor::SUCCESSFUL === $result_status ) {
$wc_order->add_order_note(
__( 'Payment successfully captured.', 'woocommerce-paypal-payments' )
);
$wc_order->update_meta_data( self::CAPTURED_META_KEY, 'true' );
$wc_order->save();
$wc_order->payment_complete();
return true;
}
if ( AuthorizedPaymentsProcessor::ALREADY_CAPTURED === $result_status ) { if ( AuthorizedPaymentsProcessor::ALREADY_CAPTURED === $result_status ) {
if ( $wc_order->get_status() === 'on-hold' ) { if ( $wc_order->get_status() === 'on-hold' ) {
$wc_order->add_order_note( $wc_order->add_order_note(
@ -308,6 +308,23 @@ class PayPalGateway extends \WC_Payment_Gateway {
$wc_order->payment_complete(); $wc_order->payment_complete();
return true; return true;
} }
$captures = $this->authorized_payments->captures();
if ( empty( $captures ) ) {
return false;
}
$this->handle_capture_status( end( $captures ), $wc_order );
if ( AuthorizedPaymentsProcessor::SUCCESSFUL === $result_status ) {
$wc_order->add_order_note(
__( 'Payment successfully captured.', 'woocommerce-paypal-payments' )
);
$wc_order->update_meta_data( self::CAPTURED_META_KEY, 'true' );
$wc_order->save();
return true;
}
return false; return false;
} }
@ -474,4 +491,13 @@ class PayPalGateway extends \WC_Payment_Gateway {
return $ret; return $ret;
} }
/**
* Returns the environment.
*
* @return Environment
*/
protected function environment(): Environment {
return $this->environment;
}
} }

View file

@ -10,16 +10,20 @@ declare( strict_types=1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway; namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
use Exception; use Exception;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait;
use WooCommerce\PayPalCommerce\WcGateway\Processor\PaymentsStatusHandlingTrait;
/** /**
* Trait ProcessPaymentTrait * Trait ProcessPaymentTrait
*/ */
trait ProcessPaymentTrait { trait ProcessPaymentTrait {
use OrderMetaTrait, PaymentsStatusHandlingTrait;
/** /**
* Process a payment for an WooCommerce order. * Process a payment for an WooCommerce order.
* *
@ -78,41 +82,37 @@ trait ProcessPaymentTrait {
$selected_token $selected_token
); );
if ( $order->status()->is( OrderStatus::COMPLETED ) && $order->intent() === 'CAPTURE' ) { $this->add_paypal_meta( $wc_order, $order, $this->environment() );
$wc_order->update_status(
'processing',
__( 'Payment received.', 'woocommerce-paypal-payments' )
);
$this->session_handler->destroy_session_data(); if ( ! $order->status()->is( OrderStatus::COMPLETED ) ) {
return array( $this->logger->warning( "Unexpected status for order {$order->id()} using a saved credit card: " . $order->status()->name() );
'result' => 'success', return null;
'redirect' => $this->get_return_url( $wc_order ),
);
} }
if ( $order->status()->is( OrderStatus::COMPLETED ) && $order->intent() === 'AUTHORIZE' ) { if ( ! in_array(
$this->order_endpoint->authorize( $order ); $order->intent(),
array( 'CAPTURE', 'AUTHORIZE' ),
true
) ) {
$this->logger->warning( "Could neither capture nor authorize order {$order->id()} using a saved credit card:" . 'Status: ' . $order->status()->name() . ' Intent: ' . $order->intent() );
return null;
}
if ( $order->intent() === 'AUTHORIZE' ) {
$order = $this->order_endpoint->authorize( $order );
$wc_order->update_meta_data( PayPalGateway::CAPTURED_META_KEY, 'false' ); $wc_order->update_meta_data( PayPalGateway::CAPTURED_META_KEY, 'false' );
$wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
$wc_order->update_status(
'on-hold',
__( 'Awaiting payment.', 'woocommerce-paypal-payments' )
);
$this->session_handler->destroy_session_data();
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $wc_order ),
);
} }
$this->logger->warning( "Could neither capture nor authorize order {$order->id()} using a saved credit card:" . 'Status: ' . $order->status()->name() . ' Intent: ' . $order->intent() ); $this->handle_new_order_status( $order, $wc_order );
} catch ( RuntimeException $error ) {
$this->logger->error( $error->getMessage() );
$this->session_handler->destroy_session_data(); $this->session_handler->destroy_session_data();
wc_add_notice( $error->getMessage(), 'error' ); return array(
'result' => 'success',
'redirect' => $this->get_return_url( $wc_order ),
);
} catch ( RuntimeException $error ) {
$this->handle_failure( $wc_order, $error );
return null; return null;
} }
} }
@ -267,12 +267,7 @@ trait ProcessPaymentTrait {
$this->session_handler->destroy_session_data(); $this->session_handler->destroy_session_data();
} catch ( RuntimeException $error ) { } catch ( RuntimeException $error ) {
$wc_order->update_status( $this->handle_failure( $wc_order, $error );
'failed',
__( 'Could not process order.', 'woocommerce-paypal-payments' )
);
$this->session_handler->destroy_session_data();
wc_add_notice( $error->getMessage(), 'error' );
return $failure_data; return $failure_data;
} }
@ -316,6 +311,32 @@ trait ProcessPaymentTrait {
return false; return false;
} }
/**
* Handles the payment failure.
*
* @param \WC_Order $wc_order The order.
* @param Exception $error The error causing the failure.
*/
protected function handle_failure( \WC_Order $wc_order, Exception $error ): void {
$this->logger->error( 'Payment failed: ' . $error->getMessage() );
$wc_order->update_status(
'failed',
__( 'Could not process order.', 'woocommerce-paypal-payments' )
);
$this->session_handler->destroy_session_data();
wc_add_notice( $error->getMessage(), 'error' );
}
/**
* Returns the environment.
*
* @return Environment
*/
abstract protected function environment(): Environment;
/** /**
* Checks whether the authorization can be voided. * Checks whether the authorization can be voided.
* *

View file

@ -15,6 +15,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
@ -23,6 +24,8 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
*/ */
class AuthorizedPaymentsProcessor { class AuthorizedPaymentsProcessor {
use PaymentsStatusHandlingTrait;
const SUCCESSFUL = 'SUCCESSFUL'; const SUCCESSFUL = 'SUCCESSFUL';
const ALREADY_CAPTURED = 'ALREADY_CAPTURED'; const ALREADY_CAPTURED = 'ALREADY_CAPTURED';
const FAILED = 'FAILED'; const FAILED = 'FAILED';
@ -51,6 +54,13 @@ class AuthorizedPaymentsProcessor {
*/ */
private $logger; private $logger;
/**
* The capture results.
*
* @var Capture[]
*/
private $captures;
/** /**
* AuthorizedPaymentsProcessor constructor. * AuthorizedPaymentsProcessor constructor.
* *
@ -77,6 +87,8 @@ class AuthorizedPaymentsProcessor {
* @return string One of the AuthorizedPaymentsProcessor status constants. * @return string One of the AuthorizedPaymentsProcessor status constants.
*/ */
public function process( \WC_Order $wc_order ): string { public function process( \WC_Order $wc_order ): string {
$this->captures = array();
try { try {
$order = $this->paypal_order_from_wc_order( $wc_order ); $order = $this->paypal_order_from_wc_order( $wc_order );
} catch ( Exception $exception ) { } catch ( Exception $exception ) {
@ -106,6 +118,15 @@ class AuthorizedPaymentsProcessor {
return self::SUCCESSFUL; return self::SUCCESSFUL;
} }
/**
* Returns the capture results.
*
* @return Capture[]
*/
public function captures(): array {
return $this->captures;
}
/** /**
* Returns the PayPal order from a given WooCommerce order. * Returns the PayPal order from a given WooCommerce order.
* *
@ -144,7 +165,7 @@ class AuthorizedPaymentsProcessor {
private function capture_authorizations( Authorization ...$authorizations ) { private function capture_authorizations( Authorization ...$authorizations ) {
$uncaptured_authorizations = $this->authorizations_to_capture( ...$authorizations ); $uncaptured_authorizations = $this->authorizations_to_capture( ...$authorizations );
foreach ( $uncaptured_authorizations as $authorization ) { foreach ( $uncaptured_authorizations as $authorization ) {
$this->payments_endpoint->capture( $authorization->id() ); $this->captures[] = $this->payments_endpoint->capture( $authorization->id() );
} }
} }

View file

@ -0,0 +1,41 @@
<?php
/**
* Adds common metadata to the order.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Processor
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
use WC_Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
/**
* Trait OrderMetaTrait.
*/
trait OrderMetaTrait {
/**
* Adds common metadata to the order.
*
* @param WC_Order $wc_order The WC order to which metadata will be added.
* @param Order $order The PayPal order.
* @param Environment $environment The environment.
*/
protected function add_paypal_meta(
WC_Order $wc_order,
Order $order,
Environment $environment
): void {
$wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
$wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() );
$wc_order->update_meta_data(
PayPalGateway::ORDER_PAYMENT_MODE_META_KEY,
$environment->current_environment_is( Environment::SANDBOX ) ? 'sandbox' : 'live'
);
}
}

View file

@ -15,6 +15,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure; use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
@ -25,12 +26,14 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
*/ */
class OrderProcessor { class OrderProcessor {
use OrderMetaTrait, PaymentsStatusHandlingTrait;
/** /**
* Whether current payment mode is sandbox. * The environment.
* *
* @var bool * @var Environment
*/ */
protected $sandbox_mode; protected $environment;
/** /**
* The payment token repository. * The payment token repository.
@ -105,7 +108,7 @@ class OrderProcessor {
* @param AuthorizedPaymentsProcessor $authorized_payments_processor The Authorized Payments Processor. * @param AuthorizedPaymentsProcessor $authorized_payments_processor The Authorized Payments Processor.
* @param Settings $settings The Settings. * @param Settings $settings The Settings.
* @param LoggerInterface $logger A logger service. * @param LoggerInterface $logger A logger service.
* @param bool $sandbox_mode Whether sandbox mode enabled. * @param Environment $environment The environment.
*/ */
public function __construct( public function __construct(
SessionHandler $session_handler, SessionHandler $session_handler,
@ -115,7 +118,7 @@ class OrderProcessor {
AuthorizedPaymentsProcessor $authorized_payments_processor, AuthorizedPaymentsProcessor $authorized_payments_processor,
Settings $settings, Settings $settings,
LoggerInterface $logger, LoggerInterface $logger,
bool $sandbox_mode Environment $environment
) { ) {
$this->session_handler = $session_handler; $this->session_handler = $session_handler;
@ -124,7 +127,7 @@ class OrderProcessor {
$this->threed_secure = $three_d_secure; $this->threed_secure = $three_d_secure;
$this->authorized_payments_processor = $authorized_payments_processor; $this->authorized_payments_processor = $authorized_payments_processor;
$this->settings = $settings; $this->settings = $settings;
$this->sandbox_mode = $sandbox_mode; $this->environment = $environment;
$this->logger = $logger; $this->logger = $logger;
} }
@ -140,12 +143,8 @@ class OrderProcessor {
if ( ! $order ) { if ( ! $order ) {
return false; return false;
} }
$wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
$wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() ); $this->add_paypal_meta( $wc_order, $order, $this->environment );
$wc_order->update_meta_data(
PayPalGateway::ORDER_PAYMENT_MODE_META_KEY,
$this->sandbox_mode ? 'sandbox' : 'live'
);
$error_message = null; $error_message = null;
if ( ! $this->order_is_approved( $order ) ) { if ( ! $this->order_is_approved( $order ) ) {
@ -164,12 +163,14 @@ class OrderProcessor {
} }
$order = $this->patch_order( $wc_order, $order ); $order = $this->patch_order( $wc_order, $order );
if ( $order->intent() === 'CAPTURE' ) { if ( $order->intent() === 'CAPTURE' ) {
$order = $this->order_endpoint->capture( $order ); $order = $this->order_endpoint->capture( $order );
} }
if ( $order->intent() === 'AUTHORIZE' ) { if ( $order->intent() === 'AUTHORIZE' ) {
$order = $this->order_endpoint->authorize( $order ); $order = $this->order_endpoint->authorize( $order );
$wc_order->update_meta_data( PayPalGateway::CAPTURED_META_KEY, 'false' ); $wc_order->update_meta_data( PayPalGateway::CAPTURED_META_KEY, 'false' );
} }
@ -179,14 +180,7 @@ class OrderProcessor {
$this->set_order_transaction_id( $transaction_id, $wc_order ); $this->set_order_transaction_id( $transaction_id, $wc_order );
} }
$wc_order->update_status( $this->handle_new_order_status( $order, $wc_order );
'on-hold',
__( 'Awaiting payment.', 'woocommerce-paypal-payments' )
);
if ( $order->status()->is( OrderStatus::COMPLETED ) && $order->intent() === 'CAPTURE' ) {
$wc_order->payment_complete();
}
if ( $this->capture_authorized_downloads( $order ) && AuthorizedPaymentsProcessor::SUCCESSFUL === $this->authorized_payments_processor->process( $wc_order ) ) { if ( $this->capture_authorized_downloads( $order ) && AuthorizedPaymentsProcessor::SUCCESSFUL === $this->authorized_payments_processor->process( $wc_order ) ) {
$wc_order->add_order_note( $wc_order->add_order_note(

View file

@ -0,0 +1,141 @@
<?php
/**
* Common operations performed after payment authorization/capture.
*
* @package WooCommerce\PayPalCommerce\WcGateway\Processor
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
use WC_Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Trait PaymentsStatusHandlingTrait.
*/
trait PaymentsStatusHandlingTrait {
/**
* Changes status of a newly created order, based on the capture/authorization.
*
* @param Order $order The PayPal order.
* @param WC_Order $wc_order The WC order.
*
* @throws RuntimeException If payment denied.
*/
protected function handle_new_order_status(
Order $order,
WC_Order $wc_order
): void {
if ( $order->intent() === 'CAPTURE' ) {
$this->handle_capture_status( $order->purchase_units()[0]->payments()->captures()[0], $wc_order );
} elseif ( $order->intent() === 'AUTHORIZE' ) {
$this->handle_authorization_status( $order->purchase_units()[0]->payments()->authorizations()[0], $wc_order );
}
}
/**
* Changes the order status, based on the capture.
*
* @param Capture $capture The capture.
* @param WC_Order $wc_order The WC order.
*
* @throws RuntimeException If payment denied.
*/
protected function handle_capture_status(
Capture $capture,
WC_Order $wc_order
): void {
$status = $capture->status();
if ( $status->details() ) {
$this->add_status_details_note( $wc_order, $status->name(), $status->details()->reason() );
}
switch ( $status->name() ) {
case CaptureStatus::COMPLETED:
$wc_order->payment_complete();
break;
// It is checked in the capture endpoint already, but there are other ways to capture,
// such as when paid via saved card.
case CaptureStatus::DECLINED:
$wc_order->update_status(
'failed',
__( 'Could not capture the payment.', 'woocommerce-paypal-payments' )
);
throw new RuntimeException( __( 'Payment provider declined the payment, please use a different payment method.', 'woocommerce-paypal-payments' ) );
case CaptureStatus::PENDING:
case CaptureStatus::FAILED:
$wc_order->update_status(
'on-hold',
__( 'Awaiting payment.', 'woocommerce-paypal-payments' )
);
break;
}
}
/**
* Changes the order status, based on the authorization.
*
* @param Authorization $authorization The authorization.
* @param WC_Order $wc_order The WC order.
*
* @throws RuntimeException If payment denied.
*/
protected function handle_authorization_status(
Authorization $authorization,
WC_Order $wc_order
): void {
$status = $authorization->status();
if ( $status->details() ) {
$this->add_status_details_note( $wc_order, $status->name(), $status->details()->reason() );
}
switch ( $status->name() ) {
case AuthorizationStatus::CREATED:
case AuthorizationStatus::PENDING:
$wc_order->update_status(
'on-hold',
__( 'Awaiting payment.', 'woocommerce-paypal-payments' )
);
break;
case AuthorizationStatus::DENIED:
$wc_order->update_status(
'failed',
__( 'Could not get the payment authorization.', 'woocommerce-paypal-payments' )
);
throw new RuntimeException( __( 'Payment provider declined the payment, please use a different payment method.', 'woocommerce-paypal-payments' ) );
}
}
/**
* Adds the order note with status details.
*
* @param WC_Order $wc_order The WC order to which the note will be added.
* @param string $status The status name.
* @param string $reason The status reason.
*/
protected function add_status_details_note(
WC_Order $wc_order,
string $status,
string $reason
): void {
$wc_order->add_order_note(
sprintf(
/* translators: %1$s - PENDING, DENIED, ... %2$s - PENDING_REVIEW, ... */
__( 'PayPal order payment is set to %1$s status, details: %2$s.', 'woocommerce-paypal-payments' ),
$status,
$reason
)
);
$wc_order->save();
}
}

View file

@ -183,6 +183,7 @@ class RefundProcessor {
* @return bool * @return bool
*/ */
private function is_voidable_authorization( Authorization $authorization ): bool { private function is_voidable_authorization( Authorization $authorization ): bool {
return $authorization->status()->is( AuthorizationStatus::CREATED ); return $authorization->status()->is( AuthorizationStatus::CREATED ) ||
$authorization->status()->is( AuthorizationStatus::PENDING );
} }
} }

View file

@ -8,6 +8,7 @@ use Requests_Utility_CaseInsensitiveDictionary;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext; use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture; use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PatchCollection; use WooCommerce\PayPalCommerce\ApiClient\Entity\PatchCollection;
@ -288,7 +289,7 @@ class OrderEndpointTest extends TestCase
$expectedOrder->shouldReceive('purchase_units')->once()->andReturn(['0'=>$purchaseUnit]); $expectedOrder->shouldReceive('purchase_units')->once()->andReturn(['0'=>$purchaseUnit]);
$purchaseUnit->shouldReceive('payments')->once()->andReturn($payment); $purchaseUnit->shouldReceive('payments')->once()->andReturn($payment);
$payment->shouldReceive('captures')->once()->andReturn(['0'=>$capture]); $payment->shouldReceive('captures')->once()->andReturn(['0'=>$capture]);
$capture->shouldReceive('status')->once()->andReturn(''); $capture->shouldReceive('status')->once()->andReturn(new CaptureStatus(CaptureStatus::COMPLETED));
$result = $testee->capture($orderToCapture); $result = $testee->capture($orderToCapture);
$this->assertEquals($expectedOrder, $result); $this->assertEquals($expectedOrder, $result);

View file

@ -8,10 +8,12 @@ use Psr\Log\NullLogger;
use Requests_Utility_CaseInsensitiveDictionary; use Requests_Utility_CaseInsensitiveDictionary;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ErrorResponseCollection; use WooCommerce\PayPalCommerce\ApiClient\Entity\ErrorResponseCollection;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Token; use WooCommerce\PayPalCommerce\ApiClient\Entity\Token;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\CaptureFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ErrorResponseCollectionFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\ErrorResponseCollectionFactory;
use WooCommerce\PayPalCommerce\ApiClient\TestCase; use WooCommerce\PayPalCommerce\ApiClient\TestCase;
use Mockery; use Mockery;
@ -21,7 +23,22 @@ use function Brain\Monkey\Functions\expect;
class PaymentsEndpointTest extends TestCase class PaymentsEndpointTest extends TestCase
{ {
public function testAuthorizationDefault() private $authorizationFactory;
private $captureFactory;
private $logger;
public function setUp(): void
{
parent::setUp();
$this->authorizationFactory = Mockery::mock(AuthorizationFactory::class);
$this->captureFactory = Mockery::mock(CaptureFactory::class);
$this->logger = new NullLogger();
}
public function testAuthorizationDefault()
{ {
expect('wp_json_encode')->andReturnUsing('json_encode'); expect('wp_json_encode')->andReturnUsing('json_encode');
$host = 'https://example.com/'; $host = 'https://example.com/';
@ -35,15 +52,10 @@ class PaymentsEndpointTest extends TestCase
->expects('bearer')->andReturn($token); ->expects('bearer')->andReturn($token);
$authorization = Mockery::mock(Authorization::class); $authorization = Mockery::mock(Authorization::class);
$authorizationFactory = Mockery::mock(AuthorizationFactory::class); $this->authorizationFactory
$authorizationFactory
->expects('from_paypal_response') ->expects('from_paypal_response')
->andReturn($authorization); ->andReturn($authorization);
$logger = Mockery::mock(LoggerInterface::class);
$logger->shouldNotReceive('log');
$logger->shouldReceive('debug');
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class); $headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
$headers->shouldReceive('getAll'); $headers->shouldReceive('getAll');
$rawResponse = [ $rawResponse = [
@ -54,8 +66,9 @@ class PaymentsEndpointTest extends TestCase
$testee = new PaymentsEndpoint( $testee = new PaymentsEndpoint(
$host, $host,
$bearer, $bearer,
$authorizationFactory, $this->authorizationFactory,
$logger $this->captureFactory,
$this->logger
); );
expect('wp_remote_get')->andReturnUsing( expect('wp_remote_get')->andReturnUsing(
@ -92,12 +105,6 @@ class PaymentsEndpointTest extends TestCase
$bearer = Mockery::mock(Bearer::class); $bearer = Mockery::mock(Bearer::class);
$bearer->expects('bearer')->andReturn($token); $bearer->expects('bearer')->andReturn($token);
$authorizationFactory = Mockery::mock(AuthorizationFactory::class);
$logger = Mockery::mock(LoggerInterface::class);
$logger->shouldReceive('log');
$logger->shouldReceive('debug');
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class); $headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
$headers->shouldReceive('getAll'); $headers->shouldReceive('getAll');
$rawResponse = [ $rawResponse = [
@ -108,8 +115,9 @@ class PaymentsEndpointTest extends TestCase
$testee = new PaymentsEndpoint( $testee = new PaymentsEndpoint(
$host, $host,
$bearer, $bearer,
$authorizationFactory, $this->authorizationFactory,
$logger $this->captureFactory,
$this->logger
); );
expect('wp_remote_get')->andReturn($rawResponse); expect('wp_remote_get')->andReturn($rawResponse);
@ -131,8 +139,6 @@ class PaymentsEndpointTest extends TestCase
$bearer = Mockery::mock(Bearer::class); $bearer = Mockery::mock(Bearer::class);
$bearer->expects('bearer')->andReturn($token); $bearer->expects('bearer')->andReturn($token);
$authorizationFactory = Mockery::mock(AuthorizationFactory::class);
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class); $headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
$headers->shouldReceive('getAll'); $headers->shouldReceive('getAll');
$rawResponse = [ $rawResponse = [
@ -140,15 +146,12 @@ class PaymentsEndpointTest extends TestCase
'headers' => $headers, 'headers' => $headers,
]; ];
$logger = Mockery::mock(LoggerInterface::class);
$logger->shouldReceive('log');
$logger->shouldReceive('debug');
$testee = new PaymentsEndpoint( $testee = new PaymentsEndpoint(
$host, $host,
$bearer, $bearer,
$authorizationFactory, $this->authorizationFactory,
$logger $this->captureFactory,
$this->logger
); );
expect('wp_remote_get')->andReturn($rawResponse); expect('wp_remote_get')->andReturn($rawResponse);
@ -172,16 +175,10 @@ class PaymentsEndpointTest extends TestCase
$bearer $bearer
->expects('bearer')->andReturn($token); ->expects('bearer')->andReturn($token);
$authorization = Mockery::mock(Authorization::class); $capture = Mockery::mock(Capture::class);
$authorizationFactory = Mockery::mock(AuthorizationFactory::class); $this->captureFactory
$authorizationFactory
->expects('from_paypal_response') ->expects('from_paypal_response')
->andReturn($authorization); ->andReturn($capture);
$logger = Mockery::mock(LoggerInterface::class);
$logger->shouldNotReceive('log');
$logger->shouldReceive('debug');
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class); $headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
$headers->shouldReceive('getAll'); $headers->shouldReceive('getAll');
@ -193,8 +190,9 @@ class PaymentsEndpointTest extends TestCase
$testee = new PaymentsEndpoint( $testee = new PaymentsEndpoint(
$host, $host,
$bearer, $bearer,
$authorizationFactory, $this->authorizationFactory,
$logger $this->captureFactory,
$this->logger
); );
expect('wp_remote_get')->andReturnUsing( expect('wp_remote_get')->andReturnUsing(
@ -219,7 +217,7 @@ class PaymentsEndpointTest extends TestCase
expect('wp_remote_retrieve_response_code')->with($rawResponse)->andReturn(201); expect('wp_remote_retrieve_response_code')->with($rawResponse)->andReturn(201);
$result = $testee->capture($authorizationId); $result = $testee->capture($authorizationId);
$this->assertEquals($authorization, $result); $this->assertEquals($capture, $result);
} }
public function testCaptureIsWpError() public function testCaptureIsWpError()
@ -234,8 +232,6 @@ class PaymentsEndpointTest extends TestCase
$bearer = Mockery::mock(Bearer::class); $bearer = Mockery::mock(Bearer::class);
$bearer->expects('bearer')->andReturn($token); $bearer->expects('bearer')->andReturn($token);
$authorizationFactory = Mockery::mock(AuthorizationFactory::class);
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class); $headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
$headers->shouldReceive('getAll'); $headers->shouldReceive('getAll');
$rawResponse = [ $rawResponse = [
@ -246,8 +242,9 @@ class PaymentsEndpointTest extends TestCase
$testee = new PaymentsEndpoint( $testee = new PaymentsEndpoint(
$host, $host,
$bearer, $bearer,
$authorizationFactory, $this->authorizationFactory,
new NullLogger() $this->captureFactory,
$this->logger
); );
expect('wp_remote_get')->andReturn($rawResponse); expect('wp_remote_get')->andReturn($rawResponse);
@ -269,8 +266,6 @@ class PaymentsEndpointTest extends TestCase
$bearer = Mockery::mock(Bearer::class); $bearer = Mockery::mock(Bearer::class);
$bearer->expects('bearer')->andReturn($token); $bearer->expects('bearer')->andReturn($token);
$authorizationFactory = Mockery::mock(AuthorizationFactory::class);
$headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class); $headers = Mockery::mock(Requests_Utility_CaseInsensitiveDictionary::class);
$headers->shouldReceive('getAll'); $headers->shouldReceive('getAll');
$rawResponse = [ $rawResponse = [
@ -281,8 +276,9 @@ class PaymentsEndpointTest extends TestCase
$testee = new PaymentsEndpoint( $testee = new PaymentsEndpoint(
$host, $host,
$bearer, $bearer,
$authorizationFactory, $this->authorizationFactory,
new NullLogger() $this->captureFactory,
$this->logger
); );
expect('wp_remote_get')->andReturn($rawResponse); expect('wp_remote_get')->andReturn($rawResponse);

View file

@ -8,6 +8,9 @@ use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
@ -25,8 +28,15 @@ use function Brain\Monkey\Functions\when;
class WcGatewayTest extends TestCase class WcGatewayTest extends TestCase
{ {
private $environment;
public function testProcessPaymentSuccess() { public function setUp(): void {
parent::setUp();
$this->environment = Mockery::mock(Environment::class);
}
public function testProcessPaymentSuccess() {
expect('is_admin')->andReturn(false); expect('is_admin')->andReturn(false);
$orderId = 1; $orderId = 1;
@ -83,6 +93,7 @@ class WcGatewayTest extends TestCase
$transactionUrlProvider, $transactionUrlProvider,
$subscriptionHelper, $subscriptionHelper,
PayPalGateway::ID, PayPalGateway::ID,
$this->environment,
$paymentTokenRepository, $paymentTokenRepository,
$logger, $logger,
$paymentsEndpoint, $paymentsEndpoint,
@ -141,6 +152,7 @@ class WcGatewayTest extends TestCase
$transactionUrlProvider, $transactionUrlProvider,
$subscriptionHelper, $subscriptionHelper,
PayPalGateway::ID, PayPalGateway::ID,
$this->environment,
$paymentTokenRepository, $paymentTokenRepository,
$logger, $logger,
$paymentsEndpoint, $paymentsEndpoint,
@ -216,10 +228,12 @@ class WcGatewayTest extends TestCase
$transactionUrlProvider, $transactionUrlProvider,
$subscriptionHelper, $subscriptionHelper,
PayPalGateway::ID, PayPalGateway::ID,
$this->environment,
$paymentTokenRepository, $paymentTokenRepository,
$logger, $logger,
$paymentsEndpoint, $paymentsEndpoint,
$orderEndpoint $orderEndpoint,
PayPalGateway::ID
); );
expect('wc_get_order') expect('wc_get_order')
@ -258,11 +272,18 @@ class WcGatewayTest extends TestCase
->expects('save'); ->expects('save');
$settingsRenderer = Mockery::mock(SettingsRenderer::class); $settingsRenderer = Mockery::mock(SettingsRenderer::class);
$orderProcessor = Mockery::mock(OrderProcessor::class); $orderProcessor = Mockery::mock(OrderProcessor::class);
$capture = Mockery::mock(Capture::class);
$capture
->shouldReceive('status')
->andReturn(new CaptureStatus(CaptureStatus::COMPLETED));
$authorizedPaymentsProcessor = Mockery::mock(AuthorizedPaymentsProcessor::class); $authorizedPaymentsProcessor = Mockery::mock(AuthorizedPaymentsProcessor::class);
$authorizedPaymentsProcessor $authorizedPaymentsProcessor
->expects('process') ->expects('process')
->with($wcOrder) ->with($wcOrder)
->andReturn(AuthorizedPaymentsProcessor::SUCCESSFUL); ->andReturn(AuthorizedPaymentsProcessor::SUCCESSFUL);
$authorizedPaymentsProcessor
->expects('captures')
->andReturn([$capture]);
$authorizedOrderActionNotice = Mockery::mock(AuthorizeOrderActionNotice::class); $authorizedOrderActionNotice = Mockery::mock(AuthorizeOrderActionNotice::class);
$authorizedOrderActionNotice $authorizedOrderActionNotice
->expects('display_message') ->expects('display_message')
@ -296,6 +317,7 @@ class WcGatewayTest extends TestCase
$transactionUrlProvider, $transactionUrlProvider,
$subscriptionHelper, $subscriptionHelper,
PayPalGateway::ID, PayPalGateway::ID,
$this->environment,
$paymentTokenRepository, $paymentTokenRepository,
$logger, $logger,
$paymentsEndpoint, $paymentsEndpoint,
@ -360,6 +382,7 @@ class WcGatewayTest extends TestCase
$transactionUrlProvider, $transactionUrlProvider,
$subscriptionHelper, $subscriptionHelper,
PayPalGateway::ID, PayPalGateway::ID,
$this->environment,
$paymentTokenRepository, $paymentTokenRepository,
$logger, $logger,
$paymentsEndpoint, $paymentsEndpoint,
@ -386,6 +409,9 @@ class WcGatewayTest extends TestCase
->expects('process') ->expects('process')
->with($wcOrder) ->with($wcOrder)
->andReturn($lastStatus); ->andReturn($lastStatus);
$authorizedPaymentsProcessor
->expects('captures')
->andReturn([]);
$authorizedOrderActionNotice = Mockery::mock(AuthorizeOrderActionNotice::class); $authorizedOrderActionNotice = Mockery::mock(AuthorizeOrderActionNotice::class);
$authorizedOrderActionNotice $authorizedOrderActionNotice
->expects('display_message') ->expects('display_message')
@ -418,6 +444,7 @@ class WcGatewayTest extends TestCase
$transactionUrlProvider, $transactionUrlProvider,
$subscriptionHelper, $subscriptionHelper,
PayPalGateway::ID, PayPalGateway::ID,
$this->environment,
$paymentTokenRepository, $paymentTokenRepository,
$logger, $logger,
$paymentsEndpoint, $paymentsEndpoint,
@ -467,6 +494,7 @@ class WcGatewayTest extends TestCase
$transactionUrlProvider, $transactionUrlProvider,
$subscriptionHelper, $subscriptionHelper,
PayPalGateway::ID, PayPalGateway::ID,
$this->environment,
$paymentTokenRepository, $paymentTokenRepository,
$logger, $logger,
$paymentsEndpoint, $paymentsEndpoint,

View file

@ -10,6 +10,8 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization; use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payments; use WooCommerce\PayPalCommerce\ApiClient\Entity\Payments;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit; use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
@ -57,7 +59,7 @@ class AuthorizedPaymentsProcessorTest extends TestCase
$this->paymentsEndpoint $this->paymentsEndpoint
->expects('capture') ->expects('capture')
->with($this->authorizationId) ->with($this->authorizationId)
->andReturn($this->createAuthorization($this->authorizationId, AuthorizationStatus::CAPTURED)); ->andReturn($this->createCapture(CaptureStatus::COMPLETED));
$this->assertEquals(AuthorizedPaymentsProcessor::SUCCESSFUL, $this->testee->process($this->wcOrder)); $this->assertEquals(AuthorizedPaymentsProcessor::SUCCESSFUL, $this->testee->process($this->wcOrder));
} }
@ -78,7 +80,7 @@ class AuthorizedPaymentsProcessorTest extends TestCase
$this->paymentsEndpoint $this->paymentsEndpoint
->expects('capture') ->expects('capture')
->with($authorization->id()) ->with($authorization->id())
->andReturn($this->createAuthorization($authorization->id(), AuthorizationStatus::CAPTURED)); ->andReturn($this->createCapture(CaptureStatus::COMPLETED));
} }
$this->assertEquals(AuthorizedPaymentsProcessor::SUCCESSFUL, $this->testee->process($this->wcOrder)); $this->assertEquals(AuthorizedPaymentsProcessor::SUCCESSFUL, $this->testee->process($this->wcOrder));
@ -143,6 +145,14 @@ class AuthorizedPaymentsProcessorTest extends TestCase
return $authorization; return $authorization;
} }
private function createCapture(string $status): Capture {
$capture = Mockery::mock(Capture::class);
$capture
->shouldReceive('status')
->andReturn(new CaptureStatus($status));
return $capture;
}
private function createPaypalOrder(array $authorizations): Order { private function createPaypalOrder(array $authorizations): Order {
$payments = Mockery::mock(Payments::class); $payments = Mockery::mock(Payments::class);
$payments $payments

View file

@ -4,15 +4,20 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway\Processor; namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
use Dhii\Container\Dictionary;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture; use Woocommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payments; use WooCommerce\PayPalCommerce\ApiClient\Entity\Payments;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit; use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure; use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\TestCase; use WooCommerce\PayPalCommerce\TestCase;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
@ -22,35 +27,44 @@ use function Brain\Monkey\Functions\when;
class OrderProcessorTest extends TestCase class OrderProcessorTest extends TestCase
{ {
private $environment;
public function setUp(): void {
parent::setUp();
$this->environment = new Environment(new Dictionary([]));
}
public function testAuthorize() { public function testAuthorize() {
$transactionId = 'ABC123'; $transactionId = 'ABC123';
$capture = Mockery::mock(Capture::class); $authorization = Mockery::mock(Authorization::class);
$capture->expects('id') $authorization->shouldReceive('id')
->andReturn($transactionId); ->andReturn($transactionId);
$authorization->shouldReceive('status')
->andReturn(new AuthorizationStatus(AuthorizationStatus::CREATED));
$payments = Mockery::mock(Payments::class); $payments = Mockery::mock(Payments::class);
$payments->expects('captures') $payments->shouldReceive('authorizations')
->andReturn([$capture]); ->andReturn([$authorization]);
$payments->shouldReceive('captures')
->andReturn([]);
$purchaseUnit = Mockery::mock(PurchaseUnit::class); $purchaseUnit = Mockery::mock(PurchaseUnit::class);
$purchaseUnit->expects('payments') $purchaseUnit->shouldReceive('payments')
->andReturn($payments); ->andReturn($payments);
$wcOrder = Mockery::mock(\WC_Order::class); $wcOrder = Mockery::mock(\WC_Order::class);
$wcOrder->expects('update_meta_data') $wcOrder->expects('update_meta_data')
->with(PayPalGateway::ORDER_PAYMENT_MODE_META_KEY, 'live'); ->with(PayPalGateway::ORDER_PAYMENT_MODE_META_KEY, 'live');
$wcOrder->expects('set_transaction_id')
->with($transactionId);
$orderStatus = Mockery::mock(OrderStatus::class); $orderStatus = Mockery::mock(OrderStatus::class);
$orderStatus $orderStatus
->expects('is') ->shouldReceive('is')
->with(OrderStatus::APPROVED) ->with(OrderStatus::APPROVED)
->andReturn(true); ->andReturn(true);
$orderStatus $orderStatus
->expects('is') ->shouldReceive('is')
->with(OrderStatus::COMPLETED) ->with(OrderStatus::COMPLETED)
->andReturn(true); ->andReturn(true);
@ -67,7 +81,7 @@ class OrderProcessorTest extends TestCase
$currentOrder $currentOrder
->shouldReceive('status') ->shouldReceive('status')
->andReturn($orderStatus); ->andReturn($orderStatus);
$currentOrder->expects('purchase_units') $currentOrder->shouldReceive('purchase_units')
->andReturn([$purchaseUnit]); ->andReturn([$purchaseUnit]);
$sessionHandler = Mockery::mock(SessionHandler::class); $sessionHandler = Mockery::mock(SessionHandler::class);
@ -112,7 +126,7 @@ class OrderProcessorTest extends TestCase
$authorizedPaymentProcessor, $authorizedPaymentProcessor,
$settings, $settings,
$logger, $logger,
false $this->environment
); );
$cart = Mockery::mock(\WC_Cart::class); $cart = Mockery::mock(\WC_Cart::class);
@ -156,23 +170,25 @@ class OrderProcessorTest extends TestCase
$capture = Mockery::mock(Capture::class); $capture = Mockery::mock(Capture::class);
$capture->expects('id') $capture->expects('id')
->andReturn($transactionId); ->andReturn($transactionId);
$capture->expects('status')
->andReturn(new CaptureStatus(CaptureStatus::COMPLETED));
$payments = Mockery::mock(Payments::class); $payments = Mockery::mock(Payments::class);
$payments->expects('captures') $payments->shouldReceive('captures')
->andReturn([$capture]); ->andReturn([$capture]);
$purchaseUnit = Mockery::mock(PurchaseUnit::class); $purchaseUnit = Mockery::mock(PurchaseUnit::class);
$purchaseUnit->expects('payments') $purchaseUnit->shouldReceive('payments')
->andReturn($payments); ->andReturn($payments);
$wcOrder = Mockery::mock(\WC_Order::class); $wcOrder = Mockery::mock(\WC_Order::class);
$orderStatus = Mockery::mock(OrderStatus::class); $orderStatus = Mockery::mock(OrderStatus::class);
$orderStatus $orderStatus
->expects('is') ->shouldReceive('is')
->with(OrderStatus::APPROVED) ->with(OrderStatus::APPROVED)
->andReturn(true); ->andReturn(true);
$orderStatus $orderStatus
->expects('is') ->shouldReceive('is')
->with(OrderStatus::COMPLETED) ->with(OrderStatus::COMPLETED)
->andReturn(true); ->andReturn(true);
$orderId = 'abc'; $orderId = 'abc';
@ -188,7 +204,7 @@ class OrderProcessorTest extends TestCase
->shouldReceive('status') ->shouldReceive('status')
->andReturn($orderStatus); ->andReturn($orderStatus);
$currentOrder $currentOrder
->expects('purchase_units') ->shouldReceive('purchase_units')
->andReturn([$purchaseUnit]); ->andReturn([$purchaseUnit]);
$sessionHandler = Mockery::mock(SessionHandler::class); $sessionHandler = Mockery::mock(SessionHandler::class);
$sessionHandler $sessionHandler
@ -240,7 +256,7 @@ class OrderProcessorTest extends TestCase
$authorizedPaymentProcessor, $authorizedPaymentProcessor,
$settings, $settings,
$logger, $logger,
false $this->environment
); );
$cart = Mockery::mock(\WC_Cart::class); $cart = Mockery::mock(\WC_Cart::class);
@ -264,9 +280,6 @@ class OrderProcessorTest extends TestCase
PayPalGateway::INTENT_META_KEY, PayPalGateway::INTENT_META_KEY,
$orderIntent $orderIntent
); );
$wcOrder
->expects('update_status')
->with('on-hold', 'Awaiting payment.');
$wcOrder->expects('update_meta_data') $wcOrder->expects('update_meta_data')
->with(PayPalGateway::ORDER_PAYMENT_MODE_META_KEY, 'live'); ->with(PayPalGateway::ORDER_PAYMENT_MODE_META_KEY, 'live');
$wcOrder->expects('set_transaction_id') $wcOrder->expects('set_transaction_id')
@ -340,7 +353,7 @@ class OrderProcessorTest extends TestCase
$authorizedPaymentProcessor, $authorizedPaymentProcessor,
$settings, $settings,
$logger, $logger,
false $this->environment
); );
$wcOrder $wcOrder
@ -355,7 +368,7 @@ class OrderProcessorTest extends TestCase
PayPalGateway::INTENT_META_KEY, PayPalGateway::INTENT_META_KEY,
$orderIntent $orderIntent
); );
$this->assertFalse($testee->process($wcOrder)); $this->assertFalse($testee->process($wcOrder));
$this->assertNotEmpty($testee->last_error()); $this->assertNotEmpty($testee->last_error());
} }