From eca8a3b09ca12d22621c26e232ee75a52d35d474 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 2 May 2023 12:35:46 +0200 Subject: [PATCH] Fix psalm --- .../Endpoint/BillingAgreementsEndpoint.php | 8 +- .../src/Endpoint/BillingPlans.php | 36 ++-- .../src/Endpoint/BillingSubscriptions.php | 4 +- .../src/Endpoint/CatalogProducts.php | 26 +-- .../src/Entity/BillingCycle.php | 8 +- modules/ppcp-api-client/src/Entity/Plan.php | 10 +- .../src/Factory/PlanFactory.php | 16 +- .../src/Factory/ProductFactory.php | 2 +- .../ppcp-button/src/Assets/SmartButton.php | 11 +- .../Endpoint/ApproveSubscriptionEndpoint.php | 4 +- modules/ppcp-subscription/services.php | 14 +- .../src/SubscriptionModule.php | 87 +++++++- .../src/SubscriptionsApiHandler.php | 194 +++++++++++------- modules/ppcp-wc-gateway/services.php | 4 +- .../src/Settings/SettingsListener.php | 6 +- modules/ppcp-webhooks/services.php | 4 +- .../BillingPlanPricingChangeActivated.php | 17 +- .../src/Handler/BillingPlanUpdated.php | 47 +++-- .../Handler/BillingSubscriptionCancelled.php | 3 + .../src/Handler/CatalogProductUpdated.php | 40 ++-- .../src/Handler/PaymentSaleCompleted.php | 19 +- 21 files changed, 357 insertions(+), 203 deletions(-) diff --git a/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php b/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php index 6085872b9..27bf25d27 100644 --- a/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/BillingAgreementsEndpoint.php @@ -120,8 +120,8 @@ class BillingAgreementsEndpoint { */ public function reference_transaction_enabled(): bool { try { - $reference_transaction_enabled = get_transient('ppcp_reference_transaction_enabled') ?? ''; - if($reference_transaction_enabled === true) { + $reference_transaction_enabled = get_transient( 'ppcp_reference_transaction_enabled' ) ?? ''; + if ( $reference_transaction_enabled === true ) { return true; } @@ -135,12 +135,12 @@ class BillingAgreementsEndpoint { ); } finally { $this->is_request_logging_enabled = true; - set_transient('ppcp_reference_transaction_enabled', true, 3 * MONTH_IN_SECONDS); + set_transient( 'ppcp_reference_transaction_enabled', true, 3 * MONTH_IN_SECONDS ); } return true; } catch ( PayPalApiException $exception ) { - delete_transient('ppcp_reference_transaction_enabled'); + delete_transient( 'ppcp_reference_transaction_enabled' ); return false; } } diff --git a/modules/ppcp-api-client/src/Endpoint/BillingPlans.php b/modules/ppcp-api-client/src/Endpoint/BillingPlans.php index 24a576001..3210b0d19 100644 --- a/modules/ppcp-api-client/src/Endpoint/BillingPlans.php +++ b/modules/ppcp-api-client/src/Endpoint/BillingPlans.php @@ -70,11 +70,11 @@ class BillingPlans { PlanFactory $plan_factory, LoggerInterface $logger ) { - $this->host = $host; - $this->bearer = $bearer; + $this->host = $host; + $this->bearer = $bearer; $this->billing_cycle_factory = $billing_cycle_factory; - $this->plan_factory = $plan_factory; - $this->logger = $logger; + $this->plan_factory = $plan_factory; + $this->logger = $logger; } /** @@ -95,10 +95,10 @@ class BillingPlans { ): Plan { $data = array( - 'name' => $name, - 'product_id' => $product_id, - 'billing_cycles' => $billing_cycles, - 'payment_preferences' => $payment_preferences + 'name' => $name, + 'product_id' => $product_id, + 'billing_cycles' => $billing_cycles, + 'payment_preferences' => $payment_preferences, ); $bearer = $this->bearer->bearer(); @@ -108,7 +108,7 @@ class BillingPlans { 'headers' => array( 'Authorization' => 'Bearer ' . $bearer->token(), 'Content-Type' => 'application/json', - 'Prefer' => 'return=representation' + 'Prefer' => 'return=representation', ), 'body' => wp_json_encode( $data ), ); @@ -128,17 +128,17 @@ class BillingPlans { ); } - return $this->plan_factory->from_paypal_response($json); + return $this->plan_factory->from_paypal_response( $json ); } - public function plan(string $id): Plan { + public function plan( string $id ): Plan { $bearer = $this->bearer->bearer(); $url = trailingslashit( $this->host ) . 'v1/billing/plans/' . $id; $args = array( 'headers' => array( 'Authorization' => 'Bearer ' . $bearer->token(), 'Content-Type' => 'application/json', - 'Prefer' => 'return=representation' + 'Prefer' => 'return=representation', ), ); @@ -156,15 +156,15 @@ class BillingPlans { ); } - return $this->plan_factory->from_paypal_response($json); + return $this->plan_factory->from_paypal_response( $json ); } - public function update_pricing(string $id, BillingCycle $billing_cycle): void { + public function update_pricing( string $id, BillingCycle $billing_cycle ): void { $data = array( - "pricing_schemes" => array( - (object)array( - "billing_cycle_sequence" => 1, - "pricing_scheme" => $billing_cycle->pricing_scheme(), + 'pricing_schemes' => array( + (object) array( + 'billing_cycle_sequence' => 1, + 'pricing_scheme' => $billing_cycle->pricing_scheme(), ), ), ); diff --git a/modules/ppcp-api-client/src/Endpoint/BillingSubscriptions.php b/modules/ppcp-api-client/src/Endpoint/BillingSubscriptions.php index 164055065..c7f910858 100644 --- a/modules/ppcp-api-client/src/Endpoint/BillingSubscriptions.php +++ b/modules/ppcp-api-client/src/Endpoint/BillingSubscriptions.php @@ -179,14 +179,14 @@ class BillingSubscriptions { * @param string $id Subscription ID. * @return stdClass */ - public function subscription(string $id): stdClass { + public function subscription( string $id ): stdClass { $bearer = $this->bearer->bearer(); $url = trailingslashit( $this->host ) . 'v1/billing/subscriptions/' . $id; $args = array( 'headers' => array( 'Authorization' => 'Bearer ' . $bearer->token(), 'Content-Type' => 'application/json', - 'Prefer' => 'return=representation' + 'Prefer' => 'return=representation', ), ); diff --git a/modules/ppcp-api-client/src/Endpoint/CatalogProducts.php b/modules/ppcp-api-client/src/Endpoint/CatalogProducts.php index 2a4c8a003..6d276bc3d 100644 --- a/modules/ppcp-api-client/src/Endpoint/CatalogProducts.php +++ b/modules/ppcp-api-client/src/Endpoint/CatalogProducts.php @@ -60,10 +60,10 @@ class CatalogProducts { ProductFactory $product_factory, LoggerInterface $logger ) { - $this->host = $host; - $this->bearer = $bearer; + $this->host = $host; + $this->bearer = $bearer; $this->product_factory = $product_factory; - $this->logger = $logger; + $this->logger = $logger; } /** @@ -77,12 +77,12 @@ class CatalogProducts { * @throws RuntimeException If the request fails. * @throws PayPalApiException If the request fails. */ - public function create(string $name, string $description): Product { + public function create( string $name, string $description ): Product { $data = array( 'name' => $name, ); - if($description) { + if ( $description ) { $data['description'] = $description; } @@ -93,7 +93,7 @@ class CatalogProducts { 'headers' => array( 'Authorization' => 'Bearer ' . $bearer->token(), 'Content-Type' => 'application/json', - 'Prefer' => 'return=representation' + 'Prefer' => 'return=representation', ), 'body' => wp_json_encode( $data ), ); @@ -113,17 +113,17 @@ class CatalogProducts { ); } - return $this->product_factory->from_paypal_response($json); + return $this->product_factory->from_paypal_response( $json ); } /** * Updates a Product. * * @param string $id Product ID. - * @param array $data Data to update. + * @param array $data Data to update. * @return void */ - public function update(string $id, array $data): void { + public function update( string $id, array $data ): void { $bearer = $this->bearer->bearer(); $url = trailingslashit( $this->host ) . 'v1/catalogs/products/' . $id; $args = array( @@ -131,7 +131,7 @@ class CatalogProducts { 'headers' => array( 'Authorization' => 'Bearer ' . $bearer->token(), 'Content-Type' => 'application/json', - 'Prefer' => 'return=representation' + 'Prefer' => 'return=representation', ), 'body' => wp_json_encode( $data ), ); @@ -157,14 +157,14 @@ class CatalogProducts { * @param string $id Product ID. * @return Product */ - public function product(string $id): Product { + public function product( string $id ): Product { $bearer = $this->bearer->bearer(); $url = trailingslashit( $this->host ) . 'v1/catalogs/products/' . $id; $args = array( 'headers' => array( 'Authorization' => 'Bearer ' . $bearer->token(), 'Content-Type' => 'application/json', - 'Prefer' => 'return=representation' + 'Prefer' => 'return=representation', ), ); @@ -182,6 +182,6 @@ class CatalogProducts { ); } - return $this->product_factory->from_paypal_response($json); + return $this->product_factory->from_paypal_response( $json ); } } diff --git a/modules/ppcp-api-client/src/Entity/BillingCycle.php b/modules/ppcp-api-client/src/Entity/BillingCycle.php index 01a275114..7e8253be9 100644 --- a/modules/ppcp-api-client/src/Entity/BillingCycle.php +++ b/modules/ppcp-api-client/src/Entity/BillingCycle.php @@ -92,11 +92,11 @@ class BillingCycle { */ public function to_array(): array { return array( - 'frequency' => $this->frequency(), - 'sequence' => $this->sequence(), - 'tenure_type' => $this->tenure_type(), + 'frequency' => $this->frequency(), + 'sequence' => $this->sequence(), + 'tenure_type' => $this->tenure_type(), 'pricing_scheme' => $this->pricing_scheme(), - 'total_cycles' => $this->total_cycles(), + 'total_cycles' => $this->total_cycles(), ); } } diff --git a/modules/ppcp-api-client/src/Entity/Plan.php b/modules/ppcp-api-client/src/Entity/Plan.php index 2a756cb14..a04f99c5a 100644 --- a/modules/ppcp-api-client/src/Entity/Plan.php +++ b/modules/ppcp-api-client/src/Entity/Plan.php @@ -101,12 +101,12 @@ class Plan { public function to_array():array { return array( - 'id' => $this->id(), - 'name' => $this->name(), - 'product_id' => $this->product_id(), - 'billing_cycles' => $this->billing_cycles(), + 'id' => $this->id(), + 'name' => $this->name(), + 'product_id' => $this->product_id(), + 'billing_cycles' => $this->billing_cycles(), 'payment_preferences' => $this->payment_preferences(), - 'status' => $this->status(), + 'status' => $this->status(), ); } } diff --git a/modules/ppcp-api-client/src/Factory/PlanFactory.php b/modules/ppcp-api-client/src/Factory/PlanFactory.php index 6c0097b18..c51104bac 100644 --- a/modules/ppcp-api-client/src/Factory/PlanFactory.php +++ b/modules/ppcp-api-client/src/Factory/PlanFactory.php @@ -6,8 +6,8 @@ use stdClass; use WooCommerce\PayPalCommerce\ApiClient\Entity\Plan; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; -class PlanFactory -{ +class PlanFactory { + /** * @var BillingCycleFactory */ @@ -20,12 +20,12 @@ class PlanFactory public function __construct( BillingCycleFactory $billing_cycle_factory, PaymentPreferencesFactory $payment_preferences_factory - ){ - $this->billing_cycle_factory = $billing_cycle_factory; + ) { + $this->billing_cycle_factory = $billing_cycle_factory; $this->payment_preferences_factory = $payment_preferences_factory; } - public function from_paypal_response(stdClass $data): Plan { + public function from_paypal_response( stdClass $data ): Plan { if ( ! isset( $data->id ) ) { throw new RuntimeException( __( 'No id for given plan', 'woocommerce-paypal-payments' ) @@ -48,11 +48,11 @@ class PlanFactory } $billing_cycles = array(); - foreach ($data->billing_cycles as $billing_cycle) { - $billing_cycles[] = $this->billing_cycle_factory->from_paypal_response($billing_cycle); + foreach ( $data->billing_cycles as $billing_cycle ) { + $billing_cycles[] = $this->billing_cycle_factory->from_paypal_response( $billing_cycle ); } - $payment_preferences = $this->payment_preferences_factory->from_paypal_response($data->payment_preferences); + $payment_preferences = $this->payment_preferences_factory->from_paypal_response( $data->payment_preferences ); return new Plan( $data->id, diff --git a/modules/ppcp-api-client/src/Factory/ProductFactory.php b/modules/ppcp-api-client/src/Factory/ProductFactory.php index c52798e5e..bd58a0e0c 100644 --- a/modules/ppcp-api-client/src/Factory/ProductFactory.php +++ b/modules/ppcp-api-client/src/Factory/ProductFactory.php @@ -23,7 +23,7 @@ class ProductFactory { * @return Product * @throws RuntimeException When JSON object is malformed. */ - public function from_paypal_response(stdClass $data): Product { + public function from_paypal_response( stdClass $data ): Product { if ( ! isset( $data->id ) ) { throw new RuntimeException( __( 'No id for product given', 'woocommerce-paypal-payments' ) diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index e0be6cab6..ec6aced40 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -257,7 +257,7 @@ class SmartButton implements SmartButtonInterface { $this->module_url = $module_url; $this->version = $version; - $this->session_handler = $session_handler; + $this->session_handler = $session_handler; $this->settings = $settings; $this->payer_factory = $payer_factory; $this->client_id = $client_id; @@ -826,15 +826,15 @@ class SmartButton implements SmartButtonInterface { 'endpoint' => \WC_AJAX::get_endpoint( StartPayPalVaultingEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( StartPayPalVaultingEndpoint::nonce() ), ), - 'save_checkout_form' => array( + 'save_checkout_form' => array( 'endpoint' => \WC_AJAX::get_endpoint( SaveCheckoutFormEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( SaveCheckoutFormEndpoint::nonce() ), ), - 'validate_checkout' => array( + 'validate_checkout' => array( 'endpoint' => \WC_AJAX::get_endpoint( ValidateCheckoutEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ValidateCheckoutEndpoint::nonce() ), ), - 'cart_script_params' => array( + 'cart_script_params' => array( 'endpoint' => \WC_AJAX::get_endpoint( CartScriptParamsEndpoint::ENDPOINT ), ), ), @@ -1380,14 +1380,13 @@ class SmartButton implements SmartButtonInterface { * @return string */ private function paypal_subscription_id(): string { - if($this->subscription_helper->current_product_is_subscription() ) { + if ( $this->subscription_helper->current_product_is_subscription() ) { $product = wc_get_product(); assert( $product instanceof WC_Product ); if ( $product->get_type() === 'subscription' && $product->meta_exists( 'ppcp_subscription_plan' ) ) { return $product->get_meta( 'ppcp_subscription_plan' )['id']; } - } $items = WC()->cart->get_cart_contents(); diff --git a/modules/ppcp-button/src/Endpoint/ApproveSubscriptionEndpoint.php b/modules/ppcp-button/src/Endpoint/ApproveSubscriptionEndpoint.php index 86a5e06e3..92bda6ef4 100644 --- a/modules/ppcp-button/src/Endpoint/ApproveSubscriptionEndpoint.php +++ b/modules/ppcp-button/src/Endpoint/ApproveSubscriptionEndpoint.php @@ -44,8 +44,8 @@ class ApproveSubscriptionEndpoint implements EndpointInterface { /** * ApproveSubscriptionEndpoint constructor. * - * @param RequestData $request_data The request data helper. - * @param OrderEndpoint $order_endpoint The order endpoint. + * @param RequestData $request_data The request data helper. + * @param OrderEndpoint $order_endpoint The order endpoint. * @param SessionHandler $session_handler The session handler. */ public function __construct( diff --git a/modules/ppcp-subscription/services.php b/modules/ppcp-subscription/services.php index e84fe6cf4..fa54e6be1 100644 --- a/modules/ppcp-subscription/services.php +++ b/modules/ppcp-subscription/services.php @@ -43,14 +43,14 @@ return array( $endpoint = $container->get( 'api.endpoint.payment-token' ); return new PaymentTokenRepository( $factory, $endpoint ); }, - 'subscription.api-handler' => static function(ContainerInterface $container): SubscriptionsApiHandler { + 'subscription.api-handler' => static function( ContainerInterface $container ): SubscriptionsApiHandler { return new SubscriptionsApiHandler( - $container->get('api.endpoint.catalog-products'), - $container->get('api.factory.product'), - $container->get('api.endpoint.billing-plans'), - $container->get('api.factory.billing-cycle'), - $container->get('api.factory.payment-preferences'), + $container->get( 'api.endpoint.catalog-products' ), + $container->get( 'api.factory.product' ), + $container->get( 'api.endpoint.billing-plans' ), + $container->get( 'api.factory.billing-cycle' ), + $container->get( 'api.factory.payment-preferences' ), $container->get( 'woocommerce.logger.woocommerce' ) ); - } + }, ); diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index 5287f9375..d04319458 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Subscription; +use WC_Product; use WC_Product_Subscription; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; @@ -142,6 +143,11 @@ class SubscriptionModule implements ModuleInterface { add_action( 'save_post', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ function( $product_id ) use ( $c ) { $settings = $c->get( 'wcgateway.settings' ); assert( $settings instanceof Settings ); @@ -152,14 +158,19 @@ class SubscriptionModule implements ModuleInterface { return; } - $nonce = wc_clean(wp_unslash($_POST['_wcsnonce'])) ?? ''; + $nonce = wc_clean( wp_unslash( $_POST['_wcsnonce'] ?? '' ) ); if ( $subscriptions_mode !== 'subscriptions_api' + || ! is_string( $nonce ) || ! wp_verify_nonce( $nonce, 'wcs_subscription_meta' ) ) { return; } - $product = wc_get_product( $product_id ); + $product = wc_get_product( $product_id ); + if ( ! is_a( $product, WC_Product::class ) ) { + return; + } + $enable_subscription_product = wc_clean( wp_unslash( $_POST['_ppcp_enable_subscription_product'] ?? '' ) ); $product->update_meta_data( '_ppcp_enable_subscription_product', $enable_subscription_product ); $product->save(); @@ -180,6 +191,10 @@ class SubscriptionModule implements ModuleInterface { if ( $product->meta_exists( 'ppcp_subscription_product' ) && ! $product->meta_exists( 'ppcp_subscription_plan' ) ) { $subscription_plan_name = wc_clean( wp_unslash( $_POST['_ppcp_subscription_plan_name'] ?? '' ) ); + if ( ! is_string( $subscription_plan_name ) ) { + return; + } + $product->update_meta_data( '_ppcp_subscription_plan_name', $subscription_plan_name ); $product->save(); @@ -192,7 +207,12 @@ class SubscriptionModule implements ModuleInterface { add_filter( 'woocommerce_order_data_store_cpt_get_orders_query', - function( $query, $query_vars ) { + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ + function( $query, $query_vars ): array { if ( ! empty( $query_vars['ppcp_subscription'] ) ) { $query['meta_query'][] = array( 'key' => 'ppcp_subscription', @@ -208,6 +228,11 @@ class SubscriptionModule implements ModuleInterface { add_action( 'woocommerce_customer_changed_subscription_to_cancelled', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ function( $subscription ) use ( $c ) { $subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? ''; if ( $subscription_id ) { @@ -231,6 +256,11 @@ class SubscriptionModule implements ModuleInterface { add_action( 'woocommerce_customer_changed_subscription_to_active', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ function( $subscription ) use ( $c ) { $subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? ''; if ( $subscription_id ) { @@ -261,8 +291,17 @@ class SubscriptionModule implements ModuleInterface { try { $subscriptions_mode = $settings->get( 'subscriptions_mode' ); if ( $subscriptions_mode === 'subscriptions_api' ) { + /** + * Needed for getting global post object. + * + * @psalm-suppress InvalidGlobal + */ global $post; - $product = wc_get_product( $post->ID ); + $product = wc_get_product( $post->ID ); + if ( ! is_a( $product, WC_Product::class ) ) { + return; + } + $enable_subscription_product = $product->get_meta( '_ppcp_enable_subscription_product' ); $subscription_plan_name = $product->get_meta( '_ppcp_subscription_plan_name' ); @@ -289,6 +328,11 @@ class SubscriptionModule implements ModuleInterface { add_action( 'woocommerce_subscription_before_actions', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ function( $subscription ) use ( $c ) { $subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? ''; if ( $subscription_id ) { @@ -308,7 +352,16 @@ class SubscriptionModule implements ModuleInterface { add_filter( 'wcs_view_subscription_actions', - function( $actions, $subscription ) { + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ + function( $actions, $subscription ): array { + if ( ! is_a( $subscription, WC_Subscription::class ) ) { + return $actions; + } + $subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? ''; if ( $subscription_id && $subscription->get_status() === 'active' ) { $url = wp_nonce_url( @@ -342,18 +395,22 @@ class SubscriptionModule implements ModuleInterface { add_action( 'wp_loaded', function() use ( $c ) { - if(! function_exists('wcs_get_subscription')) { + if ( ! function_exists( 'wcs_get_subscription' ) ) { return; } $cancel_subscription_id = wc_clean( wp_unslash( $_GET['ppcp_cancel_subscription'] ?? '' ) ); $subscription = wcs_get_subscription( absint( $cancel_subscription_id ) ); - if ( ! wcs_is_subscription( $subscription ) ) { + if ( ! wcs_is_subscription( $subscription ) || $subscription === false ) { return; } $subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? ''; $nonce = wc_clean( wp_unslash( $_GET['_wpnonce'] ?? '' ) ); + if ( ! is_string( $nonce ) ) { + return; + } + if ( $subscription_id && $cancel_subscription_id @@ -393,7 +450,16 @@ class SubscriptionModule implements ModuleInterface { add_filter( 'woocommerce_order_actions', - function( $actions, $subscription ) { + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ + function( $actions, $subscription ): array { + if ( ! is_a( $subscription, WC_Subscription::class ) ) { + return $actions; + } + $subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? ''; if ( $subscription_id && isset( $actions['wcs_process_renewal'] ) ) { unset( $actions['wcs_process_renewal'] ); @@ -407,6 +473,11 @@ class SubscriptionModule implements ModuleInterface { add_action( 'woocommerce_process_shop_subscription_meta', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ function( $id, $subscription ) use ( $c ) { $subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? ''; if ( $subscription_id ) { diff --git a/modules/ppcp-subscription/src/SubscriptionsApiHandler.php b/modules/ppcp-subscription/src/SubscriptionsApiHandler.php index b702aac0d..9ec411840 100644 --- a/modules/ppcp-subscription/src/SubscriptionsApiHandler.php +++ b/modules/ppcp-subscription/src/SubscriptionsApiHandler.php @@ -1,4 +1,11 @@ products_endpoint = $products_endpoint; - $this->product_factory = $product_factory; - $this->billing_plans_endpoint = $billing_plans_endpoint; - $this->billing_cycle_factory = $billing_cycle_factory; + $this->products_endpoint = $products_endpoint; + $this->product_factory = $product_factory; + $this->billing_plans_endpoint = $billing_plans_endpoint; + $this->billing_cycle_factory = $billing_cycle_factory; $this->payment_preferences_factory = $payment_preferences_factory; - $this->logger = $logger; - } + $this->logger = $logger; + } /** * Creates a Catalog Product and adds it as WC product meta. * - * @param WC_Product $product + * @param WC_Product $product The WC product. * @return void */ public function create_product( WC_Product $product ) { try { - $subscription_product = $this->products_endpoint->create( $product->get_title(), $product->get_description()); + $subscription_product = $this->products_endpoint->create( $product->get_title(), $product->get_description() ); $product->update_meta_data( 'ppcp_subscription_product', $subscription_product->to_array() ); $product->save(); } catch ( RuntimeException $exception ) { @@ -81,13 +113,20 @@ class SubscriptionsApiHandler { } } - public function create_plan( string $plan_name, WC_Product $product ) { + /** + * Creates a subscription plan. + * + * @param string $plan_name The plan name. + * @param WC_Product $product The WC product. + * @return void + */ + public function create_plan( string $plan_name, WC_Product $product ): void { try { $subscription_plan = $this->billing_plans_endpoint->create( - $plan_name, + $plan_name, $product->get_meta( 'ppcp_subscription_product' )['id'] ?? '', - array($this->billing_cycle_factory->from_wc_product($product)->to_array()), - $this->payment_preferences_factory->from_wc_product($product)->to_array() + array( $this->billing_cycle_factory->from_wc_product( $product )->to_array() ), + $this->payment_preferences_factory->from_wc_product( $product )->to_array() ); $product->update_meta_data( 'ppcp_subscription_plan', $subscription_plan->to_array() ); @@ -102,65 +141,76 @@ class SubscriptionsApiHandler { } } - public function update_product(WC_Product $product) { - try { - $catalog_product_id = $product->get_meta( 'ppcp_subscription_product' )['id'] ?? ''; - if($catalog_product_id) { - $catalog_product = $this->products_endpoint->product($catalog_product_id); - $catalog_product_name = $catalog_product->name() ?? ''; - $catalog_product_description = $catalog_product->description() ?? ''; - if($catalog_product_name !== $product->get_title() || $catalog_product_description !== $product->get_description()) { - $data = array(); - if($catalog_product_name !== $product->get_title()) { - $data[] = (object) array( - 'op' => 'replace', - 'path' => '/name', - 'value' => $product->get_title(), - ); - } - if($catalog_product_description !== $product->get_description()) { - $data[] = (object) array( - 'op' => 'replace', - 'path' => '/description', - 'value' => $product->get_description(), - ); - } + /** + * Updates a product. + * + * @param WC_Product $product The WC product. + * @return void + */ + public function update_product( WC_Product $product ): void { + try { + $catalog_product_id = $product->get_meta( 'ppcp_subscription_product' )['id'] ?? ''; + if ( $catalog_product_id ) { + $catalog_product = $this->products_endpoint->product( $catalog_product_id ); + $catalog_product_name = $catalog_product->name() ?: ''; + $catalog_product_description = $catalog_product->description() ?: ''; + if ( $catalog_product_name !== $product->get_title() || $catalog_product_description !== $product->get_description() ) { + $data = array(); + if ( $catalog_product_name !== $product->get_title() ) { + $data[] = (object) array( + 'op' => 'replace', + 'path' => '/name', + 'value' => $product->get_title(), + ); + } + if ( $catalog_product_description !== $product->get_description() ) { + $data[] = (object) array( + 'op' => 'replace', + 'path' => '/description', + 'value' => $product->get_description(), + ); + } - $this->products_endpoint->update($catalog_product_id, $data); - } - } + $this->products_endpoint->update( $catalog_product_id, $data ); + } + } + } catch ( RuntimeException $exception ) { + $error = $exception->getMessage(); + if ( is_a( $exception, PayPalApiException::class ) ) { + $error = $exception->get_details( $error ); + } - } catch (RuntimeException $exception) { - $error = $exception->getMessage(); - if ( is_a( $exception, PayPalApiException::class ) ) { - $error = $exception->get_details( $error ); - } - - $this->logger->error( 'Could not update catalog product on PayPal. ' . $error ); - } + $this->logger->error( 'Could not update catalog product on PayPal. ' . $error ); + } } - public function update_plan(WC_Product $product) { - try { - $subscription_plan_id = $product->get_meta('ppcp_subscription_plan')['id'] ?? ''; - if ($subscription_plan_id) { - $subscription_plan = $this->billing_plans_endpoint->plan($subscription_plan_id); + /** + * Updates a plan. + * + * @param WC_Product $product The WC product. + * @return void + */ + public function update_plan( WC_Product $product ): void { + try { + $subscription_plan_id = $product->get_meta( 'ppcp_subscription_plan' )['id'] ?? ''; + if ( $subscription_plan_id ) { + $subscription_plan = $this->billing_plans_endpoint->plan( $subscription_plan_id ); - $price = $subscription_plan->billing_cycles()[0]->pricing_scheme()['fixed_price']['value'] ?? ''; - if($price && round($price, 2) !== round($product->get_price(), 2)) { - $this->billing_plans_endpoint->update_pricing( - $subscription_plan_id, - $this->billing_cycle_factory->from_wc_product($product) - ); - } - } - } catch (RuntimeException $exception) { - $error = $exception->getMessage(); - if ( is_a( $exception, PayPalApiException::class ) ) { - $error = $exception->get_details( $error ); - } + $price = $subscription_plan->billing_cycles()[0]->pricing_scheme()['fixed_price']['value'] ?? ''; + if ( $price && round( $price, 2 ) !== round( (float) $product->get_price(), 2 ) ) { + $this->billing_plans_endpoint->update_pricing( + $subscription_plan_id, + $this->billing_cycle_factory->from_wc_product( $product ) + ); + } + } + } catch ( RuntimeException $exception ) { + $error = $exception->getMessage(); + if ( is_a( $exception, PayPalApiException::class ) ) { + $error = $exception->get_details( $error ); + } - $this->logger->error( 'Could not update subscription plan on PayPal. ' . $error ); - } + $this->logger->error( 'Could not update subscription plan on PayPal. ' . $error ); + } } } diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index f5375c0b3..201ab8ea5 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -798,8 +798,8 @@ return array( 'description' => '', 'default' => 'vaulting_api', 'options' => array( - 'vaulting_api' => __( 'PayPal Vaulting', 'woocommerce-paypal-payments' ), - 'subscriptions_api' => __( 'PayPal Subscriptions', 'woocommerce-paypal-payments' ), + 'vaulting_api' => __( 'PayPal Vaulting', 'woocommerce-paypal-payments' ), + 'subscriptions_api' => __( 'PayPal Subscriptions', 'woocommerce-paypal-payments' ), 'disable_paypal_subscriptions' => __( 'Disable PayPal for subscriptions', 'woocommerce-paypal-payments' ), ), 'screens' => array( diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index c85176346..0756e73f9 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -338,9 +338,9 @@ class SettingsListener { $this->dcc_status_cache->delete( DCCProductStatus::DCC_STATUS_CACHE_KEY ); } - $ppcp_reference_transaction_enabled = get_transient('ppcp_reference_transaction_enabled') ?? ''; - if($ppcp_reference_transaction_enabled) { - delete_transient('ppcp_reference_transaction_enabled'); + $ppcp_reference_transaction_enabled = get_transient( 'ppcp_reference_transaction_enabled' ) ?? ''; + if ( $ppcp_reference_transaction_enabled ) { + delete_transient( 'ppcp_reference_transaction_enabled' ); } $redirect_url = false; diff --git a/modules/ppcp-webhooks/services.php b/modules/ppcp-webhooks/services.php index 44fb8523b..bf73ce76d 100644 --- a/modules/ppcp-webhooks/services.php +++ b/modules/ppcp-webhooks/services.php @@ -92,8 +92,8 @@ return array( new PaymentSaleCompleted( $logger ), new BillingSubscriptionCancelled( $logger ), new BillingPlanPricingChangeActivated( $logger ), - new CatalogProductUpdated($logger), - new BillingPlanUpdated($logger), + new CatalogProductUpdated( $logger ), + new BillingPlanUpdated( $logger ), ); }, diff --git a/modules/ppcp-webhooks/src/Handler/BillingPlanPricingChangeActivated.php b/modules/ppcp-webhooks/src/Handler/BillingPlanPricingChangeActivated.php index 1735b3254..f854b0453 100644 --- a/modules/ppcp-webhooks/src/Handler/BillingPlanPricingChangeActivated.php +++ b/modules/ppcp-webhooks/src/Handler/BillingPlanPricingChangeActivated.php @@ -65,19 +65,24 @@ class BillingPlanPricingChangeActivated implements RequestHandler { */ public function handle_request( WP_REST_Request $request ): WP_REST_Response { $response = array( 'success' => false ); + if ( is_null( $request['resource'] ) ) { + return new WP_REST_Response( $response ); + } $plan_id = wc_clean( wp_unslash( $request['resource']['id'] ?? '' ) ); $price = wc_clean( wp_unslash( $request['resource']['billing_cycles'][0]['pricing_scheme']['fixed_price']['value'] ?? '' ) ); if ( $plan_id && $price ) { - $args = array( + $args = array( 'meta_key' => 'ppcp_subscription_plan', ); - $products = wc_get_products( $args ); - foreach ( $products as $product ) { - if ( $product->get_meta( 'ppcp_subscription_plan' )->id === $plan_id ) { - $product->update_meta_data( '_subscription_price', $price ); - $product->save(); + $products = wc_get_products( $args ); + if ( is_array( $products ) ) { + foreach ( $products as $product ) { + if ( $product->get_meta( 'ppcp_subscription_plan' )->id === $plan_id ) { + $product->update_meta_data( '_subscription_price', $price ); + $product->save(); + } } } } diff --git a/modules/ppcp-webhooks/src/Handler/BillingPlanUpdated.php b/modules/ppcp-webhooks/src/Handler/BillingPlanUpdated.php index 984fccec2..949c5130d 100644 --- a/modules/ppcp-webhooks/src/Handler/BillingPlanUpdated.php +++ b/modules/ppcp-webhooks/src/Handler/BillingPlanUpdated.php @@ -64,6 +64,11 @@ class BillingPlanUpdated implements RequestHandler { * @return WP_REST_Response */ public function handle_request( WP_REST_Request $request ): WP_REST_Response { + $response = array( 'success' => false ); + if ( is_null( $request['resource'] ) ) { + return new WP_REST_Response( $response ); + } + $plan_id = wc_clean( wp_unslash( $request['resource']['id'] ?? '' ) ); if ( $plan_id ) { $products = wc_get_products( @@ -72,29 +77,31 @@ class BillingPlanUpdated implements RequestHandler { ) ); - foreach ( $products as $product ) { - if ( $product->meta_exists( 'ppcp_subscription_plan' ) ) { - $plan_name = wc_clean( wp_unslash( $request['resource']['name'] ?? '' ) ); - if ( $plan_name !== $product->get_meta( '_ppcp_subscription_plan_name' ) ) { - $product->update_meta_data( '_ppcp_subscription_plan_name', $plan_name ); - $product->save(); - } - - $billing_cycles = wc_clean( wp_unslash( $request['resource']['billing_cycles'] ?? array() ) ); - if ( $billing_cycles ) { - $price = $billing_cycles[0]['pricing_scheme']['fixed_price']['value'] ?? ''; - if ( $price && round( $price, 2 ) !== round( $product->get_meta( '_subscription_price' ), 2 ) ) { - $product->update_meta_data( '_subscription_price', $price ); + if ( is_array( $products ) ) { + foreach ( $products as $product ) { + if ( $product->meta_exists( 'ppcp_subscription_plan' ) ) { + $plan_name = wc_clean( wp_unslash( $request['resource']['name'] ?? '' ) ); + if ( $plan_name !== $product->get_meta( '_ppcp_subscription_plan_name' ) ) { + $product->update_meta_data( '_ppcp_subscription_plan_name', $plan_name ); $product->save(); } - } - $payment_preferences = wc_clean( wp_unslash( $request['resource']['payment_preferences'] ?? array() ) ); - if ( $payment_preferences ) { - $setup_fee = $payment_preferences['setup_fee']['value'] ?? ''; - if ( $setup_fee && round( $setup_fee, 2 ) !== round( $product->get_meta( '_subscription_sign_up_fee' ), 2 ) ) { - $product->update_meta_data( '_subscription_sign_up_fee', $setup_fee ); - $product->save(); + $billing_cycles = wc_clean( wp_unslash( $request['resource']['billing_cycles'] ?? array() ) ); + if ( $billing_cycles ) { + $price = $billing_cycles[0]['pricing_scheme']['fixed_price']['value'] ?? ''; + if ( $price && round( $price, 2 ) !== round( $product->get_meta( '_subscription_price' ), 2 ) ) { + $product->update_meta_data( '_subscription_price', $price ); + $product->save(); + } + } + + $payment_preferences = wc_clean( wp_unslash( $request['resource']['payment_preferences'] ?? array() ) ); + if ( $payment_preferences ) { + $setup_fee = $payment_preferences['setup_fee']['value'] ?? ''; + if ( $setup_fee && round( $setup_fee, 2 ) !== round( $product->get_meta( '_subscription_sign_up_fee' ), 2 ) ) { + $product->update_meta_data( '_subscription_sign_up_fee', $setup_fee ); + $product->save(); + } } } } diff --git a/modules/ppcp-webhooks/src/Handler/BillingSubscriptionCancelled.php b/modules/ppcp-webhooks/src/Handler/BillingSubscriptionCancelled.php index 2c723b0d7..aa3e50658 100644 --- a/modules/ppcp-webhooks/src/Handler/BillingSubscriptionCancelled.php +++ b/modules/ppcp-webhooks/src/Handler/BillingSubscriptionCancelled.php @@ -65,6 +65,9 @@ class BillingSubscriptionCancelled implements RequestHandler { */ public function handle_request( WP_REST_Request $request ): WP_REST_Response { $response = array( 'success' => false ); + if ( is_null( $request['resource'] ) ) { + return new WP_REST_Response( $response ); + } $subscription_id = wc_clean( wp_unslash( $request['resource']['id'] ?? '' ) ); if ( $subscription_id ) { diff --git a/modules/ppcp-webhooks/src/Handler/CatalogProductUpdated.php b/modules/ppcp-webhooks/src/Handler/CatalogProductUpdated.php index 0c1d2d08a..a3865b518 100644 --- a/modules/ppcp-webhooks/src/Handler/CatalogProductUpdated.php +++ b/modules/ppcp-webhooks/src/Handler/CatalogProductUpdated.php @@ -64,6 +64,11 @@ class CatalogProductUpdated implements RequestHandler { * @return WP_REST_Response */ public function handle_request( WP_REST_Request $request ): WP_REST_Response { + $response = array( 'success' => false ); + if ( is_null( $request['resource'] ) ) { + return new WP_REST_Response( $response ); + } + $product_id = wc_clean( wp_unslash( $request['resource']['id'] ?? '' ) ); $name = wc_clean( wp_unslash( $request['resource']['name'] ?? '' ) ); if ( $product_id && $name ) { @@ -72,21 +77,28 @@ class CatalogProductUpdated implements RequestHandler { ); $products = wc_get_products( $args ); - foreach ( $products as $product ) { - if ( - $product->meta_exists( 'ppcp_subscription_product' ) - && isset( $product->get_meta( 'ppcp_subscription_product' )['id'] ) - && $product->get_meta( 'ppcp_subscription_product' )['id'] === $product_id - && $product->get_title() !== $name - ) { - wp_update_post( - array( - 'ID' => $product->get_id(), - 'post_title' => $name, - ) - ); + if ( is_array( $products ) ) { + foreach ( $products as $product ) { + if ( + $product->meta_exists( 'ppcp_subscription_product' ) + && isset( $product->get_meta( 'ppcp_subscription_product' )['id'] ) + && $product->get_meta( 'ppcp_subscription_product' )['id'] === $product_id + && $product->get_title() !== $name + ) { + /** + * Suppress ArgumentTypeCoercion + * + * @psalm-suppress ArgumentTypeCoercion + */ + wp_update_post( + array( + 'ID' => $product->get_id(), + 'post_title' => $name, + ) + ); - break; + break; + } } } } diff --git a/modules/ppcp-webhooks/src/Handler/PaymentSaleCompleted.php b/modules/ppcp-webhooks/src/Handler/PaymentSaleCompleted.php index 69199b15e..6b2472bee 100644 --- a/modules/ppcp-webhooks/src/Handler/PaymentSaleCompleted.php +++ b/modules/ppcp-webhooks/src/Handler/PaymentSaleCompleted.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Webhooks\Handler; use Psr\Log\LoggerInterface; +use WC_Order; use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait; use WP_REST_Request; use WP_REST_Response; @@ -67,8 +68,11 @@ class PaymentSaleCompleted implements RequestHandler { */ public function handle_request( WP_REST_Request $request ): WP_REST_Response { $response = array( 'success' => false ); + if ( is_null( $request['resource'] ) ) { + return new WP_REST_Response( $response ); + } - $billing_agreement_id = wc_clean( wp_unslash( $request['resource']['billing_agreement_id'] ) ) ?? ''; + $billing_agreement_id = wc_clean( wp_unslash( $request['resource']['billing_agreement_id'] ?? '' ) ); if ( ! $billing_agreement_id ) { $message = 'Could not retrieve billing agreement id for subscription.'; $this->logger->warning( $message, array( 'request' => $request ) ); @@ -88,7 +92,8 @@ class PaymentSaleCompleted implements RequestHandler { $subscriptions = wcs_get_subscriptions( $args ); if ( ! $subscriptions ) { - $message = "Could not retrieve WC subscriptions for billing agreement: {$billing_agreement_id}"; + $billing_agreement_id = is_string( $billing_agreement_id ) ? $billing_agreement_id : ''; + $message = "Could not retrieve WC subscriptions for billing agreement: {$billing_agreement_id}"; $this->logger->warning( $message, array( 'request' => $request ) ); $response['message'] = $message; return new WP_REST_Response( $response ); @@ -96,11 +101,13 @@ class PaymentSaleCompleted implements RequestHandler { foreach ( $subscriptions as $subscription ) { $renewal_order = wcs_create_renewal_order( $subscription ); - $renewal_order->payment_complete(); + if ( is_a( $renewal_order, WC_Order::class ) ) { + $renewal_order->payment_complete(); - $transaction_id = wc_clean( wp_unslash( $request['resource']['id'] ) ) ?? ''; - if ( $transaction_id ) { - $this->update_transaction_id( $transaction_id, $renewal_order ); + $transaction_id = wc_clean( wp_unslash( $request['resource']['id'] ?? '' ) ); + if ( $transaction_id && is_string( $transaction_id ) ) { + $this->update_transaction_id( $transaction_id, $renewal_order ); + } } }