mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
🔀 Merge branch 'trunk'
# Conflicts: # modules/ppcp-applepay/resources/js/ApplepayButton.js # modules/ppcp-applepay/resources/js/boot-admin.js # modules/ppcp-button/resources/js/modules/Preview/PreviewButtonManager.js # modules/ppcp-googlepay/resources/js/GooglepayButton.js # modules/ppcp-googlepay/resources/js/boot-admin.js
This commit is contained in:
commit
a128228c86
303 changed files with 21512 additions and 5378 deletions
|
@ -1,7 +1,7 @@
|
|||
name: woocommerce-paypal-payments
|
||||
type: php
|
||||
docroot: .ddev/wordpress
|
||||
php_version: "7.2"
|
||||
php_version: "7.4"
|
||||
webserver_type: apache-fpm
|
||||
router_http_port: "80"
|
||||
router_https_port: "443"
|
||||
|
@ -18,7 +18,7 @@ hooks:
|
|||
pre-start:
|
||||
- exec-host: "mkdir -p .ddev/wordpress/wp-content/plugins/${DDEV_PROJECT}"
|
||||
web_environment:
|
||||
- WP_VERSION=6.2.2
|
||||
- WP_VERSION=6.3.3
|
||||
- WP_LOCALE=en_US
|
||||
- WP_TITLE=WooCommerce PayPal Payments
|
||||
- WP_MULTISITE=true
|
||||
|
|
34
.distignore
Normal file
34
.distignore
Normal file
|
@ -0,0 +1,34 @@
|
|||
.distignore
|
||||
.editorconfig
|
||||
.gitattributes
|
||||
.gitignore
|
||||
.eslintrc
|
||||
.ddev
|
||||
.git*
|
||||
.phpstorm*
|
||||
.idea
|
||||
*.env*
|
||||
.psalm*
|
||||
tests
|
||||
*.xml*
|
||||
.phpunit.result.cache
|
||||
babel.config.json
|
||||
node_modules
|
||||
modules/*/resources/css
|
||||
modules/*/resources/js/**/*.js
|
||||
*.lock
|
||||
webpack.config.js
|
||||
wp-cli.yml
|
||||
ngrok.yml
|
||||
composer.json
|
||||
package.json
|
||||
package-lock.json
|
||||
.composer_compiled_assets
|
||||
assets-compiler.json
|
||||
patchwork.json
|
||||
.babelrc
|
||||
README.md
|
||||
wordpress_org_assets
|
||||
.DS_Store
|
||||
auth.json
|
||||
*.log
|
|
@ -1,7 +1,11 @@
|
|||
{
|
||||
"extends": [ "plugin:@wordpress/eslint-plugin/recommended" ],
|
||||
"env": {
|
||||
"browser": true
|
||||
},
|
||||
"globals": {
|
||||
"wc": true
|
||||
"wc": true,
|
||||
"jQuery": "readonly"
|
||||
},
|
||||
"rules": {
|
||||
"no-console": ["error", { "allow": ["warn", "error"] }]
|
||||
|
|
4
.github/workflows/e2e.yml
vendored
4
.github/workflows/e2e.yml
vendored
|
@ -7,8 +7,8 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['7.3', '7.4', '8.2']
|
||||
wc-versions: ['5.9.5', '7.7.2']
|
||||
php-versions: ['7.4', '8.2']
|
||||
wc-versions: ['6.9.4', '7.7.2']
|
||||
|
||||
name: PHP ${{ matrix.php-versions }} WC ${{ matrix.wc-versions }}
|
||||
steps:
|
||||
|
|
37
.github/workflows/package-new.yml
vendored
Normal file
37
.github/workflows/package-new.yml
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
name: Build package (New)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
packageVersion:
|
||||
description: 'Package version'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
check_version:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PACKAGE_VERSION: ${{ github.event.inputs.packageVersion }}
|
||||
outputs:
|
||||
version: ${{ env.PACKAGE_VERSION }}
|
||||
name: Check version
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fix plugin version input # Add the version number if only suffix entered
|
||||
run: echo "PACKAGE_VERSION=$(sed -nE '/Version:/s/.* ([0-9.]+).*/\1/p' woocommerce-paypal-payments.php)-$PACKAGE_VERSION" >> $GITHUB_ENV
|
||||
if: env.PACKAGE_VERSION && !contains(env.PACKAGE_VERSION, '.')
|
||||
- name: Fill plugin version # If the version number was not set, retrieve it from the file
|
||||
run: echo "PACKAGE_VERSION=$(sed -nE '/Version:/s/.* ([0-9.]+).*/\1/p' woocommerce-paypal-payments.php)" >> $GITHUB_ENV
|
||||
if: "!env.PACKAGE_VERSION"
|
||||
|
||||
create_archive:
|
||||
needs: check_version
|
||||
uses: inpsyde/reusable-workflows/.github/workflows/build-plugin-archive.yml@main
|
||||
with:
|
||||
PHP_VERSION: 7.4
|
||||
PLUGIN_MAIN_FILE: ./woocommerce-paypal-payments.php
|
||||
PLUGIN_VERSION: ${{ needs.check_version.outputs.version }}
|
||||
PLUGIN_FOLDER_NAME: woocommerce-paypal-payments
|
||||
ARCHIVE_NAME: woocommerce-paypal-payments-${{ needs.check_version.outputs.version }}
|
||||
COMPILE_ASSETS_ARGS: '-vv --env=root'
|
2
.github/workflows/package.yml
vendored
2
.github/workflows/package.yml
vendored
|
@ -27,7 +27,7 @@ jobs:
|
|||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 7.2
|
||||
php-version: 7.4
|
||||
|
||||
- name: Fix plugin version input # Add the version number if only suffix entered
|
||||
run: echo "PACKAGE_VERSION=$(sed -nE '/Version:/s/.* ([0-9.]+).*/\1/p' woocommerce-paypal-payments.php)-$PACKAGE_VERSION" >> $GITHUB_ENV
|
||||
|
|
2
.github/workflows/php.yml
vendored
2
.github/workflows/php.yml
vendored
|
@ -7,7 +7,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
|
||||
php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3']
|
||||
|
||||
name: PHP ${{ matrix.php-versions }}
|
||||
steps:
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,3 +13,4 @@ modules/*/assets/*
|
|||
auth.json
|
||||
.DS_Store
|
||||
tests/.DS_Store
|
||||
.composer_compiled_assets
|
||||
|
|
20
.psalm/wc-bookings.php
Normal file
20
.psalm/wc-bookings.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validate and create a new booking manually.
|
||||
*
|
||||
* @version 1.10.7
|
||||
* @see WC_Booking::new_booking() for available $new_booking_data args
|
||||
* @param int $product_id you are booking
|
||||
* @param array $new_booking_data
|
||||
* @param string $status
|
||||
* @param bool $exact If false, the function will look for the next available block after your start date if the date is unavailable.
|
||||
* @return mixed WC_Booking object on success or false on fail
|
||||
*/
|
||||
function create_wc_booking( $product_id, $new_booking_data = array(), $status = 'confirmed', $exact = false ) {}
|
||||
|
||||
/**
|
||||
* Returns true if the product is a booking product, false if not
|
||||
* @return bool
|
||||
*/
|
||||
function is_wc_booking_product( $product ) {}
|
|
@ -4,9 +4,9 @@ PayPal's latest complete payments processing solution. Accept PayPal, Pay Later,
|
|||
|
||||
## Dependencies
|
||||
|
||||
* PHP >= 7.2
|
||||
* WordPress >=5.3
|
||||
* WooCommerce >=4.5
|
||||
* PHP >= 7.4
|
||||
* WordPress >= 6.3
|
||||
* WooCommerce >= 6.9
|
||||
|
||||
## Development
|
||||
|
||||
|
|
4
assets-compiler.json
Normal file
4
assets-compiler.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"dependencies": "install",
|
||||
"commands": "yarn"
|
||||
}
|
|
@ -5,13 +5,8 @@
|
|||
* @package WooCommerce\PayPalCommerce
|
||||
*/
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\CachingContainer;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\CompositeCachingServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\CompositeContainer;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\DelegatingContainer;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ProxyContainer;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Package;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties\PluginProperties;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
return function (
|
||||
|
@ -34,38 +29,15 @@ return function (
|
|||
*/
|
||||
$modules = apply_filters( 'woocommerce_paypal_payments_modules', $modules );
|
||||
|
||||
$providers = array_map(
|
||||
function ( ModuleInterface $module ): ServiceProviderInterface {
|
||||
return $module->setup();
|
||||
},
|
||||
$modules
|
||||
);
|
||||
|
||||
$provider = new CompositeCachingServiceProvider( $providers );
|
||||
$proxy_container = new ProxyContainer();
|
||||
// TODO: caching does not work currently,
|
||||
// may want to consider fixing it later (pass proxy as parent to DelegatingContainer)
|
||||
// for now not fixed since we were using this behavior for long time and fixing it now may break things.
|
||||
$container = new DelegatingContainer( $provider );
|
||||
/**
|
||||
* Skip iterable vs array check.
|
||||
*
|
||||
* @psalm-suppress PossiblyInvalidArgument
|
||||
*/
|
||||
$app_container = new CachingContainer(
|
||||
new CompositeContainer(
|
||||
array_merge(
|
||||
$additional_containers,
|
||||
array( $container )
|
||||
)
|
||||
)
|
||||
);
|
||||
$proxy_container->setInnerContainer( $app_container );
|
||||
// Initialize plugin.
|
||||
$properties = PluginProperties::new( __FILE__ );
|
||||
$bootstrap = Package::new( $properties );
|
||||
|
||||
foreach ( $modules as $module ) {
|
||||
/* @var $module ModuleInterface module */
|
||||
$module->run( $app_container );
|
||||
$bootstrap->addModule( $module );
|
||||
}
|
||||
|
||||
return $app_container;
|
||||
$bootstrap->boot();
|
||||
|
||||
return $bootstrap->container();
|
||||
};
|
||||
|
|
|
@ -1,5 +1,60 @@
|
|||
*** Changelog ***
|
||||
|
||||
= 2.9.0 - 2024-09-02 =
|
||||
* Fix - Fatal error in Block Editor when using WooCommerce blocks #2534
|
||||
* Fix - Can't pay from block pages when the shipping callback is enabled and no shipping methods defined #2429
|
||||
* Fix - Various Google Pay button fixes #2496
|
||||
* Fix - Buying a free trial subscription with ACDC results in a $1 charge in the API call #2465
|
||||
* Fix - Problem with Google Pay and Apple Pay button placement on Pay for Order page #2542
|
||||
* Fix - When there isn't any shipping option for the address the order is still created from classic cart #2437
|
||||
* Fix - Patch the order with no shipping methods, instead of throwing an error #2435
|
||||
* Enhancement - Separate Apple Pay button for Classic Checkout #2457
|
||||
* Enhancement - Remove AMEX support for ACDC when store location is set to China #2526
|
||||
* Enhancement - Inform users of Pay Later messaging configuration when Pay Later wasn't recently enabled #2529
|
||||
* Enhancement - Update ACDC signup URLs #2475
|
||||
* Enhancement - Implement country based APMs via Orders API #2511
|
||||
* Enhancement - Update PaymentsStatusHandlingTrait.php (author @callmeahmedr) #2523
|
||||
* Enhancement - Disable PayPal Shipping callback by default #2527
|
||||
* Enhancement - Change Apple Pay and Google Pay default button labels to plain #2476
|
||||
* Enhancement - Add Package Tracking compatibility with DHL Shipping plugin #2463
|
||||
* Enhancement - Add support for WC Bookings when skipping checkout confirmation #2452
|
||||
* Enhancement - Remove currencies from country-currency matrix in card fields module #2441
|
||||
|
||||
= 2.8.3 - 2024-08-12 =
|
||||
* Fix - Google Pay: Prevent field validation from being triggered on checkout page load #2474
|
||||
* Fix - Do not add tax info into order meta during order creation #2471
|
||||
* Fix - PayPal declares subscription support when for Subscription mode is set Disable PayPal for subscription #2425
|
||||
* Fix - PayPal js files loaded on non PayPal pages #2411
|
||||
* Fix - Google Pay: Fix the incorrect popup triggering #2414
|
||||
* Fix - Add tax configurator when programmatically creating WC orders #2431
|
||||
* Fix - Shipping callback compatibility with WC Name Your Price plugin #2402
|
||||
* Fix - Uncaught Error: Cannot use object of type ...\Settings as array in .../AbstractPaymentMethodType.php (3253) #2334
|
||||
* Fix - Prevent displaying smart button multiple times on variable product page #2420
|
||||
* Fix - Prevent enabling Standard Card Button when ACDC is enabled #2404
|
||||
* Fix - Use client credentials for user tokens #2491
|
||||
* Fix - Apple Pay: Fix the shipping callback #2492
|
||||
* Enhancement - Separate Google Pay button for Classic Checkout #2430
|
||||
* Enhancement - Add Apple Pay and Google Pay support for China, simplify country-currency matrix #2468
|
||||
* Enhancement - Add AMEX support for Advanced Card Processing in China #2469
|
||||
|
||||
= 2.8.2 - 2024-07-22 =
|
||||
* Fix - Sold individually checkbox automatically disabled after adding product to the cart more than once #2415
|
||||
* Fix - All products "Sold individually" when PayPal Subscriptions selected as Subscriptions Mode #2400
|
||||
* Fix - W3 Total Cache: Remove type from file parameter as sometimes null gets passed causing errors #2403
|
||||
* Fix - Shipping methods during callback not updated correctly #2421
|
||||
* Fix - Preserve subscription renewal processing when switching Subscriptions Mode or disabling gateway #2394
|
||||
* Fix - Remove shipping callback for Venmo express button #2374
|
||||
* Fix - Google Pay: Fix issuse with data.paymentSource being undefined #2390
|
||||
* Fix - Loading of non-Order as a WC_Order causes warnings and potential data corruption #2343
|
||||
* Fix - Apple Pay and Google Pay buttons don't appear in PayPal Button stack on multi-step Checkout #2372
|
||||
* Fix - Apple Pay: Fix when shipping is disabled #2391
|
||||
* Fix - Wrong string in smart button preview on Standard Payments tab #2409
|
||||
* Fix - Don't break orders screen when there is an exception for package tracking #2369
|
||||
* Fix - Pay Later button preview is missing #2371
|
||||
* Fix - Apple Pay button layout #2367
|
||||
* Enhancement - Remove BCDC button from block Express Checkout area #2381
|
||||
* Enhancement - Extend Advanced Card Processing country eligibility for China #2397
|
||||
|
||||
= 2.8.1 - 2024-07-01 =
|
||||
* Fix - Don't render tracking metabox if PayPal order does not belong to connected merchant #2360
|
||||
* Fix - Fatal error when the ppcp-paylater-configurator module is disabled via code snippet #2327
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"description": "PayPal Commerce Platform for WooCommerce",
|
||||
"license": "GPL-2.0",
|
||||
"require": {
|
||||
"php": "^7.2 | ^8.0",
|
||||
"php": "^7.4 | ^8.0",
|
||||
"ext-json": "*",
|
||||
"psr/log": "^1.1",
|
||||
"ralouphie/getallheaders": "^3.0",
|
||||
|
@ -14,10 +14,12 @@
|
|||
"symfony/polyfill-php80": "^1.19"
|
||||
},
|
||||
"require-dev": {
|
||||
"inpsyde/composer-assets-compiler": "^2.5",
|
||||
"psr/container": "^1.0",
|
||||
"dhii/module-interface": "^0.2 || ^0.3",
|
||||
"container-interop/service-provider": "^0.4.0",
|
||||
"dhii/containers": "^0.1.0-alpha1",
|
||||
"inpsyde/modularity": "^1.7",
|
||||
"woocommerce/woocommerce-sniffs": "^0.1.0",
|
||||
"phpunit/phpunit": "^7.0 | ^8.0 | ^9.0",
|
||||
"brain/monkey": "^2.4",
|
||||
|
@ -69,6 +71,15 @@
|
|||
"vendor/bin/phpcbf"
|
||||
]
|
||||
},
|
||||
"composer-asset-compiler": {
|
||||
"auto-run": false,
|
||||
"packages": {
|
||||
"inpsyde/*": {
|
||||
"dependencies": "install",
|
||||
"script": "build"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mozart": {
|
||||
"dep_namespace": "WooCommerce\\PayPalCommerce\\Vendor\\",
|
||||
"dep_directory": "/lib/packages/",
|
||||
|
@ -77,13 +88,18 @@
|
|||
"packages": [
|
||||
"psr/container",
|
||||
"dhii/containers",
|
||||
"dhii/module-interface"
|
||||
"dhii/module-interface",
|
||||
"inpsyde/modularity"
|
||||
],
|
||||
"delete_vendor_directories": true
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.4"
|
||||
},
|
||||
"allow-plugins": {
|
||||
"inpsyde/composer-assets-compiler": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"wikimedia/composer-merge-plugin": true
|
||||
}
|
||||
|
|
1358
composer.lock
generated
1358
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerExceptionInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
class ContainerConfigurator
|
||||
{
|
||||
/**
|
||||
* @var array<string, callable(ContainerInterface $container):mixed>
|
||||
*/
|
||||
private $services = [];
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $factoryIds = [];
|
||||
|
||||
/**
|
||||
* @var array<string, array<callable(mixed $service, ContainerInterface $container):mixed>>
|
||||
*/
|
||||
private $extensions = [];
|
||||
|
||||
/**
|
||||
* @var ContainerInterface[]
|
||||
*/
|
||||
private $containers = [];
|
||||
|
||||
/**
|
||||
* @var null|ContainerInterface
|
||||
*/
|
||||
private $compiledContainer;
|
||||
|
||||
/**
|
||||
* ContainerConfigurator constructor.
|
||||
*
|
||||
* @param ContainerInterface[] $containers
|
||||
*/
|
||||
public function __construct(array $containers = [])
|
||||
{
|
||||
array_map([$this, 'addContainer'], $containers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allowing to add child containers.
|
||||
*
|
||||
* @param ContainerInterface $container
|
||||
*/
|
||||
public function addContainer(ContainerInterface $container): void
|
||||
{
|
||||
$this->containers[] = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param callable(ContainerInterface $container):mixed $factory
|
||||
*/
|
||||
public function addFactory(string $id, callable $factory): void
|
||||
{
|
||||
$this->addService($id, $factory);
|
||||
// We're using a hash table to detect later
|
||||
// via isset() if a Service as a Factory.
|
||||
$this->factoryIds[$id] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param callable(ContainerInterface $container):mixed $service
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addService(string $id, callable $service): void
|
||||
{
|
||||
/*
|
||||
* We are being intentionally permissive here,
|
||||
* allowing a simple workflow for *intentional* overrides
|
||||
* while accepting the (small?) risk of *accidental* overrides
|
||||
* that could be hard to notice and debug.
|
||||
*
|
||||
* Clear a factory flag in case it was a factory.
|
||||
* If needs be, it will get re-added after this function completes.
|
||||
*/
|
||||
unset($this->factoryIds[$id]);
|
||||
|
||||
$this->services[$id] = $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasService(string $id): bool
|
||||
{
|
||||
if (array_key_exists($id, $this->services)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->containers as $container) {
|
||||
if ($container->has($id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param callable(mixed $service, ContainerInterface $container):mixed $extender
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addExtension(string $id, callable $extender): void
|
||||
{
|
||||
if (!isset($this->extensions[$id])) {
|
||||
$this->extensions[$id] = [];
|
||||
}
|
||||
|
||||
$this->extensions[$id][] = $extender;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasExtension(string $id): bool
|
||||
{
|
||||
return isset($this->extensions[$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a read only version of this Container.
|
||||
*
|
||||
* @return ContainerInterface
|
||||
*/
|
||||
public function createReadOnlyContainer(): ContainerInterface
|
||||
{
|
||||
if (!$this->compiledContainer) {
|
||||
$this->compiledContainer = new ReadOnlyContainer(
|
||||
$this->services,
|
||||
$this->factoryIds,
|
||||
$this->extensions,
|
||||
$this->containers
|
||||
);
|
||||
}
|
||||
|
||||
return $this->compiledContainer;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Package;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerExceptionInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
class PackageProxyContainer implements ContainerInterface
|
||||
{
|
||||
/**
|
||||
* @var Package
|
||||
*/
|
||||
private $package;
|
||||
|
||||
/**
|
||||
* @var ContainerInterface|null
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* @param Package $package
|
||||
*/
|
||||
public function __construct(Package $package)
|
||||
{
|
||||
$this->package = $package;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get(string $id)
|
||||
{
|
||||
$this->assertPackageBooted($id);
|
||||
|
||||
return $this->container->get($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @return bool
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function has(string $id): bool
|
||||
{
|
||||
return $this->tryContainer() && $this->container->has($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
* @throws \Exception
|
||||
* @psalm-assert-if-true ContainerInterface $this->container
|
||||
*/
|
||||
private function tryContainer(): bool
|
||||
{
|
||||
if ($this->container) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** TODO: We need a better way to deal with status checking besides equality */
|
||||
if (
|
||||
$this->package->statusIs(Package::STATUS_READY)
|
||||
|| $this->package->statusIs(Package::STATUS_BOOTED)
|
||||
) {
|
||||
$this->container = $this->package->container();
|
||||
}
|
||||
|
||||
return (bool)$this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @return void
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @psalm-assert ContainerInterface $this->container
|
||||
*/
|
||||
private function assertPackageBooted(string $id): void
|
||||
{
|
||||
if ($this->tryContainer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$name = $this->package->name();
|
||||
$status = $this->package->statusIs(Package::STATUS_FAILED)
|
||||
? 'is errored'
|
||||
: 'is not ready yet';
|
||||
|
||||
throw new class ("Error retrieving service {$id} because package {$name} {$status}.")
|
||||
extends \Exception
|
||||
implements ContainerExceptionInterface {
|
||||
};
|
||||
}
|
||||
}
|
138
lib/packages/Inpsyde/Modularity/Container/ReadOnlyContainer.php
Normal file
138
lib/packages/Inpsyde/Modularity/Container/ReadOnlyContainer.php
Normal file
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
class ReadOnlyContainer implements ContainerInterface
|
||||
{
|
||||
/**
|
||||
* @var array<string, callable(\WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface $container):mixed>
|
||||
*/
|
||||
private $services;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $factoryIds;
|
||||
|
||||
/**
|
||||
* @var array<string, array<callable(mixed, ContainerInterface $container):mixed>>
|
||||
*/
|
||||
private $extensions;
|
||||
|
||||
/**
|
||||
* Resolved factories.
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
private $resolvedServices = [];
|
||||
|
||||
/**
|
||||
* @var ContainerInterface[]
|
||||
*/
|
||||
private $containers;
|
||||
|
||||
/**
|
||||
* ReadOnlyContainer constructor.
|
||||
*
|
||||
* @param array<string, callable(ContainerInterface $container):mixed> $services
|
||||
* @param array<string, bool> $factoryIds
|
||||
* @param array<string, array<callable(mixed, ContainerInterface $container):mixed>> $extensions
|
||||
* @param ContainerInterface[] $containers
|
||||
*/
|
||||
public function __construct(
|
||||
array $services,
|
||||
array $factoryIds,
|
||||
array $extensions,
|
||||
array $containers
|
||||
) {
|
||||
$this->services = $services;
|
||||
$this->factoryIds = $factoryIds;
|
||||
$this->extensions = $extensions;
|
||||
$this->containers = $containers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $id)
|
||||
{
|
||||
if (array_key_exists($id, $this->resolvedServices)) {
|
||||
return $this->resolvedServices[$id];
|
||||
}
|
||||
|
||||
if (array_key_exists($id, $this->services)) {
|
||||
$service = $this->services[$id]($this);
|
||||
$resolved = $this->resolveExtensions($id, $service);
|
||||
|
||||
if (!isset($this->factoryIds[$id])) {
|
||||
$this->resolvedServices[$id] = $resolved;
|
||||
unset($this->services[$id]);
|
||||
}
|
||||
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
foreach ($this->containers as $container) {
|
||||
if ($container->has($id)) {
|
||||
$service = $container->get($id);
|
||||
|
||||
return $this->resolveExtensions($id, $service);
|
||||
}
|
||||
}
|
||||
|
||||
throw new class ("Service with ID {$id} not found.")
|
||||
extends \Exception
|
||||
implements NotFoundExceptionInterface {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $id): bool
|
||||
{
|
||||
if (array_key_exists($id, $this->services)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (array_key_exists($id, $this->resolvedServices)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->containers as $container) {
|
||||
if ($container->has($id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param mixed $service
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function resolveExtensions(string $id, $service)
|
||||
{
|
||||
if (!isset($this->extensions[$id])) {
|
||||
return $service;
|
||||
}
|
||||
|
||||
foreach ($this->extensions[$id] as $extender) {
|
||||
$service = $extender($service, $this);
|
||||
}
|
||||
|
||||
return $service;
|
||||
}
|
||||
}
|
21
lib/packages/Inpsyde/Modularity/Module/ExecutableModule.php
Normal file
21
lib/packages/Inpsyde/Modularity/Module/ExecutableModule.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
interface ExecutableModule extends Module
|
||||
{
|
||||
|
||||
/**
|
||||
* Perform actions with objects retrieved from the container. Usually, adding WordPress hooks.
|
||||
* Return true to signal a success, false to signal a failure.
|
||||
*
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return bool true when successfully booted, otherwise false.
|
||||
*/
|
||||
public function run(ContainerInterface $container): bool;
|
||||
}
|
24
lib/packages/Inpsyde/Modularity/Module/ExtendingModule.php
Normal file
24
lib/packages/Inpsyde/Modularity/Module/ExtendingModule.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
||||
|
||||
interface ExtendingModule extends Module
|
||||
{
|
||||
|
||||
/**
|
||||
* Return application services' extensions.
|
||||
*
|
||||
* Array keys will be services' IDs in the container, array values are callback that
|
||||
* accepts as parameters the original service and a PSR-11 container and return an instance of
|
||||
* the extended service.
|
||||
*
|
||||
* It is possible to explicitly extend extensions made by other modules.
|
||||
* That is done by using as ID (array key in the `extensions` method) the target module ID
|
||||
* and the service ID.
|
||||
*
|
||||
* @return array<string, callable(mixed $service, \WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface $container):mixed>
|
||||
*/
|
||||
public function extensions(): array;
|
||||
}
|
18
lib/packages/Inpsyde/Modularity/Module/FactoryModule.php
Normal file
18
lib/packages/Inpsyde/Modularity/Module/FactoryModule.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
||||
|
||||
interface FactoryModule extends Module
|
||||
{
|
||||
/**
|
||||
* Return application factories.
|
||||
*
|
||||
* Similar to `services`, but object created by given factories are not "cached", but a *new*
|
||||
* instance is returned everytime `get()` is called in the container.
|
||||
*
|
||||
* @return array<string, callable(\WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface $container):mixed>
|
||||
*/
|
||||
public function factories(): array;
|
||||
}
|
20
lib/packages/Inpsyde/Modularity/Module/Module.php
Normal file
20
lib/packages/Inpsyde/Modularity/Module/Module.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
||||
|
||||
/**
|
||||
* @package WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module
|
||||
*/
|
||||
interface Module
|
||||
{
|
||||
|
||||
/**
|
||||
* Unique identifier for your Module.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string;
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
||||
|
||||
/**
|
||||
* Trait ModuleClassNameIdTrait
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module
|
||||
*/
|
||||
trait ModuleClassNameIdTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @see Module::id()
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return __CLASS__;
|
||||
}
|
||||
}
|
21
lib/packages/Inpsyde/Modularity/Module/ServiceModule.php
Normal file
21
lib/packages/Inpsyde/Modularity/Module/ServiceModule.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module;
|
||||
|
||||
interface ServiceModule extends Module
|
||||
{
|
||||
|
||||
/**
|
||||
* Return application services' factories.
|
||||
*
|
||||
* Array keys will be services' IDs in the container, array values are callback that
|
||||
* accepts a PSR-11 container as parameter and return an instance of the service.
|
||||
* Services are "cached", so the given factory is called once the first time `get()` is called
|
||||
* in the container, and on subsequent `get()` the same instance is returned again and again.
|
||||
*
|
||||
* @return array<string, callable(\WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface $container):mixed>
|
||||
*/
|
||||
public function services(): array;
|
||||
}
|
727
lib/packages/Inpsyde/Modularity/Package.php
Normal file
727
lib/packages/Inpsyde/Modularity/Package.php
Normal file
|
@ -0,0 +1,727 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container\ContainerConfigurator;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container\PackageProxyContainer;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\FactoryModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\Module;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties\Properties;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
class Package
|
||||
{
|
||||
/**
|
||||
* All the hooks fired in this class use this prefix.
|
||||
* @var string
|
||||
*/
|
||||
private const HOOK_PREFIX = 'inpsyde.modularity.';
|
||||
|
||||
/**
|
||||
* Identifier to access Properties in Container.
|
||||
*
|
||||
* @example
|
||||
* <code>
|
||||
* $package = Package::new();
|
||||
* $package->boot();
|
||||
*
|
||||
* $container = $package->container();
|
||||
* $container->has(Package::PROPERTIES);
|
||||
* $container->get(Package::PROPERTIES);
|
||||
* </code>
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const PROPERTIES = 'properties';
|
||||
|
||||
/**
|
||||
* Custom action to be used to add Modules to the package.
|
||||
* It might also be used to access package properties.
|
||||
*
|
||||
* @example
|
||||
* <code>
|
||||
* $package = Package::new();
|
||||
*
|
||||
* add_action(
|
||||
* $package->hookName(Package::ACTION_INIT),
|
||||
* $callback
|
||||
* );
|
||||
* </code>
|
||||
*/
|
||||
public const ACTION_INIT = 'init';
|
||||
|
||||
/**
|
||||
* Custom action which is triggered after the application
|
||||
* is booted to access container and properties.
|
||||
*
|
||||
* @example
|
||||
* <code>
|
||||
* $package = Package::new();
|
||||
*
|
||||
* add_action(
|
||||
* $package->hookName(Package::ACTION_READY),
|
||||
* $callback
|
||||
* );
|
||||
* </code>
|
||||
*/
|
||||
public const ACTION_READY = 'ready';
|
||||
|
||||
/**
|
||||
* Custom action which is triggered when a failure happens during the building stage.
|
||||
*
|
||||
* @example
|
||||
* <code>
|
||||
* $package = Package::new();
|
||||
*
|
||||
* add_action(
|
||||
* $package->hookName(Package::ACTION_FAILED_BUILD),
|
||||
* $callback
|
||||
* );
|
||||
* </code>
|
||||
*/
|
||||
public const ACTION_FAILED_BUILD = 'failed-build';
|
||||
|
||||
/**
|
||||
* Custom action which is triggered when a failure happens during the booting stage.
|
||||
*
|
||||
* @example
|
||||
* <code>
|
||||
* $package = Package::new();
|
||||
*
|
||||
* add_action(
|
||||
* $package->hookName(Package::ACTION_FAILED_BOOT),
|
||||
* $callback
|
||||
* );
|
||||
* </code>
|
||||
*/
|
||||
public const ACTION_FAILED_BOOT = 'failed-boot';
|
||||
|
||||
/**
|
||||
* Custom action which is triggered when a package is connected.
|
||||
*/
|
||||
public const ACTION_PACKAGE_CONNECTED = 'package-connected';
|
||||
|
||||
/**
|
||||
* Custom action which is triggered when a package cannot be connected.
|
||||
*/
|
||||
public const ACTION_FAILED_CONNECTION = 'failed-connection';
|
||||
|
||||
/**
|
||||
* Module states can be used to get information about your module.
|
||||
*
|
||||
* @example
|
||||
* <code>
|
||||
* $package = Package::new();
|
||||
* $package->moduleIs(SomeModule::class, Package::MODULE_ADDED); // false
|
||||
* $package->boot(new SomeModule());
|
||||
* $package->moduleIs(SomeModule::class, Package::MODULE_ADDED); // true
|
||||
* </code>
|
||||
*/
|
||||
public const MODULE_ADDED = 'added';
|
||||
public const MODULE_NOT_ADDED = 'not-added';
|
||||
public const MODULE_REGISTERED = 'registered';
|
||||
public const MODULE_REGISTERED_FACTORIES = 'registered-factories';
|
||||
public const MODULE_EXTENDED = 'extended';
|
||||
public const MODULE_EXECUTED = 'executed';
|
||||
public const MODULE_EXECUTION_FAILED = 'executed-failed';
|
||||
public const MODULES_ALL = '*';
|
||||
|
||||
/**
|
||||
* Custom states for the class.
|
||||
*
|
||||
* @example
|
||||
* <code>
|
||||
* $package = Package::new();
|
||||
* $package->statusIs(Package::IDLE); // true
|
||||
* $package->boot();
|
||||
* $package->statusIs(Package::BOOTED); // true
|
||||
* </code>
|
||||
*/
|
||||
public const STATUS_IDLE = 2;
|
||||
public const STATUS_INITIALIZED = 4;
|
||||
public const STATUS_MODULES_ADDED = 5;
|
||||
public const STATUS_READY = 7;
|
||||
public const STATUS_BOOTED = 8;
|
||||
public const STATUS_FAILED = -8;
|
||||
|
||||
/**
|
||||
* Current state of the application.
|
||||
*
|
||||
* @see Package::STATUS_*
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $status = self::STATUS_IDLE;
|
||||
|
||||
/**
|
||||
* Contains the progress of all modules.
|
||||
*
|
||||
* @see Package::moduleProgress()
|
||||
*
|
||||
* @var array<string, list<string>>
|
||||
*/
|
||||
private $moduleStatus = [self::MODULES_ALL => []];
|
||||
|
||||
/**
|
||||
* Hashmap of where keys are names of connected packages, and values are boolean, true
|
||||
* if connection was successful.
|
||||
*
|
||||
* @see Package::connect()
|
||||
*
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $connectedPackages = [];
|
||||
|
||||
/**
|
||||
* @var list<ExecutableModule>
|
||||
*/
|
||||
private $executables = [];
|
||||
|
||||
/**
|
||||
* @var Properties
|
||||
*/
|
||||
private $properties;
|
||||
|
||||
/**
|
||||
* @var ContainerConfigurator
|
||||
*/
|
||||
private $containerConfigurator;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $built = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $hasContainer = false;
|
||||
|
||||
/**
|
||||
* @var \Throwable|null
|
||||
*/
|
||||
private $lastError = null;
|
||||
|
||||
/**
|
||||
* @param Properties $properties
|
||||
* @param ContainerInterface[] $containers
|
||||
*
|
||||
* @return Package
|
||||
*/
|
||||
public static function new(Properties $properties, ContainerInterface ...$containers): Package
|
||||
{
|
||||
return new self($properties, ...$containers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Properties $properties
|
||||
* @param ContainerInterface[] $containers
|
||||
*/
|
||||
private function __construct(Properties $properties, ContainerInterface ...$containers)
|
||||
{
|
||||
$this->properties = $properties;
|
||||
|
||||
$this->containerConfigurator = new ContainerConfigurator($containers);
|
||||
$this->containerConfigurator->addService(
|
||||
self::PROPERTIES,
|
||||
static function () use ($properties) {
|
||||
return $properties;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Module $module
|
||||
*
|
||||
* @return static
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function addModule(Module $module): Package
|
||||
{
|
||||
try {
|
||||
$this->assertStatus(self::STATUS_IDLE, sprintf('add module %s', $module->id()));
|
||||
|
||||
$registeredServices = $this->addModuleServices(
|
||||
$module,
|
||||
self::MODULE_REGISTERED
|
||||
);
|
||||
$registeredFactories = $this->addModuleServices(
|
||||
$module,
|
||||
self::MODULE_REGISTERED_FACTORIES
|
||||
);
|
||||
$extended = $this->addModuleServices(
|
||||
$module,
|
||||
self::MODULE_EXTENDED
|
||||
);
|
||||
$isExecutable = $module instanceof ExecutableModule;
|
||||
|
||||
// ExecutableModules are collected and executed on Package::boot()
|
||||
// when the Container is being compiled.
|
||||
if ($isExecutable) {
|
||||
/** @var ExecutableModule $module */
|
||||
$this->executables[] = $module;
|
||||
}
|
||||
|
||||
$added = $registeredServices || $registeredFactories || $extended || $isExecutable;
|
||||
$status = $added ? self::MODULE_ADDED : self::MODULE_NOT_ADDED;
|
||||
$this->moduleProgress($module->id(), $status);
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->handleFailure($throwable, self::ACTION_FAILED_BUILD);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Package $package
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function connect(Package $package): bool
|
||||
{
|
||||
try {
|
||||
if ($package === $this) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$packageName = $package->name();
|
||||
$errorData = ['package' => $packageName, 'status' => $this->status];
|
||||
$errorMessage = "Failed connecting package {$packageName}";
|
||||
|
||||
// Don't connect, if already connected
|
||||
if (array_key_exists($packageName, $this->connectedPackages)) {
|
||||
$error = "{$errorMessage} because it was already connected.";
|
||||
do_action(
|
||||
$this->hookName(self::ACTION_FAILED_CONNECTION),
|
||||
$packageName,
|
||||
new \WP_Error('already_connected', $error, $errorData)
|
||||
);
|
||||
|
||||
throw new \Exception($error, 0, $this->lastError);
|
||||
}
|
||||
|
||||
// Don't connect, if already booted or boot failed
|
||||
$failed = $this->statusIs(self::STATUS_FAILED);
|
||||
if ($failed || $this->statusIs(self::STATUS_BOOTED)) {
|
||||
$status = $failed ? 'errored' : 'booted';
|
||||
$error = "{$errorMessage} to a {$status} package.";
|
||||
do_action(
|
||||
$this->hookName(self::ACTION_FAILED_CONNECTION),
|
||||
$packageName,
|
||||
new \WP_Error("no_connect_on_{$status}", $error, $errorData)
|
||||
);
|
||||
|
||||
throw new \Exception($error, 0, $this->lastError);
|
||||
}
|
||||
|
||||
$this->connectedPackages[$packageName] = true;
|
||||
|
||||
// We put connected package's properties in this package's container, so that in modules
|
||||
// "run" method we can access them if we need to.
|
||||
$this->containerConfigurator->addService(
|
||||
sprintf('%s.%s', $package->name(), self::PROPERTIES),
|
||||
static function () use ($package): Properties {
|
||||
return $package->properties();
|
||||
}
|
||||
);
|
||||
|
||||
// If the other package is booted, we can obtain a container, otherwise
|
||||
// we build a proxy container
|
||||
$container = $package->statusIs(self::STATUS_BOOTED)
|
||||
? $package->container()
|
||||
: new PackageProxyContainer($package);
|
||||
|
||||
$this->containerConfigurator->addContainer($container);
|
||||
|
||||
do_action(
|
||||
$this->hookName(self::ACTION_PACKAGE_CONNECTED),
|
||||
$packageName,
|
||||
$this->status,
|
||||
$container instanceof PackageProxyContainer
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (\Throwable $throwable) {
|
||||
if (isset($packageName)) {
|
||||
$this->connectedPackages[$packageName] = false;
|
||||
}
|
||||
$this->handleFailure($throwable, self::ACTION_FAILED_BUILD);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function build(): Package
|
||||
{
|
||||
try {
|
||||
// Don't allow building the application multiple times.
|
||||
$this->assertStatus(self::STATUS_IDLE, 'build package');
|
||||
|
||||
do_action(
|
||||
$this->hookName(self::ACTION_INIT),
|
||||
$this
|
||||
);
|
||||
// Changing the status here ensures we can not call this method again, and also we can not
|
||||
// add new modules, because both this and `addModule()` methods check for idle status.
|
||||
// For backward compatibility, adding new modules via `boot()` will still be possible, even
|
||||
// if deprecated, at the condition that the container was not yet accessed at that point.
|
||||
$this->progress(self::STATUS_INITIALIZED);
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->handleFailure($throwable, self::ACTION_FAILED_BUILD);
|
||||
} finally {
|
||||
$this->built = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Module ...$defaultModules Deprecated, use `addModule()` to add default modules.
|
||||
* @return bool
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function boot(Module ...$defaultModules): bool
|
||||
{
|
||||
try {
|
||||
// Call build() if not called yet, and ensure any new module passed here is added
|
||||
// as well, throwing if the container was already built.
|
||||
$this->doBuild(...$defaultModules);
|
||||
|
||||
// Don't allow booting the application multiple times.
|
||||
$this->assertStatus(self::STATUS_MODULES_ADDED, 'boot application', '<');
|
||||
$this->assertStatus(self::STATUS_FAILED, 'boot application', '!=');
|
||||
|
||||
$this->progress(self::STATUS_MODULES_ADDED);
|
||||
|
||||
$this->doExecute();
|
||||
|
||||
$this->progress(self::STATUS_READY);
|
||||
|
||||
do_action(
|
||||
$this->hookName(self::ACTION_READY),
|
||||
$this
|
||||
);
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->handleFailure($throwable, self::ACTION_FAILED_BOOT);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->progress(self::STATUS_BOOTED);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Module ...$defaultModules
|
||||
* @return void
|
||||
*/
|
||||
private function doBuild(Module ...$defaultModules): void
|
||||
{
|
||||
if ($defaultModules) {
|
||||
$this->deprecatedArgument(
|
||||
sprintf(
|
||||
'Passing default modules to %1$s::boot() is deprecated since version 1.7.0.'
|
||||
. ' Please add modules via %1$s::addModule().',
|
||||
__CLASS__
|
||||
),
|
||||
__METHOD__,
|
||||
'1.7.0'
|
||||
);
|
||||
}
|
||||
|
||||
if (!$this->built) {
|
||||
array_map([$this, 'addModule'], $defaultModules);
|
||||
$this->build();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!$defaultModules
|
||||
|| ($this->status >= self::STATUS_MODULES_ADDED)
|
||||
|| ($this->statusIs(self::STATUS_FAILED))
|
||||
) {
|
||||
// if we don't have default modules, there's nothing to do, and if the status is beyond
|
||||
// "modules added" or is failed, we do nothing as well and let `boot()` throw.
|
||||
return;
|
||||
}
|
||||
|
||||
$backup = $this->status;
|
||||
|
||||
try {
|
||||
// simulate idle status to prevent `addModule()` from throwing
|
||||
// only if we don't have a container yet
|
||||
$this->hasContainer or $this->status = self::STATUS_IDLE;
|
||||
|
||||
foreach ($defaultModules as $defaultModule) {
|
||||
// If a module was added by `build()` or `addModule()` we can skip it, a
|
||||
// deprecation was trigger to make it noticeable without breakage
|
||||
if (!$this->moduleIs($defaultModule->id(), self::MODULE_ADDED)) {
|
||||
$this->addModule($defaultModule);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
$this->status = $backup;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Module $module
|
||||
* @param string $status
|
||||
* @return bool
|
||||
*/
|
||||
private function addModuleServices(Module $module, string $status): bool
|
||||
{
|
||||
$services = null;
|
||||
$addCallback = null;
|
||||
switch ($status) {
|
||||
case self::MODULE_REGISTERED:
|
||||
$services = $module instanceof ServiceModule ? $module->services() : null;
|
||||
$addCallback = [$this->containerConfigurator, 'addService'];
|
||||
break;
|
||||
case self::MODULE_REGISTERED_FACTORIES:
|
||||
$services = $module instanceof FactoryModule ? $module->factories() : null;
|
||||
$addCallback = [$this->containerConfigurator, 'addFactory'];
|
||||
break;
|
||||
case self::MODULE_EXTENDED:
|
||||
$services = $module instanceof ExtendingModule ? $module->extensions() : null;
|
||||
$addCallback = [$this->containerConfigurator, 'addExtension'];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$services) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
array_walk(
|
||||
$services,
|
||||
static function (callable $service, string $id) use ($addCallback, &$ids) {
|
||||
/** @var callable(string, callable) $addCallback */
|
||||
$addCallback($id, $service);
|
||||
/** @var list<string> $ids */
|
||||
$ids[] = $id;
|
||||
}
|
||||
);
|
||||
/** @var list<string> $ids */
|
||||
$this->moduleProgress($module->id(), $status, $ids);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
private function doExecute(): void
|
||||
{
|
||||
foreach ($this->executables as $executable) {
|
||||
$success = $executable->run($this->container());
|
||||
$this->moduleProgress(
|
||||
$executable->id(),
|
||||
$success
|
||||
? self::MODULE_EXECUTED
|
||||
: self::MODULE_EXECUTION_FAILED
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $moduleId
|
||||
* @param string $status
|
||||
* @param list<string>|null $serviceIds
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function moduleProgress(string $moduleId, string $status, ?array $serviceIds = null)
|
||||
{
|
||||
isset($this->moduleStatus[$status]) or $this->moduleStatus[$status] = [];
|
||||
$this->moduleStatus[$status][] = $moduleId;
|
||||
|
||||
if (!$serviceIds || !$this->properties->isDebug()) {
|
||||
$this->moduleStatus[self::MODULES_ALL][] = "{$moduleId} {$status}";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$description = sprintf('%s %s (%s)', $moduleId, $status, implode(', ', $serviceIds));
|
||||
$this->moduleStatus[self::MODULES_ALL][] = $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function modulesStatus(): array
|
||||
{
|
||||
return $this->moduleStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool>
|
||||
*/
|
||||
public function connectedPackages(): array
|
||||
{
|
||||
return $this->connectedPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return bool
|
||||
*/
|
||||
public function isPackageConnected(string $packageName): bool
|
||||
{
|
||||
return $this->connectedPackages[$packageName] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $moduleId
|
||||
* @param string $status
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function moduleIs(string $moduleId, string $status): bool
|
||||
{
|
||||
return in_array($moduleId, $this->moduleStatus[$status] ?? [], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filter name to be used to extend modules of the plugin.
|
||||
*
|
||||
* If the plugin is single file `my-plugin.php` in plugins folder the filter name will be:
|
||||
* `inpsyde.modularity.my-plugin`.
|
||||
*
|
||||
* If the plugin is in a sub-folder e.g. `my-plugin/index.php` the filter name will be:
|
||||
* `inpsyde.modularity.my-plugin` anyway, so the file name is not relevant.
|
||||
*
|
||||
* @param string $suffix
|
||||
*
|
||||
* @return string
|
||||
* @see Package::name()
|
||||
*
|
||||
*/
|
||||
public function hookName(string $suffix = ''): string
|
||||
{
|
||||
$filter = self::HOOK_PREFIX . $this->properties->baseName();
|
||||
|
||||
if ($suffix) {
|
||||
$filter .= '.' . $suffix;
|
||||
}
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Properties
|
||||
*/
|
||||
public function properties(): Properties
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ContainerInterface
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function container(): ContainerInterface
|
||||
{
|
||||
$this->assertStatus(self::STATUS_INITIALIZED, 'obtain the container instance', '>=');
|
||||
$this->hasContainer = true;
|
||||
|
||||
return $this->containerConfigurator->createReadOnlyContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->properties->baseName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $status
|
||||
*/
|
||||
private function progress(int $status): void
|
||||
{
|
||||
$this->status = $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $status
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function statusIs(int $status): bool
|
||||
{
|
||||
return $this->status === $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Throwable $throwable
|
||||
* @param Package::ACTION_FAILED_* $action
|
||||
* @return void
|
||||
* @throws \Throwable
|
||||
*/
|
||||
private function handleFailure(\Throwable $throwable, string $action): void
|
||||
{
|
||||
$this->progress(self::STATUS_FAILED);
|
||||
$hook = $this->hookName($action);
|
||||
did_action($hook) or do_action($hook, $throwable);
|
||||
|
||||
if ($this->properties->isDebug()) {
|
||||
throw $throwable;
|
||||
}
|
||||
|
||||
$this->lastError = $throwable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $status
|
||||
* @param string $action
|
||||
* @param string $operator
|
||||
*
|
||||
* @throws \Exception
|
||||
* @psalm-suppress ArgumentTypeCoercion
|
||||
*/
|
||||
private function assertStatus(int $status, string $action, string $operator = '=='): void
|
||||
{
|
||||
if (!version_compare((string) $this->status, (string) $status, $operator)) {
|
||||
throw new \Exception(
|
||||
sprintf("Can't %s at this point of application.", $action),
|
||||
0,
|
||||
$this->lastError
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to WP's `_deprecated_argument()`, but executes regardless of WP_DEBUG and without
|
||||
* translated message (so without attempting loading translation files).
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $function
|
||||
* @param string $version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function deprecatedArgument(string $message, string $function, string $version): void
|
||||
{
|
||||
do_action('deprecated_argument_run', $function, $message, $version);
|
||||
|
||||
if (apply_filters('deprecated_argument_trigger_error', true)) {
|
||||
trigger_error($message, \E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
}
|
217
lib/packages/Inpsyde/Modularity/Properties/BaseProperties.php
Normal file
217
lib/packages/Inpsyde/Modularity/Properties/BaseProperties.php
Normal file
|
@ -0,0 +1,217 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties;
|
||||
|
||||
class BaseProperties implements Properties
|
||||
{
|
||||
/**
|
||||
* @var null|bool
|
||||
*/
|
||||
protected $isDebug = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $baseName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $basePath;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $baseUrl;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $properties;
|
||||
|
||||
/**
|
||||
* @param string $baseName
|
||||
* @param string $basePath
|
||||
* @param string|null $baseUrl
|
||||
* @param array $properties
|
||||
*/
|
||||
protected function __construct(
|
||||
string $baseName,
|
||||
string $basePath,
|
||||
string $baseUrl = null,
|
||||
array $properties = []
|
||||
) {
|
||||
$baseName = $this->sanitizeBaseName($baseName);
|
||||
$basePath = (string) trailingslashit($basePath);
|
||||
if ($baseUrl) {
|
||||
$baseUrl = (string) trailingslashit($baseUrl);
|
||||
}
|
||||
|
||||
$this->baseName = $baseName;
|
||||
$this->basePath = $basePath;
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->properties = array_replace(Properties::DEFAULT_PROPERTIES, $properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function sanitizeBaseName(string $name): string
|
||||
{
|
||||
substr_count($name, '/') and $name = dirname($name);
|
||||
|
||||
return strtolower(pathinfo($name, PATHINFO_FILENAME));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function baseName(): string
|
||||
{
|
||||
return $this->baseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function basePath(): string
|
||||
{
|
||||
return $this->basePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function baseUrl(): ?string
|
||||
{
|
||||
return $this->baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function author(): string
|
||||
{
|
||||
return (string) $this->get(self::PROP_AUTHOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function authorUri(): string
|
||||
{
|
||||
return (string) $this->get(self::PROP_AUTHOR_URI);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return (string) $this->get(self::PROP_DESCRIPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function textDomain(): string
|
||||
{
|
||||
return (string) $this->get(self::PROP_TEXTDOMAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function domainPath(): string
|
||||
{
|
||||
return (string) $this->get(self::PROP_DOMAIN_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return (string) $this->get(self::PROP_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function uri(): string
|
||||
{
|
||||
return (string) $this->get(self::PROP_URI);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function version(): string
|
||||
{
|
||||
return (string) $this->get(self::PROP_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function requiresWp(): ?string
|
||||
{
|
||||
$value = $this->get(self::PROP_REQUIRES_WP);
|
||||
|
||||
return $value && is_string($value) ? $value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function requiresPhp(): ?string
|
||||
{
|
||||
$value = $this->get(self::PROP_REQUIRES_PHP);
|
||||
|
||||
return $value && is_string($value) ? $value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function tags(): array
|
||||
{
|
||||
return (array) $this->get(self::PROP_TAGS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key, $default = null)
|
||||
{
|
||||
return $this->properties[$key] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return isset($this->properties[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @see Properties::isDebug()
|
||||
*/
|
||||
public function isDebug(): bool
|
||||
{
|
||||
if ($this->isDebug === null) {
|
||||
$this->isDebug = defined('WP_DEBUG') && WP_DEBUG;
|
||||
}
|
||||
|
||||
return $this->isDebug;
|
||||
}
|
||||
}
|
209
lib/packages/Inpsyde/Modularity/Properties/LibraryProperties.php
Normal file
209
lib/packages/Inpsyde/Modularity/Properties/LibraryProperties.php
Normal file
|
@ -0,0 +1,209 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties;
|
||||
|
||||
/**
|
||||
* Class LibraryProperties
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties
|
||||
*/
|
||||
class LibraryProperties extends BaseProperties
|
||||
{
|
||||
/**
|
||||
* Allowed configuration in composer.json "extra.modularity".
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public const EXTRA_KEYS = [
|
||||
self::PROP_DOMAIN_PATH,
|
||||
self::PROP_NAME,
|
||||
self::PROP_TEXTDOMAIN,
|
||||
self::PROP_URI,
|
||||
self::PROP_VERSION,
|
||||
self::PROP_REQUIRES_WP,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $composerJsonFile
|
||||
* @param string|null $baseUrl
|
||||
*
|
||||
* @return LibraryProperties
|
||||
*
|
||||
* @throws \Exception
|
||||
* @psalm-suppress MixedArrayAccess
|
||||
*/
|
||||
public static function new(string $composerJsonFile, ?string $baseUrl = null): LibraryProperties
|
||||
{
|
||||
if (!\is_file($composerJsonFile) || !\is_readable($composerJsonFile)) {
|
||||
throw new \Exception("File {$composerJsonFile} does not exist or is not readable.");
|
||||
}
|
||||
|
||||
$content = (string) file_get_contents($composerJsonFile);
|
||||
/** @var array $composerJsonData */
|
||||
$composerJsonData = json_decode($content, true);
|
||||
|
||||
$properties = Properties::DEFAULT_PROPERTIES;
|
||||
$properties[self::PROP_DESCRIPTION] = $composerJsonData['description'] ?? '';
|
||||
$properties[self::PROP_TAGS] = $composerJsonData['keywords'] ?? [];
|
||||
|
||||
$authors = $composerJsonData['authors'] ?? [];
|
||||
$names = [];
|
||||
foreach ((array) $authors as $author) {
|
||||
$name = $author['name'] ?? null;
|
||||
if ($name && is_string($name)) {
|
||||
$names[] = $name;
|
||||
}
|
||||
$url = $author['homepage'] ?? null;
|
||||
if ($url && !$properties['authorUri'] && is_string($url)) {
|
||||
$properties[self::PROP_AUTHOR_URI] = $url;
|
||||
}
|
||||
}
|
||||
if (count($names) > 0) {
|
||||
$properties[self::PROP_AUTHOR] = implode(', ', $names);
|
||||
}
|
||||
|
||||
// Custom settings which can be stored in composer.json "extra.modularity"
|
||||
$extra = $composerJsonData['extra']['modularity'] ?? [];
|
||||
foreach (self::EXTRA_KEYS as $key) {
|
||||
$properties[$key] = $extra[$key] ?? '';
|
||||
}
|
||||
|
||||
// PHP requirement in composer.json "require" or "require-dev"
|
||||
$properties[self::PROP_REQUIRES_PHP] = self::extractPhpVersion($composerJsonData);
|
||||
|
||||
// composer.json might have "version" in root
|
||||
$version = $composerJsonData['version'] ?? null;
|
||||
if ($version && is_string($version)) {
|
||||
$properties[self::PROP_VERSION] = $version;
|
||||
}
|
||||
|
||||
[$baseName, $name] = static::buildNames($composerJsonData);
|
||||
$basePath = dirname($composerJsonFile);
|
||||
if (empty($properties[self::PROP_NAME])) {
|
||||
$properties[self::PROP_NAME] = $name;
|
||||
}
|
||||
|
||||
return new self(
|
||||
$baseName,
|
||||
$basePath,
|
||||
$baseUrl,
|
||||
$properties
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $composerJsonData
|
||||
*
|
||||
* @return array{string, string}
|
||||
*/
|
||||
private static function buildNames(array $composerJsonData): array
|
||||
{
|
||||
$composerName = (string) ($composerJsonData['name'] ?? '');
|
||||
$packageNamePieces = explode('/', $composerName, 2);
|
||||
$basename = implode('-', $packageNamePieces);
|
||||
// "inpsyde/foo-bar-baz" => "Inpsyde Foo Bar Baz"
|
||||
$name = mb_convert_case(
|
||||
str_replace(['-', '_', '.'], ' ', implode(' ', $packageNamePieces)),
|
||||
MB_CASE_TITLE
|
||||
);
|
||||
|
||||
return [$basename, $name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check PHP version in require, require-dev.
|
||||
*
|
||||
* Attempt to parse requirements to find the _minimum_ accepted version (consistent with WP).
|
||||
* Composer requirements are parsed in a way that, for example:
|
||||
* `>=7.2` returns `7.2`
|
||||
* `^7.3` returns `7.3`
|
||||
* `5.6 || >= 7.1` returns `5.6`
|
||||
* `>= 7.1 < 8` returns `7.1`
|
||||
*
|
||||
* @param array $composerData
|
||||
* @param string $key
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private static function extractPhpVersion(array $composerData, string $key = 'require'): ?string
|
||||
{
|
||||
$nextKey = ($key === 'require')
|
||||
? 'require-dev'
|
||||
: null;
|
||||
$base = (array) ($composerData[$key] ?? []);
|
||||
$requirement = $base['php'] ?? null;
|
||||
$version = ($requirement && is_string($requirement))
|
||||
? trim($requirement)
|
||||
: null;
|
||||
if (!$version) {
|
||||
return $nextKey
|
||||
? static::extractPhpVersion($composerData, $nextKey)
|
||||
: null;
|
||||
}
|
||||
|
||||
static $matcher;
|
||||
$matcher or $matcher = static function (string $version): ?string {
|
||||
$version = trim($version);
|
||||
if (!$version) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// versions range like `>= 7.2.4 < 8`
|
||||
if (preg_match('{>=?([\s0-9\.]+)<}', $version, $matches)) {
|
||||
return trim($matches[1], " \t\n\r\0\x0B.");
|
||||
}
|
||||
|
||||
// aliases like `dev-src#abcde as 7.4`
|
||||
if (preg_match('{as\s*([\s0-9\.]+)}', $version, $matches)) {
|
||||
return trim($matches[1], " \t\n\r\0\x0B.");
|
||||
}
|
||||
|
||||
// Basic requirements like 7.2, >=7.2, ^7.2, ~7.2
|
||||
if (preg_match('{^(?:[>=\s~\^]+)?([0-9\.]+)}', $version, $matches)) {
|
||||
return trim($matches[1], " \t\n\r\0\x0B.");
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// support for simpler requirements like `7.3`, `>=7.4` or alternative like `5.6 || >=7`
|
||||
|
||||
$alternatives = explode('||', $version);
|
||||
$found = null;
|
||||
foreach ($alternatives as $alternative) {
|
||||
/** @var callable(string):?string $matcher */
|
||||
$itemFound = $matcher($alternative);
|
||||
if ($itemFound && (!$found || version_compare($itemFound, $found, '<'))) {
|
||||
$found = $itemFound;
|
||||
}
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
return $found;
|
||||
}
|
||||
|
||||
return $nextKey
|
||||
? static::extractPhpVersion($composerData, $nextKey)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function withBaseUrl(string $url): LibraryProperties
|
||||
{
|
||||
if ($this->baseUrl !== null) {
|
||||
throw new \Exception(sprintf('%s::$baseUrl property is not overridable.', __CLASS__));
|
||||
}
|
||||
|
||||
$this->baseUrl = trailingslashit($url);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
176
lib/packages/Inpsyde/Modularity/Properties/PluginProperties.php
Normal file
176
lib/packages/Inpsyde/Modularity/Properties/PluginProperties.php
Normal file
|
@ -0,0 +1,176 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties;
|
||||
|
||||
/**
|
||||
* Class PluginProperties
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties
|
||||
*
|
||||
* @psalm-suppress PossiblyFalseArgument, InvalidArgument
|
||||
*/
|
||||
class PluginProperties extends BaseProperties
|
||||
{
|
||||
/**
|
||||
* Custom properties for Plugins.
|
||||
*/
|
||||
public const PROP_NETWORK = 'network';
|
||||
/**
|
||||
* Available methods of Properties::__call()
|
||||
* from plugin headers.
|
||||
*
|
||||
* @link https://developer.wordpress.org/reference/functions/get_plugin_data/
|
||||
*/
|
||||
protected const HEADERS = [
|
||||
self::PROP_AUTHOR => 'Author',
|
||||
self::PROP_AUTHOR_URI => 'AuthorURI',
|
||||
self::PROP_DESCRIPTION => 'Description',
|
||||
self::PROP_DOMAIN_PATH => 'DomainPath',
|
||||
self::PROP_NAME => 'Name',
|
||||
self::PROP_TEXTDOMAIN => 'TextDomain',
|
||||
self::PROP_URI => 'PluginURI',
|
||||
self::PROP_VERSION => 'Version',
|
||||
self::PROP_REQUIRES_WP => 'RequiresWP',
|
||||
self::PROP_REQUIRES_PHP => 'RequiresPHP',
|
||||
|
||||
// additional headers
|
||||
self::PROP_NETWORK => 'Network',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $pluginMainFile;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $pluginBaseName;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
protected $isMu;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
protected $isActive;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
protected $isNetworkActive;
|
||||
|
||||
/**
|
||||
* @param string $pluginMainFile
|
||||
*
|
||||
* @return PluginProperties
|
||||
*/
|
||||
public static function new(string $pluginMainFile): PluginProperties
|
||||
{
|
||||
return new self($pluginMainFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* PluginProperties constructor.
|
||||
*
|
||||
* @param string $pluginMainFile
|
||||
*/
|
||||
protected function __construct(string $pluginMainFile)
|
||||
{
|
||||
if (!function_exists('get_plugin_data')) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
|
||||
$pluginData = get_plugin_data($pluginMainFile);
|
||||
$properties = Properties::DEFAULT_PROPERTIES;
|
||||
|
||||
// Map pluginData to internal structure.
|
||||
foreach (self::HEADERS as $key => $pluginDataKey) {
|
||||
$properties[$key] = $pluginData[$pluginDataKey] ?? '';
|
||||
unset($pluginData[$pluginDataKey]);
|
||||
}
|
||||
$properties = array_merge($properties, $pluginData);
|
||||
|
||||
$this->pluginMainFile = wp_normalize_path($pluginMainFile);
|
||||
|
||||
$this->pluginBaseName = plugin_basename($pluginMainFile);
|
||||
$basePath = plugin_dir_path($pluginMainFile);
|
||||
$baseUrl = plugins_url('/', $pluginMainFile);
|
||||
|
||||
parent::__construct(
|
||||
$this->pluginBaseName,
|
||||
$basePath,
|
||||
$baseUrl,
|
||||
$properties
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function pluginMainFile(): string
|
||||
{
|
||||
return $this->pluginMainFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
* @psalm-suppress PossiblyFalseArgument
|
||||
*/
|
||||
public function network(): bool
|
||||
{
|
||||
return (bool) $this->get(self::PROP_NETWORK, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isActive(): bool
|
||||
{
|
||||
if ($this->isActive === null) {
|
||||
if (!function_exists('is_plugin_active')) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
$this->isActive = is_plugin_active($this->pluginBaseName);
|
||||
}
|
||||
|
||||
return $this->isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNetworkActive(): bool
|
||||
{
|
||||
if ($this->isNetworkActive === null) {
|
||||
if (!function_exists('is_plugin_active_for_network')) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
$this->isNetworkActive = is_plugin_active_for_network($this->pluginBaseName);
|
||||
}
|
||||
|
||||
return $this->isNetworkActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isMuPlugin(): bool
|
||||
{
|
||||
if ($this->isMu === null) {
|
||||
/**
|
||||
* @psalm-suppress UndefinedConstant
|
||||
* @psalm-suppress MixedArgument
|
||||
*/
|
||||
$muPluginDir = wp_normalize_path(WPMU_PLUGIN_DIR);
|
||||
$this->isMu = strpos($this->pluginMainFile, $muPluginDir) === 0;
|
||||
}
|
||||
|
||||
return $this->isMu;
|
||||
}
|
||||
}
|
139
lib/packages/Inpsyde/Modularity/Properties/Properties.php
Normal file
139
lib/packages/Inpsyde/Modularity/Properties/Properties.php
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties;
|
||||
|
||||
interface Properties
|
||||
{
|
||||
public const PROP_AUTHOR = 'author';
|
||||
public const PROP_AUTHOR_URI = 'authorUri';
|
||||
public const PROP_DESCRIPTION = 'description';
|
||||
public const PROP_DOMAIN_PATH = 'domainPath';
|
||||
public const PROP_NAME = 'name';
|
||||
public const PROP_TEXTDOMAIN = 'textDomain';
|
||||
public const PROP_URI = 'uri';
|
||||
public const PROP_VERSION = 'version';
|
||||
public const PROP_REQUIRES_WP = 'requiresWp';
|
||||
public const PROP_REQUIRES_PHP = 'requiresPhp';
|
||||
public const PROP_TAGS = 'tags';
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public const DEFAULT_PROPERTIES = [
|
||||
self::PROP_AUTHOR => '',
|
||||
self::PROP_AUTHOR_URI => '',
|
||||
self::PROP_DESCRIPTION => '',
|
||||
self::PROP_DOMAIN_PATH => '',
|
||||
self::PROP_NAME => '',
|
||||
self::PROP_TEXTDOMAIN => '',
|
||||
self::PROP_URI => '',
|
||||
self::PROP_VERSION => '',
|
||||
self::PROP_REQUIRES_WP => null,
|
||||
self::PROP_REQUIRES_PHP => null,
|
||||
self::PROP_TAGS => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param null $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key, $default = null);
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $key): bool;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isDebug(): bool;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function baseName(): string;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function basePath(): string;
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function baseUrl(): ?string;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function author(): string;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function authorUri(): string;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function description(): string;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function textDomain(): string;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function domainPath(): string;
|
||||
|
||||
/**
|
||||
* The name of the plugin, theme or library.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string;
|
||||
|
||||
/**
|
||||
* The home page of the plugin, theme or library.
|
||||
* @return string
|
||||
*/
|
||||
public function uri(): string;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function version(): string;
|
||||
|
||||
/**
|
||||
* Optional. Specify the minimum required WordPress version.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function requiresWp(): ?string;
|
||||
|
||||
/**
|
||||
* Optional. Specify the minimum required PHP version.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function requiresPhp(): ?string;
|
||||
|
||||
/**
|
||||
* Optional. Currently, only available for Theme and Library.
|
||||
* Plugins do not have support for "tags"/"keywords" in header.
|
||||
*
|
||||
* @link https://developer.wordpress.org/reference/classes/wp_theme/#properties
|
||||
* @link https://getcomposer.org/doc/04-schema.md#keywords
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function tags(): array;
|
||||
}
|
131
lib/packages/Inpsyde/Modularity/Properties/ThemeProperties.php
Normal file
131
lib/packages/Inpsyde/Modularity/Properties/ThemeProperties.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties;
|
||||
|
||||
/**
|
||||
* Class ThemeProperties
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Properties
|
||||
*
|
||||
* @psalm-suppress PossiblyFalseArgument, InvalidArgument
|
||||
*/
|
||||
class ThemeProperties extends BaseProperties
|
||||
{
|
||||
/**
|
||||
* Additional properties specific for themes.
|
||||
*/
|
||||
public const PROP_STATUS = 'status';
|
||||
public const PROP_TEMPLATE = 'template';
|
||||
/**
|
||||
* Available methods of Properties::__call()
|
||||
* from theme headers.
|
||||
*
|
||||
* @link https://developer.wordpress.org/reference/classes/wp_theme/
|
||||
*/
|
||||
protected const HEADERS = [
|
||||
self::PROP_AUTHOR => 'Author',
|
||||
self::PROP_AUTHOR_URI => 'AuthorURI',
|
||||
self::PROP_DESCRIPTION => 'Description',
|
||||
self::PROP_DOMAIN_PATH => 'DomainPath',
|
||||
self::PROP_NAME => 'Name',
|
||||
self::PROP_TEXTDOMAIN => 'TextDomain',
|
||||
self::PROP_URI => 'ThemeURI',
|
||||
self::PROP_VERSION => 'Version',
|
||||
self::PROP_REQUIRES_WP => 'RequiresWP',
|
||||
self::PROP_REQUIRES_PHP => 'RequiresPHP',
|
||||
|
||||
// additional headers
|
||||
self::PROP_STATUS => 'Status',
|
||||
self::PROP_TAGS => 'Tags',
|
||||
self::PROP_TEMPLATE => 'Template',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $themeDirectory
|
||||
*
|
||||
* @return ThemeProperties
|
||||
*/
|
||||
public static function new(string $themeDirectory): ThemeProperties
|
||||
{
|
||||
return new self($themeDirectory);
|
||||
}
|
||||
|
||||
/**
|
||||
* ThemeProperties constructor.
|
||||
*
|
||||
* @param string $themeDirectory
|
||||
*/
|
||||
protected function __construct(string $themeDirectory)
|
||||
{
|
||||
if (!function_exists('wp_get_theme')) {
|
||||
require_once ABSPATH . 'wp-includes/theme.php';
|
||||
}
|
||||
|
||||
$theme = wp_get_theme($themeDirectory);
|
||||
$properties = Properties::DEFAULT_PROPERTIES;
|
||||
|
||||
foreach (self::HEADERS as $key => $themeKey) {
|
||||
/** @psalm-suppress DocblockTypeContradiction */
|
||||
$properties[$key] = $theme->get($themeKey) ?? '';
|
||||
}
|
||||
|
||||
$baseName = $theme->get_stylesheet();
|
||||
$basePath = $theme->get_stylesheet_directory();
|
||||
$baseUrl = (string) trailingslashit($theme->get_stylesheet_directory_uri());
|
||||
|
||||
parent::__construct(
|
||||
$baseName,
|
||||
$basePath,
|
||||
$baseUrl,
|
||||
$properties
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the theme is published.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function status(): string
|
||||
{
|
||||
return (string) $this->get(self::PROP_STATUS);
|
||||
}
|
||||
|
||||
public function template(): string
|
||||
{
|
||||
return (string) $this->get(self::PROP_TEMPLATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isChildTheme(): bool
|
||||
{
|
||||
return (bool) $this->template();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isCurrentTheme(): bool
|
||||
{
|
||||
return get_stylesheet() === $this->baseName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ThemeProperties|null
|
||||
*/
|
||||
public function parentThemeProperties(): ?ThemeProperties
|
||||
{
|
||||
$template = $this->template();
|
||||
if (!$template) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parent = wp_get_theme($template, get_theme_root($template));
|
||||
|
||||
return static::new($parent->get_template_directory());
|
||||
}
|
||||
}
|
|
@ -1,7 +1,4 @@
|
|||
<?php
|
||||
/**
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
|
||||
*/
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Psr\Container;
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Psr\Container;
|
||||
|
||||
|
@ -20,7 +19,7 @@ interface ContainerInterface
|
|||
*
|
||||
* @return mixed Entry.
|
||||
*/
|
||||
public function get($id);
|
||||
public function get(string $id);
|
||||
|
||||
/**
|
||||
* Returns true if the container can return an entry for the given identifier.
|
||||
|
@ -33,5 +32,5 @@ interface ContainerInterface
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($id);
|
||||
public function has(string $id);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
<?php
|
||||
/**
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
|
||||
*/
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Vendor\Psr\Container;
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ return function ( string $root_dir ): iterable {
|
|||
( require "$modules_dir/ppcp-uninstall/module.php" )(),
|
||||
( require "$modules_dir/ppcp-blocks/module.php" )(),
|
||||
( require "$modules_dir/ppcp-paypal-subscriptions/module.php" )(),
|
||||
( require "$modules_dir/ppcp-local-alternative-payment-methods/module.php" )(),
|
||||
);
|
||||
// phpcs:disable WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||
|
||||
|
|
14
modules/ppcp-admin-notices/.babelrc
Normal file
14
modules/ppcp-admin-notices/.babelrc
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.25.0"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/preset-react"
|
||||
]
|
||||
]
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
"description": "Admin notices module for PPCP",
|
||||
"license": "GPL-2.0",
|
||||
"require": {
|
||||
"php": "^7.2 | ^8.0",
|
||||
"php": "^7.4 | ^8.0",
|
||||
"dhii/module-interface": "^0.3.0-alpha1"
|
||||
},
|
||||
"autoload": {
|
||||
|
|
|
@ -9,8 +9,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\AdminNotices;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
|
||||
return static function (): ModuleInterface {
|
||||
return static function (): AdminNotices {
|
||||
return new AdminNotices();
|
||||
};
|
||||
|
|
31
modules/ppcp-admin-notices/package.json
Normal file
31
modules/ppcp-admin-notices/package.json
Normal file
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"name": "ppcp-admin-notices",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"browserslist": [
|
||||
"> 0.5%",
|
||||
"Safari >= 8",
|
||||
"Chrome >= 41",
|
||||
"Firefox >= 43",
|
||||
"Edge >= 14"
|
||||
],
|
||||
"dependencies": {
|
||||
"core-js": "^3.25.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19",
|
||||
"@babel/preset-env": "^7.19",
|
||||
"babel-loader": "^8.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"sass": "^1.42.1",
|
||||
"sass-loader": "^12.1.0",
|
||||
"webpack": "^5.76",
|
||||
"webpack-cli": "^4.10"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env BABEL_ENV=default NODE_ENV=production webpack",
|
||||
"watch": "cross-env BABEL_ENV=default NODE_ENV=production webpack --watch",
|
||||
"dev": "cross-env BABEL_ENV=default webpack --watch"
|
||||
}
|
||||
}
|
10
modules/ppcp-admin-notices/resources/css/styles.scss
Normal file
10
modules/ppcp-admin-notices/resources/css/styles.scss
Normal file
|
@ -0,0 +1,10 @@
|
|||
.notice.is-dismissible {
|
||||
.spinner.doing-ajax {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
right: 0;
|
||||
top: 0;
|
||||
margin: 9px;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
137
modules/ppcp-admin-notices/resources/js/DismissibleMessage.js
Normal file
137
modules/ppcp-admin-notices/resources/js/DismissibleMessage.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
export default class DismissibleMessage {
|
||||
#notice = null;
|
||||
|
||||
#muteConfig = {};
|
||||
|
||||
#closeButton = null;
|
||||
|
||||
#msgId = '';
|
||||
|
||||
constructor( noticeElement, muteConfig ) {
|
||||
this.#notice = noticeElement;
|
||||
this.#muteConfig = muteConfig;
|
||||
this.#msgId = this.#notice.dataset.ppcpMsgId;
|
||||
|
||||
// Quick sanitation.
|
||||
if ( ! this.#muteConfig?.endpoint || ! this.#muteConfig?.nonce ) {
|
||||
console.error( 'Ajax config (Mute):', this.#muteConfig );
|
||||
throw new Error(
|
||||
'Invalid ajax configuration for DismissibleMessage. Nonce/Endpoint missing'
|
||||
);
|
||||
}
|
||||
if ( ! this.#msgId ) {
|
||||
console.error( 'Notice Element:', this.#notice );
|
||||
throw new Error(
|
||||
'Invalid notice element passed to DismissibleMessage. No MsgId defined'
|
||||
);
|
||||
}
|
||||
|
||||
this.onDismissClickProxy = this.onDismissClickProxy.bind( this );
|
||||
this.enableCloseButtons = this.enableCloseButtons.bind( this );
|
||||
this.disableCloseButtons = this.disableCloseButtons.bind( this );
|
||||
this.dismiss = this.dismiss.bind( this );
|
||||
|
||||
this.addEventListeners();
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.#msgId;
|
||||
}
|
||||
|
||||
get closeButton() {
|
||||
if ( ! this.#closeButton ) {
|
||||
this.#closeButton = this.#notice.querySelector(
|
||||
'button.notice-dismiss'
|
||||
);
|
||||
}
|
||||
|
||||
return this.#closeButton;
|
||||
}
|
||||
|
||||
addEventListeners() {
|
||||
this.#notice.addEventListener(
|
||||
'click',
|
||||
this.onDismissClickProxy,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
removeEventListeners() {
|
||||
this.#notice.removeEventListener(
|
||||
'click',
|
||||
this.onDismissClickProxy,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
onDismissClickProxy( event ) {
|
||||
if ( ! event.target?.matches( 'button.notice-dismiss' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.disableCloseButtons();
|
||||
this.muteMessage();
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
disableCloseButtons() {
|
||||
this.closeButton.setAttribute( 'disabled', 'disabled' );
|
||||
this.closeButton.style.pointerEvents = 'none';
|
||||
this.closeButton.style.opacity = 0;
|
||||
}
|
||||
|
||||
enableCloseButtons() {
|
||||
this.closeButton.removeAttribute( 'disabled', 'disabled' );
|
||||
this.closeButton.style.pointerEvents = '';
|
||||
this.closeButton.style.opacity = '';
|
||||
}
|
||||
|
||||
showSpinner() {
|
||||
const spinner = document.createElement( 'span' );
|
||||
spinner.classList.add( 'spinner', 'is-active', 'doing-ajax' );
|
||||
|
||||
this.#notice.appendChild( spinner );
|
||||
}
|
||||
|
||||
/**
|
||||
* Mute the message (on server side) and dismiss it (in browser).
|
||||
*/
|
||||
muteMessage() {
|
||||
this.#ajaxMuteMessage().then( this.dismiss );
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an ajax request that marks the message as "muted" on server side.
|
||||
*
|
||||
* @return {Promise<any>} Resolves after the ajax request is completed.
|
||||
*/
|
||||
#ajaxMuteMessage() {
|
||||
this.showSpinner();
|
||||
|
||||
const ajaxData = {
|
||||
id: this.id,
|
||||
nonce: this.#muteConfig.nonce,
|
||||
};
|
||||
|
||||
return fetch( this.#muteConfig.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify( ajaxData ),
|
||||
} ).then( ( response ) => response.json() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy to the original dismiss logic provided by WP core JS.
|
||||
*/
|
||||
dismiss() {
|
||||
this.removeEventListeners();
|
||||
this.enableCloseButtons();
|
||||
|
||||
this.closeButton.dispatchEvent( new Event( 'click' ) );
|
||||
}
|
||||
}
|
27
modules/ppcp-admin-notices/resources/js/boot-admin.js
Normal file
27
modules/ppcp-admin-notices/resources/js/boot-admin.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import DismissibleMessage from './DismissibleMessage';
|
||||
|
||||
class AdminMessageHandler {
|
||||
#config = {};
|
||||
|
||||
constructor( config ) {
|
||||
this.#config = config;
|
||||
this.setupDismissibleMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all mutable admin messages in the DOM and initializes them.
|
||||
*/
|
||||
setupDismissibleMessages() {
|
||||
const muteConfig = this.#config?.ajax?.mute_message;
|
||||
|
||||
const addDismissibleMessage = ( element ) => {
|
||||
new DismissibleMessage( element, muteConfig );
|
||||
};
|
||||
|
||||
document
|
||||
.querySelectorAll( '.notice[data-ppcp-msg-id]' )
|
||||
.forEach( addDismissibleMessage );
|
||||
}
|
||||
}
|
||||
|
||||
new AdminMessageHandler( window.wc_admin_notices );
|
|
@ -14,15 +14,33 @@ use WooCommerce\PayPalCommerce\AdminNotices\Renderer\Renderer;
|
|||
use WooCommerce\PayPalCommerce\AdminNotices\Renderer\RendererInterface;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Repository\RepositoryInterface;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Endpoint\MuteMessageEndpoint;
|
||||
|
||||
return array(
|
||||
'admin-notices.url' => static function ( ContainerInterface $container ): string {
|
||||
$path = realpath( __FILE__ );
|
||||
if ( false === $path ) {
|
||||
return '';
|
||||
}
|
||||
return plugins_url(
|
||||
'/modules/ppcp-admin-notices/',
|
||||
dirname( $path, 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
'admin-notices.renderer' => static function ( ContainerInterface $container ): RendererInterface {
|
||||
|
||||
$repository = $container->get( 'admin-notices.repository' );
|
||||
return new Renderer( $repository );
|
||||
return new Renderer(
|
||||
$container->get( 'admin-notices.repository' ),
|
||||
$container->get( 'admin-notices.url' ),
|
||||
$container->get( 'ppcp.asset-version' )
|
||||
);
|
||||
},
|
||||
'admin-notices.repository' => static function ( ContainerInterface $container ): RepositoryInterface {
|
||||
|
||||
return new Repository();
|
||||
},
|
||||
'admin-notices.mute-message-endpoint' => static function ( ContainerInterface $container ): MuteMessageEndpoint {
|
||||
return new MuteMessageEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'admin-notices.repository' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -9,36 +9,46 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\AdminNotices;
|
||||
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Endpoint\MuteMessageEndpoint;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Renderer\RendererInterface;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Entity\PersistentMessage;
|
||||
|
||||
/**
|
||||
* Class AdminNotices
|
||||
*/
|
||||
class AdminNotices implements ModuleInterface {
|
||||
class AdminNotices implements ServiceModule, ExtendingModule, ExecutableModule {
|
||||
use ModuleClassNameIdTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setup(): ServiceProviderInterface {
|
||||
return new ServiceProvider(
|
||||
require __DIR__ . '/../services.php',
|
||||
require __DIR__ . '/../extensions.php'
|
||||
);
|
||||
public function services(): array {
|
||||
return require __DIR__ . '/../services.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): void {
|
||||
public function extensions(): array {
|
||||
return require __DIR__ . '/../extensions.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): bool {
|
||||
$renderer = $c->get( 'admin-notices.renderer' );
|
||||
assert( $renderer instanceof RendererInterface );
|
||||
|
||||
add_action(
|
||||
'admin_notices',
|
||||
function() use ( $c ) {
|
||||
$renderer = $c->get( 'admin-notices.renderer' );
|
||||
function() use ( $renderer ) {
|
||||
$renderer->render();
|
||||
}
|
||||
);
|
||||
|
@ -70,13 +80,36 @@ class AdminNotices implements ModuleInterface {
|
|||
return $notices;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key for the module.
|
||||
*
|
||||
* @return string|void
|
||||
* Since admin notices are rendered after the initial `admin_enqueue_scripts`
|
||||
* action fires, we use the `admin_footer` hook to enqueue the optional assets
|
||||
* for admin-notices in the page footer.
|
||||
*/
|
||||
public function getKey() {
|
||||
add_action(
|
||||
'admin_footer',
|
||||
static function () use ( $renderer ) {
|
||||
$renderer->enqueue_admin();
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wp_ajax_' . MuteMessageEndpoint::ENDPOINT,
|
||||
static function () use ( $c ) {
|
||||
$endpoint = $c->get( 'admin-notices.mute-message-endpoint' );
|
||||
assert( $endpoint instanceof MuteMessageEndpoint );
|
||||
|
||||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_paypal_payments_uninstall',
|
||||
static function () {
|
||||
PersistentMessage::clear_all();
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
/**
|
||||
* Permanently mutes an admin notification for the current user.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\AdminNotices\Endpoint
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\AdminNotices\Endpoint;
|
||||
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Entity\PersistentMessage;
|
||||
|
||||
/**
|
||||
* Class MuteMessageEndpoint
|
||||
*/
|
||||
class MuteMessageEndpoint {
|
||||
const ENDPOINT = 'ppc-mute-message';
|
||||
|
||||
/**
|
||||
* The request data helper.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
private $request_data;
|
||||
|
||||
/**
|
||||
* Message repository to retrieve the message object to mute.
|
||||
*
|
||||
* @var Repository
|
||||
*/
|
||||
private $message_repository;
|
||||
|
||||
/**
|
||||
* UpdateShippingEndpoint constructor.
|
||||
*
|
||||
* @param RequestData $request_data The Request Data Helper.
|
||||
* @param Repository $message_repository Message repository, to access messages.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
Repository $message_repository
|
||||
) {
|
||||
$this->request_data = $request_data;
|
||||
$this->message_repository = $message_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function nonce() : string {
|
||||
return self::ENDPOINT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the request.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle_request() : void {
|
||||
try {
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
} catch ( RuntimeException $ex ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
$id = $data['id'] ?? '';
|
||||
if ( ! $id || ! is_string( $id ) ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a dummy message with the provided ID and mark it as muted.
|
||||
*
|
||||
* This helps to keep code cleaner and make the mute-endpoint more reliable,
|
||||
* as other modules do not need to register the PersistentMessage on every
|
||||
* ajax request.
|
||||
*/
|
||||
$message = new PersistentMessage( $id, '', '', '' );
|
||||
$message->mute();
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
* @package WooCommerce\PayPalCommerce\AdminNotices\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\AdminNotices\Entity;
|
||||
|
||||
|
@ -15,7 +15,7 @@ namespace WooCommerce\PayPalCommerce\AdminNotices\Entity;
|
|||
class Message {
|
||||
|
||||
/**
|
||||
* The messagte text.
|
||||
* The message text.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
|
@ -29,11 +29,11 @@ class Message {
|
|||
private $type;
|
||||
|
||||
/**
|
||||
* Whether the message is dismissable.
|
||||
* Whether the message is dismissible.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $dismissable;
|
||||
private $dismissible;
|
||||
|
||||
/**
|
||||
* The wrapper selector that will contain the notice.
|
||||
|
@ -47,13 +47,13 @@ class Message {
|
|||
*
|
||||
* @param string $message The message text.
|
||||
* @param string $type The message type.
|
||||
* @param bool $dismissable Whether the message is dismissable.
|
||||
* @param bool $dismissible Whether the message is dismissible.
|
||||
* @param string $wrapper The wrapper selector that will contain the notice.
|
||||
*/
|
||||
public function __construct( string $message, string $type, bool $dismissable = true, string $wrapper = '' ) {
|
||||
public function __construct( string $message, string $type, bool $dismissible = true, string $wrapper = '' ) {
|
||||
$this->type = $type;
|
||||
$this->message = $message;
|
||||
$this->dismissable = $dismissable;
|
||||
$this->dismissible = $dismissible;
|
||||
$this->wrapper = $wrapper;
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ class Message {
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function message(): string {
|
||||
public function message() : string {
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
|
@ -71,17 +71,17 @@ class Message {
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string {
|
||||
public function type() : string {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the message is dismissable.
|
||||
* Returns whether the message is dismissible.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dismissable(): bool {
|
||||
return $this->dismissable;
|
||||
public function is_dismissible() : bool {
|
||||
return $this->dismissible;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,21 +89,37 @@ class Message {
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function wrapper(): string {
|
||||
public function wrapper() : string {
|
||||
return $this->wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
* Returns the object as array, for serialization.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
public function to_array() : array {
|
||||
return array(
|
||||
'type' => $this->type,
|
||||
'message' => $this->message,
|
||||
'dismissable' => $this->dismissable,
|
||||
'dismissible' => $this->dismissible,
|
||||
'wrapper' => $this->wrapper,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a plain array to a full Message instance, during deserialization.
|
||||
*
|
||||
* @param array $data Data generated by `Message::to_array()`.
|
||||
*
|
||||
* @return Message
|
||||
*/
|
||||
public static function from_array( array $data ) : Message {
|
||||
return new Message(
|
||||
(string) ( $data['message'] ?? '' ),
|
||||
(string) ( $data['type'] ?? '' ),
|
||||
(bool) ( $data['dismissible'] ?? true ),
|
||||
(string) ( $data['wrapper'] ?? '' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
125
modules/ppcp-admin-notices/src/Entity/PersistentMessage.php
Normal file
125
modules/ppcp-admin-notices/src/Entity/PersistentMessage.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
/**
|
||||
* Extends the Message class to permanently dismiss notices for single users.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\AdminNotices\Entity
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\AdminNotices\Entity;
|
||||
|
||||
/**
|
||||
* Class PersistentMessage
|
||||
*/
|
||||
class PersistentMessage extends Message {
|
||||
|
||||
/**
|
||||
* Prefix for DB keys to store IDs of permanently muted notices.
|
||||
*/
|
||||
public const USER_META_PREFIX = '_ppcp_notice_';
|
||||
|
||||
/**
|
||||
* An internal ID to permanently dismiss the persistent message.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $message_id;
|
||||
|
||||
/**
|
||||
* Message constructor.
|
||||
*
|
||||
* @param string $id ID of this message, to allow permanent dismissal.
|
||||
* @param string $message The message text.
|
||||
* @param string $type The message type.
|
||||
* @param string $wrapper The wrapper selector that will contain the notice.
|
||||
*/
|
||||
public function __construct( string $id, string $message, string $type, string $wrapper = '' ) {
|
||||
parent::__construct( $message, $type, true, $wrapper );
|
||||
|
||||
$this->message_id = sanitize_key( $id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sanitized ID that identifies a permanently dismissible message.
|
||||
*
|
||||
* @param bool $with_db_prefix Whether to add the user-meta prefix.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id( bool $with_db_prefix = false ) : string {
|
||||
if ( ! $this->message_id ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $with_db_prefix ? self::USER_META_PREFIX . $this->message_id : $this->message_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function to_array() : array {
|
||||
$data = parent::to_array();
|
||||
$data['id'] = $this->message_id;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return PersistentMessage
|
||||
*/
|
||||
public static function from_array( array $data ) : Message {
|
||||
return new PersistentMessage(
|
||||
(string) ( $data['id'] ?? '' ),
|
||||
(string) ( $data['message'] ?? '' ),
|
||||
(string) ( $data['type'] ?? '' ),
|
||||
(string) ( $data['wrapper'] ?? '' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the message was permanently muted by the current user.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_muted() : bool {
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
if ( ! $this->message_id || ! $user_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 0 < (int) get_user_meta( $user_id, $this->id( true ), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the message as permanently muted by the current user.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function mute() : void {
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
if ( $this->message_id && $user_id && ! $this->is_muted() ) {
|
||||
update_user_meta( $user_id, $this->id( true ), time() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all user-meta flags for muted messages.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clear_all() : void {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"DELETE FROM $wpdb->usermeta WHERE meta_key LIKE %s",
|
||||
$wpdb->esc_like( self::USER_META_PREFIX ) . '%'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\AdminNotices\Renderer;
|
||||
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Repository\RepositoryInterface;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Endpoint\MuteMessageEndpoint;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Entity\PersistentMessage;
|
||||
|
||||
/**
|
||||
* Class Renderer
|
||||
|
@ -23,32 +25,123 @@ class Renderer implements RendererInterface {
|
|||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* Used to enqueue assets.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* Used to enqueue assets.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* Whether the current page contains at least one message that can be muted.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $can_mute_message = false;
|
||||
|
||||
/**
|
||||
* Renderer constructor.
|
||||
*
|
||||
* @param RepositoryInterface $repository The message repository.
|
||||
* @param string $module_url The module URL.
|
||||
* @param string $version The module version.
|
||||
*/
|
||||
public function __construct( RepositoryInterface $repository ) {
|
||||
public function __construct(
|
||||
RepositoryInterface $repository,
|
||||
string $module_url,
|
||||
string $version
|
||||
) {
|
||||
$this->repository = $repository;
|
||||
$this->module_url = untrailingslashit( $module_url );
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the current messages.
|
||||
*
|
||||
* @return bool
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function render(): bool {
|
||||
$messages = $this->repository->current_message();
|
||||
|
||||
foreach ( $messages as $message ) {
|
||||
$mute_message_id = '';
|
||||
|
||||
if ( $message instanceof PersistentMessage ) {
|
||||
$this->can_mute_message = true;
|
||||
|
||||
$mute_message_id = $message->id();
|
||||
}
|
||||
|
||||
printf(
|
||||
'<div class="notice notice-%s %s" %s><p>%s</p></div>',
|
||||
'<div class="notice notice-%s %s" %s%s><p>%s</p></div>',
|
||||
$message->type(),
|
||||
( $message->is_dismissable() ) ? 'is-dismissible' : '',
|
||||
( $message->is_dismissible() ) ? 'is-dismissible' : '',
|
||||
( $message->wrapper() ? sprintf( 'data-ppcp-wrapper="%s"', esc_attr( $message->wrapper() ) ) : '' ),
|
||||
// Use `empty()` in condition, to avoid false phpcs warning.
|
||||
( empty( $mute_message_id ) ? '' : sprintf( 'data-ppcp-msg-id="%s"', esc_attr( $mute_message_id ) ) ),
|
||||
wp_kses_post( $message->message() )
|
||||
);
|
||||
}
|
||||
|
||||
return (bool) count( $messages );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function enqueue_admin() : void {
|
||||
if ( ! $this->can_mute_message ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_register_style(
|
||||
'wc-ppcp-admin-notice',
|
||||
$this->module_url . '/assets/css/styles.css',
|
||||
array(),
|
||||
$this->version
|
||||
);
|
||||
wp_register_script(
|
||||
'wc-ppcp-admin-notice',
|
||||
$this->module_url . '/assets/js/boot-admin.js',
|
||||
array(),
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'wc-ppcp-admin-notice',
|
||||
'wc_admin_notices',
|
||||
$this->script_data_for_admin()
|
||||
);
|
||||
|
||||
wp_enqueue_style( 'wc-ppcp-admin-notice' );
|
||||
wp_enqueue_script( 'wc-ppcp-admin-notice' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Data to inject into the current admin page, which is required by JS assets.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function script_data_for_admin() : array {
|
||||
$ajax_url = admin_url( 'admin-ajax.php' );
|
||||
|
||||
return array(
|
||||
'ajax' => array(
|
||||
'mute_message' => array(
|
||||
'endpoint' => add_query_arg(
|
||||
array( 'action' => MuteMessageEndpoint::ENDPOINT ),
|
||||
$ajax_url
|
||||
),
|
||||
'nonce' => wp_create_nonce( MuteMessageEndpoint::nonce() ),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,11 @@ interface RendererInterface {
|
|||
* @return bool
|
||||
*/
|
||||
public function render(): bool;
|
||||
|
||||
/**
|
||||
* Enqueues common assets required for the admin notice behavior.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_admin() : void;
|
||||
}
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
* @package WooCommerce\PayPalCommerce\AdminNotices\Repository
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\AdminNotices\Repository;
|
||||
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Entity\PersistentMessage;
|
||||
|
||||
/**
|
||||
* Class Repository
|
||||
|
@ -20,11 +21,11 @@ class Repository implements RepositoryInterface {
|
|||
const PERSISTED_NOTICES_OPTION = 'woocommerce_ppcp-admin-notices';
|
||||
|
||||
/**
|
||||
* Returns the current messages.
|
||||
* Returns current messages to display, which excludes muted messages.
|
||||
*
|
||||
* @return Message[]
|
||||
*/
|
||||
public function current_message(): array {
|
||||
public function current_message() : array {
|
||||
return array_filter(
|
||||
/**
|
||||
* Returns the list of admin messages.
|
||||
|
@ -33,7 +34,11 @@ class Repository implements RepositoryInterface {
|
|||
self::NOTICES_FILTER,
|
||||
array()
|
||||
),
|
||||
function( $element ) : bool {
|
||||
function ( $element ) : bool {
|
||||
if ( $element instanceof PersistentMessage ) {
|
||||
return ! $element->is_muted();
|
||||
}
|
||||
|
||||
return is_a( $element, Message::class );
|
||||
}
|
||||
);
|
||||
|
@ -43,9 +48,10 @@ class Repository implements RepositoryInterface {
|
|||
* Adds a message to persist between page reloads.
|
||||
*
|
||||
* @param Message $message The message.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function persist( Message $message ): void {
|
||||
public function persist( Message $message ) : void {
|
||||
$persisted_notices = get_option( self::PERSISTED_NOTICES_OPTION ) ?: array();
|
||||
|
||||
$persisted_notices[] = $message->to_array();
|
||||
|
@ -58,20 +64,18 @@ class Repository implements RepositoryInterface {
|
|||
*
|
||||
* @return array|Message[]
|
||||
*/
|
||||
public function get_persisted_and_clear(): array {
|
||||
public function get_persisted_and_clear() : array {
|
||||
$notices = array();
|
||||
|
||||
$persisted_data = get_option( self::PERSISTED_NOTICES_OPTION ) ?: array();
|
||||
foreach ( $persisted_data as $notice_data ) {
|
||||
$notices[] = new Message(
|
||||
(string) ( $notice_data['message'] ?? '' ),
|
||||
(string) ( $notice_data['type'] ?? '' ),
|
||||
(bool) ( $notice_data['dismissable'] ?? true ),
|
||||
(string) ( $notice_data['wrapper'] ?? '' )
|
||||
);
|
||||
if ( is_array( $notice_data ) ) {
|
||||
$notices[] = Message::from_array( $notice_data );
|
||||
}
|
||||
}
|
||||
|
||||
update_option( self::PERSISTED_NOTICES_OPTION, array(), true );
|
||||
|
||||
return $notices;
|
||||
}
|
||||
}
|
||||
|
|
38
modules/ppcp-admin-notices/webpack.config.js
Normal file
38
modules/ppcp-admin-notices/webpack.config.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
const path = require( 'path' );
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
module.exports = {
|
||||
devtool: isProduction ? 'source-map' : 'eval-source-map',
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
target: 'web',
|
||||
entry: {
|
||||
'boot-admin': path.resolve( './resources/js/boot-admin.js' ),
|
||||
"styles": path.resolve('./resources/css/styles.scss')
|
||||
},
|
||||
output: {
|
||||
path: path.resolve( __dirname, 'assets/' ),
|
||||
filename: 'js/[name].js',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js?$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: 'css/[name].css',
|
||||
},
|
||||
},
|
||||
{ loader: 'sass-loader' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
2158
modules/ppcp-admin-notices/yarn.lock
Normal file
2158
modules/ppcp-admin-notices/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,7 @@
|
|||
"description": "API client module for PPCP",
|
||||
"license": "GPL-2.0",
|
||||
"require": {
|
||||
"php": "^7.2 | ^8.0",
|
||||
"php": "^7.4 | ^8.0",
|
||||
"dhii/module-interface": "^0.3.0-alpha1"
|
||||
},
|
||||
"autoload": {
|
||||
|
|
|
@ -9,8 +9,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
|
||||
return function (): ModuleInterface {
|
||||
return function (): ApiModule {
|
||||
return new ApiModule();
|
||||
};
|
||||
|
|
|
@ -9,8 +9,10 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\ClientCredentials;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\SdkClientToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\Orders;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentMethodTokensEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\CardAuthenticationResult;
|
||||
|
@ -239,6 +241,13 @@ return array(
|
|||
$bn_code
|
||||
);
|
||||
},
|
||||
'api.endpoint.orders' => static function ( ContainerInterface $container ): Orders {
|
||||
return new Orders(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.billing-agreements' => static function ( ContainerInterface $container ): BillingAgreementsEndpoint {
|
||||
return new BillingAgreementsEndpoint(
|
||||
$container->get( 'api.host' ),
|
||||
|
@ -1655,18 +1664,27 @@ return array(
|
|||
return new PurchaseUnitSanitizer( $behavior, $line_name );
|
||||
}
|
||||
),
|
||||
'api.client-credentials' => static function( ContainerInterface $container ): ClientCredentials {
|
||||
return new ClientCredentials(
|
||||
$container->get( 'wcgateway.settings' )
|
||||
);
|
||||
},
|
||||
'api.client-credentials-cache' => static function( ContainerInterface $container ): Cache {
|
||||
return new Cache( 'ppcp-client-credentials-cache' );
|
||||
},
|
||||
'api.user-id-token' => static function( ContainerInterface $container ): UserIdToken {
|
||||
return new UserIdToken(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'api.client-credentials' )
|
||||
);
|
||||
},
|
||||
'api.sdk-client-token' => static function( ContainerInterface $container ): SdkClientToken {
|
||||
return new SdkClientToken(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'api.client-credentials' ),
|
||||
$container->get( 'api.client-credentials-cache' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -10,33 +10,41 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\ApiClient;
|
||||
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
|
||||
/**
|
||||
* Class ApiModule
|
||||
*/
|
||||
class ApiModule implements ModuleInterface {
|
||||
class ApiModule implements ServiceModule, ExtendingModule, ExecutableModule {
|
||||
use ModuleClassNameIdTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setup(): ServiceProviderInterface {
|
||||
return new ServiceProvider(
|
||||
require __DIR__ . '/../services.php',
|
||||
require __DIR__ . '/../extensions.php'
|
||||
);
|
||||
public function services(): array {
|
||||
return require __DIR__ . '/../services.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): void {
|
||||
public function extensions(): array {
|
||||
return require __DIR__ . '/../extensions.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): bool {
|
||||
add_action(
|
||||
'woocommerce_after_calculate_totals',
|
||||
function ( \WC_Cart $cart ) {
|
||||
|
@ -94,13 +102,7 @@ class ApiModule implements ModuleInterface {
|
|||
10,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key for the module.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function getKey() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
/**
|
||||
* The client credentials.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Authentication
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Authentication;
|
||||
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class ClientCredentials
|
||||
*/
|
||||
class ClientCredentials {
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* ClientCredentials constructor.
|
||||
*
|
||||
* @param Settings $settings The settings.
|
||||
*/
|
||||
public function __construct( Settings $settings ) {
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns encoded client credentials.
|
||||
*
|
||||
* @return string
|
||||
* @throws NotFoundException If setting does not found.
|
||||
*/
|
||||
public function credentials(): string {
|
||||
$client_id = $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : '';
|
||||
$client_secret = $this->settings->has( 'client_secret' ) ? $this->settings->get( 'client_secret' ) : '';
|
||||
|
||||
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
|
||||
return 'Basic ' . base64_encode( $client_id . ':' . $client_secret );
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ use Psr\Log\LoggerInterface;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
|
@ -20,6 +21,8 @@ class SdkClientToken {
|
|||
|
||||
use RequestTrait;
|
||||
|
||||
const CACHE_KEY = 'sdk-client-token-key';
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
|
@ -27,13 +30,6 @@ class SdkClientToken {
|
|||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
|
@ -41,35 +37,52 @@ class SdkClientToken {
|
|||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* The client credentials.
|
||||
*
|
||||
* @var ClientCredentials
|
||||
*/
|
||||
private $client_credentials;
|
||||
|
||||
/**
|
||||
* The cache.
|
||||
*
|
||||
* @var Cache
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* SdkClientToken constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param ClientCredentials $client_credentials The client credentials.
|
||||
* @param Cache $cache The cache.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
LoggerInterface $logger
|
||||
LoggerInterface $logger,
|
||||
ClientCredentials $client_credentials,
|
||||
Cache $cache
|
||||
) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
$this->client_credentials = $client_credentials;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `sdk_client_token` which uniquely identifies the payer.
|
||||
*
|
||||
* @param string $target_customer_id Vaulted customer id.
|
||||
* Returns the client token for SDK `data-sdk-client-token`.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws PayPalApiException If the request fails.
|
||||
* @throws RuntimeException If something unexpected happens.
|
||||
*/
|
||||
public function sdk_client_token( string $target_customer_id = '' ): string {
|
||||
$bearer = $this->bearer->bearer();
|
||||
public function sdk_client_token(): string {
|
||||
if ( $this->cache->has( self::CACHE_KEY ) ) {
|
||||
return $this->cache->get( self::CACHE_KEY );
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
$domain = wp_unslash( $_SERVER['HTTP_HOST'] ?? '' );
|
||||
|
@ -77,19 +90,10 @@ class SdkClientToken {
|
|||
|
||||
$url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=client_token&intent=sdk_init&domains[]=' . $domain;
|
||||
|
||||
if ( $target_customer_id ) {
|
||||
$url = add_query_arg(
|
||||
array(
|
||||
'target_customer_id' => $target_customer_id,
|
||||
),
|
||||
$url
|
||||
);
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Authorization' => $this->client_credentials->credentials(),
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
),
|
||||
);
|
||||
|
@ -105,6 +109,11 @@ class SdkClientToken {
|
|||
throw new PayPalApiException( $json, $status_code );
|
||||
}
|
||||
|
||||
return $json->access_token;
|
||||
$access_token = $json->access_token;
|
||||
$expires_in = (int) $json->expires_in;
|
||||
|
||||
$this->cache->set( self::CACHE_KEY, $access_token, $expires_in );
|
||||
|
||||
return $access_token;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,13 +27,6 @@ class UserIdToken {
|
|||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
|
@ -41,21 +34,28 @@ class UserIdToken {
|
|||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* The client credentials.
|
||||
*
|
||||
* @var ClientCredentials
|
||||
*/
|
||||
private $client_credentials;
|
||||
|
||||
/**
|
||||
* UserIdToken constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param ClientCredentials $client_credentials The client credentials.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
LoggerInterface $logger
|
||||
LoggerInterface $logger,
|
||||
ClientCredentials $client_credentials
|
||||
) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
$this->client_credentials = $client_credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,8 +69,6 @@ class UserIdToken {
|
|||
* @throws RuntimeException If something unexpected happens.
|
||||
*/
|
||||
public function id_token( string $target_customer_id = '' ): string {
|
||||
$bearer = $this->bearer->bearer();
|
||||
|
||||
$url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token';
|
||||
if ( $target_customer_id ) {
|
||||
$url = add_query_arg(
|
||||
|
@ -84,7 +82,7 @@ class UserIdToken {
|
|||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Authorization' => $this->client_credentials->credentials(),
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
),
|
||||
);
|
||||
|
|
206
modules/ppcp-api-client/src/Endpoint/Orders.php
Normal file
206
modules/ppcp-api-client/src/Endpoint/Orders.php
Normal file
|
@ -0,0 +1,206 @@
|
|||
<?php
|
||||
/**
|
||||
* Orders API endpoints.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
* @link https://developer.paypal.com/docs/api/orders/v2/ Orders API documentation.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class Orders
|
||||
*/
|
||||
class Orders {
|
||||
|
||||
use RequestTrait;
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* Orders constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PayPal order.
|
||||
*
|
||||
* @param array $request_body The request body.
|
||||
* @param array $headers The request headers.
|
||||
* @return array
|
||||
* @throws RuntimeException If something went wrong with the request.
|
||||
* @throws PayPalApiException If something went wrong with the PayPal API request.
|
||||
*/
|
||||
public function create( array $request_body, array $headers = array() ): array {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v2/checkout/orders';
|
||||
|
||||
$default_headers = array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'PayPal-Request-Id' => uniqid( 'ppcp-', true ),
|
||||
);
|
||||
$headers = array_merge(
|
||||
$default_headers,
|
||||
$headers
|
||||
);
|
||||
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => $headers,
|
||||
'body' => wp_json_encode( $request_body ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( $response instanceof WP_Error ) {
|
||||
throw new RuntimeException( $response->get_error_message() );
|
||||
}
|
||||
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( ! in_array( $status_code, array( 200, 201 ), true ) ) {
|
||||
$body = json_decode( $response['body'] );
|
||||
|
||||
$message = $body->details[0]->description ?? '';
|
||||
if ( $message ) {
|
||||
throw new RuntimeException( $message );
|
||||
}
|
||||
|
||||
throw new PayPalApiException(
|
||||
$body,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms the given order.
|
||||
*
|
||||
* @link https://developer.paypal.com/docs/api/orders/v2/#orders_confirm
|
||||
*
|
||||
* @param array $request_body The request body.
|
||||
* @param string $id PayPal order ID.
|
||||
* @return array
|
||||
* @throws RuntimeException If something went wrong with the request.
|
||||
* @throws PayPalApiException If something went wrong with the PayPal API request.
|
||||
*/
|
||||
public function confirm_payment_source( array $request_body, string $id ): array {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $id . '/confirm-payment-source';
|
||||
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'PayPal-Request-Id' => uniqid( 'ppcp-', true ),
|
||||
),
|
||||
'body' => wp_json_encode( $request_body ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( $response instanceof WP_Error ) {
|
||||
throw new RuntimeException( $response->get_error_message() );
|
||||
}
|
||||
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( $status_code !== 200 ) {
|
||||
$body = json_decode( $response['body'] );
|
||||
|
||||
$message = $body->details[0]->description ?? '';
|
||||
if ( $message ) {
|
||||
throw new RuntimeException( $message );
|
||||
}
|
||||
|
||||
throw new PayPalApiException(
|
||||
$body,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PayPal order by id.
|
||||
*
|
||||
* @param string $id PayPal order ID.
|
||||
* @return array
|
||||
* @throws RuntimeException If something went wrong with the request.
|
||||
* @throws PayPalApiException If something went wrong with the PayPal API request.
|
||||
*/
|
||||
public function order( string $id ): array {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $id;
|
||||
|
||||
$args = array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'PayPal-Request-Id' => uniqid( 'ppcp-', true ),
|
||||
),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( $response instanceof WP_Error ) {
|
||||
throw new RuntimeException( $response->get_error_message() );
|
||||
}
|
||||
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( $status_code !== 200 ) {
|
||||
$body = json_decode( $response['body'] );
|
||||
|
||||
$message = $body->details[0]->description ?? '';
|
||||
if ( $message ) {
|
||||
throw new RuntimeException( $message );
|
||||
}
|
||||
|
||||
throw new PayPalApiException(
|
||||
$body,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
83
modules/ppcp-applepay/assets/images/applepay.svg
Normal file
83
modules/ppcp-applepay/assets/images/applepay.svg
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="78.102px" height="50px" viewBox="-5 -5 175.52107 115.9651" enable-background="new 0 0 165.52107 105.9651"
|
||||
xml:space="preserve">
|
||||
<g>
|
||||
<path id="XMLID_4_" d="M150.69807,0H14.82318c-0.5659,0-1.1328,0-1.69769,0.0033c-0.47751,0.0034-0.95391,0.0087-1.43031,0.0217
|
||||
c-1.039,0.0281-2.0869,0.0894-3.1129,0.2738c-1.0424,0.1876-2.0124,0.4936-2.9587,0.9754
|
||||
c-0.9303,0.4731-1.782,1.0919-2.52009,1.8303c-0.73841,0.7384-1.35721,1.5887-1.83021,2.52
|
||||
c-0.4819,0.9463-0.7881,1.9166-0.9744,2.9598c-0.18539,1.0263-0.2471,2.074-0.2751,3.1119
|
||||
c-0.0128,0.4764-0.01829,0.9528-0.0214,1.4291c-0.0033,0.5661-0.0022,1.1318-0.0022,1.6989V91.142
|
||||
c0,0.5671-0.0011,1.13181,0.0022,1.69901c0.00311,0.4763,0.0086,0.9527,0.0214,1.4291
|
||||
c0.028,1.03699,0.08971,2.08469,0.2751,3.11069c0.1863,1.0436,0.4925,2.0135,0.9744,2.9599
|
||||
c0.473,0.9313,1.0918,1.7827,1.83021,2.52c0.73809,0.7396,1.58979,1.3583,2.52009,1.8302
|
||||
c0.9463,0.4831,1.9163,0.7892,2.9587,0.9767c1.026,0.1832,2.0739,0.2456,3.1129,0.2737c0.4764,0.0108,0.9528,0.0172,1.43031,0.0194
|
||||
c0.56489,0.0044,1.13179,0.0044,1.69769,0.0044h135.87489c0.5649,0,1.13181,0,1.69659-0.0044
|
||||
c0.47641-0.0022,0.95282-0.0086,1.4314-0.0194c1.0368-0.0281,2.0845-0.0905,3.11301-0.2737
|
||||
c1.041-0.1875,2.0112-0.4936,2.9576-0.9767c0.9313-0.4719,1.7805-1.0906,2.52011-1.8302c0.7372-0.7373,1.35599-1.5887,1.8302-2.52
|
||||
c0.48299-0.9464,0.78889-1.9163,0.97429-2.9599c0.1855-1.026,0.2457-2.0737,0.2738-3.11069
|
||||
c0.013-0.4764,0.01941-0.9528,0.02161-1.4291c0.00439-0.5672,0.00439-1.1319,0.00439-1.69901V14.8242
|
||||
c0-0.5671,0-1.1328-0.00439-1.6989c-0.0022-0.4763-0.00861-0.9527-0.02161-1.4291c-0.02811-1.0379-0.0883-2.0856-0.2738-3.1119
|
||||
c-0.18539-1.0432-0.4913-2.0135-0.97429-2.9598c-0.47421-0.9313-1.093-1.7816-1.8302-2.52
|
||||
c-0.73961-0.7384-1.58881-1.3572-2.52011-1.8303c-0.9464-0.4818-1.9166-0.7878-2.9576-0.9754
|
||||
c-1.0285-0.1844-2.0762-0.2457-3.11301-0.2738c-0.47858-0.013-0.95499-0.0183-1.4314-0.0217C151.82988,0,151.26297,0,150.69807,0
|
||||
L150.69807,0z"/>
|
||||
<path id="XMLID_3_" fill="#FFFFFF" d="M150.69807,3.532l1.67149,0.0032c0.4528,0.0032,0.90561,0.0081,1.36092,0.0205
|
||||
c0.79201,0.0214,1.71849,0.0643,2.58209,0.2191c0.7507,0.1352,1.38029,0.3408,1.9845,0.6484
|
||||
c0.5965,0.3031,1.14301,0.7003,1.62019,1.1768c0.479,0.4797,0.87671,1.0271,1.18381,1.6302
|
||||
c0.30589,0.5995,0.51019,1.2261,0.64459,1.9823c0.1544,0.8542,0.1971,1.7832,0.21881,2.5801
|
||||
c0.01219,0.4498,0.01819,0.8996,0.0204,1.3601c0.00429,0.5569,0.0042,1.1135,0.0042,1.6715V91.142
|
||||
c0,0.558,0.00009,1.1136-0.0043,1.6824c-0.00211,0.4497-0.0081,0.8995-0.0204,1.3501c-0.02161,0.7957-0.0643,1.7242-0.2206,2.5885
|
||||
c-0.13251,0.7458-0.3367,1.3725-0.64429,1.975c-0.30621,0.6016-0.70331,1.1484-1.18022,1.6251
|
||||
c-0.47989,0.48-1.0246,0.876-1.62819,1.1819c-0.5997,0.3061-1.22821,0.51151-1.97151,0.6453
|
||||
c-0.88109,0.157-1.84639,0.2002-2.57339,0.2199c-0.4574,0.0103-0.9126,0.01649-1.37889,0.0187
|
||||
c-0.55571,0.0043-1.1134,0.0042-1.6692,0.0042H14.82318c-0.0074,0-0.0146,0-0.0221,0c-0.5494,0-1.0999,0-1.6593-0.0043
|
||||
c-0.4561-0.00211-0.9112-0.0082-1.3512-0.0182c-0.7436-0.0201-1.7095-0.0632-2.5834-0.2193
|
||||
c-0.74969-0.1348-1.3782-0.3402-1.9858-0.6503c-0.59789-0.3032-1.1422-0.6988-1.6223-1.1797
|
||||
c-0.4764-0.4756-0.8723-1.0207-1.1784-1.6232c-0.3064-0.6019-0.5114-1.2305-0.64619-1.9852
|
||||
c-0.15581-0.8626-0.19861-1.7874-0.22-2.5777c-0.01221-0.4525-0.01731-0.9049-0.02021-1.3547l-0.0022-1.3279l0.0001-0.3506V14.8242
|
||||
l-0.0001-0.3506l0.0021-1.3251c0.003-0.4525,0.0081-0.9049,0.02031-1.357c0.02139-0.7911,0.06419-1.7163,0.22129-2.5861
|
||||
c0.1336-0.7479,0.3385-1.3765,0.6465-1.9814c0.3037-0.5979,0.7003-1.1437,1.17921-1.6225
|
||||
c0.477-0.4772,1.02309-0.8739,1.62479-1.1799c0.6011-0.3061,1.2308-0.5116,1.9805-0.6465c0.8638-0.1552,1.7909-0.198,2.5849-0.2195
|
||||
c0.4526-0.0123,0.9052-0.0172,1.3544-0.0203l1.6771-0.0033H150.69807"/>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M45.1862,35.64053c1.41724-1.77266,2.37897-4.15282,2.12532-6.58506c-2.07464,0.10316-4.60634,1.36871-6.07207,3.14276
|
||||
c-1.31607,1.5192-2.4809,3.99902-2.17723,6.3293C41.39111,38.72954,43.71785,37.36345,45.1862,35.64053"/>
|
||||
<path d="M47.28506,38.98252c-3.38211-0.20146-6.25773,1.91951-7.87286,1.91951c-1.61602,0-4.08931-1.81799-6.76438-1.76899
|
||||
c-3.48177,0.05114-6.71245,2.01976-8.4793,5.15079c-3.63411,6.2636-0.95904,15.55471,2.57494,20.65606
|
||||
c1.71618,2.5238,3.78447,5.30269,6.50976,5.20287c2.57494-0.10104,3.58421-1.66732,6.71416-1.66732
|
||||
c3.12765,0,4.03679,1.66732,6.76252,1.61681c2.82665-0.05054,4.59381-2.52506,6.30997-5.05132
|
||||
c1.96878-2.877,2.77473-5.65498,2.82542-5.80748c-0.0507-0.05051-5.45058-2.12204-5.50065-8.33358
|
||||
c-0.05098-5.20101,4.23951-7.6749,4.44144-7.82832C52.3832,39.4881,48.5975,39.08404,47.28506,38.98252"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M76.73385,31.94381c7.35096,0,12.4697,5.06708,12.4697,12.44437c0,7.40363-5.22407,12.49704-12.65403,12.49704h-8.13892
|
||||
v12.94318h-5.88037v-37.8846H76.73385z M68.41059,51.9493h6.74732c5.11975,0,8.0336-2.75636,8.0336-7.53479
|
||||
c0-4.77792-2.91385-7.50845-8.00727-7.50845h-6.77365V51.9493z"/>
|
||||
<path d="M90.73997,61.97864c0-4.8311,3.70182-7.79761,10.26583-8.16526l7.56061-0.44614v-2.12639
|
||||
c0-3.07185-2.07423-4.90959-5.53905-4.90959c-3.28251,0-5.33041,1.57492-5.82871,4.04313h-5.35574
|
||||
c0.31499-4.98859,4.56777-8.66407,11.3941-8.66407c6.69466,0,10.97377,3.54432,10.97377,9.08388v19.03421h-5.43472v-4.54194
|
||||
h-0.13065c-1.60125,3.07185-5.09341,5.01441-8.71623,5.01441C94.52078,70.30088,90.73997,66.94038,90.73997,61.97864z
|
||||
M108.56641,59.4846v-2.17905l-6.8,0.41981c-3.38683,0.23649-5.30306,1.73291-5.30306,4.09579
|
||||
c0,2.41504,1.99523,3.99046,5.04075,3.99046C105.46823,65.81161,108.56641,63.08108,108.56641,59.4846z"/>
|
||||
<path d="M119.34167,79.9889v-4.5946c0.4193,0.10483,1.36425,0.10483,1.83723,0.10483c2.6252,0,4.04313-1.10245,4.90908-3.9378
|
||||
c0-0.05267,0.49931-1.68025,0.49931-1.70658l-9.97616-27.64562h6.14268l6.98432,22.47371h0.10432l6.98433-22.47371h5.9857
|
||||
l-10.34483,29.06304c-2.36186,6.69517-5.0924,8.84789-10.81577,8.84789C121.17891,80.12006,119.76098,80.06739,119.34167,79.9889
|
||||
z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.1 KiB |
|
@ -4,7 +4,7 @@
|
|||
"description": "Applepay module for PPCP",
|
||||
"license": "GPL-2.0",
|
||||
"require": {
|
||||
"php": "^7.2 | ^8.0",
|
||||
"php": "^7.4 | ^8.0",
|
||||
"dhii/module-interface": "^0.3.0-alpha1"
|
||||
},
|
||||
"autoload": {
|
||||
|
|
|
@ -18,7 +18,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
|||
|
||||
|
||||
return array(
|
||||
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
||||
'wcgateway.settings.fields' => function ( array $fields, ContainerInterface $container ): array {
|
||||
|
||||
// Used in various places to mark fields for the preview button.
|
||||
$apm_name = 'ApplePay';
|
||||
|
@ -101,7 +101,7 @@ return array(
|
|||
'applepay_button_enabled' => array(
|
||||
'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ),
|
||||
'title_html' => sprintf(
|
||||
'<img src="%sassets/images/applepay.png" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
'<img src="%sassets/images/applepay.svg" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
$module_url,
|
||||
__( 'Apple Pay', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
|
@ -155,7 +155,7 @@ return array(
|
|||
'applepay_button_enabled' => array(
|
||||
'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ),
|
||||
'title_html' => sprintf(
|
||||
'<img src="%sassets/images/applepay.png" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
'<img src="%sassets/images/applepay.svg" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
$module_url,
|
||||
__( 'Apple Pay', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
|
@ -269,7 +269,7 @@ return array(
|
|||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'class' => array(),
|
||||
'input_class' => array( 'wc-enhanced-select' ),
|
||||
'default' => 'pay',
|
||||
'default' => 'plain',
|
||||
'options' => PropertiesDictionary::button_types(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'dcc',
|
||||
|
|
|
@ -9,8 +9,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Applepay;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
|
||||
return static function (): ModuleInterface {
|
||||
return static function (): ApplepayModule {
|
||||
return new ApplepayModule();
|
||||
};
|
||||
|
|
|
@ -57,3 +57,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ppc-button-ppcp-applepay {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/* global ApplePaySession */
|
||||
/* global PayPalCommerceGateway */
|
||||
|
||||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||
import { createAppleErrors } from './Helper/applePayError';
|
||||
import { setVisible } from '../../../ppcp-button/resources/js/modules/Helper/Hiding';
|
||||
|
@ -7,18 +10,95 @@ import ErrorHandler from '../../../ppcp-button/resources/js/modules/ErrorHandler
|
|||
import widgetBuilder from '../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder';
|
||||
import { apmButtonsInit } from '../../../ppcp-button/resources/js/modules/Helper/ApmButtons';
|
||||
|
||||
class ApplepayButton {
|
||||
constructor( context, externalHandler, buttonConfig, ppcpConfig ) {
|
||||
apmButtonsInit( ppcpConfig );
|
||||
/**
|
||||
* Plugin-specific styling.
|
||||
*
|
||||
* Note that most properties of this object do not apply to the Apple Pay button.
|
||||
*
|
||||
* @typedef {Object} PPCPStyle
|
||||
* @property {string} shape - Outline shape.
|
||||
* @property {?number} height - Button height in pixel.
|
||||
*/
|
||||
|
||||
this.isInitialized = false;
|
||||
/**
|
||||
* Style options that are defined by the Apple Pay SDK and are required to render the button.
|
||||
*
|
||||
* @typedef {Object} ApplePayStyle
|
||||
* @property {string} type - Defines the button label.
|
||||
* @property {string} color - Button color
|
||||
* @property {string} lang - The locale; an empty string will apply the user-agent's language.
|
||||
*/
|
||||
|
||||
/**
|
||||
* List of valid context values that the button can have.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const CONTEXT = {
|
||||
Product: 'product',
|
||||
Cart: 'cart',
|
||||
Checkout: 'checkout',
|
||||
PayNow: 'pay-now',
|
||||
MiniCart: 'mini-cart',
|
||||
BlockCart: 'cart-block',
|
||||
BlockCheckout: 'checkout-block',
|
||||
Preview: 'preview',
|
||||
|
||||
// Block editor contexts.
|
||||
Blocks: [ 'cart-block', 'checkout-block' ],
|
||||
|
||||
// Custom gateway contexts.
|
||||
Gateways: [ 'checkout', 'pay-now' ],
|
||||
};
|
||||
|
||||
/**
|
||||
* A payment button for Apple Pay.
|
||||
*
|
||||
* On a single page, multiple Apple Pay buttons can be displayed, which also means multiple
|
||||
* ApplePayButton instances exist. A typical case is on the product page, where one Apple Pay button
|
||||
* is located inside the minicart-popup, and another pay-now button is in the product context.
|
||||
*/
|
||||
class ApplePayButton {
|
||||
/**
|
||||
* Whether the payment button is initialized.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
#isInitialized = false;
|
||||
|
||||
#wrapperId = '';
|
||||
#ppcpButtonWrapperId = '';
|
||||
|
||||
/**
|
||||
* Context describes the button's location on the website and what details it submits.
|
||||
*
|
||||
* @type {''|'product'|'cart'|'checkout'|'pay-now'|'mini-cart'|'cart-block'|'checkout-block'|'preview'}
|
||||
*/
|
||||
context = '';
|
||||
|
||||
externalHandler = null;
|
||||
buttonConfig = null;
|
||||
ppcpConfig = null;
|
||||
paymentsClient = null;
|
||||
formData = null;
|
||||
contextHandler = null;
|
||||
updatedContactInfo = [];
|
||||
selectedShippingMethod = [];
|
||||
|
||||
/**
|
||||
* Stores initialization data sent to the button.
|
||||
*/
|
||||
initialPaymentRequest = null;
|
||||
|
||||
constructor( context, externalHandler, buttonConfig, ppcpConfig ) {
|
||||
this._initDebug( !! buttonConfig?.is_debug );
|
||||
|
||||
apmButtonsInit( ppcpConfig );
|
||||
|
||||
this.context = context;
|
||||
this.externalHandler = externalHandler;
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
this.paymentsClient = null;
|
||||
this.formData = null;
|
||||
|
||||
this.contextHandler = ContextHandlerFactory.create(
|
||||
this.context,
|
||||
|
@ -26,36 +106,226 @@ class ApplepayButton {
|
|||
this.ppcpConfig
|
||||
);
|
||||
|
||||
this.updatedContactInfo = [];
|
||||
this.selectedShippingMethod = [];
|
||||
this.nonce =
|
||||
document.getElementById( 'woocommerce-process-checkout-nonce' )
|
||||
?.value || buttonConfig.nonce;
|
||||
|
||||
// Stores initialization data sent to the button.
|
||||
this.initialPaymentRequest = null;
|
||||
|
||||
// Default eligibility status.
|
||||
this.isEligible = true;
|
||||
|
||||
this.log = function () {
|
||||
if ( this.buttonConfig.is_debug ) {
|
||||
//console.log('[ApplePayButton]', ...arguments);
|
||||
}
|
||||
};
|
||||
|
||||
this.refreshContextData();
|
||||
}
|
||||
|
||||
/**
|
||||
* NOOP log function to avoid errors when debugging is disabled.
|
||||
*/
|
||||
log() {}
|
||||
|
||||
/**
|
||||
* Enables debugging tools, when the button's is_debug flag is set.
|
||||
*
|
||||
* @param {boolean} enableDebugging If debugging features should be enabled for this instance.
|
||||
* @private
|
||||
*/
|
||||
_initDebug( enableDebugging ) {
|
||||
if ( ! enableDebugging || this.#isInitialized ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Debug helpers
|
||||
jQuery( document ).on( 'ppcp-applepay-debug', () => {
|
||||
console.log( 'ApplePayButton', this.context, this );
|
||||
} );
|
||||
document.ppcpApplepayButtons = document.ppcpApplepayButtons || {};
|
||||
document.ppcpApplepayButtons[ this.context ] = this;
|
||||
|
||||
this.log = ( ...args ) => {
|
||||
console.log( `[ApplePayButton | ${ this.context }]`, ...args );
|
||||
};
|
||||
|
||||
jQuery( document ).on( 'ppcp-applepay-debug', () => {
|
||||
this.log( this );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* The nonce for ajax requests.
|
||||
*
|
||||
* @return {string} The nonce value
|
||||
*/
|
||||
get nonce() {
|
||||
const input = document.getElementById(
|
||||
'woocommerce-process-checkout-nonce'
|
||||
);
|
||||
|
||||
return input?.value || this.buttonConfig.nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current page qualifies to use the Apple Pay button.
|
||||
*
|
||||
* In admin, the button is always eligible, to display an accurate preview.
|
||||
* On front-end, PayPal's response decides if customers can use Apple Pay.
|
||||
*
|
||||
* @return {boolean} True, if the button can be displayed.
|
||||
*/
|
||||
get isEligible() {
|
||||
if ( ! this.#isInitialized ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( CONTEXT.Preview === this.context ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the ApplePaySession is available and accepts payments
|
||||
* This check is required when using Apple Pay SDK v1; canMakePayments() returns false
|
||||
* if the current device is not liked to iCloud or the Apple Wallet is not available
|
||||
* for a different reason.
|
||||
*/
|
||||
try {
|
||||
if ( ! window.ApplePaySession?.canMakePayments() ) {
|
||||
return false;
|
||||
}
|
||||
} catch ( error ) {
|
||||
console.warn( error );
|
||||
return false;
|
||||
}
|
||||
|
||||
return !! this.applePayConfig.isEligible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current payment button should be rendered as a stand-alone gateway.
|
||||
* The return value `false` usually means, that the payment button is bundled with all available
|
||||
* payment buttons.
|
||||
*
|
||||
* The decision depends on the button context (placement) and the plugin settings.
|
||||
*
|
||||
* @return {boolean} True, if the current button represents a stand-alone gateway.
|
||||
*/
|
||||
get isSeparateGateway() {
|
||||
return (
|
||||
this.buttonConfig.is_wc_gateway_enabled &&
|
||||
CONTEXT.Gateways.includes( this.context )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wrapper ID for the current button context.
|
||||
* The ID varies for the MiniCart context.
|
||||
*
|
||||
* @return {string} The wrapper-element's ID (without the `#` prefix).
|
||||
*/
|
||||
get wrapperId() {
|
||||
if ( ! this.#wrapperId ) {
|
||||
let id;
|
||||
|
||||
if ( CONTEXT.MiniCart === this.context ) {
|
||||
id = this.buttonConfig.button.mini_cart_wrapper;
|
||||
} else if ( this.isSeparateGateway ) {
|
||||
id = 'ppc-button-ppcp-applepay';
|
||||
} else {
|
||||
id = this.buttonConfig.button.wrapper;
|
||||
}
|
||||
|
||||
this.#wrapperId = id.replace( /^#/, '' );
|
||||
}
|
||||
|
||||
return this.#wrapperId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wrapper ID for the ppcpButton
|
||||
*
|
||||
* @return {string} The wrapper-element's ID (without the `#` prefix).
|
||||
*/
|
||||
get ppcpButtonWrapperId() {
|
||||
if ( ! this.#ppcpButtonWrapperId ) {
|
||||
let id;
|
||||
|
||||
if ( CONTEXT.MiniCart === this.context ) {
|
||||
id = this.ppcpConfig.button.mini_cart_wrapper;
|
||||
} else if ( CONTEXT.Blocks.includes( this.context ) ) {
|
||||
id = '#express-payment-method-ppcp-gateway-paypal';
|
||||
} else {
|
||||
id = this.ppcpConfig.button.wrapper;
|
||||
}
|
||||
|
||||
this.#ppcpButtonWrapperId = id.replace( /^#/, '' );
|
||||
}
|
||||
|
||||
return this.#ppcpButtonWrapperId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context-relevant PPCP style object.
|
||||
* The style for the MiniCart context can be different.
|
||||
*
|
||||
* The PPCP style are custom style options, that are provided by this plugin.
|
||||
*
|
||||
* @return {PPCPStyle} The style object.
|
||||
*/
|
||||
get ppcpStyle() {
|
||||
if ( CONTEXT.MiniCart === this.context ) {
|
||||
return this.ppcpConfig.button.mini_cart_style;
|
||||
}
|
||||
|
||||
return this.ppcpConfig.button.style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default style options that are propagated to and rendered by the Apple Pay button.
|
||||
*
|
||||
* These styles are the official style options provided by the Apple Pay SDK.
|
||||
*
|
||||
* @return {ApplePayStyle} The style object.
|
||||
*/
|
||||
get buttonStyle() {
|
||||
return {
|
||||
type: this.buttonConfig.button.type,
|
||||
lang: this.buttonConfig.button.lang,
|
||||
color: this.buttonConfig.button.color,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTML element that wraps the current button
|
||||
*
|
||||
* @return {HTMLElement|null} The wrapper element, or null.
|
||||
*/
|
||||
get wrapperElement() {
|
||||
return document.getElementById( this.wrapperId );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of HTMLElements that belong to the payment button.
|
||||
*
|
||||
* @return {HTMLElement[]} List of payment button wrapper elements.
|
||||
*/
|
||||
get allElements() {
|
||||
const selectors = [];
|
||||
|
||||
// Payment button (Pay now, smart button block)
|
||||
selectors.push( `#${ this.wrapperId }` );
|
||||
|
||||
// Block Checkout: Express checkout button.
|
||||
if ( CONTEXT.Blocks.includes( this.context ) ) {
|
||||
selectors.push( '#express-payment-method-ppcp-applepay' );
|
||||
}
|
||||
|
||||
// Classic Checkout: Apple Pay gateway.
|
||||
if ( CONTEXT.Gateways.includes( this.context ) ) {
|
||||
selectors.push( '.wc_payment_method.payment_method_ppcp-applepay' );
|
||||
}
|
||||
|
||||
this.log( 'Wrapper Elements:', selectors );
|
||||
return /** @type {HTMLElement[]} */ selectors.flatMap( ( selector ) =>
|
||||
Array.from( document.querySelectorAll( selector ) )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the main button-wrapper is present in the current DOM.
|
||||
*
|
||||
* @return {boolean} True, if the button context (wrapper element) is found.
|
||||
*/
|
||||
get isPresent() {
|
||||
return this.wrapperElement instanceof HTMLElement;
|
||||
}
|
||||
|
||||
init( config ) {
|
||||
if ( this.isInitialized ) {
|
||||
if ( this.#isInitialized ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -63,41 +333,41 @@ class ApplepayButton {
|
|||
return;
|
||||
}
|
||||
|
||||
this.log( 'Init', this.context );
|
||||
this.log( 'Init' );
|
||||
this.initEventHandlers();
|
||||
this.isInitialized = true;
|
||||
|
||||
this.#isInitialized = true;
|
||||
this.applePayConfig = config;
|
||||
this.isEligible =
|
||||
( this.applePayConfig.isEligible && window.ApplePaySession ) ||
|
||||
this.buttonConfig.is_admin;
|
||||
|
||||
if ( this.isEligible ) {
|
||||
this.fetchTransactionInfo().then( () => {
|
||||
this.addButton();
|
||||
const id_minicart =
|
||||
'#apple-' + this.buttonConfig.button.mini_cart_wrapper;
|
||||
const id = '#apple-' + this.buttonConfig.button.wrapper;
|
||||
|
||||
if ( this.context === 'mini-cart' ) {
|
||||
if ( this.isSeparateGateway ) {
|
||||
document
|
||||
.querySelector( id_minicart )
|
||||
?.addEventListener( 'click', ( evt ) => {
|
||||
evt.preventDefault();
|
||||
this.onButtonClick();
|
||||
} );
|
||||
} else {
|
||||
document
|
||||
.querySelector( id )
|
||||
?.addEventListener( 'click', ( evt ) => {
|
||||
evt.preventDefault();
|
||||
this.onButtonClick();
|
||||
} );
|
||||
.querySelectorAll( '#ppc-button-applepay-container' )
|
||||
.forEach( ( el ) => el.remove() );
|
||||
}
|
||||
} );
|
||||
|
||||
if ( ! this.isEligible ) {
|
||||
this.hide();
|
||||
} else {
|
||||
jQuery( '#' + this.buttonConfig.button.wrapper ).hide();
|
||||
jQuery( '#' + this.buttonConfig.button.mini_cart_wrapper ).hide();
|
||||
jQuery( '#express-payment-method-ppcp-applepay' ).hide();
|
||||
// Bail if the button wrapper is not present; handles mini-cart logic on checkout page.
|
||||
if ( ! this.isPresent ) {
|
||||
this.log( 'Abort init (no wrapper found)' );
|
||||
return;
|
||||
}
|
||||
|
||||
this.show();
|
||||
|
||||
this.fetchTransactionInfo().then( () => {
|
||||
const button = this.addButton();
|
||||
|
||||
if ( ! button ) {
|
||||
return;
|
||||
}
|
||||
|
||||
button.addEventListener( 'click', ( evt ) => {
|
||||
evt.preventDefault();
|
||||
this.onButtonClick();
|
||||
} );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,49 +376,51 @@ class ApplepayButton {
|
|||
return;
|
||||
}
|
||||
|
||||
this.isInitialized = false;
|
||||
this.#isInitialized = false;
|
||||
this.init( this.applePayConfig );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides all wrappers that belong to this ApplePayButton instance.
|
||||
*/
|
||||
hide() {
|
||||
this.log( 'Hide button' );
|
||||
this.allElements.forEach( ( element ) => {
|
||||
element.style.display = 'none';
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures all wrapper elements of this ApplePayButton instance are visible.
|
||||
*/
|
||||
show() {
|
||||
this.log( 'Show button' );
|
||||
if ( ! this.isPresent ) {
|
||||
this.log( '!! Cannot show button, wrapper is not present' );
|
||||
return;
|
||||
}
|
||||
|
||||
// Classic Checkout/PayNow: Make the Apple Pay gateway visible after page load.
|
||||
document
|
||||
.querySelectorAll( 'style#ppcp-hide-apple-pay' )
|
||||
.forEach( ( el ) => el.remove() );
|
||||
|
||||
this.allElements.forEach( ( element ) => {
|
||||
element.style.display = '';
|
||||
} );
|
||||
}
|
||||
|
||||
async fetchTransactionInfo() {
|
||||
this.transactionInfo = await this.contextHandler.transactionInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns configurations relative to this button context.
|
||||
*/
|
||||
contextConfig() {
|
||||
const config = {
|
||||
wrapper: this.buttonConfig.button.wrapper,
|
||||
ppcpStyle: this.ppcpConfig.button.style,
|
||||
buttonStyle: this.buttonConfig.button.style,
|
||||
ppcpButtonWrapper: this.ppcpConfig.button.wrapper,
|
||||
};
|
||||
|
||||
if ( this.context === 'mini-cart' ) {
|
||||
config.wrapper = this.buttonConfig.button.mini_cart_wrapper;
|
||||
config.ppcpStyle = this.ppcpConfig.button.mini_cart_style;
|
||||
config.buttonStyle = this.buttonConfig.button.mini_cart_style;
|
||||
config.ppcpButtonWrapper = this.ppcpConfig.button.mini_cart_wrapper;
|
||||
}
|
||||
|
||||
if (
|
||||
[ 'cart-block', 'checkout-block' ].indexOf( this.context ) !== -1
|
||||
) {
|
||||
config.ppcpButtonWrapper =
|
||||
'#express-payment-method-ppcp-gateway-paypal';
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
initEventHandlers() {
|
||||
const { wrapper, ppcpButtonWrapper } = this.contextConfig();
|
||||
const wrapper_id = '#' + wrapper;
|
||||
const ppcpButtonWrapper = `#${ this.ppcpButtonWrapperId }`;
|
||||
const wrapperId = `#${ this.wrapperId }`;
|
||||
|
||||
if ( wrapper_id === ppcpButtonWrapper ) {
|
||||
if ( wrapperId === ppcpButtonWrapper ) {
|
||||
throw new Error(
|
||||
`[ApplePayButton] "wrapper" and "ppcpButtonWrapper" values must differ to avoid infinite loop. Current value: "${ wrapper_id }"`
|
||||
`[ApplePayButton] "wrapper" and "ppcpButtonWrapper" values must differ to avoid infinite loop. Current value: "${ wrapperId }"`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -158,9 +430,9 @@ class ApplepayButton {
|
|||
}
|
||||
|
||||
const $ppcpButtonWrapper = jQuery( ppcpButtonWrapper );
|
||||
setVisible( wrapper_id, $ppcpButtonWrapper.is( ':visible' ) );
|
||||
setVisible( wrapperId, $ppcpButtonWrapper.is( ':visible' ) );
|
||||
setEnabled(
|
||||
wrapper_id,
|
||||
wrapperId,
|
||||
! $ppcpButtonWrapper.hasClass( 'ppcp-disabled' )
|
||||
);
|
||||
};
|
||||
|
@ -178,8 +450,9 @@ class ApplepayButton {
|
|||
}
|
||||
|
||||
/**
|
||||
* Starts an ApplePay session.
|
||||
* @param paymentRequest
|
||||
* Starts an Apple Pay session.
|
||||
*
|
||||
* @param {Object} paymentRequest The payment request object.
|
||||
*/
|
||||
applePaySession( paymentRequest ) {
|
||||
this.log( 'applePaySession', paymentRequest );
|
||||
|
@ -192,6 +465,7 @@ class ApplepayButton {
|
|||
session.onshippingcontactselected =
|
||||
this.onShippingContactSelected( session );
|
||||
}
|
||||
|
||||
session.onvalidatemerchant = this.onValidateMerchant( session );
|
||||
session.onpaymentauthorized = this.onPaymentAuthorized( session );
|
||||
return session;
|
||||
|
@ -199,33 +473,39 @@ class ApplepayButton {
|
|||
|
||||
/**
|
||||
* Adds an Apple Pay purchase button.
|
||||
*
|
||||
* @return {HTMLElement|null} The newly created `<apple-pay-button>` element. Null on failure.
|
||||
*/
|
||||
addButton() {
|
||||
this.log( 'addButton', this.context );
|
||||
this.log( 'addButton' );
|
||||
|
||||
const { wrapper, ppcpStyle } = this.contextConfig();
|
||||
const wrapper = this.wrapperElement;
|
||||
const style = this.buttonStyle;
|
||||
const id = 'apple-' + this.wrapperId;
|
||||
|
||||
const appleContainer = document.getElementById( wrapper );
|
||||
const type = this.buttonConfig.button.type;
|
||||
const language = this.buttonConfig.button.lang;
|
||||
const color = this.buttonConfig.button.color;
|
||||
const id = 'apple-' + wrapper;
|
||||
|
||||
if ( appleContainer ) {
|
||||
appleContainer.innerHTML = `<apple-pay-button id="${ id }" buttonstyle="${ color }" type="${ type }" locale="${ language }">`;
|
||||
if ( ! wrapper ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const $wrapper = jQuery( '#' + wrapper );
|
||||
$wrapper.removeClass( 'ppcp-button-rect ppcp-button-pill' );
|
||||
$wrapper.addClass( 'ppcp-button-' + ppcpStyle.shape );
|
||||
const ppcpStyle = this.ppcpStyle;
|
||||
|
||||
wrapper.innerHTML = `<apple-pay-button id='${ id }' buttonstyle='${ style.color }' type='${ style.type }' locale='${ style.lang }' />`;
|
||||
wrapper.classList.remove( 'ppcp-button-rect', 'ppcp-button-pill' );
|
||||
wrapper.classList.add(
|
||||
`ppcp-button-${ ppcpStyle.shape }`,
|
||||
'ppcp-button-apm',
|
||||
'ppcp-button-applepay'
|
||||
);
|
||||
|
||||
if ( ppcpStyle.height ) {
|
||||
$wrapper.css(
|
||||
wrapper.style.setProperty(
|
||||
'--apple-pay-button-height',
|
||||
`${ ppcpStyle.height }px`
|
||||
);
|
||||
$wrapper.css( 'height', `${ ppcpStyle.height }px` );
|
||||
wrapper.style.height = `${ ppcpStyle.height }px`;
|
||||
}
|
||||
|
||||
return wrapper.querySelector( 'apple-pay-button' );
|
||||
}
|
||||
|
||||
//------------------------
|
||||
|
@ -236,19 +516,21 @@ class ApplepayButton {
|
|||
* Show Apple Pay payment sheet when Apple Pay payment button is clicked
|
||||
*/
|
||||
async onButtonClick() {
|
||||
this.log( 'onButtonClick', this.context );
|
||||
this.log( 'onButtonClick' );
|
||||
|
||||
const paymentRequest = this.paymentRequest();
|
||||
|
||||
window.ppcpFundingSource = 'apple_pay'; // Do this on another place like on create order endpoint handler.
|
||||
// Do this on another place like on create order endpoint handler.
|
||||
window.ppcpFundingSource = 'apple_pay';
|
||||
|
||||
// Trigger woocommerce validation if we are in the checkout page.
|
||||
if ( this.context === 'checkout' ) {
|
||||
if ( CONTEXT.Checkout === this.context ) {
|
||||
const checkoutFormSelector = 'form.woocommerce-checkout';
|
||||
const errorHandler = new ErrorHandler(
|
||||
PayPalCommerceGateway.labels.error.generic,
|
||||
document.querySelector( '.woocommerce-notices-wrapper' )
|
||||
);
|
||||
|
||||
try {
|
||||
const formData = new FormData(
|
||||
document.querySelector( checkoutFormSelector )
|
||||
|
@ -270,6 +552,7 @@ class ApplepayButton {
|
|||
PayPalCommerceGateway.ajax.validate_checkout.nonce
|
||||
)
|
||||
: null;
|
||||
|
||||
if ( formValidator ) {
|
||||
try {
|
||||
const errors = await formValidator.validate(
|
||||
|
@ -297,13 +580,13 @@ class ApplepayButton {
|
|||
/**
|
||||
* If the button should show the shipping fields.
|
||||
*
|
||||
* @return {false|*}
|
||||
* @return {boolean} True, if shipping fields should be captured by ApplePay.
|
||||
*/
|
||||
shouldRequireShippingInButton() {
|
||||
return (
|
||||
this.contextHandler.shippingAllowed() &&
|
||||
this.buttonConfig.product.needShipping &&
|
||||
( this.context !== 'checkout' ||
|
||||
( CONTEXT.Checkout !== this.context ||
|
||||
this.shouldUpdateButtonWithFormData() )
|
||||
);
|
||||
}
|
||||
|
@ -311,10 +594,10 @@ class ApplepayButton {
|
|||
/**
|
||||
* If the button should be updated with the form addresses.
|
||||
*
|
||||
* @return {boolean}
|
||||
* @return {boolean} True, when Apple Pay data should be submitted to WooCommerce.
|
||||
*/
|
||||
shouldUpdateButtonWithFormData() {
|
||||
if ( this.context !== 'checkout' ) {
|
||||
if ( CONTEXT.Checkout !== this.context ) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
|
@ -324,29 +607,28 @@ class ApplepayButton {
|
|||
}
|
||||
|
||||
/**
|
||||
* Indicates how payment completion should be handled if with the context handler default actions.
|
||||
* Or with ApplePay module specific completion.
|
||||
* Indicates how payment completion should be handled if with the context handler default
|
||||
* actions. Or with Apple Pay module specific completion.
|
||||
*
|
||||
* @return {boolean}
|
||||
* @return {boolean} True, when the Apple Pay data should be submitted to WooCommerce.
|
||||
*/
|
||||
shouldCompletePaymentWithContextHandler() {
|
||||
// Data already handled, ex: PayNow
|
||||
if ( ! this.contextHandler.shippingAllowed() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use WC form data mode in Checkout.
|
||||
if (
|
||||
this.context === 'checkout' &&
|
||||
return (
|
||||
CONTEXT.Checkout === this.context &&
|
||||
! this.shouldUpdateButtonWithFormData()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates ApplePay paymentRequest with form data.
|
||||
* @param paymentRequest
|
||||
* Updates Apple Pay paymentRequest with form data.
|
||||
*
|
||||
* @param {Object} paymentRequest Object to extend with form data.
|
||||
*/
|
||||
updateRequestDataWithForm( paymentRequest ) {
|
||||
if ( ! this.shouldUpdateButtonWithFormData() ) {
|
||||
|
@ -359,8 +641,9 @@ class ApplepayButton {
|
|||
);
|
||||
|
||||
// Add custom data.
|
||||
// "applicationData" is originating a "PayPalApplePayError: An internal server error has occurred" on paypal.Applepay().confirmOrder().
|
||||
// paymentRequest.applicationData = this.fillApplicationData(this.formData);
|
||||
// "applicationData" is originating a "PayPalApplePayError: An internal server error has
|
||||
// occurred" on paypal.Applepay().confirmOrder(). paymentRequest.applicationData =
|
||||
// this.fillApplicationData(this.formData);
|
||||
|
||||
if ( ! this.shouldRequireShippingInButton() ) {
|
||||
return;
|
||||
|
@ -426,7 +709,8 @@ class ApplepayButton {
|
|||
'email',
|
||||
'phone',
|
||||
],
|
||||
requiredBillingContactFields: [ 'postalAddress' ], // ApplePay does not implement billing email and phone fields.
|
||||
requiredBillingContactFields: [ 'postalAddress' ], // ApplePay does not implement billing
|
||||
// email and phone fields.
|
||||
};
|
||||
|
||||
if ( ! this.shouldRequireShippingInButton() ) {
|
||||
|
@ -454,14 +738,11 @@ class ApplepayButton {
|
|||
}
|
||||
|
||||
refreshContextData() {
|
||||
switch ( this.context ) {
|
||||
case 'product':
|
||||
if ( CONTEXT.Product === this.context ) {
|
||||
// Refresh product data that makes the price change.
|
||||
this.productQuantity =
|
||||
document.querySelector( 'input.qty' )?.value;
|
||||
this.productQuantity = document.querySelector( 'input.qty' )?.value;
|
||||
this.products = this.contextHandler.products();
|
||||
this.log( 'Products updated', this.products );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,8 +750,36 @@ class ApplepayButton {
|
|||
// Payment process
|
||||
//------------------------
|
||||
|
||||
/**
|
||||
* Make ajax call to change the verification-status of the current domain.
|
||||
*
|
||||
* @param {boolean} isValid
|
||||
*/
|
||||
adminValidation( isValid ) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const ignored = fetch( this.buttonConfig.ajax_url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: new URLSearchParams( {
|
||||
action: 'ppcp_validate',
|
||||
'woocommerce-process-checkout-nonce': this.nonce,
|
||||
validation: isValid,
|
||||
} ).toString(),
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an event handler that Apple Pay calls when displaying the payment sheet.
|
||||
*
|
||||
* @see https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession/1778021-onvalidatemerchant
|
||||
*
|
||||
* @param {Object} session The ApplePaySession object.
|
||||
*
|
||||
* @return {(function(*): void)|*} Callback that runs after the merchant validation
|
||||
*/
|
||||
onValidateMerchant( session ) {
|
||||
this.log( 'onvalidatemerchant', this.buttonConfig.ajax_url );
|
||||
return ( applePayValidateMerchantEvent ) => {
|
||||
this.log( 'onvalidatemerchant call' );
|
||||
|
||||
|
@ -480,34 +789,15 @@ class ApplepayButton {
|
|||
validationUrl: applePayValidateMerchantEvent.validationURL,
|
||||
} )
|
||||
.then( ( validateResult ) => {
|
||||
this.log( 'onvalidatemerchant ok' );
|
||||
session.completeMerchantValidation(
|
||||
validateResult.merchantSession
|
||||
);
|
||||
//call backend to update validation to true
|
||||
jQuery.ajax( {
|
||||
url: this.buttonConfig.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'ppcp_validate',
|
||||
validation: true,
|
||||
'woocommerce-process-checkout-nonce': this.nonce,
|
||||
},
|
||||
} );
|
||||
|
||||
this.adminValidation( true );
|
||||
} )
|
||||
.catch( ( validateError ) => {
|
||||
this.log( 'onvalidatemerchant error', validateError );
|
||||
console.error( validateError );
|
||||
//call backend to update validation to false
|
||||
jQuery.ajax( {
|
||||
url: this.buttonConfig.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'ppcp_validate',
|
||||
validation: false,
|
||||
'woocommerce-process-checkout-nonce': this.nonce,
|
||||
},
|
||||
} );
|
||||
this.adminValidation( false );
|
||||
this.log( 'onvalidatemerchant session abort' );
|
||||
session.abort();
|
||||
} );
|
||||
|
@ -516,14 +806,14 @@ class ApplepayButton {
|
|||
|
||||
onShippingMethodSelected( session ) {
|
||||
this.log( 'onshippingmethodselected', this.buttonConfig.ajax_url );
|
||||
const ajax_url = this.buttonConfig.ajax_url;
|
||||
const ajaxUrl = this.buttonConfig.ajax_url;
|
||||
return ( event ) => {
|
||||
this.log( 'onshippingmethodselected call' );
|
||||
|
||||
const data = this.getShippingMethodData( event );
|
||||
|
||||
jQuery.ajax( {
|
||||
url: ajax_url,
|
||||
url: ajaxUrl,
|
||||
method: 'POST',
|
||||
data,
|
||||
success: (
|
||||
|
@ -538,7 +828,8 @@ class ApplepayButton {
|
|||
}
|
||||
this.selectedShippingMethod = event.shippingMethod;
|
||||
|
||||
// Sort the response shipping methods, so that the selected shipping method is the first one.
|
||||
// Sort the response shipping methods, so that the selected shipping method is
|
||||
// the first one.
|
||||
response.newShippingMethods =
|
||||
response.newShippingMethods.sort( ( a, b ) => {
|
||||
if (
|
||||
|
@ -566,7 +857,7 @@ class ApplepayButton {
|
|||
onShippingContactSelected( session ) {
|
||||
this.log( 'onshippingcontactselected', this.buttonConfig.ajax_url );
|
||||
|
||||
const ajax_url = this.buttonConfig.ajax_url;
|
||||
const ajaxUrl = this.buttonConfig.ajax_url;
|
||||
|
||||
return ( event ) => {
|
||||
this.log( 'onshippingcontactselected call' );
|
||||
|
@ -574,7 +865,7 @@ class ApplepayButton {
|
|||
const data = this.getShippingContactData( event );
|
||||
|
||||
jQuery.ajax( {
|
||||
url: ajax_url,
|
||||
url: ajaxUrl,
|
||||
method: 'POST',
|
||||
data,
|
||||
success: (
|
||||
|
@ -604,15 +895,15 @@ class ApplepayButton {
|
|||
}
|
||||
|
||||
getShippingContactData( event ) {
|
||||
const product_id = this.buttonConfig.product.id;
|
||||
const productId = this.buttonConfig.product.id;
|
||||
|
||||
this.refreshContextData();
|
||||
|
||||
switch ( this.context ) {
|
||||
case 'product':
|
||||
case CONTEXT.Product:
|
||||
return {
|
||||
action: 'ppcp_update_shipping_contact',
|
||||
product_id,
|
||||
product_id: productId,
|
||||
products: JSON.stringify( this.products ),
|
||||
caller_page: 'productDetail',
|
||||
product_quantity: this.productQuantity,
|
||||
|
@ -620,11 +911,12 @@ class ApplepayButton {
|
|||
need_shipping: this.shouldRequireShippingInButton(),
|
||||
'woocommerce-process-checkout-nonce': this.nonce,
|
||||
};
|
||||
case 'cart':
|
||||
case 'checkout':
|
||||
case 'cart-block':
|
||||
case 'checkout-block':
|
||||
case 'mini-cart':
|
||||
|
||||
case CONTEXT.Cart:
|
||||
case CONTEXT.Checkout:
|
||||
case CONTEXT.BlockCart:
|
||||
case CONTEXT.BlockCheckout:
|
||||
case CONTEXT.MiniCart:
|
||||
return {
|
||||
action: 'ppcp_update_shipping_contact',
|
||||
simplified_contact: event.shippingContact,
|
||||
|
@ -636,37 +928,42 @@ class ApplepayButton {
|
|||
}
|
||||
|
||||
getShippingMethodData( event ) {
|
||||
const product_id = this.buttonConfig.product.id;
|
||||
const productId = this.buttonConfig.product.id;
|
||||
|
||||
this.refreshContextData();
|
||||
|
||||
switch ( this.context ) {
|
||||
case 'product':
|
||||
case CONTEXT.Product:
|
||||
return {
|
||||
action: 'ppcp_update_shipping_method',
|
||||
shipping_method: event.shippingMethod,
|
||||
simplified_contact:
|
||||
this.updatedContactInfo ||
|
||||
this.initialPaymentRequest.shippingContact ||
|
||||
this.initialPaymentRequest.billingContact,
|
||||
product_id,
|
||||
simplified_contact: this.hasValidContactInfo(
|
||||
this.updatedContactInfo
|
||||
)
|
||||
? this.updatedContactInfo
|
||||
: this.initialPaymentRequest?.shippingContact ??
|
||||
this.initialPaymentRequest?.billingContact,
|
||||
product_id: productId,
|
||||
products: JSON.stringify( this.products ),
|
||||
caller_page: 'productDetail',
|
||||
product_quantity: this.productQuantity,
|
||||
'woocommerce-process-checkout-nonce': this.nonce,
|
||||
};
|
||||
case 'cart':
|
||||
case 'checkout':
|
||||
case 'cart-block':
|
||||
case 'checkout-block':
|
||||
case 'mini-cart':
|
||||
|
||||
case CONTEXT.Cart:
|
||||
case CONTEXT.Checkout:
|
||||
case CONTEXT.BlockCart:
|
||||
case CONTEXT.BlockCheckout:
|
||||
case CONTEXT.MiniCart:
|
||||
return {
|
||||
action: 'ppcp_update_shipping_method',
|
||||
shipping_method: event.shippingMethod,
|
||||
simplified_contact:
|
||||
this.updatedContactInfo ||
|
||||
this.initialPaymentRequest.shippingContact ||
|
||||
this.initialPaymentRequest.billingContact,
|
||||
simplified_contact: this.hasValidContactInfo(
|
||||
this.updatedContactInfo
|
||||
)
|
||||
? this.updatedContactInfo
|
||||
: this.initialPaymentRequest?.shippingContact ??
|
||||
this.initialPaymentRequest?.billingContact,
|
||||
caller_page: 'cart',
|
||||
'woocommerce-process-checkout-nonce': this.nonce,
|
||||
};
|
||||
|
@ -678,9 +975,6 @@ class ApplepayButton {
|
|||
return async ( event ) => {
|
||||
this.log( 'onpaymentauthorized call' );
|
||||
|
||||
function form() {
|
||||
return document.querySelector( 'form.cart' );
|
||||
}
|
||||
const processInWooAndCapture = async ( data ) => {
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
try {
|
||||
|
@ -695,7 +989,7 @@ class ApplepayButton {
|
|||
( this.initialPaymentRequest.shippingMethods ||
|
||||
[] )[ 0 ];
|
||||
|
||||
const request_data = {
|
||||
const requestData = {
|
||||
action: 'ppcp_create_order',
|
||||
caller_page: this.context,
|
||||
product_id: this.buttonConfig.product.id ?? null,
|
||||
|
@ -720,7 +1014,7 @@ class ApplepayButton {
|
|||
jQuery.ajax( {
|
||||
url: this.buttonConfig.ajax_url,
|
||||
method: 'POST',
|
||||
data: request_data,
|
||||
data: requestData,
|
||||
complete: ( jqXHR, textStatus ) => {
|
||||
this.log( 'onpaymentauthorized complete' );
|
||||
},
|
||||
|
@ -782,7 +1076,8 @@ class ApplepayButton {
|
|||
if (
|
||||
this.shouldCompletePaymentWithContextHandler()
|
||||
) {
|
||||
// No shipping, expect immediate capture, ex: PayNow, Checkout with form data.
|
||||
// No shipping, expect immediate capture, ex: PayNow, Checkout with
|
||||
// form data.
|
||||
|
||||
let approveFailed = false;
|
||||
await this.contextHandler.approveOrder(
|
||||
|
@ -949,6 +1244,12 @@ class ApplepayButton {
|
|||
|
||||
return btoa( utf8Str );
|
||||
}
|
||||
|
||||
hasValidContactInfo( value ) {
|
||||
return Array.isArray( value )
|
||||
? value.length > 0
|
||||
: Object.keys( value || {} ).length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
export default ApplepayButton;
|
||||
export default ApplePayButton;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
|
||||
import ApplepayButton from './ApplepayButton';
|
||||
/* global paypal */
|
||||
|
||||
class ApplepayManager {
|
||||
import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
|
||||
import ApplePayButton from './ApplepayButton';
|
||||
|
||||
class ApplePayManager {
|
||||
constructor( buttonConfig, ppcpConfig ) {
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
|
@ -9,7 +11,7 @@ class ApplepayManager {
|
|||
this.buttons = [];
|
||||
|
||||
buttonModuleWatcher.watchContextBootstrap( ( bootstrap ) => {
|
||||
const button = new ApplepayButton(
|
||||
const button = new ApplePayButton(
|
||||
bootstrap.context,
|
||||
bootstrap.handler,
|
||||
buttonConfig,
|
||||
|
@ -40,8 +42,7 @@ class ApplepayManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets ApplePay configuration of the PayPal merchant.
|
||||
* @return {Promise<null>}
|
||||
* Gets Apple Pay configuration of the PayPal merchant.
|
||||
*/
|
||||
async config() {
|
||||
this.ApplePayConfig = await paypal.Applepay().config();
|
||||
|
@ -49,4 +50,4 @@ class ApplepayManager {
|
|||
}
|
||||
}
|
||||
|
||||
export default ApplepayManager;
|
||||
export default ApplePayManager;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import ApplepayButton from './ApplepayButton';
|
||||
/* global paypal */
|
||||
|
||||
class ApplepayManagerBlockEditor {
|
||||
import ApplePayButton from './ApplepayButton';
|
||||
|
||||
class ApplePayManagerBlockEditor {
|
||||
constructor( buttonConfig, ppcpConfig ) {
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
|
@ -17,7 +19,7 @@ class ApplepayManagerBlockEditor {
|
|||
try {
|
||||
this.applePayConfig = await paypal.Applepay().config();
|
||||
|
||||
const button = new ApplepayButton(
|
||||
const button = new ApplePayButton(
|
||||
this.ppcpConfig.context,
|
||||
null,
|
||||
this.buttonConfig,
|
||||
|
@ -31,4 +33,4 @@ class ApplepayManagerBlockEditor {
|
|||
}
|
||||
}
|
||||
|
||||
export default ApplepayManagerBlockEditor;
|
||||
export default ApplePayManagerBlockEditor;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import ErrorHandler from '../../../../ppcp-button/resources/js/modules/ErrorHandler';
|
||||
import CartActionHandler from '../../../../ppcp-button/resources/js/modules/ActionHandler/CartActionHandler';
|
||||
import { isPayPalSubscription } from '../../../../ppcp-blocks/resources/js/Helper/Subscription';
|
||||
|
||||
class BaseHandler {
|
||||
constructor( buttonConfig, ppcpConfig ) {
|
||||
|
@ -24,7 +23,7 @@ class BaseHandler {
|
|||
}
|
||||
|
||||
shippingAllowed() {
|
||||
return this.buttonConfig.product.needsShipping;
|
||||
return this.buttonConfig.product.needShipping;
|
||||
}
|
||||
|
||||
transactionInfo() {
|
||||
|
@ -76,13 +75,6 @@ class BaseHandler {
|
|||
document.querySelector( '.woocommerce-notices-wrapper' )
|
||||
);
|
||||
}
|
||||
|
||||
errorHandler() {
|
||||
return new ErrorHandler(
|
||||
this.ppcpConfig.labels.error.generic,
|
||||
document.querySelector( '.woocommerce-notices-wrapper' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseHandler;
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
import BaseHandler from './BaseHandler';
|
||||
|
||||
class PreviewHandler extends BaseHandler {
|
||||
constructor( buttonConfig, ppcpConfig, externalHandler ) {
|
||||
super( buttonConfig, ppcpConfig, externalHandler );
|
||||
}
|
||||
|
||||
transactionInfo() {
|
||||
// We need to return something as ApplePay button initialization expects valid data.
|
||||
return {
|
||||
|
@ -19,7 +15,7 @@ class PreviewHandler extends BaseHandler {
|
|||
throw new Error( 'Create order fail. This is just a preview.' );
|
||||
}
|
||||
|
||||
approveOrder( data, actions ) {
|
||||
approveOrder() {
|
||||
throw new Error( 'Approve order fail. This is just a preview.' );
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export const buttonID = 'applepay-container';
|
||||
export const buttonID = 'ppc-button-applepay-container';
|
||||
export const endpoints = {
|
||||
validation: '_apple_pay_validation',
|
||||
createOrderCart: '_apple_pay_create_order_cart',
|
||||
|
|
|
@ -4,8 +4,8 @@ import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Help
|
|||
import { cartHasSubscriptionProducts } from '../../../ppcp-blocks/resources/js/Helper/Subscription';
|
||||
import { loadCustomScript } from '@paypal/paypal-js';
|
||||
import CheckoutHandler from './Context/CheckoutHandler';
|
||||
import ApplepayManager from './ApplepayManager';
|
||||
import ApplepayManagerBlockEditor from './ApplepayManagerBlockEditor';
|
||||
import ApplePayManager from './ApplepayManager';
|
||||
import ApplePayManagerBlockEditor from './ApplepayManagerBlockEditor';
|
||||
|
||||
const ppcpData = wc.wcSettings.getSetting( 'ppcp-gateway_data' );
|
||||
const ppcpConfig = ppcpData.scriptData;
|
||||
|
@ -24,8 +24,8 @@ const ApplePayComponent = ( props ) => {
|
|||
|
||||
const bootstrap = function () {
|
||||
const ManagerClass = props.isEditing
|
||||
? ApplepayManagerBlockEditor
|
||||
: ApplepayManager;
|
||||
? ApplePayManagerBlockEditor
|
||||
: ApplePayManager;
|
||||
const manager = new ManagerClass( buttonConfig, ppcpConfig );
|
||||
manager.init();
|
||||
};
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { loadCustomScript } from '@paypal/paypal-js';
|
||||
import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading';
|
||||
import ApplepayManager from './ApplepayManager';
|
||||
import ApplePayManager from './ApplepayManager';
|
||||
import { setupButtonEvents } from '../../../ppcp-button/resources/js/modules/Helper/ButtonRefreshHelper';
|
||||
|
||||
( function ( { buttonConfig, ppcpConfig, jQuery } ) {
|
||||
let manager;
|
||||
|
||||
const bootstrap = function () {
|
||||
manager = new ApplepayManager( buttonConfig, ppcpConfig );
|
||||
manager = new ApplePayManager( buttonConfig, ppcpConfig );
|
||||
manager.init();
|
||||
};
|
||||
|
||||
|
|
|
@ -28,11 +28,12 @@ return array(
|
|||
$apm_applies = $container->get( 'applepay.helpers.apm-applies' );
|
||||
assert( $apm_applies instanceof ApmApplies );
|
||||
|
||||
return $apm_applies->for_country_currency();
|
||||
return $apm_applies->for_country() && $apm_applies->for_currency();
|
||||
},
|
||||
'applepay.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies {
|
||||
return new ApmApplies(
|
||||
$container->get( 'applepay.supported-country-currency-matrix' ),
|
||||
$container->get( 'applepay.supported-countries' ),
|
||||
$container->get( 'applepay.supported-currencies' ),
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'api.shop.country' )
|
||||
);
|
||||
|
@ -164,769 +165,91 @@ return array(
|
|||
$container->get( 'blocks.method' )
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for ApplePay.
|
||||
* The list of which countries can be used for ApplePay.
|
||||
*/
|
||||
'applepay.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array {
|
||||
'applepay.supported-countries' => static function ( ContainerInterface $container ) : array {
|
||||
/**
|
||||
* Returns which countries and currency combinations can be used for ApplePay.
|
||||
* Returns which countries can be used for ApplePay.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_applepay_supported_country_currency_matrix',
|
||||
'woocommerce_paypal_payments_applepay_supported_countries',
|
||||
// phpcs:disable Squiz.Commenting.InlineComment
|
||||
array(
|
||||
'AU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'AT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BG' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CA' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CY' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CZ' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'EE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'HU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LV' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'MT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'RO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'ES' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GB' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'US' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'JPY',
|
||||
'USD',
|
||||
),
|
||||
'AU', // Australia
|
||||
'AT', // Austria
|
||||
'BE', // Belgium
|
||||
'BG', // Bulgaria
|
||||
'CA', // Canada
|
||||
'CN', // China
|
||||
'CY', // Cyprus
|
||||
'CZ', // Czech Republic
|
||||
'DK', // Denmark
|
||||
'EE', // Estonia
|
||||
'FI', // Finland
|
||||
'FR', // France
|
||||
'DE', // Germany
|
||||
'GR', // Greece
|
||||
'HU', // Hungary
|
||||
'IE', // Ireland
|
||||
'IT', // Italy
|
||||
'LV', // Latvia
|
||||
'LI', // Liechtenstein
|
||||
'LT', // Lithuania
|
||||
'LU', // Luxembourg
|
||||
'MT', // Malta
|
||||
'NL', // Netherlands
|
||||
'NO', // Norway
|
||||
'PL', // Poland
|
||||
'PT', // Portugal
|
||||
'RO', // Romania
|
||||
'SK', // Slovakia
|
||||
'SI', // Slovenia
|
||||
'ES', // Spain
|
||||
'SE', // Sweden
|
||||
'US', // United States
|
||||
'GB', // United Kingdom
|
||||
)
|
||||
// phpcs:enable Squiz.Commenting.InlineComment
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* The list of which currencies can be used for ApplePay.
|
||||
*/
|
||||
'applepay.supported-currencies' => static function ( ContainerInterface $container ) : array {
|
||||
/**
|
||||
* Returns which currencies can be used for ApplePay.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_applepay_supported_currencies',
|
||||
// phpcs:disable Squiz.Commenting.InlineComment
|
||||
array(
|
||||
'AUD', // Australian Dollar
|
||||
'BRL', // Brazilian Real
|
||||
'CAD', // Canadian Dollar
|
||||
'CHF', // Swiss Franc
|
||||
'CZK', // Czech Koruna
|
||||
'DKK', // Danish Krone
|
||||
'EUR', // Euro
|
||||
'GBP', // British Pound Sterling
|
||||
'HKD', // Hong Kong Dollar
|
||||
'HUF', // Hungarian Forint
|
||||
'ILS', // Israeli New Shekel
|
||||
'JPY', // Japanese Yen
|
||||
'MXN', // Mexican Peso
|
||||
'NOK', // Norwegian Krone
|
||||
'NZD', // New Zealand Dollar
|
||||
'PHP', // Philippine Peso
|
||||
'PLN', // Polish Zloty
|
||||
'SEK', // Swedish Krona
|
||||
'SGD', // Singapore Dollar
|
||||
'THB', // Thai Baht
|
||||
'TWD', // New Taiwan Dollar
|
||||
'USD', // United States Dollar
|
||||
)
|
||||
// phpcs:enable Squiz.Commenting.InlineComment
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -976,5 +299,16 @@ return array(
|
|||
esc_html( $button_text )
|
||||
);
|
||||
},
|
||||
'applepay.wc-gateway' => static function ( ContainerInterface $container ): ApplePayGateway {
|
||||
return new ApplePayGateway(
|
||||
$container->get( 'wcgateway.order-processor' ),
|
||||
$container->get( 'api.factory.paypal-checkout-url' ),
|
||||
$container->get( 'wcgateway.processor.refunds' ),
|
||||
$container->get( 'wcgateway.transaction-url-provider' ),
|
||||
$container->get( 'session.handler' ),
|
||||
$container->get( 'applepay.url' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
||||
);
|
||||
|
|
242
modules/ppcp-applepay/src/ApplePayGateway.php
Normal file
242
modules/ppcp-applepay/src/ApplePayGateway.php
Normal file
|
@ -0,0 +1,242 @@
|
|||
<?php
|
||||
/**
|
||||
* The Apple Pay Payment Gateway
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Applepay
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Applepay;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WC_Payment_Gateway;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\ProcessPaymentTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\GatewayGenericException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\PayPalOrderMissingException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\Messages;
|
||||
|
||||
/**
|
||||
* Class ApplePayGateway
|
||||
*/
|
||||
class ApplePayGateway extends WC_Payment_Gateway {
|
||||
use ProcessPaymentTrait;
|
||||
|
||||
const ID = 'ppcp-applepay';
|
||||
|
||||
/**
|
||||
* The processor for orders.
|
||||
*
|
||||
* @var OrderProcessor
|
||||
*/
|
||||
protected $order_processor;
|
||||
|
||||
/**
|
||||
* The function return the PayPal checkout URL for the given order ID.
|
||||
*
|
||||
* @var callable(string):string
|
||||
*/
|
||||
private $paypal_checkout_url_factory;
|
||||
|
||||
/**
|
||||
* The Refund Processor.
|
||||
*
|
||||
* @var RefundProcessor
|
||||
*/
|
||||
private $refund_processor;
|
||||
|
||||
/**
|
||||
* Service able to provide transaction url for an order.
|
||||
*
|
||||
* @var TransactionUrlProvider
|
||||
*/
|
||||
protected $transaction_url_provider;
|
||||
|
||||
/**
|
||||
* The Session Handler.
|
||||
*
|
||||
* @var SessionHandler
|
||||
*/
|
||||
protected $session_handler;
|
||||
|
||||
/**
|
||||
* The URL to the module.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* ApplePayGateway constructor.
|
||||
*
|
||||
* @param OrderProcessor $order_processor The Order Processor.
|
||||
* @param callable(string):string $paypal_checkout_url_factory The function return the PayPal
|
||||
* checkout URL for the given order
|
||||
* ID.
|
||||
* @param RefundProcessor $refund_processor The Refund Processor.
|
||||
* @param TransactionUrlProvider $transaction_url_provider Service providing transaction
|
||||
* view URL based on order.
|
||||
* @param SessionHandler $session_handler The Session Handler.
|
||||
* @param string $module_url The URL to the module.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
OrderProcessor $order_processor,
|
||||
callable $paypal_checkout_url_factory,
|
||||
RefundProcessor $refund_processor,
|
||||
TransactionUrlProvider $transaction_url_provider,
|
||||
SessionHandler $session_handler,
|
||||
string $module_url,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->id = self::ID;
|
||||
|
||||
$this->method_title = __( 'Apple Pay (via PayPal) ', 'woocommerce-paypal-payments' );
|
||||
$this->method_description = __( 'Display Apple Pay as a standalone payment option instead of bundling it with PayPal.', 'woocommerce-paypal-payments' );
|
||||
|
||||
$this->title = $this->get_option( 'title', __( 'Apple Pay', 'woocommerce-paypal-payments' ) );
|
||||
$this->description = $this->get_option( 'description', '' );
|
||||
|
||||
$this->module_url = $module_url;
|
||||
$this->icon = esc_url( $this->module_url ) . 'assets/images/applepay.svg';
|
||||
|
||||
$this->init_form_fields();
|
||||
$this->init_settings();
|
||||
$this->order_processor = $order_processor;
|
||||
$this->paypal_checkout_url_factory = $paypal_checkout_url_factory;
|
||||
$this->refund_processor = $refund_processor;
|
||||
$this->transaction_url_provider = $transaction_url_provider;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->logger = $logger;
|
||||
|
||||
add_action(
|
||||
'woocommerce_update_options_payment_gateways_' . $this->id,
|
||||
array( $this, 'process_admin_options' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the form fields.
|
||||
*/
|
||||
public function init_form_fields() {
|
||||
$this->form_fields = array(
|
||||
'enabled' => array(
|
||||
'title' => __( 'Enable/Disable', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Enable Apple Pay', 'woocommerce-paypal-payments' ),
|
||||
'default' => 'no',
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Enable/Disable Apple Pay payment gateway.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'title' => array(
|
||||
'title' => __( 'Title', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'default' => $this->title,
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'description' => array(
|
||||
'title' => __( 'Description', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'default' => $this->description,
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process payment for a WooCommerce order.
|
||||
*
|
||||
* @param int $order_id The WooCommerce order id.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function process_payment( $order_id ) : array {
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return $this->handle_payment_failure(
|
||||
null,
|
||||
new GatewayGenericException( new Exception( 'WC order was not found.' ) )
|
||||
);
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_before_process_order', $wc_order );
|
||||
|
||||
try {
|
||||
try {
|
||||
$this->order_processor->process( $wc_order );
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_before_handle_payment_success', $wc_order );
|
||||
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
} catch ( PayPalOrderMissingException $exc ) {
|
||||
$order = $this->order_processor->create_order( $wc_order );
|
||||
|
||||
return array(
|
||||
'result' => 'success',
|
||||
'redirect' => ( $this->paypal_checkout_url_factory )( $order->id() ),
|
||||
);
|
||||
}
|
||||
} catch ( PayPalApiException $error ) {
|
||||
return $this->handle_payment_failure(
|
||||
$wc_order,
|
||||
new Exception(
|
||||
Messages::generic_payment_error_message() . ' ' . $error->getMessage(),
|
||||
$error->getCode(),
|
||||
$error
|
||||
)
|
||||
);
|
||||
} catch ( Exception $error ) {
|
||||
return $this->handle_payment_failure( $wc_order, $error );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process refund.
|
||||
*
|
||||
* If the gateway declares 'refunds' support, this will allow it to refund.
|
||||
* a passed in amount.
|
||||
*
|
||||
* @param int $order_id Order ID.
|
||||
* @param float $amount Refund amount.
|
||||
* @param string $reason Refund reason.
|
||||
*
|
||||
* @return boolean True or false based on success, or a WP_Error object.
|
||||
*/
|
||||
public function process_refund( $order_id, $amount = null, $reason = '' ) : bool {
|
||||
$order = wc_get_order( $order_id );
|
||||
if ( ! is_a( $order, WC_Order::class ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->refund_processor->process( $order, (float) $amount, (string) $reason );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return transaction url for this gateway and given order.
|
||||
*
|
||||
* @param WC_Order $order WC order to get transaction url by.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_transaction_url( $order ) : string {
|
||||
$this->view_transaction_url = $this->transaction_url_provider->get_transaction_url_base( $order );
|
||||
|
||||
return parent::get_transaction_url( $order );
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Applepay;
|
||||
|
||||
use WC_Payment_Gateway;
|
||||
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
|
||||
use WooCommerce\PayPalCommerce\Applepay\Assets\ApplePayButton;
|
||||
use WooCommerce\PayPalCommerce\Applepay\Assets\AppleProductStatus;
|
||||
|
@ -17,30 +18,37 @@ use WooCommerce\PayPalCommerce\Button\Assets\ButtonInterface;
|
|||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
use WooCommerce\PayPalCommerce\Applepay\Helper\AvailabilityNotice;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
/**
|
||||
* Class ApplepayModule
|
||||
*/
|
||||
class ApplepayModule implements ModuleInterface {
|
||||
class ApplepayModule implements ServiceModule, ExtendingModule, ExecutableModule {
|
||||
use ModuleClassNameIdTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setup(): ServiceProviderInterface {
|
||||
return new ServiceProvider(
|
||||
require __DIR__ . '/../services.php',
|
||||
require __DIR__ . '/../extensions.php'
|
||||
);
|
||||
public function services(): array {
|
||||
return require __DIR__ . '/../services.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): void {
|
||||
public function extensions(): array {
|
||||
return require __DIR__ . '/../extensions.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): bool {
|
||||
$module = $this;
|
||||
|
||||
// Clears product status when appropriate.
|
||||
|
@ -117,14 +125,50 @@ class ApplepayModule implements ModuleInterface {
|
|||
100,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_payment_gateways',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
static function ( $methods ) use ( $c ): array {
|
||||
if ( ! is_array( $methods ) ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key for the module.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function getKey() {
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
if ( $settings->has( 'applepay_button_enabled' ) && $settings->get( 'applepay_button_enabled' ) ) {
|
||||
$applepay_gateway = $c->get( 'applepay.wc-gateway' );
|
||||
assert( $applepay_gateway instanceof WC_Payment_Gateway );
|
||||
|
||||
$methods[] = $applepay_gateway;
|
||||
}
|
||||
|
||||
return $methods;
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_review_order_after_submit',
|
||||
function () {
|
||||
// Wrapper ID: #ppc-button-ppcp-applepay.
|
||||
echo '<div id="ppc-button-' . esc_attr( ApplePayGateway::ID ) . '"></div>';
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_pay_order_after_submit',
|
||||
function () {
|
||||
// Wrapper ID: #ppc-button-ppcp-applepay.
|
||||
echo '<div id="ppc-button-' . esc_attr( ApplePayGateway::ID ) . '"></div>';
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -306,7 +350,7 @@ class ApplepayModule implements ModuleInterface {
|
|||
* @param bool $is_sandbox The environment for this merchant.
|
||||
* @return string
|
||||
*/
|
||||
public function validation_string( bool $is_sandbox ) {
|
||||
public function validation_string( bool $is_sandbox ) : string {
|
||||
$sandbox_string = $this->sandbox_validation_string();
|
||||
$live_string = $this->live_validation_string();
|
||||
return $is_sandbox ? $sandbox_string : $live_string;
|
||||
|
|
|
@ -20,12 +20,13 @@ use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\RequestHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
|
||||
|
||||
/**
|
||||
* Class ApplePayButton
|
||||
*/
|
||||
class ApplePayButton implements ButtonInterface {
|
||||
use RequestHandlerTrait;
|
||||
use RequestHandlerTrait, ContextTrait;
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
|
@ -340,7 +341,7 @@ class ApplePayButton implements ButtonInterface {
|
|||
}
|
||||
$response = $this->response_templates->apple_formatted_response( $payment_details );
|
||||
$this->response_templates->response_success( $response );
|
||||
} catch ( \Exception $e ) {
|
||||
} catch ( Exception $e ) {
|
||||
$this->response_templates->response_with_data_errors(
|
||||
array(
|
||||
array(
|
||||
|
@ -382,7 +383,7 @@ class ApplePayButton implements ButtonInterface {
|
|||
}
|
||||
$response = $this->response_templates->apple_formatted_response( $payment_details );
|
||||
$this->response_templates->response_success( $response );
|
||||
} catch ( \Exception $e ) {
|
||||
} catch ( Exception $e ) {
|
||||
$this->response_templates->response_with_data_errors(
|
||||
array(
|
||||
array(
|
||||
|
@ -399,7 +400,7 @@ class ApplePayButton implements ButtonInterface {
|
|||
* On error returns an array of errors to be handled by the script
|
||||
* On success returns the new order data
|
||||
*
|
||||
* @throws \Exception When validation fails.
|
||||
* @throws Exception When validation fails.
|
||||
*/
|
||||
public function create_wc_order(): void {
|
||||
$applepay_request_data_object = $this->applepay_data_object_http();
|
||||
|
@ -420,15 +421,18 @@ class ApplePayButton implements ButtonInterface {
|
|||
$applepay_request_data_object->order_data( $context );
|
||||
|
||||
$this->update_posted_data( $applepay_request_data_object );
|
||||
|
||||
if ( $context === 'product' ) {
|
||||
$cart_item_key = $this->prepare_cart( $applepay_request_data_object );
|
||||
$cart = WC()->cart;
|
||||
$address = $applepay_request_data_object->shipping_address();
|
||||
|
||||
$this->calculate_totals_single_product(
|
||||
$cart,
|
||||
$address,
|
||||
$applepay_request_data_object->shipping_method()
|
||||
);
|
||||
|
||||
if ( ! $cart_item_key ) {
|
||||
$this->response_templates->response_with_data_errors(
|
||||
array(
|
||||
|
@ -438,20 +442,17 @@ class ApplePayButton implements ButtonInterface {
|
|||
),
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
add_filter(
|
||||
'woocommerce_payment_successful_result',
|
||||
function ( array $result ) use ( $cart, $cart_item_key ) : array {
|
||||
if ( ! is_string( $cart_item_key ) ) {
|
||||
return $result;
|
||||
}
|
||||
$this->clear_current_cart( $cart, $cart_item_key );
|
||||
$this->reload_cart( $cart );
|
||||
return $result;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
WC()->checkout()->process_checkout();
|
||||
}
|
||||
|
@ -460,17 +461,20 @@ class ApplePayButton implements ButtonInterface {
|
|||
/**
|
||||
* Checks if the nonce in the data object is valid
|
||||
*
|
||||
* @return bool|int
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_nonce_valid(): bool {
|
||||
$nonce = filter_input( INPUT_POST, 'woocommerce-process-checkout-nonce', FILTER_SANITIZE_SPECIAL_CHARS );
|
||||
if ( ! $nonce ) {
|
||||
return false;
|
||||
}
|
||||
return wp_verify_nonce(
|
||||
|
||||
// Return value 1 indicates "valid nonce, generated in past 12 hours".
|
||||
// Return value 2 also indicated valid nonce, but older than 12 hours.
|
||||
return 1 === wp_verify_nonce(
|
||||
$nonce,
|
||||
'woocommerce-process_checkout'
|
||||
) === 1;
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -511,7 +515,7 @@ class ApplePayButton implements ButtonInterface {
|
|||
$address,
|
||||
$applepay_request_data_object->shipping_method()
|
||||
);
|
||||
if ( is_string( $cart_item_key ) ) {
|
||||
if ( $cart_item_key ) {
|
||||
$this->clear_current_cart( $cart, $cart_item_key );
|
||||
$this->reload_cart( $cart );
|
||||
}
|
||||
|
@ -819,9 +823,9 @@ class ApplePayButton implements ButtonInterface {
|
|||
/**
|
||||
* Removes the old cart, saves it, and creates a new one
|
||||
*
|
||||
* @throws Exception If it cannot be added to cart.
|
||||
* @param ApplePayDataObjectHttp $applepay_request_data_object The request data object.
|
||||
* @return bool | string The cart item key after adding to the new cart.
|
||||
* @throws \Exception If it cannot be added to cart.
|
||||
* @return string The cart item key after adding to the new cart.
|
||||
*/
|
||||
public function prepare_cart( ApplePayDataObjectHttp $applepay_request_data_object ): string {
|
||||
$this->save_old_cart();
|
||||
|
@ -838,7 +842,7 @@ class ApplePayButton implements ButtonInterface {
|
|||
);
|
||||
|
||||
$this->cart_products->add_products( array( $product ) );
|
||||
return $this->cart_products->cart_item_keys()[0];
|
||||
return $this->cart_products->cart_item_keys()[0] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -949,6 +953,7 @@ class ApplePayButton implements ButtonInterface {
|
|||
$render_placeholder,
|
||||
function () {
|
||||
$this->applepay_button();
|
||||
$this->hide_gateway_until_eligible();
|
||||
},
|
||||
21
|
||||
);
|
||||
|
@ -961,6 +966,7 @@ class ApplePayButton implements ButtonInterface {
|
|||
$render_placeholder,
|
||||
function () {
|
||||
$this->applepay_button();
|
||||
$this->hide_gateway_until_eligible();
|
||||
},
|
||||
21
|
||||
);
|
||||
|
@ -973,7 +979,7 @@ class ApplePayButton implements ButtonInterface {
|
|||
add_action(
|
||||
$render_placeholder,
|
||||
function () {
|
||||
echo '<span id="applepay-container-minicart" class="ppcp-button-apm ppcp-button-applepay ppcp-button-minicart"></span>';
|
||||
echo '<span id="ppc-button-applepay-container-minicart" class="ppcp-button-apm ppcp-button-applepay ppcp-button-minicart"></span>';
|
||||
},
|
||||
21
|
||||
);
|
||||
|
@ -981,24 +987,29 @@ class ApplePayButton implements ButtonInterface {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ApplePay button markup
|
||||
*/
|
||||
protected function applepay_button(): void {
|
||||
?>
|
||||
<div id="applepay-container" class="ppcp-button-apm ppcp-button-applepay">
|
||||
<div id="ppc-button-applepay-container" class="ppcp-button-apm ppcp-button-applepay">
|
||||
<?php wp_nonce_field( 'woocommerce-process_checkout', 'woocommerce-process-checkout-nonce' ); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the module should load the script.
|
||||
* Outputs an inline CSS style that hides the Apple Pay gateway (on Classic Checkout).
|
||||
* The style is removed by `ApplepayButton.js` once the eligibility of the payment method
|
||||
* is confirmed.
|
||||
*
|
||||
* @return bool
|
||||
* @return void
|
||||
*/
|
||||
public function should_load_script(): bool {
|
||||
return true;
|
||||
protected function hide_gateway_until_eligible(): void {
|
||||
?>
|
||||
<style id="ppcp-hide-apple-pay">.wc_payment_method.payment_method_ppcp-applepay{display:none}</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,12 +5,14 @@
|
|||
* @package WooCommerce\PayPalCommerce\Applepay
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Applepay\Assets;
|
||||
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\Applepay\ApplePayGateway;
|
||||
use WC_Product;
|
||||
|
||||
/**
|
||||
* Class DataToAppleButtonScripts
|
||||
|
@ -45,57 +47,92 @@ class DataToAppleButtonScripts {
|
|||
* Sets the appropriate data to send to ApplePay script
|
||||
* Data differs between product page and cart page
|
||||
*
|
||||
* @param bool $is_block Whether the button is in a block or not.
|
||||
* @return array
|
||||
* @throws NotFoundException When the setting is not found.
|
||||
*/
|
||||
public function apple_pay_script_data( bool $is_block = false ): array {
|
||||
$base_location = wc_get_base_location();
|
||||
$shop_country_code = $base_location['country'];
|
||||
$currency_code = get_woocommerce_currency();
|
||||
$total_label = get_bloginfo( 'name' );
|
||||
public function apple_pay_script_data() : array {
|
||||
if ( is_product() ) {
|
||||
return $this->data_for_product_page(
|
||||
$shop_country_code,
|
||||
$currency_code,
|
||||
$total_label
|
||||
);
|
||||
return $this->data_for_product_page();
|
||||
}
|
||||
|
||||
return $this->data_for_cart_page(
|
||||
$shop_country_code,
|
||||
$currency_code,
|
||||
$total_label
|
||||
);
|
||||
return $this->data_for_cart_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate admin data to send to ApplePay script
|
||||
*
|
||||
* @return array
|
||||
* @throws NotFoundException When the setting is not found.
|
||||
*/
|
||||
public function apple_pay_script_data_for_admin() : array {
|
||||
return $this->data_for_admin_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full config array for the Apple Pay integration with default values.
|
||||
*
|
||||
* @param array $product - Optional. Product details for the payment button.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_apple_pay_data( array $product = array() ) : array {
|
||||
// true: Use Apple Pay as distinct gateway.
|
||||
// false: integrate it with the smart buttons.
|
||||
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
|
||||
$is_wc_gateway_enabled = isset( $available_gateways[ ApplePayGateway::ID ] );
|
||||
|
||||
// use_wc: Use WC checkout data
|
||||
// use_applepay: Use data provided by Apple Pay.
|
||||
$checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' )
|
||||
? $this->settings->get( 'applepay_checkout_data_mode' )
|
||||
: PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
|
||||
|
||||
// Store country, currency and name.
|
||||
$base_location = wc_get_base_location();
|
||||
$shop_country_code = $base_location['country'];
|
||||
$currency_code = get_woocommerce_currency();
|
||||
$total_label = get_bloginfo( 'name' );
|
||||
|
||||
return $this->data_for_admin_page(
|
||||
$shop_country_code,
|
||||
$currency_code,
|
||||
$total_label
|
||||
// Button layout (label, color, language).
|
||||
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
|
||||
$color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : '';
|
||||
$lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
|
||||
$lang = apply_filters( 'woocommerce_paypal_payments_applepay_button_language', $lang );
|
||||
$is_enabled = $this->settings->has( 'applepay_button_enabled' ) && $this->settings->get( 'applepay_button_enabled' );
|
||||
|
||||
return array(
|
||||
'sdk_url' => $this->sdk_url,
|
||||
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
||||
'is_admin' => false,
|
||||
'is_enabled' => $is_enabled,
|
||||
'is_wc_gateway_enabled' => $is_wc_gateway_enabled,
|
||||
'preferences' => array(
|
||||
'checkout_data_mode' => $checkout_data_mode,
|
||||
),
|
||||
'button' => array(
|
||||
'wrapper' => 'ppc-button-applepay-container',
|
||||
'mini_cart_wrapper' => 'ppc-button-applepay-container-minicart',
|
||||
'type' => $type,
|
||||
'color' => $color,
|
||||
'lang' => $lang,
|
||||
),
|
||||
'product' => $product,
|
||||
'shop' => array(
|
||||
'countryCode' => $shop_country_code,
|
||||
'currencyCode' => $currency_code,
|
||||
'totalLabel' => $total_label,
|
||||
),
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the product needs shipping
|
||||
*
|
||||
* @param \WC_Product $product The product.
|
||||
* @param WC_Product $product Product to check.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function check_if_need_shipping( $product ) {
|
||||
protected function check_if_need_shipping( WC_Product $product ) : bool {
|
||||
if (
|
||||
! wc_shipping_enabled()
|
||||
|| 0 === wc_get_shipping_method_count(
|
||||
|
@ -104,30 +141,20 @@ class DataToAppleButtonScripts {
|
|||
) {
|
||||
return false;
|
||||
}
|
||||
$needs_shipping = false;
|
||||
|
||||
if ( $product->needs_shipping() ) {
|
||||
$needs_shipping = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return $needs_shipping;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the data for the product page.
|
||||
*
|
||||
* @param string $shop_country_code The shop country code.
|
||||
* @param string $currency_code The currency code.
|
||||
* @param string $total_label The label for the total amount.
|
||||
*
|
||||
* @return array
|
||||
* @throws NotFoundException When the setting is not found.
|
||||
*/
|
||||
protected function data_for_product_page(
|
||||
$shop_country_code,
|
||||
$currency_code,
|
||||
$total_label
|
||||
) {
|
||||
protected function data_for_product_page() : array {
|
||||
$product = wc_get_product( get_the_id() );
|
||||
if ( ! $product ) {
|
||||
return array();
|
||||
|
@ -136,146 +163,59 @@ class DataToAppleButtonScripts {
|
|||
if ( $product->get_type() === 'variable' || $product->get_type() === 'variable-subscription' ) {
|
||||
$is_variation = true;
|
||||
}
|
||||
|
||||
$product_need_shipping = $this->check_if_need_shipping( $product );
|
||||
$product_id = get_the_id();
|
||||
$product_price = $product->get_price();
|
||||
$product_stock = $product->get_stock_status();
|
||||
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
|
||||
$color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : '';
|
||||
$lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
|
||||
$checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' ) ? $this->settings->get( 'applepay_checkout_data_mode' ) : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
|
||||
|
||||
return array(
|
||||
'sdk_url' => $this->sdk_url,
|
||||
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false,
|
||||
'is_admin' => false,
|
||||
'preferences' => array(
|
||||
'checkout_data_mode' => $checkout_data_mode,
|
||||
),
|
||||
'button' => array(
|
||||
'wrapper' => 'applepay-container',
|
||||
'mini_cart_wrapper' => 'applepay-container-minicart',
|
||||
'type' => $type,
|
||||
'color' => $color,
|
||||
'lang' => $lang,
|
||||
),
|
||||
'product' => array(
|
||||
return $this->get_apple_pay_data(
|
||||
array(
|
||||
'needShipping' => $product_need_shipping,
|
||||
'id' => $product_id,
|
||||
'price' => $product_price,
|
||||
'isVariation' => $is_variation,
|
||||
'stock' => $product_stock,
|
||||
),
|
||||
'shop' => array(
|
||||
'countryCode' => $shop_country_code,
|
||||
'currencyCode' => $currency_code,
|
||||
'totalLabel' => $total_label,
|
||||
),
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the data for the cart page.
|
||||
*
|
||||
* @param string $shop_country_code The shop country code.
|
||||
* @param string $currency_code The currency code.
|
||||
* @param string $total_label The label for the total amount.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function data_for_cart_page(
|
||||
$shop_country_code,
|
||||
$currency_code,
|
||||
$total_label
|
||||
) {
|
||||
protected function data_for_cart_page() : array {
|
||||
$cart = WC()->cart;
|
||||
if ( ! $cart ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
|
||||
$color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : '';
|
||||
$lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
|
||||
$lang = apply_filters( 'woocommerce_paypal_payments_applepay_button_language', $lang );
|
||||
$checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' ) ? $this->settings->get( 'applepay_checkout_data_mode' ) : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
|
||||
|
||||
return array(
|
||||
'sdk_url' => $this->sdk_url,
|
||||
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false,
|
||||
'is_admin' => false,
|
||||
'preferences' => array(
|
||||
'checkout_data_mode' => $checkout_data_mode,
|
||||
),
|
||||
'button' => array(
|
||||
'wrapper' => 'applepay-container',
|
||||
'mini_cart_wrapper' => 'applepay-container-minicart',
|
||||
'type' => $type,
|
||||
'color' => $color,
|
||||
'lang' => $lang,
|
||||
),
|
||||
'product' => array(
|
||||
return $this->get_apple_pay_data(
|
||||
array(
|
||||
'needShipping' => $cart->needs_shipping(),
|
||||
'subtotal' => $cart->get_subtotal(),
|
||||
),
|
||||
'shop' => array(
|
||||
'countryCode' => $shop_country_code,
|
||||
'currencyCode' => $currency_code,
|
||||
'totalLabel' => $total_label,
|
||||
),
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'woocommerce-process_checkout' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the data for the cart page.
|
||||
* Consider refactoring this method along with data_for_cart_page() and data_for_product_page() methods.
|
||||
*
|
||||
* @param string $shop_country_code The shop country code.
|
||||
* @param string $currency_code The currency code.
|
||||
* @param string $total_label The label for the total amount.
|
||||
* Consider refactoring this method along with data_for_cart_page() and data_for_product_page()
|
||||
* methods.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function data_for_admin_page(
|
||||
$shop_country_code,
|
||||
$currency_code,
|
||||
$total_label
|
||||
) {
|
||||
$type = $this->settings->has( 'applepay_button_type' ) ? $this->settings->get( 'applepay_button_type' ) : '';
|
||||
$color = $this->settings->has( 'applepay_button_color' ) ? $this->settings->get( 'applepay_button_color' ) : '';
|
||||
$lang = $this->settings->has( 'applepay_button_language' ) ? $this->settings->get( 'applepay_button_language' ) : '';
|
||||
$lang = apply_filters( 'woocommerce_paypal_payments_applepay_button_language', $lang );
|
||||
$checkout_data_mode = $this->settings->has( 'applepay_checkout_data_mode' ) ? $this->settings->get( 'applepay_checkout_data_mode' ) : PropertiesDictionary::BILLING_DATA_MODE_DEFAULT;
|
||||
$is_enabled = $this->settings->has( 'applepay_button_enabled' ) && $this->settings->get( 'applepay_button_enabled' );
|
||||
|
||||
return array(
|
||||
'sdk_url' => $this->sdk_url,
|
||||
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
||||
'is_admin' => true,
|
||||
'is_enabled' => $is_enabled,
|
||||
'preferences' => array(
|
||||
'checkout_data_mode' => $checkout_data_mode,
|
||||
),
|
||||
'button' => array(
|
||||
'wrapper' => 'applepay-container',
|
||||
'mini_cart_wrapper' => 'applepay-container-minicart',
|
||||
'type' => $type,
|
||||
'color' => $color,
|
||||
'lang' => $lang,
|
||||
),
|
||||
'product' => array(
|
||||
protected function data_for_admin_page() : array {
|
||||
$data = $this->get_apple_pay_data(
|
||||
array(
|
||||
'needShipping' => false,
|
||||
'subtotal' => 0,
|
||||
),
|
||||
'shop' => array(
|
||||
'countryCode' => $shop_country_code,
|
||||
'currencyCode' => $currency_code,
|
||||
'totalLabel' => $total_label,
|
||||
),
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
)
|
||||
);
|
||||
|
||||
$data['is_admin'] = true;
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,18 @@ namespace WooCommerce\PayPalCommerce\Applepay\Helper;
|
|||
class ApmApplies {
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for ApplePay.
|
||||
* The list of which countries can be used for ApplePay.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_country_currency_matrix;
|
||||
private $allowed_countries;
|
||||
|
||||
/**
|
||||
* The list of which currencies can be used for ApplePay.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_currencies;
|
||||
|
||||
/**
|
||||
* 3-letter currency code of the shop.
|
||||
|
@ -37,32 +44,41 @@ class ApmApplies {
|
|||
private $country;
|
||||
|
||||
/**
|
||||
* ApmApplies constructor.
|
||||
* DccApplies constructor.
|
||||
*
|
||||
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for ApplePay.
|
||||
* @param array $allowed_countries The list of which countries can be used for ApplePay.
|
||||
* @param array $allowed_currencies The list of which currencies can be used for ApplePay.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
*/
|
||||
public function __construct(
|
||||
array $allowed_country_currency_matrix,
|
||||
array $allowed_countries,
|
||||
array $allowed_currencies,
|
||||
string $currency,
|
||||
string $country
|
||||
) {
|
||||
$this->allowed_country_currency_matrix = $allowed_country_currency_matrix;
|
||||
$this->allowed_countries = $allowed_countries;
|
||||
$this->allowed_currencies = $allowed_currencies;
|
||||
$this->currency = $currency;
|
||||
$this->country = $country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether ApplePay can be used in the current country and the current currency used.
|
||||
* Returns whether ApplePay can be used in the current country used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_country_currency(): bool {
|
||||
if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) {
|
||||
return false;
|
||||
public function for_country(): bool {
|
||||
return in_array( $this->country, $this->allowed_countries, true );
|
||||
}
|
||||
return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true );
|
||||
|
||||
/**
|
||||
* Returns whether ApplePay can be used in the current currency used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_currency(): bool {
|
||||
return in_array( $this->currency, $this->allowed_currencies, true );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"description": "Axo module for PPCP",
|
||||
"license": "GPL-2.0",
|
||||
"require": {
|
||||
"php": "^7.2 | ^8.0",
|
||||
"php": "^7.4 | ^8.0",
|
||||
"dhii/module-interface": "^0.3.0-alpha1"
|
||||
},
|
||||
"autoload": {
|
||||
|
|
|
@ -17,7 +17,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager;
|
|||
|
||||
return array(
|
||||
|
||||
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
||||
'wcgateway.settings.fields' => function ( array $fields, ContainerInterface $container ): array {
|
||||
|
||||
$insert_after = function( array $array, string $key, array $new ): array {
|
||||
$keys = array_keys( $array );
|
||||
|
@ -83,7 +83,7 @@ return array(
|
|||
->rule()
|
||||
->condition_element( 'axo_enabled', '1' )
|
||||
->action_visible( 'axo_gateway_title' )
|
||||
->action_visible( 'axo_checkout_config_notice' )
|
||||
->action_visible( 'axo_main_notice' )
|
||||
->action_visible( 'axo_privacy' )
|
||||
->action_visible( 'axo_name_on_card' )
|
||||
->action_visible( 'axo_style_heading' )
|
||||
|
@ -114,9 +114,17 @@ return array(
|
|||
),
|
||||
'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ),
|
||||
),
|
||||
'axo_checkout_config_notice' => array(
|
||||
'axo_main_notice' => array(
|
||||
'heading' => '',
|
||||
'html' => $container->get( 'axo.checkout-config-notice' ),
|
||||
'html' => implode(
|
||||
'',
|
||||
array(
|
||||
$container->get( 'axo.settings-conflict-notice' ),
|
||||
$container->get( 'axo.shipping-config-notice' ),
|
||||
$container->get( 'axo.checkout-config-notice' ),
|
||||
$container->get( 'axo.incompatible-plugins-notice' ),
|
||||
)
|
||||
),
|
||||
'type' => 'ppcp-html',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'class' => array(),
|
||||
|
|
|
@ -9,8 +9,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Axo;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
|
||||
return static function (): ModuleInterface {
|
||||
return static function (): AxoModule {
|
||||
return new AxoModule();
|
||||
};
|
||||
|
|
|
@ -709,6 +709,8 @@ class AxoManager {
|
|||
}`
|
||||
);
|
||||
|
||||
this.emailInput.value = this.stripSpaces( this.emailInput.value );
|
||||
|
||||
this.$( this.el.paymentContainer.selector + '-detail' ).html( '' );
|
||||
this.$( this.el.paymentContainer.selector + '-form' ).html( '' );
|
||||
|
||||
|
@ -1134,6 +1136,10 @@ class AxoManager {
|
|||
return emailPattern.test( value );
|
||||
}
|
||||
|
||||
stripSpaces( str ) {
|
||||
return str.replace( /\s+/g, '' );
|
||||
}
|
||||
|
||||
validateEmail( billingEmail ) {
|
||||
const billingEmailSelector = document.querySelector( billingEmail );
|
||||
const value = document.querySelector( billingEmail + ' input' ).value;
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
export function log( message, level = 'info' ) {
|
||||
const wpDebug = window.wc_ppcp_axo?.wp_debug;
|
||||
const endpoint = window.wc_ppcp_axo?.ajax?.frontend_logger?.endpoint;
|
||||
if ( ! endpoint ) {
|
||||
const loggingEnabled = window.wc_ppcp_axo?.logging_enabled;
|
||||
|
||||
if ( wpDebug ) {
|
||||
switch ( level ) {
|
||||
case 'error':
|
||||
console.error( `[AXO] ${ message }` );
|
||||
break;
|
||||
default:
|
||||
console.log( `[AXO] ${ message }` );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! endpoint || ! loggingEnabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -15,15 +27,5 @@ export function log( message, level = 'info' ) {
|
|||
level,
|
||||
},
|
||||
} ),
|
||||
} ).then( () => {
|
||||
if ( wpDebug ) {
|
||||
switch ( level ) {
|
||||
case 'error':
|
||||
console.error( `[AXO] ${ message }` );
|
||||
break;
|
||||
default:
|
||||
console.log( `[AXO] ${ message }` );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
|
|
@ -12,10 +12,10 @@ namespace WooCommerce\PayPalCommerce\Axo;
|
|||
use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager;
|
||||
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
||||
use WooCommerce\PayPalCommerce\Axo\Helper\ApmApplies;
|
||||
use WooCommerce\PayPalCommerce\Axo\Helper\SettingsNoticeGenerator;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
return array(
|
||||
|
@ -25,7 +25,7 @@ return array(
|
|||
$apm_applies = $container->get( 'axo.helpers.apm-applies' );
|
||||
assert( $apm_applies instanceof ApmApplies );
|
||||
|
||||
return $apm_applies->for_country_currency() && $apm_applies->for_settings();
|
||||
return $apm_applies->for_country_currency();
|
||||
},
|
||||
|
||||
'axo.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies {
|
||||
|
@ -36,6 +36,10 @@ return array(
|
|||
);
|
||||
},
|
||||
|
||||
'axo.helpers.settings-notice-generator' => static function ( ContainerInterface $container ) : SettingsNoticeGenerator {
|
||||
return new SettingsNoticeGenerator();
|
||||
},
|
||||
|
||||
// If AXO is configured and onboarded.
|
||||
'axo.available' => static function ( ContainerInterface $container ): bool {
|
||||
return true;
|
||||
|
@ -159,48 +163,35 @@ return array(
|
|||
);
|
||||
},
|
||||
|
||||
'axo.settings-conflict-notice' => static function ( ContainerInterface $container ) : string {
|
||||
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
|
||||
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
|
||||
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
return $settings_notice_generator->generate_settings_conflict_notice( $settings );
|
||||
},
|
||||
|
||||
'axo.checkout-config-notice' => static function ( ContainerInterface $container ) : string {
|
||||
$checkout_page_link = esc_url( get_edit_post_link( wc_get_page_id( 'checkout' ) ) ?? '' );
|
||||
$block_checkout_docs_link = __(
|
||||
'https://woocommerce.com/document/cart-checkout-blocks-status/#reverting-to-the-cart-and-checkout-shortcodes',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
|
||||
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
|
||||
|
||||
if ( CartCheckoutDetector::has_elementor_checkout() ) {
|
||||
$notice_content = sprintf(
|
||||
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
|
||||
__(
|
||||
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the <code>Elementor Checkout widget</code>. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $checkout_page_link ),
|
||||
esc_url( $block_checkout_docs_link )
|
||||
);
|
||||
} elseif ( CartCheckoutDetector::has_block_checkout() ) {
|
||||
$notice_content = sprintf(
|
||||
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
|
||||
__(
|
||||
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the WooCommerce <code>Checkout</code> block. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $checkout_page_link ),
|
||||
esc_url( $block_checkout_docs_link )
|
||||
);
|
||||
} elseif ( ! CartCheckoutDetector::has_classic_checkout() ) {
|
||||
$notice_content = sprintf(
|
||||
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
|
||||
__(
|
||||
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store does not seem to be properly configured or uses an incompatible <code>third-party Checkout</code> solution. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $checkout_page_link ),
|
||||
esc_url( $block_checkout_docs_link )
|
||||
);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
return $settings_notice_generator->generate_checkout_notice();
|
||||
},
|
||||
|
||||
return '<div class="ppcp-notice ppcp-notice-error"><p>' . $notice_content . '</p></div>';
|
||||
'axo.shipping-config-notice' => static function ( ContainerInterface $container ) : string {
|
||||
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
|
||||
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
|
||||
|
||||
return $settings_notice_generator->generate_shipping_notice();
|
||||
},
|
||||
|
||||
'axo.incompatible-plugins-notice' => static function ( ContainerInterface $container ) : string {
|
||||
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
|
||||
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
|
||||
|
||||
return $settings_notice_generator->generate_incompatible_plugins_notice();
|
||||
},
|
||||
|
||||
'axo.smart-button-location-notice' => static function ( ContainerInterface $container ) : string {
|
||||
|
@ -230,6 +221,7 @@ return array(
|
|||
|
||||
return '<div class="ppcp-notice ppcp-notice-warning"><p>' . $notice_content . '</p></div>';
|
||||
},
|
||||
|
||||
'axo.endpoint.frontend-logger' => static function ( ContainerInterface $container ): FrontendLoggerEndpoint {
|
||||
return new FrontendLoggerEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
|
|
|
@ -216,6 +216,7 @@ class AxoManager {
|
|||
'nonce' => wp_create_nonce( FrontendLoggerEndpoint::nonce() ),
|
||||
),
|
||||
),
|
||||
'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '',
|
||||
'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
||||
'billing_email_button_text' => __( 'Continue', 'woocommerce-paypal-payments' ),
|
||||
);
|
||||
|
|
|
@ -17,9 +17,10 @@ use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager;
|
|||
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
@ -29,21 +30,27 @@ use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
|||
/**
|
||||
* Class AxoModule
|
||||
*/
|
||||
class AxoModule implements ModuleInterface {
|
||||
class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
|
||||
use ModuleClassNameIdTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setup(): ServiceProviderInterface {
|
||||
return new ServiceProvider(
|
||||
require __DIR__ . '/../services.php',
|
||||
require __DIR__ . '/../extensions.php'
|
||||
);
|
||||
public function services(): array {
|
||||
return require __DIR__ . '/../services.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): void {
|
||||
public function extensions(): array {
|
||||
return require __DIR__ . '/../extensions.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): bool {
|
||||
|
||||
add_filter(
|
||||
'woocommerce_payment_gateways',
|
||||
|
@ -66,7 +73,7 @@ class AxoModule implements ModuleInterface {
|
|||
|
||||
// Add the gateway in admin area.
|
||||
if ( is_admin() ) {
|
||||
$methods[] = $gateway;
|
||||
// $methods[] = $gateway; - Temporarily remove Fastlane from the payment gateway list in admin area.
|
||||
return $methods;
|
||||
}
|
||||
|
||||
|
@ -77,9 +84,10 @@ class AxoModule implements ModuleInterface {
|
|||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$is_paypal_enabled = $settings->has( 'enabled' ) && $settings->get( 'enabled' ) ?? false;
|
||||
$is_dcc_enabled = $settings->has( 'dcc_enabled' ) && $settings->get( 'dcc_enabled' ) ?? false;
|
||||
|
||||
if ( ! $is_dcc_enabled ) {
|
||||
if ( ! $is_paypal_enabled || ! $is_dcc_enabled ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
|
@ -87,6 +95,10 @@ class AxoModule implements ModuleInterface {
|
|||
return $methods;
|
||||
}
|
||||
|
||||
if ( ! $this->is_compatible_shipping_config() ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
$methods[] = $gateway;
|
||||
return $methods;
|
||||
},
|
||||
|
@ -144,13 +156,20 @@ class AxoModule implements ModuleInterface {
|
|||
function () use ( $c ) {
|
||||
$module = $this;
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$is_paypal_enabled = $settings->has( 'enabled' ) && $settings->get( 'enabled' ) ?? false;
|
||||
|
||||
$subscription_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscription_helper instanceof SubscriptionHelper );
|
||||
|
||||
// Check if the module is applicable, correct country, currency, ... etc.
|
||||
if ( ! $c->get( 'axo.eligible' )
|
||||
if ( ! $is_paypal_enabled
|
||||
|| ! $c->get( 'axo.eligible' )
|
||||
|| 'continuation' === $c->get( 'button.context' )
|
||||
|| $subscription_helper->cart_contains_subscription() ) {
|
||||
|| $subscription_helper->cart_contains_subscription()
|
||||
|| ! $this->is_compatible_shipping_config() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -194,9 +213,17 @@ class AxoModule implements ModuleInterface {
|
|||
|
||||
add_action(
|
||||
'wp_head',
|
||||
function () {
|
||||
function () use ( $c ) {
|
||||
// phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
|
||||
echo '<script async src="https://www.paypalobjects.com/insights/v1/paypal-insights.sandbox.min.js"></script>';
|
||||
|
||||
// Add meta tag to allow feature-detection of the site's AXO payment state.
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$this->add_feature_detection_tag(
|
||||
$settings->has( 'axo_enabled' ) && $settings->get( 'axo_enabled' )
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -264,6 +291,7 @@ class AxoModule implements ModuleInterface {
|
|||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -280,15 +308,7 @@ class AxoModule implements ModuleInterface {
|
|||
array $localized_script_data
|
||||
): array {
|
||||
try {
|
||||
$target_customer_id = '';
|
||||
if ( is_user_logged_in() ) {
|
||||
$target_customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
|
||||
if ( ! $target_customer_id ) {
|
||||
$target_customer_id = get_user_meta( get_current_user_id(), 'ppcp_customer_id', true );
|
||||
}
|
||||
}
|
||||
|
||||
$sdk_client_token = $api->sdk_client_token( $target_customer_id );
|
||||
$sdk_client_token = $api->sdk_client_token();
|
||||
$localized_script_data['axo'] = array(
|
||||
'sdk_client_token' => $sdk_client_token,
|
||||
);
|
||||
|
@ -305,14 +325,6 @@ class AxoModule implements ModuleInterface {
|
|||
return $localized_script_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key for the module.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function getKey() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition to evaluate if Credit Card gateway should be hidden.
|
||||
*
|
||||
|
@ -341,6 +353,7 @@ class AxoModule implements ModuleInterface {
|
|||
|
||||
return ! is_user_logged_in()
|
||||
&& CartCheckoutDetector::has_classic_checkout()
|
||||
&& $this->is_compatible_shipping_config()
|
||||
&& $is_axo_enabled
|
||||
&& $is_dcc_enabled
|
||||
&& ! $this->is_excluded_endpoint();
|
||||
|
@ -396,4 +409,32 @@ class AxoModule implements ModuleInterface {
|
|||
// Exclude the Order Pay endpoint.
|
||||
return is_wc_endpoint_url( 'order-pay' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition to evaluate if the shipping configuration is compatible.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_compatible_shipping_config(): bool {
|
||||
return ! wc_shipping_enabled() || ( wc_shipping_enabled() && ! wc_ship_to_billing_address_only() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a meta tag to allow feature detection on certain pages.
|
||||
*
|
||||
* @param bool $axo_enabled Whether the gateway is enabled.
|
||||
* @return void
|
||||
*/
|
||||
private function add_feature_detection_tag( bool $axo_enabled ) {
|
||||
$show_tag = is_checkout() || is_cart() || is_shop();
|
||||
|
||||
if ( ! $show_tag ) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf(
|
||||
'<meta name="ppcp.axo" content="ppcp.axo.%s" />',
|
||||
$axo_enabled ? 'enabled' : 'disabled'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ class AxoGateway extends WC_Payment_Gateway {
|
|||
? $this->ppcp_settings->get( 'axo_gateway_title' )
|
||||
: $this->get_option( 'title', $this->method_title );
|
||||
|
||||
$this->description = __( 'Enter your email address to continue.', 'woocommerce-paypal-payments' );
|
||||
$this->description = __( 'Enter your email address above to continue.', 'woocommerce-paypal-payments' );
|
||||
|
||||
$this->init_form_fields();
|
||||
$this->init_settings();
|
||||
|
|
|
@ -64,19 +64,4 @@ class ApmApplies {
|
|||
}
|
||||
return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the settings are compatible with AXO.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_settings(): bool {
|
||||
if ( get_option( 'woocommerce_ship_to_destination' ) === 'billing_only' ) { // Force shipping to the customer billing address.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
179
modules/ppcp-axo/src/Helper/SettingsNoticeGenerator.php
Normal file
179
modules/ppcp-axo/src/Helper/SettingsNoticeGenerator.php
Normal file
|
@ -0,0 +1,179 @@
|
|||
<?php
|
||||
/**
|
||||
* Settings notice generator.
|
||||
* Generates the settings notices.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Axo\Helper
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Axo\Helper;
|
||||
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class SettingsNoticeGenerator
|
||||
*/
|
||||
class SettingsNoticeGenerator {
|
||||
/**
|
||||
* Generates the full HTML of the notification.
|
||||
*
|
||||
* @param string $message HTML of the inner message contents.
|
||||
* @param bool $is_error Whether the provided message is an error. Affects the notice color.
|
||||
*
|
||||
* @return string The full HTML code of the notification, or an empty string.
|
||||
*/
|
||||
private function render_notice( string $message, bool $is_error = false ) : string {
|
||||
if ( ! $message ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<div class="ppcp-notice %1$s"><p>%2$s</p></div>',
|
||||
$is_error ? 'ppcp-notice-error' : '',
|
||||
$message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the checkout notice.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generate_checkout_notice(): string {
|
||||
$checkout_page_link = esc_url( get_edit_post_link( wc_get_page_id( 'checkout' ) ) ?? '' );
|
||||
$block_checkout_docs_link = __(
|
||||
'https://woocommerce.com/document/cart-checkout-blocks-status/#reverting-to-the-cart-and-checkout-shortcodes',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
|
||||
$notice_content = '';
|
||||
|
||||
if ( CartCheckoutDetector::has_elementor_checkout() ) {
|
||||
$notice_content = sprintf(
|
||||
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
|
||||
__(
|
||||
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the <code>Elementor Checkout widget</code>. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $checkout_page_link ),
|
||||
esc_url( $block_checkout_docs_link )
|
||||
);
|
||||
} elseif ( CartCheckoutDetector::has_block_checkout() ) {
|
||||
$notice_content = sprintf(
|
||||
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
|
||||
__(
|
||||
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the WooCommerce <code>Checkout</code> block. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $checkout_page_link ),
|
||||
esc_url( $block_checkout_docs_link )
|
||||
);
|
||||
} elseif ( ! CartCheckoutDetector::has_classic_checkout() ) {
|
||||
$notice_content = sprintf(
|
||||
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
|
||||
__(
|
||||
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store does not seem to be properly configured or uses an incompatible <code>third-party Checkout</code> solution. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $checkout_page_link ),
|
||||
esc_url( $block_checkout_docs_link )
|
||||
);
|
||||
}
|
||||
|
||||
return $notice_content ? '<div class="ppcp-notice ppcp-notice-error"><p>' . $notice_content . '</p></div>' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the shipping notice.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generate_shipping_notice(): string {
|
||||
$shipping_settings_link = admin_url( 'admin.php?page=wc-settings&tab=shipping§ion=options' );
|
||||
|
||||
$notice_content = '';
|
||||
|
||||
if ( wc_shipping_enabled() && wc_ship_to_billing_address_only() ) {
|
||||
$notice_content = sprintf(
|
||||
/* translators: %1$s: URL to the Shipping destination settings page. */
|
||||
__(
|
||||
'<span class="highlight">Warning:</span> The <a href="%1$s">Shipping destination</a> of your store is currently configured to <code>Force shipping to the customer billing address</code>. To enable Fastlane and accelerate payments, the shipping destination must be configured either to <code>Default to customer shipping address</code> or <code>Default to customer billing address</code> so buyers can set separate billing and shipping details.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $shipping_settings_link )
|
||||
);
|
||||
}
|
||||
|
||||
return $notice_content ? '<div class="ppcp-notice ppcp-notice-error"><p>' . $notice_content . '</p></div>' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the incompatible plugins notice.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generate_incompatible_plugins_notice(): string {
|
||||
$incompatible_plugins = array(
|
||||
'Elementor' => did_action( 'elementor/loaded' ),
|
||||
'CheckoutWC' => defined( 'CFW_NAME' ),
|
||||
);
|
||||
|
||||
$active_plugins_list = array_filter( $incompatible_plugins );
|
||||
|
||||
if ( empty( $active_plugins_list ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$incompatible_plugin_items = array_map(
|
||||
function ( $plugin ) {
|
||||
return "<li>{$plugin}</li>";
|
||||
},
|
||||
array_keys( $active_plugins_list )
|
||||
);
|
||||
|
||||
$plugins_settings_link = esc_url( admin_url( 'plugins.php' ) );
|
||||
$notice_content = sprintf(
|
||||
/* translators: %1$s: URL to the plugins settings page. %2$s: List of incompatible plugins. */
|
||||
__(
|
||||
'<span class="highlight">Note:</span> The accelerated guest buyer experience provided by Fastlane may not be fully compatible with some of the following <a href="%1$s">active plugins</a>: <ul class="ppcp-notice-list">%2$s</ul>',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
$plugins_settings_link,
|
||||
implode( '', $incompatible_plugin_items )
|
||||
);
|
||||
|
||||
return '<div class="ppcp-notice"><p>' . $notice_content . '</p></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a warning notice with instructions on conflicting plugin-internal settings.
|
||||
*
|
||||
* @param Settings $settings The plugin settings container, which is checked for conflicting
|
||||
* values.
|
||||
* @return string
|
||||
*/
|
||||
public function generate_settings_conflict_notice( Settings $settings ) : string {
|
||||
$notice_content = '';
|
||||
$is_dcc_enabled = false;
|
||||
|
||||
try {
|
||||
$is_dcc_enabled = $settings->has( 'dcc_enabled' ) && $settings->get( 'dcc_enabled' );
|
||||
// phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||
} catch ( NotFoundException $ignored ) {
|
||||
// Never happens.
|
||||
}
|
||||
|
||||
if ( ! $is_dcc_enabled ) {
|
||||
$notice_content = __(
|
||||
'<span class="highlight">Warning:</span> To enable Fastlane and accelerate payments, the <strong>Advanced Card Processing</strong> payment method must also be enabled.',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->render_notice( $notice_content, true );
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
"description": "Blocks module for PPCP",
|
||||
"license": "GPL-2.0",
|
||||
"require": {
|
||||
"php": "^7.2 | ^8.0",
|
||||
"php": "^7.4 | ^8.0",
|
||||
"dhii/module-interface": "^0.3.0-alpha1"
|
||||
},
|
||||
"autoload": {
|
||||
|
|
|
@ -13,7 +13,7 @@ use WooCommerce\PayPalCommerce\Onboarding\State;
|
|||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
return array(
|
||||
'wcgateway.button.locations' => function ( ContainerInterface $container, array $locations ): array {
|
||||
'wcgateway.button.locations' => function ( array $locations, ContainerInterface $container ): array {
|
||||
return array_merge(
|
||||
$locations,
|
||||
array(
|
||||
|
@ -22,13 +22,13 @@ return array(
|
|||
)
|
||||
);
|
||||
},
|
||||
'wcgateway.settings.pay-later.messaging-locations' => function ( ContainerInterface $container, array $locations ): array {
|
||||
'wcgateway.settings.pay-later.messaging-locations' => function ( array $locations, ContainerInterface $container ): array {
|
||||
unset( $locations['checkout-block-express'] );
|
||||
unset( $locations['cart-block'] );
|
||||
return $locations;
|
||||
},
|
||||
|
||||
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
||||
'wcgateway.settings.fields' => function ( array $fields, ContainerInterface $container ): array {
|
||||
$insert_after = function( array $array, string $key, array $new ): array {
|
||||
$keys = array_keys( $array );
|
||||
$index = array_search( $key, $keys, true );
|
||||
|
@ -61,7 +61,7 @@ return array(
|
|||
'title' => __( 'Require final confirmation on checkout', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'label' => $label,
|
||||
'default' => false,
|
||||
'default' => true,
|
||||
'screens' => array( State::STATE_START, State::STATE_ONBOARDED ),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'paypal',
|
||||
|
@ -72,7 +72,7 @@ return array(
|
|||
);
|
||||
},
|
||||
|
||||
'button.pay-now-contexts' => function ( ContainerInterface $container, array $contexts ): array {
|
||||
'button.pay-now-contexts' => function ( array $contexts, ContainerInterface $container ): array {
|
||||
if ( ! $container->get( 'blocks.settings.final_review_enabled' ) ) {
|
||||
$contexts[] = 'checkout-block';
|
||||
$contexts[] = 'cart-block';
|
||||
|
@ -81,7 +81,7 @@ return array(
|
|||
return $contexts;
|
||||
},
|
||||
|
||||
'button.handle-shipping-in-paypal' => function ( ContainerInterface $container ): bool {
|
||||
'button.handle-shipping-in-paypal' => function ( bool $previous, ContainerInterface $container ): bool {
|
||||
return ! $container->get( 'blocks.settings.final_review_enabled' );
|
||||
},
|
||||
);
|
||||
|
|
|
@ -9,8 +9,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Blocks;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
|
||||
return static function (): ModuleInterface {
|
||||
return static function (): BlocksModule {
|
||||
return new BlocksModule();
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"Edge >= 14"
|
||||
],
|
||||
"dependencies": {
|
||||
"@paypal/react-paypal-js": "^8.3.0",
|
||||
"@paypal/react-paypal-js": "^8.5.0",
|
||||
"core-js": "^3.25.0",
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0"
|
||||
|
|
|
@ -7,7 +7,12 @@ import {
|
|||
} from '@paypal/react-paypal-js';
|
||||
|
||||
import { CheckoutHandler } from './checkout-handler';
|
||||
import { createOrder, onApprove } from '../card-fields-config';
|
||||
import {
|
||||
createOrder,
|
||||
onApprove,
|
||||
createVaultSetupToken,
|
||||
onApproveSavePayment,
|
||||
} from '../card-fields-config';
|
||||
import { cartHasSubscriptionProducts } from '../Helper/Subscription';
|
||||
|
||||
export function CardFields( {
|
||||
|
@ -70,8 +75,21 @@ export function CardFields( {
|
|||
} }
|
||||
>
|
||||
<PayPalCardFieldsProvider
|
||||
createOrder={ createOrder }
|
||||
onApprove={ onApprove }
|
||||
createVaultSetupToken={
|
||||
config.scriptData.is_free_trial_cart
|
||||
? createVaultSetupToken
|
||||
: undefined
|
||||
}
|
||||
createOrder={
|
||||
config.scriptData.is_free_trial_cart
|
||||
? undefined
|
||||
: createOrder
|
||||
}
|
||||
onApprove={
|
||||
config.scriptData.is_free_trial_cart
|
||||
? onApproveSavePayment
|
||||
: onApprove
|
||||
}
|
||||
onError={ ( err ) => {
|
||||
console.error( err );
|
||||
} }
|
||||
|
|
|
@ -44,3 +44,61 @@ export async function onApprove( data ) {
|
|||
console.error( err );
|
||||
} );
|
||||
}
|
||||
|
||||
export async function createVaultSetupToken() {
|
||||
const config = wc.wcSettings.getSetting( 'ppcp-credit-card-gateway_data' );
|
||||
|
||||
return fetch( config.scriptData.ajax.create_setup_token.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify( {
|
||||
nonce: config.scriptData.ajax.create_setup_token.nonce,
|
||||
payment_method: 'ppcp-credit-card-gateway',
|
||||
} ),
|
||||
} )
|
||||
.then( ( response ) => response.json() )
|
||||
.then( ( result ) => {
|
||||
console.log( result );
|
||||
return result.data.id;
|
||||
} )
|
||||
.catch( ( err ) => {
|
||||
console.error( err );
|
||||
} );
|
||||
}
|
||||
|
||||
export async function onApproveSavePayment( { vaultSetupToken } ) {
|
||||
const config = wc.wcSettings.getSetting( 'ppcp-credit-card-gateway_data' );
|
||||
|
||||
let endpoint =
|
||||
config.scriptData.ajax.create_payment_token_for_guest.endpoint;
|
||||
let bodyContent = {
|
||||
nonce: config.scriptData.ajax.create_payment_token_for_guest.nonce,
|
||||
vault_setup_token: vaultSetupToken,
|
||||
};
|
||||
|
||||
if ( config.scriptData.user.is_logged_in ) {
|
||||
endpoint = config.scriptData.ajax.create_payment_token.endpoint;
|
||||
|
||||
bodyContent = {
|
||||
nonce: config.scriptData.ajax.create_payment_token.nonce,
|
||||
vault_setup_token: vaultSetupToken,
|
||||
is_free_trial_cart: config.scriptData.is_free_trial_cart,
|
||||
};
|
||||
}
|
||||
|
||||
const response = await fetch( endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify( bodyContent ),
|
||||
} );
|
||||
|
||||
const result = await response.json();
|
||||
if ( result.success !== true ) {
|
||||
console.error( result );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -227,7 +227,7 @@ const PayPalComponent = ( {
|
|||
throw new Error( config.scriptData.labels.error.generic );
|
||||
}
|
||||
|
||||
if ( ! shouldHandleShippingInPayPal() ) {
|
||||
if ( ! shouldskipFinalConfirmation() ) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
} else {
|
||||
setGotoContinuationOnError( true );
|
||||
|
@ -318,7 +318,7 @@ const PayPalComponent = ( {
|
|||
throw new Error( config.scriptData.labels.error.generic );
|
||||
}
|
||||
|
||||
if ( ! shouldHandleShippingInPayPal() ) {
|
||||
if ( ! shouldskipFinalConfirmation() ) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
} else {
|
||||
setGotoContinuationOnError( true );
|
||||
|
@ -364,6 +364,10 @@ const PayPalComponent = ( {
|
|||
};
|
||||
|
||||
const shouldHandleShippingInPayPal = () => {
|
||||
return shouldskipFinalConfirmation() && config.needShipping
|
||||
};
|
||||
|
||||
const shouldskipFinalConfirmation = () => {
|
||||
if ( config.finalReviewEnabled ) {
|
||||
return false;
|
||||
}
|
||||
|
@ -544,7 +548,7 @@ const PayPalComponent = ( {
|
|||
if ( config.scriptData.continuation ) {
|
||||
return true;
|
||||
}
|
||||
if ( shouldHandleShippingInPayPal() ) {
|
||||
if ( shouldskipFinalConfirmation() ) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
}
|
||||
return true;
|
||||
|
@ -606,9 +610,11 @@ const PayPalComponent = ( {
|
|||
}
|
||||
|
||||
return ( data, actions ) => {
|
||||
shouldHandleShippingInPayPal()
|
||||
let shippingAddressChange = shouldHandleShippingInPayPal()
|
||||
? handleShippingAddressChange( data, actions )
|
||||
: null;
|
||||
|
||||
return shippingAddressChange;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ use WooCommerce\PayPalCommerce\Blocks\Endpoint\GetPayPalOrderFromSession;
|
|||
use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
use WC_Cart;
|
||||
|
||||
return array(
|
||||
'blocks.url' => static function ( ContainerInterface $container ): string {
|
||||
|
@ -27,6 +28,13 @@ return array(
|
|||
);
|
||||
},
|
||||
'blocks.method' => static function ( ContainerInterface $container ): PayPalPaymentMethod {
|
||||
/**
|
||||
* Cart instance; might be null, esp. in customizer or in Block Editor.
|
||||
*
|
||||
* @var null|WC_Cart $cart
|
||||
*/
|
||||
$cart = WC()->cart;
|
||||
|
||||
return new PayPalPaymentMethod(
|
||||
$container->get( 'blocks.url' ),
|
||||
$container->get( 'ppcp.asset-version' ),
|
||||
|
@ -43,7 +51,8 @@ return array(
|
|||
$container->get( 'wcgateway.use-place-order-button' ),
|
||||
$container->get( 'wcgateway.place-order-button-text' ),
|
||||
$container->get( 'wcgateway.place-order-button-description' ),
|
||||
$container->get( 'wcgateway.all-funding-sources' )
|
||||
$container->get( 'wcgateway.all-funding-sources' ),
|
||||
$cart && $cart->needs_shipping()
|
||||
);
|
||||
},
|
||||
'blocks.advanced-card-method' => static function( ContainerInterface $container ): AdvancedCardPaymentMethod {
|
||||
|
|
|
@ -52,7 +52,7 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType {
|
|||
*
|
||||
* @var Settings
|
||||
*/
|
||||
protected $settings;
|
||||
protected $plugin_settings;
|
||||
|
||||
/**
|
||||
* AdvancedCardPaymentMethod constructor.
|
||||
|
@ -75,7 +75,7 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType {
|
|||
$this->version = $version;
|
||||
$this->gateway = $gateway;
|
||||
$this->smart_button = $smart_button;
|
||||
$this->settings = $settings;
|
||||
$this->plugin_settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,8 +118,8 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType {
|
|||
'scriptData' => $script_data,
|
||||
'supports' => $this->gateway->supports,
|
||||
'save_card_text' => esc_html__( 'Save your card', 'woocommerce-paypal-payments' ),
|
||||
'is_vaulting_enabled' => $this->settings->has( 'vault_enabled_dcc' ) && $this->settings->get( 'vault_enabled_dcc' ),
|
||||
'card_icons' => $this->settings->has( 'card_icons' ) ? (array) $this->settings->get( 'card_icons' ) : array(),
|
||||
'is_vaulting_enabled' => $this->plugin_settings->has( 'vault_enabled_dcc' ) && $this->plugin_settings->get( 'vault_enabled_dcc' ),
|
||||
'card_icons' => $this->plugin_settings->has( 'card_icons' ) ? (array) $this->plugin_settings->get( 'card_icons' ) : array(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,29 +12,36 @@ namespace WooCommerce\PayPalCommerce\Blocks;
|
|||
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
|
||||
use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Class BlocksModule
|
||||
*/
|
||||
class BlocksModule implements ModuleInterface {
|
||||
class BlocksModule implements ServiceModule, ExtendingModule, ExecutableModule {
|
||||
use ModuleClassNameIdTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setup(): ServiceProviderInterface {
|
||||
return new ServiceProvider(
|
||||
require __DIR__ . '/../services.php',
|
||||
require __DIR__ . '/../extensions.php'
|
||||
);
|
||||
public function services(): array {
|
||||
return require __DIR__ . '/../services.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): void {
|
||||
public function extensions(): array {
|
||||
return require __DIR__ . '/../extensions.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): bool {
|
||||
if (
|
||||
! class_exists( 'Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType' )
|
||||
|| ! function_exists( 'woocommerce_store_api_register_payment_requirements' )
|
||||
|
@ -54,7 +61,7 @@ class BlocksModule implements ModuleInterface {
|
|||
}
|
||||
);
|
||||
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
add_action(
|
||||
|
@ -118,13 +125,6 @@ class BlocksModule implements ModuleInterface {
|
|||
return $components;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key for the module.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function getKey() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue