mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-30 05:00:51 +08:00
Merge branch 'trunk' into PCP-591-save-and-display-vaulted-payment-methods-in-woo-commerce-native-endpoint
This commit is contained in:
commit
17211a8de9
51 changed files with 1400 additions and 305 deletions
|
@ -2,6 +2,8 @@ PPCP_E2E_WP_DIR=${ROOT_DIR}/.ddev/wordpress
|
|||
|
||||
BASEURL="https://woocommerce-paypal-payments.ddev.site"
|
||||
|
||||
PRODUCT_URL="/product/prod"
|
||||
|
||||
CUSTOMER_EMAIL="customer@example.com"
|
||||
CUSTOMER_PASSWORD="password"
|
||||
|
||||
|
|
|
@ -9,6 +9,10 @@ if (!defined('HOUR_IN_SECONDS')) {
|
|||
define('HOUR_IN_SECONDS', 60 * MINUTE_IN_SECONDS);
|
||||
}
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
define('ABSPATH', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the next occurrence of a scheduled action.
|
||||
*
|
||||
|
|
|
@ -73,6 +73,8 @@ Enable xdebug via `$ ddev xdebug`, and press `Start Listening for PHP Debug Conn
|
|||
After creating the server in the PHPStorm dialog, you need to set the local project path for the server plugin path.
|
||||
It should look [like this](https://i.imgur.com/ofsF1Mc.png).
|
||||
|
||||
See [tests/playwright](tests/playwright) for e2e (browser-based) tests.
|
||||
|
||||
## Test account setup
|
||||
|
||||
You will need a PayPal sandbox merchant and customer accounts to configure the plugin and make test purchases with it.
|
||||
|
|
109
api/order-functions.php
Normal file
109
api/order-functions.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
/**
|
||||
* The API for operations with orders.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Api
|
||||
*
|
||||
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Api;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\PPCP;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
|
||||
|
||||
/**
|
||||
* Returns the PayPal order.
|
||||
*
|
||||
* @param string|WC_Order $paypal_id_or_wc_order The ID of PayPal order or a WC order (with the ID in meta).
|
||||
* @throws InvalidArgumentException When the argument cannot be used for retrieving the order.
|
||||
* @throws Exception When the operation fails.
|
||||
*/
|
||||
function ppcp_get_paypal_order( $paypal_id_or_wc_order ): Order {
|
||||
if ( $paypal_id_or_wc_order instanceof WC_Order ) {
|
||||
$paypal_id_or_wc_order = $paypal_id_or_wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY );
|
||||
if ( ! $paypal_id_or_wc_order ) {
|
||||
throw new InvalidArgumentException( 'PayPal order ID not found in meta.' );
|
||||
}
|
||||
}
|
||||
if ( ! is_string( $paypal_id_or_wc_order ) ) {
|
||||
throw new InvalidArgumentException( 'Invalid PayPal order ID, string expected.' );
|
||||
}
|
||||
|
||||
$order_endpoint = PPCP::container()->get( 'api.endpoint.order' );
|
||||
assert( $order_endpoint instanceof OrderEndpoint );
|
||||
|
||||
return $order_endpoint->order( $paypal_id_or_wc_order );
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the PayPal order.
|
||||
*
|
||||
* @param WC_Order $wc_order The WC order.
|
||||
* @throws InvalidArgumentException When the order cannot be captured.
|
||||
* @throws Exception When the operation fails.
|
||||
*/
|
||||
function ppcp_capture_order( WC_Order $wc_order ): void {
|
||||
$intent = strtoupper( (string) $wc_order->get_meta( PayPalGateway::INTENT_META_KEY ) );
|
||||
|
||||
if ( $intent !== 'AUTHORIZE' ) {
|
||||
throw new InvalidArgumentException( 'Only orders with "authorize" intent can be captured.' );
|
||||
}
|
||||
$captured = wc_string_to_bool( $wc_order->get_meta( AuthorizedPaymentsProcessor::CAPTURED_META_KEY ) );
|
||||
if ( $captured ) {
|
||||
throw new InvalidArgumentException( 'The order is already captured.' );
|
||||
}
|
||||
|
||||
$authorized_payment_processor = PPCP::container()->get( 'wcgateway.processor.authorized-payments' );
|
||||
assert( $authorized_payment_processor instanceof AuthorizedPaymentsProcessor );
|
||||
|
||||
if ( ! $authorized_payment_processor->capture_authorized_payment( $wc_order ) ) {
|
||||
throw new RuntimeException( 'Capture failed.' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refunds the PayPal order.
|
||||
* Note that you can use wc_refund_payment() to trigger the refund in WC and PayPal.
|
||||
*
|
||||
* @param WC_Order $wc_order The WC order.
|
||||
* @param float $amount The refund amount.
|
||||
* @param string $reason The reason for the refund.
|
||||
* @return string The PayPal refund ID.
|
||||
* @throws InvalidArgumentException When the order cannot be refunded.
|
||||
* @throws Exception When the operation fails.
|
||||
*/
|
||||
function ppcp_refund_order( WC_Order $wc_order, float $amount, string $reason = '' ): string {
|
||||
$order = ppcp_get_paypal_order( $wc_order );
|
||||
|
||||
$refund_processor = PPCP::container()->get( 'wcgateway.processor.refunds' );
|
||||
assert( $refund_processor instanceof RefundProcessor );
|
||||
|
||||
return $refund_processor->refund( $order, $wc_order, $amount, $reason );
|
||||
}
|
||||
|
||||
/**
|
||||
* Voids the authorization.
|
||||
*
|
||||
* @param WC_Order $wc_order The WC order.
|
||||
* @throws InvalidArgumentException When the order cannot be voided.
|
||||
* @throws Exception When the operation fails.
|
||||
*/
|
||||
function ppcp_void_order( WC_Order $wc_order ): void {
|
||||
$order = ppcp_get_paypal_order( $wc_order );
|
||||
|
||||
$refund_processor = PPCP::container()->get( 'wcgateway.processor.refunds' );
|
||||
assert( $refund_processor instanceof RefundProcessor );
|
||||
|
||||
$refund_processor->void( $order );
|
||||
}
|
|
@ -19,6 +19,11 @@ return function (
|
|||
array $additional_containers = array(),
|
||||
array $additional_modules = array()
|
||||
): ContainerInterface {
|
||||
/**
|
||||
* Skip path check.
|
||||
*
|
||||
* @psalm-suppress UnresolvableInclude
|
||||
*/
|
||||
$modules = ( require "$root_dir/modules.php" )( $root_dir );
|
||||
|
||||
$modules = array_merge( $modules, $additional_modules );
|
||||
|
@ -41,7 +46,12 @@ return function (
|
|||
// TODO: caching does not work currently,
|
||||
// may want to consider fixing it later (pass proxy as parent to DelegatingContainer)
|
||||
// for now not fixed since we were using this behavior for long time and fixing it now may break things.
|
||||
$container = new DelegatingContainer( $provider );
|
||||
$container = new DelegatingContainer( $provider );
|
||||
/**
|
||||
* Skip iterable vs array check.
|
||||
*
|
||||
* @psalm-suppress PossiblyInvalidArgument
|
||||
*/
|
||||
$app_container = new CachingContainer(
|
||||
new CompositeContainer(
|
||||
array_merge(
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
*** Changelog ***
|
||||
|
||||
= 2.0.4 - TBD =
|
||||
* Fix - Allow Pay Later in mini-cart #1221
|
||||
* Fix - Duplicated auth error when credentials become wrong #1229
|
||||
* Fix - Webhook issues when switching sandbox, and delete all webhooks when unsubscribing #1239
|
||||
* Fix - High volume of traffic from merchant-integrations endpoint #1273
|
||||
* Fix - Add content type json to all fetch ajax endpoints #1275
|
||||
* Enhancement - Remove shortcodes from description #1226
|
||||
* Enhancement - Handle price suffix with price for product button check #1234
|
||||
* Enhancement - Show funding source as payment method #1220
|
||||
* Enhancement - Change "Enabled" to "Available" in status text #1237
|
||||
* Enhancement - Programmatically capturing/voiding authorized payments #590
|
||||
|
||||
= 2.0.3 - 2023-03-14 =
|
||||
* Fix - `DEVICE_DATA_NOT_AVAILABLE` error message when FraudNet is enabled #1177
|
||||
* Fix - Redirect to connection tab after manual credentials input #1201
|
||||
|
|
|
@ -30,7 +30,10 @@
|
|||
"psr-4": {
|
||||
"WooCommerce\\PayPalCommerce\\": "src",
|
||||
"WooCommerce\\PayPalCommerce\\Vendor\\": "lib/packages/"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"api/order-functions.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
|
|
483
composer.lock
generated
483
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer;
|
||||
|
@ -177,12 +178,13 @@ return array(
|
|||
$patch_collection_factory = $container->get( 'api.factory.patch-collection-factory' );
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings $settings
|
||||
*/
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
$session_handler = $container->get( 'session.handler' );
|
||||
assert( $session_handler instanceof SessionHandler );
|
||||
$bn_code = $session_handler->bn_code();
|
||||
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$intent = $settings->has( 'intent' ) && strtoupper( (string) $settings->get( 'intent' ) ) === 'AUTHORIZE' ? 'AUTHORIZE' : 'CAPTURE';
|
||||
$application_context_repository = $container->get( 'api.repository.application-context' );
|
||||
$subscription_helper = $container->get( 'subscription.helper' );
|
||||
|
@ -196,7 +198,8 @@ return array(
|
|||
$application_context_repository,
|
||||
$subscription_helper,
|
||||
$container->get( 'wcgateway.is-fraudnet-enabled' ),
|
||||
$container->get( 'wcgateway.fraudnet' )
|
||||
$container->get( 'wcgateway.fraudnet' ),
|
||||
$bn_code
|
||||
);
|
||||
},
|
||||
'api.endpoint.billing-agreements' => static function ( ContainerInterface $container ): BillingAgreementsEndpoint {
|
||||
|
|
|
@ -16,6 +16,9 @@ class CartActionHandler {
|
|||
this.config.bn_codes[this.config.context] : '';
|
||||
return fetch(this.config.ajax.create_order.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.create_order.nonce,
|
||||
|
|
|
@ -32,6 +32,9 @@ class CheckoutActionHandler {
|
|||
|
||||
return fetch(this.config.ajax.create_order.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.create_order.nonce,
|
||||
|
|
|
@ -49,6 +49,9 @@ class FreeTrialHandler {
|
|||
|
||||
const res = await fetch(this.config.ajax.vault_paypal.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.vault_paypal.nonce,
|
||||
|
|
|
@ -66,6 +66,9 @@ class SingleProductActionHandler {
|
|||
this.config.bn_codes[this.config.context] : '';
|
||||
return fetch(this.config.ajax.create_order.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.create_order.nonce,
|
||||
|
|
|
@ -27,6 +27,9 @@ const storeToken = (token) => {
|
|||
const dataClientIdAttributeHandler = (script, config) => {
|
||||
fetch(config.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: config.nonce
|
||||
|
|
|
@ -10,6 +10,9 @@ export default class FormSaver {
|
|||
|
||||
const res = await fetch(this.url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: this.nonce,
|
||||
|
|
|
@ -10,6 +10,9 @@ export default class FormValidator {
|
|||
|
||||
const res = await fetch(this.url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: this.nonce,
|
||||
|
|
|
@ -20,6 +20,9 @@ class UpdateCart {
|
|||
this.endpoint,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: this.nonce,
|
||||
|
|
|
@ -2,6 +2,9 @@ const onApprove = (context, errorHandler) => {
|
|||
return (data, actions) => {
|
||||
return fetch(context.config.ajax.approve_order.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: context.config.ajax.approve_order.nonce,
|
||||
|
|
|
@ -5,6 +5,9 @@ const onApprove = (context, errorHandler, spinner) => {
|
|||
|
||||
return fetch(context.config.ajax.approve_order.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: context.config.ajax.approve_order.nonce,
|
||||
|
|
|
@ -84,6 +84,9 @@ const ppcp_onboarding = {
|
|||
|
||||
fetch(PayPalCommerceGatewayOnboarding.pui_endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: PayPalCommerceGatewayOnboarding.pui_nonce,
|
||||
|
|
|
@ -18,6 +18,9 @@ document.addEventListener(
|
|||
submitButton.setAttribute('disabled', 'disabled');
|
||||
fetch(config.ajax.tracking_info.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: config.ajax.tracking_info.nonce,
|
||||
|
|
|
@ -9,10 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\WooCommerce\Logging\Logger\NullLogger;
|
||||
use WooCommerce\WooCommerce\Logging\Logger\WooCommerceLogger;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
@ -52,41 +49,6 @@ return array(
|
|||
$settings = $container->get( 'wcgateway.settings' );
|
||||
return $settings->has( 'prefix' ) ? (string) $settings->get( 'prefix' ) : 'WC-';
|
||||
},
|
||||
'api.endpoint.order' => static function ( ContainerInterface $container ): OrderEndpoint {
|
||||
$order_factory = $container->get( 'api.factory.order' );
|
||||
$patch_collection_factory = $container->get( 'api.factory.patch-collection-factory' );
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
/**
|
||||
* The session handler.
|
||||
*
|
||||
* @var SessionHandler $session_handler
|
||||
*/
|
||||
$session_handler = $container->get( 'session.handler' );
|
||||
$bn_code = $session_handler->bn_code();
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings $settings
|
||||
*/
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
$intent = $settings->has( 'intent' ) && strtoupper( (string) $settings->get( 'intent' ) ) === 'AUTHORIZE' ? 'AUTHORIZE' : 'CAPTURE';
|
||||
$application_context_repository = $container->get( 'api.repository.application-context' );
|
||||
$subscription_helper = $container->get( 'subscription.helper' );
|
||||
return new OrderEndpoint(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$order_factory,
|
||||
$patch_collection_factory,
|
||||
$intent,
|
||||
$logger,
|
||||
$application_context_repository,
|
||||
$subscription_helper,
|
||||
$container->get( 'wcgateway.is-fraudnet-enabled' ),
|
||||
$container->get( 'wcgateway.fraudnet' ),
|
||||
$bn_code
|
||||
);
|
||||
},
|
||||
'woocommerce.logger.woocommerce' => function ( ContainerInterface $container ): LoggerInterface {
|
||||
if ( ! function_exists( 'wc_get_logger' ) || ! $container->get( 'wcgateway.logging.is-enabled' ) ) {
|
||||
return new NullLogger();
|
||||
|
|
|
@ -231,8 +231,11 @@ return array(
|
|||
'wcgateway.settings.sections-renderer' => static function ( ContainerInterface $container ): SectionsRenderer {
|
||||
return new SectionsRenderer(
|
||||
$container->get( 'wcgateway.current-ppcp-settings-page-id' ),
|
||||
$container->get( 'wcgateway.settings.sections' ),
|
||||
$container->get( 'onboarding.state' )
|
||||
$container->get( 'onboarding.state' ),
|
||||
$container->get( 'wcgateway.helper.dcc-product-status' ),
|
||||
$container->get( 'api.helpers.dccapplies' ),
|
||||
$container->get( 'button.helper.messages-apply' ),
|
||||
$container->get( 'wcgateway.pay-upon-invoice-product-status' )
|
||||
);
|
||||
},
|
||||
'wcgateway.settings.header-renderer' => static function ( ContainerInterface $container ): HeaderRenderer {
|
||||
|
@ -241,52 +244,6 @@ return array(
|
|||
$container->get( 'wcgateway.url' )
|
||||
);
|
||||
},
|
||||
'wcgateway.settings.sections' => static function ( ContainerInterface $container ): array {
|
||||
$sections = array(
|
||||
Settings::CONNECTION_TAB_ID => __( 'Connection', 'woocommerce-paypal-payments' ),
|
||||
PayPalGateway::ID => __( 'Standard Payments', 'woocommerce-paypal-payments' ),
|
||||
Settings::PAY_LATER_TAB_ID => __( 'Pay Later', 'woocommerce-paypal-payments' ),
|
||||
CreditCardGateway::ID => __( 'Advanced Card Processing', 'woocommerce-paypal-payments' ),
|
||||
CardButtonGateway::ID => __( 'Standard Card Button', 'woocommerce-paypal-payments' ),
|
||||
OXXOGateway::ID => __( 'OXXO', 'woocommerce-paypal-payments' ),
|
||||
PayUponInvoiceGateway::ID => __( 'Pay upon Invoice', 'woocommerce-paypal-payments' ),
|
||||
);
|
||||
|
||||
// Remove for all not registered in WC gateways that cannot render anything in this case.
|
||||
$gateways = WC()->payment_gateways->payment_gateways();
|
||||
foreach ( array_diff(
|
||||
array_keys( $sections ),
|
||||
array( Settings::CONNECTION_TAB_ID, PayPalGateway::ID, CreditCardGateway::ID, Settings::PAY_LATER_TAB_ID )
|
||||
) as $id ) {
|
||||
if ( ! isset( $gateways[ $id ] ) ) {
|
||||
unset( $sections[ $id ] );
|
||||
}
|
||||
}
|
||||
|
||||
$dcc_product_status = $container->get( 'wcgateway.helper.dcc-product-status' );
|
||||
assert( $dcc_product_status instanceof DCCProductStatus );
|
||||
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
|
||||
assert( $dcc_applies instanceof DccApplies );
|
||||
if ( ! $dcc_product_status->dcc_is_active() || ! $dcc_applies->for_country_currency() ) {
|
||||
unset( $sections['ppcp-credit-card-gateway'] );
|
||||
}
|
||||
|
||||
$messages_apply = $container->get( 'button.helper.messages-apply' );
|
||||
assert( $messages_apply instanceof MessagesApply );
|
||||
|
||||
if ( ! $messages_apply->for_country() ) {
|
||||
unset( $sections[ Settings::PAY_LATER_TAB_ID ] );
|
||||
}
|
||||
|
||||
$pui_product_status = $container->get( 'wcgateway.pay-upon-invoice-product-status' );
|
||||
assert( $pui_product_status instanceof PayUponInvoiceProductStatus );
|
||||
|
||||
if ( ! $pui_product_status->pui_is_active() ) {
|
||||
unset( $sections[ PayUponInvoiceGateway::ID ] );
|
||||
}
|
||||
|
||||
return $sections;
|
||||
},
|
||||
'wcgateway.settings.status' => static function ( ContainerInterface $container ): SettingsStatus {
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
return new SettingsStatus( $settings );
|
||||
|
|
|
@ -423,15 +423,25 @@ class PayUponInvoice {
|
|||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ( $methods ) {
|
||||
if ( ! is_array( $methods ) || State::STATE_ONBOARDED !== $this->state->current_state() ) {
|
||||
if (
|
||||
! is_array( $methods )
|
||||
|| State::STATE_ONBOARDED !== $this->state->current_state()
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|| ! ( is_checkout() || isset( $_GET['pay_for_order'] ) && $_GET['pay_for_order'] === 'true' )
|
||||
) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
if (
|
||||
! $this->pui_product_status->pui_is_active()
|
||||
|| ! $this->pui_helper->is_checkout_ready_for_pui()
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|| ( isset( $_GET['pay_for_order'] ) && $_GET['pay_for_order'] === 'true' && ! $this->pui_helper->is_pay_for_order_ready_for_pui() )
|
||||
) {
|
||||
unset( $methods[ PayUponInvoiceGateway::ID ] );
|
||||
}
|
||||
|
||||
if (
|
||||
isset( $_GET['pay_for_order'] ) && $_GET['pay_for_order'] === 'true'
|
||||
&& ! $this->pui_helper->is_pay_for_order_ready_for_pui()
|
||||
) {
|
||||
unset( $methods[ PayUponInvoiceGateway::ID ] );
|
||||
}
|
||||
|
@ -440,31 +450,6 @@ class PayUponInvoice {
|
|||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_settings_checkout',
|
||||
function () {
|
||||
if (
|
||||
PayUponInvoiceGateway::ID === $this->current_ppcp_settings_page_id
|
||||
&& ! $this->pui_product_status->pui_is_active()
|
||||
) {
|
||||
$gateway_settings = get_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings' );
|
||||
$gateway_enabled = $gateway_settings['enabled'] ?? '';
|
||||
if ( 'yes' === $gateway_enabled ) {
|
||||
$gateway_settings['enabled'] = 'no';
|
||||
update_option( 'woocommerce_ppcp-pay-upon-invoice-gateway_settings', $gateway_settings );
|
||||
$redirect_url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-pay-upon-invoice-gateway' );
|
||||
wp_safe_redirect( $redirect_url );
|
||||
exit;
|
||||
}
|
||||
|
||||
printf(
|
||||
'<div class="notice notice-error"><p>%1$s</p></div>',
|
||||
esc_html__( 'Could not enable gateway because the connected PayPal account is not activated for Pay upon Invoice. Reconnect your account while Onboard with Pay upon Invoice is selected to try again.', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_update_options_checkout_ppcp-pay-upon-invoice-gateway',
|
||||
function () {
|
||||
|
@ -509,6 +494,19 @@ class PayUponInvoice {
|
|||
</div>
|
||||
<?php
|
||||
}
|
||||
} elseif ( PayUponInvoiceGateway::ID === $this->current_ppcp_settings_page_id ) {
|
||||
$pui_gateway = WC()->payment_gateways->payment_gateways()[ PayUponInvoiceGateway::ID ];
|
||||
if ( 'yes' === $pui_gateway->get_option( 'enabled' ) ) {
|
||||
$pui_gateway->update_option( 'enabled', 'no' );
|
||||
$redirect_url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-pay-upon-invoice-gateway' );
|
||||
wp_safe_redirect( $redirect_url );
|
||||
exit;
|
||||
}
|
||||
|
||||
printf(
|
||||
'<div class="notice notice-error"><p>%1$s</p></div>',
|
||||
esc_html__( 'Could not enable gateway because the connected PayPal account is not activated for Pay upon Invoice. Reconnect your account while Onboard with Pay upon Invoice is selected to try again.', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -102,11 +102,11 @@ class DCCProductStatus {
|
|||
return (bool) $this->cache->get( self::DCC_STATUS_CACHE_KEY );
|
||||
}
|
||||
|
||||
if ( is_bool( $this->current_status_cache ) ) {
|
||||
if ( $this->current_status_cache === true ) {
|
||||
return $this->current_status_cache;
|
||||
}
|
||||
|
||||
if ( $this->settings->has( 'products_dcc_enabled' ) && $this->settings->get( 'products_dcc_enabled' ) ) {
|
||||
if ( $this->settings->has( 'products_dcc_enabled' ) && $this->settings->get( 'products_dcc_enabled' ) === true ) {
|
||||
$this->current_status_cache = true;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -91,10 +91,10 @@ class PayUponInvoiceProductStatus {
|
|||
return (bool) $this->cache->get( self::PUI_STATUS_CACHE_KEY );
|
||||
}
|
||||
|
||||
if ( is_bool( $this->current_status_cache ) ) {
|
||||
if ( $this->current_status_cache === true ) {
|
||||
return $this->current_status_cache;
|
||||
}
|
||||
if ( $this->settings->has( 'products_pui_enabled' ) && $this->settings->get( 'products_pui_enabled' ) ) {
|
||||
if ( $this->settings->has( 'products_pui_enabled' ) && $this->settings->get( 'products_pui_enabled' ) === true ) {
|
||||
$this->current_status_cache = true;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -11,12 +11,14 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Processor;
|
|||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Amount;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payments;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Refund;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
@ -70,7 +72,7 @@ class RefundProcessor {
|
|||
/**
|
||||
* Processes a refund.
|
||||
*
|
||||
* @param \WC_Order $wc_order The WooCommerce order.
|
||||
* @param WC_Order $wc_order The WooCommerce order.
|
||||
* @param float|null $amount The refund amount.
|
||||
* @param string $reason The reason for the refund.
|
||||
*
|
||||
|
@ -78,7 +80,7 @@ class RefundProcessor {
|
|||
*
|
||||
* @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.Missing
|
||||
*/
|
||||
public function process( \WC_Order $wc_order, float $amount = null, string $reason = '' ) : bool {
|
||||
public function process( WC_Order $wc_order, float $amount = null, string $reason = '' ) : bool {
|
||||
try {
|
||||
$order_id = $wc_order->get_meta( PayPalGateway::ORDER_ID_META_KEY );
|
||||
if ( ! $order_id ) {
|
||||
|
@ -87,15 +89,7 @@ class RefundProcessor {
|
|||
|
||||
$order = $this->order_endpoint->order( $order_id );
|
||||
|
||||
$purchase_units = $order->purchase_units();
|
||||
if ( ! $purchase_units ) {
|
||||
throw new RuntimeException( 'No purchase units.' );
|
||||
}
|
||||
|
||||
$payments = $purchase_units[0]->payments();
|
||||
if ( ! $payments ) {
|
||||
throw new RuntimeException( 'No payments.' );
|
||||
}
|
||||
$payments = $this->get_payments( $order );
|
||||
|
||||
$this->logger->debug(
|
||||
sprintf(
|
||||
|
@ -109,39 +103,13 @@ class RefundProcessor {
|
|||
|
||||
switch ( $mode ) {
|
||||
case self::REFUND_MODE_REFUND:
|
||||
$captures = $payments->captures();
|
||||
if ( ! $captures ) {
|
||||
throw new RuntimeException( 'No capture.' );
|
||||
}
|
||||
|
||||
$capture = $captures[0];
|
||||
$refund = new Refund(
|
||||
$capture,
|
||||
$capture->invoice_id(),
|
||||
$reason,
|
||||
new Amount(
|
||||
new Money( $amount, $wc_order->get_currency() )
|
||||
)
|
||||
);
|
||||
$refund_id = $this->payments_endpoint->refund( $refund );
|
||||
$refund_id = $this->refund( $order, $wc_order, $amount, $reason );
|
||||
|
||||
$this->add_refund_to_meta( $wc_order, $refund_id );
|
||||
|
||||
break;
|
||||
case self::REFUND_MODE_VOID:
|
||||
$voidable_authorizations = array_filter(
|
||||
$payments->authorizations(),
|
||||
function ( Authorization $authorization ): bool {
|
||||
return $authorization->is_voidable();
|
||||
}
|
||||
);
|
||||
if ( ! $voidable_authorizations ) {
|
||||
throw new RuntimeException( 'No voidable authorizations.' );
|
||||
}
|
||||
|
||||
foreach ( $voidable_authorizations as $authorization ) {
|
||||
$this->payments_endpoint->void( $authorization );
|
||||
}
|
||||
$this->void( $order );
|
||||
|
||||
$wc_order->set_status( 'refunded' );
|
||||
$wc_order->save();
|
||||
|
@ -158,6 +126,67 @@ class RefundProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a refund to the PayPal order.
|
||||
*
|
||||
* @param Order $order The PayPal order.
|
||||
* @param WC_Order $wc_order The WooCommerce order.
|
||||
* @param float $amount The refund amount.
|
||||
* @param string $reason The reason for the refund.
|
||||
*
|
||||
* @throws RuntimeException When operation fails.
|
||||
* @return string The PayPal refund ID.
|
||||
*/
|
||||
public function refund(
|
||||
Order $order,
|
||||
WC_Order $wc_order,
|
||||
float $amount,
|
||||
string $reason = ''
|
||||
): string {
|
||||
$payments = $this->get_payments( $order );
|
||||
|
||||
$captures = $payments->captures();
|
||||
if ( ! $captures ) {
|
||||
throw new RuntimeException( 'No capture.' );
|
||||
}
|
||||
|
||||
$capture = $captures[0];
|
||||
$refund = new Refund(
|
||||
$capture,
|
||||
$capture->invoice_id(),
|
||||
$reason,
|
||||
new Amount(
|
||||
new Money( $amount, $wc_order->get_currency() )
|
||||
)
|
||||
);
|
||||
|
||||
return $this->payments_endpoint->refund( $refund );
|
||||
}
|
||||
|
||||
/**
|
||||
* Voids the authorization.
|
||||
*
|
||||
* @param Order $order The PayPal order.
|
||||
* @throws RuntimeException When operation fails.
|
||||
*/
|
||||
public function void( Order $order ): void {
|
||||
$payments = $this->get_payments( $order );
|
||||
|
||||
$voidable_authorizations = array_filter(
|
||||
$payments->authorizations(),
|
||||
function ( Authorization $authorization ): bool {
|
||||
return $authorization->is_voidable();
|
||||
}
|
||||
);
|
||||
if ( ! $voidable_authorizations ) {
|
||||
throw new RuntimeException( 'No voidable authorizations.' );
|
||||
}
|
||||
|
||||
foreach ( $voidable_authorizations as $authorization ) {
|
||||
$this->payments_endpoint->void( $authorization );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the refunding mode.
|
||||
*
|
||||
|
@ -181,4 +210,24 @@ class RefundProcessor {
|
|||
|
||||
return self::REFUND_MODE_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the payments object or throws.
|
||||
*
|
||||
* @param Order $order The order.
|
||||
* @throws RuntimeException When payment not available.
|
||||
*/
|
||||
protected function get_payments( Order $order ): Payments {
|
||||
$purchase_units = $order->purchase_units();
|
||||
if ( ! $purchase_units ) {
|
||||
throw new RuntimeException( 'No purchase units.' );
|
||||
}
|
||||
|
||||
$payments = $purchase_units[0]->payments();
|
||||
if ( ! $payments ) {
|
||||
throw new RuntimeException( 'No payments.' );
|
||||
}
|
||||
|
||||
return $payments;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,16 @@ declare( strict_types=1 );
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Settings;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PayUponInvoiceGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus;
|
||||
|
||||
/**
|
||||
* Class SectionsRenderer
|
||||
|
@ -26,13 +34,6 @@ class SectionsRenderer {
|
|||
*/
|
||||
protected $page_id;
|
||||
|
||||
/**
|
||||
* Key - page/gateway ID, value - displayed text.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $sections;
|
||||
|
||||
/**
|
||||
* The onboarding state.
|
||||
*
|
||||
|
@ -40,17 +41,65 @@ class SectionsRenderer {
|
|||
*/
|
||||
private $state;
|
||||
|
||||
/**
|
||||
* The DCC product status
|
||||
*
|
||||
* @var DCCProductStatus
|
||||
*/
|
||||
private $dcc_product_status;
|
||||
|
||||
/**
|
||||
* The DCC applies
|
||||
*
|
||||
* @var DccApplies
|
||||
*/
|
||||
private $dcc_applies;
|
||||
|
||||
/**
|
||||
* The messages apply.
|
||||
*
|
||||
* @var MessagesApply
|
||||
*/
|
||||
private $messages_apply;
|
||||
|
||||
/**
|
||||
* The PUI product status.
|
||||
*
|
||||
* @var PayUponInvoiceProductStatus
|
||||
*/
|
||||
private $pui_product_status;
|
||||
|
||||
/**
|
||||
* SectionsRenderer constructor.
|
||||
*
|
||||
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
|
||||
* @param array<string, string> $sections Key - page/gateway ID, value - displayed text.
|
||||
* @param State $state The onboarding state.
|
||||
*/
|
||||
public function __construct( string $page_id, array $sections, State $state ) {
|
||||
$this->page_id = $page_id;
|
||||
$this->sections = $sections;
|
||||
$this->state = $state;
|
||||
|
||||
/**
|
||||
* SectionsRenderer constructor.
|
||||
*
|
||||
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
|
||||
* @param State $state The onboarding state.
|
||||
* @param DCCProductStatus $dcc_product_status The DCC product status.
|
||||
* @param DccApplies $dcc_applies The DCC applies.
|
||||
* @param MessagesApply $messages_apply The Messages apply.
|
||||
* @param PayUponInvoiceProductStatus $pui_product_status The PUI product status.
|
||||
*/
|
||||
public function __construct(
|
||||
string $page_id,
|
||||
State $state,
|
||||
DCCProductStatus $dcc_product_status,
|
||||
DccApplies $dcc_applies,
|
||||
MessagesApply $messages_apply,
|
||||
PayUponInvoiceProductStatus $pui_product_status
|
||||
) {
|
||||
$this->page_id = $page_id;
|
||||
$this->state = $state;
|
||||
$this->dcc_product_status = $dcc_product_status;
|
||||
$this->dcc_applies = $dcc_applies;
|
||||
$this->messages_apply = $messages_apply;
|
||||
$this->pui_product_status = $pui_product_status;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -66,6 +115,8 @@ class SectionsRenderer {
|
|||
|
||||
/**
|
||||
* Renders the Sections tab.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render(): string {
|
||||
if ( ! $this->should_render() ) {
|
||||
|
@ -74,8 +125,8 @@ class SectionsRenderer {
|
|||
|
||||
$html = '<nav class="nav-tab-wrapper woo-nav-tab-wrapper">';
|
||||
|
||||
foreach ( $this->sections as $id => $label ) {
|
||||
$url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=' . $id );
|
||||
foreach ( $this->sections() as $id => $label ) {
|
||||
$url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=' . (string) $id );
|
||||
if ( in_array( $id, array( Settings::CONNECTION_TAB_ID, CreditCardGateway::ID, Settings::PAY_LATER_TAB_ID ), true ) ) {
|
||||
// We need section=ppcp-gateway for the webhooks page because it is not a gateway,
|
||||
// and for DCC because otherwise it will not render the page if gateway is not available (country/currency).
|
||||
|
@ -89,4 +140,46 @@ class SectionsRenderer {
|
|||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sections as Key - page/gateway ID, value - displayed text.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function sections(): array {
|
||||
$sections = array(
|
||||
Settings::CONNECTION_TAB_ID => __( 'Connection', 'woocommerce-paypal-payments' ),
|
||||
PayPalGateway::ID => __( 'Standard Payments', 'woocommerce-paypal-payments' ),
|
||||
Settings::PAY_LATER_TAB_ID => __( 'Pay Later', 'woocommerce-paypal-payments' ),
|
||||
CreditCardGateway::ID => __( 'Advanced Card Processing', 'woocommerce-paypal-payments' ),
|
||||
CardButtonGateway::ID => __( 'Standard Card Button', 'woocommerce-paypal-payments' ),
|
||||
OXXOGateway::ID => __( 'OXXO', 'woocommerce-paypal-payments' ),
|
||||
PayUponInvoiceGateway::ID => __( 'Pay upon Invoice', 'woocommerce-paypal-payments' ),
|
||||
);
|
||||
|
||||
// Remove for all not registered in WC gateways that cannot render anything in this case.
|
||||
$gateways = WC()->payment_gateways->payment_gateways();
|
||||
foreach ( array_diff(
|
||||
array_keys( $sections ),
|
||||
array( Settings::CONNECTION_TAB_ID, PayPalGateway::ID, CreditCardGateway::ID, Settings::PAY_LATER_TAB_ID )
|
||||
) as $id ) {
|
||||
if ( ! isset( $gateways[ $id ] ) ) {
|
||||
unset( $sections[ $id ] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $this->dcc_product_status->dcc_is_active() || ! $this->dcc_applies->for_country_currency() ) {
|
||||
unset( $sections['ppcp-credit-card-gateway'] );
|
||||
}
|
||||
|
||||
if ( ! $this->messages_apply->for_country() ) {
|
||||
unset( $sections[ Settings::PAY_LATER_TAB_ID ] );
|
||||
}
|
||||
|
||||
if ( ! $this->pui_product_status->pui_is_active() ) {
|
||||
unset( $sections[ PayUponInvoiceGateway::ID ] );
|
||||
}
|
||||
|
||||
return $sections;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -338,7 +338,7 @@ class SettingsListener {
|
|||
}
|
||||
|
||||
$redirect_url = false;
|
||||
if ( self::CREDENTIALS_UNCHANGED !== $credentials_change_status ) {
|
||||
if ( $credentials_change_status && self::CREDENTIALS_UNCHANGED !== $credentials_change_status ) {
|
||||
$redirect_url = $this->get_onboarding_redirect_url();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway;
|
|||
use Psr\Log\LoggerInterface;
|
||||
use Throwable;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WC_Order;
|
||||
|
@ -261,6 +262,24 @@ class WCGatewayModule implements ModuleInterface {
|
|||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_paypal_payments_gateway_migrate_on_update',
|
||||
static function() use ( $c ) {
|
||||
$dcc_status_cache = $c->get( 'dcc.status-cache' );
|
||||
assert( $dcc_status_cache instanceof Cache );
|
||||
$pui_status_cache = $c->get( 'pui.status-cache' );
|
||||
assert( $pui_status_cache instanceof Cache );
|
||||
|
||||
$dcc_status_cache->delete( DCCProductStatus::DCC_STATUS_CACHE_KEY );
|
||||
$pui_status_cache->delete( PayUponInvoiceProductStatus::PUI_STATUS_CACHE_KEY );
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
$settings->set( 'products_dcc_enabled', false );
|
||||
$settings->set( 'products_pui_enabled', false );
|
||||
$settings->persist();
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wp_loaded',
|
||||
function () use ( $c ) {
|
||||
|
|
12
package.json
12
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "woocommerce-paypal-payments",
|
||||
"version": "2.0.3",
|
||||
"version": "2.0.4",
|
||||
"description": "WooCommerce PayPal Payments",
|
||||
"repository": "https://github.com/woocommerce/woocommerce-paypal-payments",
|
||||
"license": "GPL-2.0",
|
||||
|
@ -40,10 +40,9 @@
|
|||
"ddev:composer-update": "ddev composer update && ddev composer update --lock",
|
||||
"ddev:unit-tests": "ddev exec phpunit",
|
||||
"ddev:e2e-tests": "cp -n .env.e2e.example .env.e2e && ddev php tests/e2e/PHPUnit/setup.php && ddev exec phpunit -c tests/e2e/phpunit.xml.dist",
|
||||
"ddev:pw-install": "ddev exec npx playwright install --with-deps",
|
||||
"ddev:pw-tests-ci": "ddev exec npx playwright test --grep @ci",
|
||||
"ddev:pw-tests-headed": "ddev exec npx playwright test --headed",
|
||||
"ddev:test": "yarn run ddev:unit-tests && yarn run ddev:e2e-tests && yarn run ddev:pw-tests-ci",
|
||||
"ddev:pw-install": "ddev yarn playwright install --with-deps",
|
||||
"ddev:pw-tests": "ddev yarn playwright test",
|
||||
"ddev:test": "yarn run ddev:unit-tests && yarn run ddev:e2e-tests && yarn run ddev:pw-tests",
|
||||
"ddev:lint": "yarn ddev:phpcs && yarn ddev:psalm",
|
||||
"ddev:phpcs": "ddev exec phpcs --parallel=8 -s",
|
||||
"ddev:psalm": "ddev exec psalm --show-info=false --threads=8 --diff",
|
||||
|
@ -51,6 +50,9 @@
|
|||
"ddev:xdebug-on": "ddev xdebug",
|
||||
"ddev:xdebug-off": "ddev xdebug",
|
||||
"ddev:build-package": "ddev yarn build",
|
||||
"pw-install": "yarn playwright install --with-deps",
|
||||
"pw-tests": "yarn playwright test",
|
||||
"pw-tests-headed": "yarn playwright test --headed",
|
||||
"prebuild": "rm -rf ./vendor && find . -name 'node_modules' -type d -maxdepth 3 -exec rm -rf {} +",
|
||||
"build": "composer install --no-dev && yarn install && yarn run archive",
|
||||
"prearchive": "rm -rf $npm_package_name.zip",
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
</rule>
|
||||
|
||||
<arg name="extensions" value="php"/>
|
||||
<file>api</file>
|
||||
<file>src</file>
|
||||
<file>modules</file>
|
||||
<file>woocommerce-paypal-payments.php</file>
|
||||
|
|
|
@ -2,9 +2,10 @@ require('dotenv').config({ path: '.env.e2e' });
|
|||
|
||||
const config = {
|
||||
testDir: './tests/playwright',
|
||||
timeout: 30000,
|
||||
timeout: 60000,
|
||||
use: {
|
||||
baseURL: process.env.BASEURL,
|
||||
ignoreHTTPSErrors: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -23,8 +23,11 @@
|
|||
errorBaseline="psalm-baseline.xml"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="api"/>
|
||||
<directory name="src"/>
|
||||
<directory name="modules" />
|
||||
<file name="bootstrap.php" />
|
||||
<file name="woocommerce-paypal-payments.php" />
|
||||
</projectFiles>
|
||||
|
||||
<stubs>
|
||||
|
|
14
readme.txt
14
readme.txt
|
@ -4,7 +4,7 @@ Tags: woocommerce, paypal, payments, ecommerce, e-commerce, store, sales, sell,
|
|||
Requires at least: 5.3
|
||||
Tested up to: 6.1
|
||||
Requires PHP: 7.2
|
||||
Stable tag: 2.0.3
|
||||
Stable tag: 2.0.4
|
||||
License: GPLv2
|
||||
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
|
||||
|
@ -81,6 +81,18 @@ Follow the steps below to connect the plugin to your PayPal account:
|
|||
|
||||
== Changelog ==
|
||||
|
||||
= 2.0.4 - TBD =
|
||||
* Fix - Allow Pay Later in mini-cart #1221
|
||||
* Fix - Duplicated auth error when credentials become wrong #1229
|
||||
* Fix - Webhook issues when switching sandbox, and delete all webhooks when unsubscribing #1239
|
||||
* Fix - High volume of traffic from merchant-integrations endpoint #1273
|
||||
* Fix - Add content type json to all fetch ajax endpoints #1275
|
||||
* Enhancement - Remove shortcodes from description #1226
|
||||
* Enhancement - Handle price suffix with price for product button check #1234
|
||||
* Enhancement - Show funding source as payment method #1220
|
||||
* Enhancement - Change "Enabled" to "Available" in status text #1237
|
||||
* Enhancement - Programmatically capturing/voiding authorized payments #590
|
||||
|
||||
= 2.0.3 - 2023-03-14 =
|
||||
* Fix - `DEVICE_DATA_NOT_AVAILABLE` error message when FraudNet is enabled #1177
|
||||
* Fix - Redirect to connection tab after manual credentials input #1201
|
||||
|
|
|
@ -58,10 +58,9 @@ class FilePathPluginFactory implements FilePathPluginFactoryInterface {
|
|||
|
||||
if ( ! function_exists( 'get_plugin_data' ) ) {
|
||||
/**
|
||||
* Skip check for WP files and constants.
|
||||
* Skip check for WP files.
|
||||
*
|
||||
* @psalm-suppress UnresolvableInclude
|
||||
* @psalm-suppress UndefinedConstant
|
||||
* @psalm-suppress MissingFile
|
||||
*/
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
|
|
48
src/PPCP.php
Normal file
48
src/PPCP.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
/**
|
||||
* Internal global data.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce;
|
||||
|
||||
use LogicException;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Internal global data.
|
||||
*/
|
||||
class PPCP {
|
||||
/**
|
||||
* The container with services of the application modules.
|
||||
*
|
||||
* @var ContainerInterface|null
|
||||
*/
|
||||
private static $container = null;
|
||||
|
||||
/**
|
||||
* The container with services of the application modules.
|
||||
* Mainly for internal usage.
|
||||
* The compatibility between different versions of the plugins is not guaranteed.
|
||||
*
|
||||
* @throws LogicException When no container.
|
||||
*/
|
||||
public static function container(): ContainerInterface {
|
||||
if ( ! self::$container ) {
|
||||
throw new LogicException( 'No PPCP container, probably called too early when the plugin is not initialized yet.' );
|
||||
}
|
||||
return self::$container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the data.
|
||||
*
|
||||
* @param ContainerInterface $container The app container.
|
||||
*/
|
||||
public static function init( ContainerInterface $container ): void {
|
||||
self::$container = $container;
|
||||
}
|
||||
}
|
83
tests/PHPUnit/Api/GetOrderTest.php
Normal file
83
tests/PHPUnit/Api/GetOrderTest.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Api;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Mockery;
|
||||
use RuntimeException;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ModularTestCase;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
||||
class GetOrderTest extends ModularTestCase
|
||||
{
|
||||
private $orderEndpoint;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->orderEndpoint = Mockery::mock(OrderEndpoint::class);
|
||||
|
||||
$this->bootstrapModule([
|
||||
'api.endpoint.order' => function () {
|
||||
return $this->orderEndpoint;
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSuccess(): void {
|
||||
$this->orderEndpoint
|
||||
->expects('order')
|
||||
->with('123abc')
|
||||
->andReturn(Mockery::mock(Order::class))
|
||||
->once();
|
||||
|
||||
ppcp_get_paypal_order('123abc');
|
||||
}
|
||||
|
||||
public function testSuccessWithOrder(): void {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn('123abc');
|
||||
|
||||
$this->orderEndpoint
|
||||
->expects('order')
|
||||
->with('123abc')
|
||||
->andReturn(Mockery::mock(Order::class))
|
||||
->once();
|
||||
|
||||
ppcp_get_paypal_order($wcOrder);
|
||||
}
|
||||
|
||||
public function testOrderWithoutId(): void {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn(false);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
ppcp_get_paypal_order($wcOrder);
|
||||
}
|
||||
|
||||
public function testFailure(): void {
|
||||
$this->orderEndpoint
|
||||
->expects('order')
|
||||
->with('123abc')
|
||||
->andThrow(new RuntimeException())
|
||||
->once();
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
|
||||
ppcp_get_paypal_order('123abc');
|
||||
}
|
||||
|
||||
public function testInvalidId(): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
ppcp_get_paypal_order(123);
|
||||
}
|
||||
}
|
92
tests/PHPUnit/Api/OrderCaptureTest.php
Normal file
92
tests/PHPUnit/Api/OrderCaptureTest.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Api;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Mockery;
|
||||
use RuntimeException;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ModularTestCase;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
||||
|
||||
class OrderCaptureTest extends ModularTestCase
|
||||
{
|
||||
private $authorizedPaymentProcessor;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->authorizedPaymentProcessor = Mockery::mock(AuthorizedPaymentsProcessor::class);
|
||||
|
||||
$this->bootstrapModule([
|
||||
'wcgateway.processor.authorized-payments' => function () {
|
||||
return $this->authorizedPaymentProcessor;
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSuccess(): void {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->expects('get_meta')
|
||||
->with(PayPalGateway::INTENT_META_KEY)
|
||||
->andReturn('AUTHORIZE');
|
||||
$wcOrder->expects('get_meta')
|
||||
->with(AuthorizedPaymentsProcessor::CAPTURED_META_KEY)
|
||||
->andReturn(false);
|
||||
|
||||
$this->authorizedPaymentProcessor
|
||||
->expects('capture_authorized_payment')
|
||||
->andReturnTrue()
|
||||
->once();
|
||||
|
||||
ppcp_capture_order($wcOrder);
|
||||
}
|
||||
|
||||
public function testFailure(): void {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->expects('get_meta')
|
||||
->with(PayPalGateway::INTENT_META_KEY)
|
||||
->andReturn('AUTHORIZE');
|
||||
$wcOrder->expects('get_meta')
|
||||
->with(AuthorizedPaymentsProcessor::CAPTURED_META_KEY)
|
||||
->andReturn(false);
|
||||
|
||||
$this->authorizedPaymentProcessor
|
||||
->expects('capture_authorized_payment')
|
||||
->andReturnFalse()
|
||||
->once();
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
|
||||
ppcp_capture_order($wcOrder);
|
||||
}
|
||||
|
||||
public function testNotAuthorize(): void {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->shouldReceive('get_meta')
|
||||
->with(PayPalGateway::INTENT_META_KEY)
|
||||
->andReturn('CAPTURE');
|
||||
$wcOrder->shouldReceive('get_meta')
|
||||
->with(AuthorizedPaymentsProcessor::CAPTURED_META_KEY)
|
||||
->andReturn(false);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
ppcp_capture_order($wcOrder);
|
||||
}
|
||||
|
||||
public function testAlreadyCaptured(): void {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->shouldReceive('get_meta')
|
||||
->with(PayPalGateway::INTENT_META_KEY)
|
||||
->andReturn('CAPTURE');
|
||||
$wcOrder->shouldReceive('get_meta')
|
||||
->with(AuthorizedPaymentsProcessor::CAPTURED_META_KEY)
|
||||
->andReturn(true);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
ppcp_capture_order($wcOrder);
|
||||
}
|
||||
}
|
90
tests/PHPUnit/Api/OrderRefundTest.php
Normal file
90
tests/PHPUnit/Api/OrderRefundTest.php
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Api;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Mockery;
|
||||
use RuntimeException;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ModularTestCase;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
|
||||
|
||||
class OrderRefundTest extends ModularTestCase
|
||||
{
|
||||
private $refundProcessor;
|
||||
|
||||
private $orderEndpoint;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->refundProcessor = Mockery::mock(RefundProcessor::class);
|
||||
$this->orderEndpoint = Mockery::mock(OrderEndpoint::class);
|
||||
|
||||
$this->bootstrapModule([
|
||||
'wcgateway.processor.refunds' => function () {
|
||||
return $this->refundProcessor;
|
||||
},
|
||||
'api.endpoint.order' => function () {
|
||||
return $this->orderEndpoint;
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSuccess(): void {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn('123abc');
|
||||
|
||||
$this->orderEndpoint
|
||||
->expects('order')
|
||||
->with('123abc')
|
||||
->andReturn(Mockery::mock(Order::class))
|
||||
->once();
|
||||
|
||||
$this->refundProcessor
|
||||
->expects('refund')
|
||||
->andReturn('456qwe')
|
||||
->once();
|
||||
|
||||
$refund_id = ppcp_refund_order($wcOrder, 42.0, 'reason');
|
||||
$this->assertEquals('456qwe', $refund_id);
|
||||
}
|
||||
|
||||
public function testOrderWithoutId(): void {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn(false);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
ppcp_refund_order($wcOrder, 42.0, 'reason');
|
||||
}
|
||||
|
||||
public function testFailure(): void {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn('123abc');
|
||||
|
||||
$this->orderEndpoint
|
||||
->expects('order')
|
||||
->with('123abc')
|
||||
->andReturn(Mockery::mock(Order::class))
|
||||
->once();
|
||||
|
||||
$this->refundProcessor
|
||||
->expects('refund')
|
||||
->andThrow(new RuntimeException())
|
||||
->once();
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
|
||||
ppcp_refund_order($wcOrder, 42.0, 'reason');
|
||||
}
|
||||
}
|
88
tests/PHPUnit/Api/OrderVoidTest.php
Normal file
88
tests/PHPUnit/Api/OrderVoidTest.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Api;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Mockery;
|
||||
use RuntimeException;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ModularTestCase;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
|
||||
|
||||
class OrderVoidTest extends ModularTestCase
|
||||
{
|
||||
private $refundProcessor;
|
||||
|
||||
private $orderEndpoint;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->refundProcessor = Mockery::mock(RefundProcessor::class);
|
||||
$this->orderEndpoint = Mockery::mock(OrderEndpoint::class);
|
||||
|
||||
$this->bootstrapModule([
|
||||
'wcgateway.processor.refunds' => function () {
|
||||
return $this->refundProcessor;
|
||||
},
|
||||
'api.endpoint.order' => function () {
|
||||
return $this->orderEndpoint;
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSuccess(): void {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn('123abc');
|
||||
|
||||
$this->orderEndpoint
|
||||
->expects('order')
|
||||
->with('123abc')
|
||||
->andReturn(Mockery::mock(Order::class))
|
||||
->once();
|
||||
|
||||
$this->refundProcessor
|
||||
->expects('void')
|
||||
->once();
|
||||
|
||||
ppcp_void_order($wcOrder);
|
||||
}
|
||||
|
||||
public function testOrderWithoutId(): void {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn(false);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
ppcp_void_order($wcOrder);
|
||||
}
|
||||
|
||||
public function testFailure(): void {
|
||||
$wcOrder = Mockery::mock(WC_Order::class);
|
||||
$wcOrder->expects('get_meta')
|
||||
->with(PayPalGateway::ORDER_ID_META_KEY)
|
||||
->andReturn('123abc');
|
||||
|
||||
$this->orderEndpoint
|
||||
->expects('order')
|
||||
->with('123abc')
|
||||
->andReturn(Mockery::mock(Order::class))
|
||||
->once();
|
||||
|
||||
$this->refundProcessor
|
||||
->expects('void')
|
||||
->andThrow(new RuntimeException())
|
||||
->once();
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
|
||||
ppcp_void_order($wcOrder);
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ class IdentityTokenTest extends TestCase
|
|||
public function testGenerateForCustomerReturnsToken()
|
||||
{
|
||||
$id = 1;
|
||||
define( 'PPCP_FLAG_SUBSCRIPTION', true );
|
||||
!defined('PPCP_FLAG_SUBSCRIPTION') && define('PPCP_FLAG_SUBSCRIPTION', true);
|
||||
$token = Mockery::mock(Token::class);
|
||||
$token
|
||||
->expects('token')->andReturn('bearer');
|
||||
|
|
|
@ -82,6 +82,8 @@ class ModularTestCase extends TestCase
|
|||
$bootstrap = require ("$rootDir/bootstrap.php");
|
||||
$appContainer = $bootstrap($rootDir, [], [$module]);
|
||||
|
||||
PPCP::init($appContainer);
|
||||
|
||||
return $appContainer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ class TestCase extends \PHPUnit\Framework\TestCase
|
|||
when('wc_print_r')->alias(function ($value, bool $return = false) {
|
||||
return print_r($value, $return);
|
||||
});
|
||||
when('wc_string_to_bool')->alias(function ($string) {
|
||||
return is_bool( $string ) ? $string : ( 'yes' === strtolower( $string ) || 1 === $string || 'true' === strtolower( $string ) || '1' === $string );
|
||||
});
|
||||
when('get_plugin_data')->justReturn(['Version' => '1.0']);
|
||||
when('plugin_basename')->justReturn('woocommerce-paypal-payments/woocommerce-paypal-payments.php');
|
||||
|
||||
|
|
|
@ -88,8 +88,6 @@ class SettingsListenerTest extends ModularTestCase
|
|||
$dcc_status_cache->shouldReceive('has')
|
||||
->andReturn(false);
|
||||
|
||||
$this->expectException(StubRedirectionException::class);
|
||||
|
||||
$testee->listen();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Tests\E2e;
|
||||
|
||||
use PPCP_E2E;
|
||||
use WooCommerce\PayPalCommerce\PPCP;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WC_Cart;
|
||||
use WC_Customer;
|
||||
|
@ -14,7 +14,7 @@ class TestCase extends \PHPUnit\Framework\TestCase
|
|||
protected $container;
|
||||
|
||||
protected function getContainer(): ContainerInterface {
|
||||
return PPCP_E2E::$container;
|
||||
return PPCP::container();
|
||||
}
|
||||
|
||||
protected function cart(): WC_Cart {
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Plugin Name: WooCommerce PayPal Payments e2e
|
||||
* Description: PPCP e2e
|
||||
* Version: 1.0.0
|
||||
* Author: Inpsyde
|
||||
* License: GPL-2.0
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
class PPCP_E2E
|
||||
{
|
||||
public static $container;
|
||||
}
|
||||
|
||||
add_filter('woocommerce_paypal_payments_built_container', function($app_container): void {
|
||||
PPCP_E2E::$container = $app_container;
|
||||
});
|
|
@ -16,18 +16,6 @@ foreach ($options as $key => $value) {
|
|||
update_option($key, $value);
|
||||
}
|
||||
|
||||
echo 'Adding ppcp-e2e-plugin.' . PHP_EOL;
|
||||
|
||||
$pluginDir = WP_ROOT_DIR . '/wp-content/plugins/ppcp-e2e-plugin';
|
||||
if (!is_dir($pluginDir)) {
|
||||
mkdir($pluginDir);
|
||||
}
|
||||
if (!copy(E2E_TESTS_ROOT_DIR . '/PHPUnit/ppcp-e2e-plugin.php', $pluginDir . '/ppcp-e2e-plugin.php')) {
|
||||
echo 'Failed to copy ppcp-e2e-plugin.' . PHP_EOL;
|
||||
}
|
||||
|
||||
activate_plugin('ppcp-e2e-plugin/ppcp-e2e-plugin.php', '', true);
|
||||
|
||||
echo 'Deleting test taxes.' . PHP_EOL;
|
||||
|
||||
$taxRates = WC_Tax::get_rates_for_tax_class('');
|
||||
|
|
|
@ -1,15 +1,40 @@
|
|||
# Playwright Testing
|
||||
|
||||
## Local Environment Variables
|
||||
Allows using environment variables inside the tests.
|
||||
## Set Environment Variables
|
||||
|
||||
- Duplicate `.env.e2e.example` and rename it as `.env.e2e`, set values and add new variables if needed.
|
||||
Duplicate [.env.e2e.example](/.env.e2e.example) and rename it to `.env.e2e`, set the values needed for the tests, like account credentials, card numbers.
|
||||
|
||||
## Install Playwright dependencies (browsers)
|
||||
|
||||
```
|
||||
$ yarn ddev:pw-install
|
||||
```
|
||||
|
||||
## Run Tests
|
||||
```
|
||||
$ npx playwright test
|
||||
$ npx playwright test --grep @ci
|
||||
$ npx playwright test example.spec.js --headed
|
||||
$ npx playwright test example.spec.js --debug
|
||||
$ npx playwright test -g "Test name here"
|
||||
$ yarn ddev:pw-tests
|
||||
```
|
||||
|
||||
You can also choose which tests to run filtering by name
|
||||
```
|
||||
$ yarn ddev:pw-tests --grep "Test name or part of the name"
|
||||
```
|
||||
|
||||
Or run without the headless mode (show the browser)
|
||||
```
|
||||
$ yarn pw-tests-headed
|
||||
```
|
||||
|
||||
Or run with [the test debugger](https://playwright.dev/docs/debug)
|
||||
```
|
||||
$ yarn playwright test --debug
|
||||
```
|
||||
|
||||
For the headed/debug mode (currently works only outside DDEV) you may need to re-install the deps if you are not on Linux.
|
||||
|
||||
```
|
||||
$ yarn pw-install
|
||||
```
|
||||
|
||||
---
|
||||
See [Playwright docs](https://playwright.dev/docs/intro) for more info.
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
const {test, expect} = require('@playwright/test');
|
||||
|
||||
const {
|
||||
CUSTOMER_EMAIL,
|
||||
CUSTOMER_PASSWORD,
|
||||
CREDIT_CARD_NUMBER,
|
||||
CREDIT_CARD_EXPIRATION,
|
||||
CREDIT_CARD_CVV
|
||||
CREDIT_CARD_CVV,
|
||||
PRODUCT_URL,
|
||||
} = process.env;
|
||||
|
||||
async function fillCheckoutForm(page) {
|
||||
|
@ -16,25 +18,49 @@ async function fillCheckoutForm(page) {
|
|||
await page.fill('#billing_city', '10715');
|
||||
await page.fill('#billing_phone', '1234567890');
|
||||
await page.fill('#billing_email', CUSTOMER_EMAIL);
|
||||
|
||||
const differentShippingLocator = page.locator('[name="ship_to_different_address"]');
|
||||
if (await differentShippingLocator.count() > 0) {
|
||||
await differentShippingLocator.uncheck();
|
||||
}
|
||||
|
||||
const termsLocator = page.locator('[name="terms"]');
|
||||
if (await termsLocator.count() > 0) {
|
||||
await termsLocator.check();
|
||||
}
|
||||
}
|
||||
|
||||
test('PayPal button place order from Product page', async ({page}) => {
|
||||
|
||||
await page.goto('/product/product/');
|
||||
async function openPaypalPopup(page) {
|
||||
await page.locator('.component-frame').scrollIntoViewIfNeeded();
|
||||
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.frameLocator('.component-frame').locator('[data-funding-source="paypal"]').click(),
|
||||
]);
|
||||
|
||||
await popup.waitForLoadState();
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
||||
async function loginIntoPaypal(popup) {
|
||||
await popup.click("text=Log in");
|
||||
await popup.fill('#email', CUSTOMER_EMAIL);
|
||||
await popup.fill('[name="login_email"]', CUSTOMER_EMAIL);
|
||||
await popup.locator('#btnNext').click();
|
||||
await popup.fill('#password', CUSTOMER_PASSWORD);
|
||||
await popup.fill('[name="login_password"]', CUSTOMER_PASSWORD);
|
||||
await popup.locator('#btnLogin').click();
|
||||
}
|
||||
|
||||
test('PayPal button place order from Product page', async ({page}) => {
|
||||
|
||||
await page.goto(PRODUCT_URL);
|
||||
|
||||
const popup = await openPaypalPopup(page);
|
||||
|
||||
await loginIntoPaypal(popup);
|
||||
|
||||
await popup.locator('#payment-submit-btn').click();
|
||||
|
||||
await fillCheckoutForm(page);
|
||||
|
||||
await Promise.all([
|
||||
|
@ -46,9 +72,9 @@ test('PayPal button place order from Product page', async ({page}) => {
|
|||
await expect(title).toHaveText('Order received');
|
||||
});
|
||||
|
||||
test('Advanced Credit and Debit Card (ACDC) place order from Checkout page @ci', async ({page}) => {
|
||||
test('Advanced Credit and Debit Card (ACDC) place order from Checkout page', async ({page}) => {
|
||||
|
||||
await page.goto('/product/product/');
|
||||
await page.goto(PRODUCT_URL);
|
||||
await page.locator('.single_add_to_cart_button').click();
|
||||
|
||||
await page.goto('/checkout/');
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
* Plugin Name: WooCommerce PayPal Payments
|
||||
* Plugin URI: https://woocommerce.com/products/woocommerce-paypal-payments/
|
||||
* Description: PayPal's latest complete payments processing solution. Accept PayPal, Pay Later, credit/debit cards, alternative digital wallets local payment types and bank accounts. Turn on only PayPal options or process a full suite of payment methods. Enable global transaction with extensive currency and country coverage.
|
||||
* Version: 2.0.3
|
||||
* Version: 2.0.4
|
||||
* Author: WooCommerce
|
||||
* Author URI: https://woocommerce.com/
|
||||
* License: GPL-2.0
|
||||
* Requires PHP: 7.2
|
||||
* WC requires at least: 3.9
|
||||
* WC tested up to: 7.4
|
||||
* WC tested up to: 7.5
|
||||
* Text Domain: woocommerce-paypal-payments
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce
|
||||
|
@ -23,7 +23,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
|||
|
||||
define( 'PAYPAL_API_URL', 'https://api.paypal.com' );
|
||||
define( 'PAYPAL_SANDBOX_API_URL', 'https://api.sandbox.paypal.com' );
|
||||
define( 'PAYPAL_INTEGRATION_DATE', '2023-02-21' );
|
||||
define( 'PAYPAL_INTEGRATION_DATE', '2023-03-20' );
|
||||
|
||||
define( 'PPCP_FLAG_SUBSCRIPTION', true );
|
||||
|
||||
|
@ -43,7 +43,7 @@ define( 'PPCP_FLAG_SUBSCRIPTION', true );
|
|||
/**
|
||||
* Initialize the plugin and its modules.
|
||||
*/
|
||||
function init() {
|
||||
function init(): void {
|
||||
$root_dir = __DIR__;
|
||||
|
||||
if ( ! is_woocommerce_activated() ) {
|
||||
|
@ -74,6 +74,8 @@ define( 'PPCP_FLAG_SUBSCRIPTION', true );
|
|||
|
||||
$app_container = $bootstrap( $root_dir );
|
||||
|
||||
PPCP::init( $app_container );
|
||||
|
||||
$initialized = true;
|
||||
/**
|
||||
* The hook fired after the plugin bootstrap with the app services container as parameter.
|
||||
|
@ -88,6 +90,11 @@ define( 'PPCP_FLAG_SUBSCRIPTION', true );
|
|||
init();
|
||||
|
||||
if ( ! function_exists( 'get_plugin_data' ) ) {
|
||||
/**
|
||||
* Skip check for WP files.
|
||||
*
|
||||
* @psalm-suppress MissingFile
|
||||
*/
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
$plugin_data = get_plugin_data( __DIR__ . '/woocommerce-paypal-payments.php' );
|
||||
|
@ -130,9 +137,14 @@ define( 'PPCP_FLAG_SUBSCRIPTION', true );
|
|||
}
|
||||
);
|
||||
|
||||
// Add "Settings" link to Plugins screen.
|
||||
add_filter(
|
||||
'plugin_action_links_' . plugin_basename( __FILE__ ),
|
||||
/**
|
||||
* Add "Settings" link to Plugins screen.
|
||||
*
|
||||
* @param array $links
|
||||
* @retun array
|
||||
*/
|
||||
function( $links ) {
|
||||
if ( ! is_woocommerce_activated() ) {
|
||||
return $links;
|
||||
|
@ -151,9 +163,15 @@ define( 'PPCP_FLAG_SUBSCRIPTION', true );
|
|||
}
|
||||
);
|
||||
|
||||
// Add links below the description on the Plugins page.
|
||||
add_filter(
|
||||
'plugin_row_meta',
|
||||
/**
|
||||
* Add links below the description on the Plugins page.
|
||||
*
|
||||
* @param array $links
|
||||
* @param string $file
|
||||
* @retun array
|
||||
*/
|
||||
function( $links, $file ) {
|
||||
if ( plugin_basename( __FILE__ ) !== $file ) {
|
||||
return $links;
|
||||
|
@ -193,6 +211,11 @@ define( 'PPCP_FLAG_SUBSCRIPTION', true );
|
|||
'before_woocommerce_init',
|
||||
function() {
|
||||
if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) {
|
||||
/**
|
||||
* Skip WC class check.
|
||||
*
|
||||
* @psalm-suppress UndefinedClass
|
||||
*/
|
||||
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue