@@ -323,118 +323,136 @@ public function validate_info_field( $key, $value ) {}
323323 * @return bool
324324 */
325325 public function is_available () {
326- $ is_available = parent ::is_available ();
327-
328- if ( ! $ is_available ) {
329- return false ;
326+ static $ checking = false ;
327+ if ( $ checking ) {
328+ return false ; // prevent reentry/loop
330329 }
331-
332- // It's not possible to support the rounding on subtotal level and still get valid tax rates and amounts.
333- // Therefore the payment methods are disabled, if this option is active.
334- if ( wc_tax_enabled () && ( 'yes ' === get_option ( 'woocommerce_tax_round_at_subtotal ' ) ) ) {
335- if ( 'yes ' === get_option ( WooCommerce_PostFinanceCheckout::POSTFINANCECHECKOUT_CK_ENFORCE_CONSISTENCY ) ) {
336- $ error_message = esc_html__ ( "'WooCommerce > Settings > PostFinanceCheckout > Enforce Consistency' and 'WooCommerce > Settings > Tax > Rounding' are both enabled. Please disable at least one of them. " , 'woo-postfinancecheckout ' );
337- WooCommerce_PostFinanceCheckout::instance ()->log ( $ error_message , WC_Log_Levels::ERROR );
330+ $ checking = true ;
331+
332+ try {
333+ // Step 1: Respect parent availability
334+ if ( ! parent ::is_available () ) {
338335 return false ;
339336 }
340- }
341-
342- // It is possbile this function is called in the WordPress admin section. There is not a cart, so all active methods are available.
343- // If it is not a checkout page the method is availalbe. Some plugins check this, on non checkout pages, without a cart available.
344- // The active gateways are available during order total caluclation, as other plugins could need them.
345- if (
346- apply_filters (
337+
338+ // Step 2: Prevent conflict with tax rounding
339+ if ( wc_tax_enabled () && 'yes ' === get_option ( 'woocommerce_tax_round_at_subtotal ' ) ) {
340+ if ( 'yes ' === get_option ( WooCommerce_PostFinanceCheckout::POSTFINANCECHECKOUT_CK_ENFORCE_CONSISTENCY ) ) {
341+ $ error_message = esc_html__ ( "'WooCommerce > Settings > PostFinanceCheckout > Enforce Consistency' and 'WooCommerce > Settings > Tax > Rounding' are both enabled. Please disable at least one of them. " , 'woo-postfinancecheckout ' );
342+ WooCommerce_PostFinanceCheckout::instance ()->log ( $ error_message , WC_Log_Levels::ERROR );
343+ return false ;
344+ }
345+ }
346+
347+ // Step 3: Use session cache if available
348+ $ gateway_available = WC ()->session && WC ()->session ->has_session ()
349+ ? WC ()->session ->get ( 'postfinancecheckout_payment_gateways ' )
350+ : array ();
351+
352+ if ( isset ( $ gateway_available [ $ this ->wle_payment_method_configuration_id ] ) ) {
353+ return $ gateway_available [ $ this ->wle_payment_method_configuration_id ];
354+ }
355+
356+ // Step 4: Allow admin and non-checkout pages to pass
357+ if ( apply_filters (
347358 'postfinancecheckout_is_method_available ' ,
348- is_admin ()
349- || ! is_checkout ()
350- || ( isset ( $ GLOBALS ['_postfinancecheckout_calculating ' ] )
351- && $ GLOBALS ['_postfinancecheckout_calculating ' ]
352- ),
359+ is_admin () || ! is_checkout () || ( isset ( $ GLOBALS ['_postfinancecheckout_calculating ' ] ) && $ GLOBALS ['_postfinancecheckout_calculating ' ] ),
353360 $ this
354- )
355- ) {//phpcs:ignore
356- return $ this ->get_payment_method_configuration ()->get_state () == WC_PostFinanceCheckout_Entity_Method_Configuration::POSTFINANCECHECKOUT_STATE_ACTIVE ;
357- }
358-
359- global $ wp ;
360- if ( is_checkout () && isset ( $ wp ->query_vars ['order-received ' ] ) ) {
361- // Sometimes, when the Thank you page is loaded, there are new attempts to get
362- // gateways availability. In this particular case, we retrieve the availability
363- // information from the session, so the plugin does not have to ask the portal
364- // for this information, creating an unused transaction in the process.
365- $ gateway_available = WC ()->session ->get ( 'postfinancecheckout_payment_gateways ' );
366- if ( ! empty ( $ gateway_available [ $ this ->pfc_payment_method_configuration_id ] ) ) {
367- return $ gateway_available [ $ this ->pfc_payment_method_configuration_id ];
361+ ) ) {
362+ return $ this ->get_payment_method_configuration ()->get_state () === WC_PostFinanceCheckout_Entity_Method_Configuration::POSTFINANCECHECKOUT_STATE_ACTIVE ;
363+ }
364+
365+ // Step 5: Handle "order received" page logic
366+ global $ wp ;
367+ if ( is_checkout () && isset ( $ wp ->query_vars ['order-received ' ] ) ) {
368+ return ! empty ( $ gateway_available [ $ this ->wle_payment_method_configuration_id ] );
369+ }
370+
371+ // Step 6: Handle "order pay" endpoint
372+ if ( apply_filters ( 'wc_postfinancecheckout_is_order_pay_endpoint ' , is_checkout_pay_page () ) ) {
373+ $ order = WC_Order_Factory::get_order ( $ wp ->query_vars ['order-pay ' ] ?? 0 );
374+ if ( ! $ order ) {
375+ return false ;
376+ }
377+ $ possible_methods = $ this ->get_safe_possible_payment_methods_for_order ( $ order );
368378 } else {
369-
370- return false ;
379+ $ possible_methods = $ this ->get_safe_possible_payment_methods_for_cart ();
371380 }
372- }
373-
374- if ( apply_filters ( 'wc_postfinancecheckout_is_order_pay_endpoint ' , is_checkout_pay_page () ) ) { //phpcs:ignore
375- // We have to use the order and not the cart for this endpoint.
376- $ order = WC_Order_Factory::get_order ( $ wp ->query_vars ['order-pay ' ] );
377- if ( ! $ order ) {
378- return false ;
381+
382+ // Step 7: Check if this gateway is among the possible ones
383+ $ possible = false ;
384+ foreach ( $ possible_methods as $ method_id ) {
385+ if ( $ method_id == $ this ->get_payment_method_configuration ()->get_configuration_id () ) {
386+ $ possible = true ;
387+ break ;
388+ }
379389 }
380- try {
381- $ possible_methods = WC_PostFinanceCheckout_Service_Transaction::instance ()->get_possible_payment_methods_for_order ( $ order );
382- } catch ( WC_PostFinanceCheckout_Exception_Invalid_Transaction_Amount $ e ) {
383- WooCommerce_PostFinanceCheckout::instance ()->log ( $ e ->getMessage () . ' Order Id: ' . $ order ->get_id (), WC_Log_Levels::ERROR );
384- return false ;
385- } catch ( Exception $ e ) {
386- WooCommerce_PostFinanceCheckout::instance ()->log ( $ e ->getMessage (), WC_Log_Levels::DEBUG );
390+
391+ if ( ! $ possible ) {
387392 return false ;
388393 }
389- } else {
390- if ( $ this ->have_already_entered === true ) {
391- return true ;
394+
395+ // Step 8: Cache success in session
396+ if ( WC ()->session && WC ()->session ->has_session () ) {
397+ $ gateway_available [ $ this ->wle_payment_method_configuration_id ] = true ;
398+ WC ()->session ->set ( 'postfinancecheckout_payment_gateways ' , $ gateway_available );
392399 }
400+
401+ return true ;
402+
403+ } finally {
404+ $ checking = false ;
405+ }
406+ }
393407
394- $ this ->have_already_entered = true ;
395-
396- try {
397- $ possible_methods = WC_PostFinanceCheckout_Service_Transaction::instance ()->get_possible_payment_methods_for_cart ();
398- } catch ( WC_PostFinanceCheckout_Exception_Invalid_Transaction_Amount $ e ) {
399- WooCommerce_PostFinanceCheckout::instance ()->log ( $ e ->getMessage (), WC_Log_Levels::ERROR );
400- return false ;
401- } catch ( \PostFinanceCheckout \Sdk \ApiException $ e ) {
402- WooCommerce_PostFinanceCheckout::instance ()->log ( $ e ->getMessage (), WC_Log_Levels::DEBUG );
403- $ response_object = $ e ->getResponseObject ();
404- $ is_client_error = ( $ response_object instanceof \PostFinanceCheckout \Sdk \Model \ClientError );
405- if ( $ is_client_error ) {
406- $ error_types = array ( 'CONFIGURATION_ERROR ' , 'DEVELOPER_ERROR ' );
407- if ( in_array ( $ response_object ->getType (), $ error_types ) ) {
408- $ message = esc_attr ( $ response_object ->getType () ) . ': ' . esc_attr ( $ response_object ->getMessage () );
409- wc_print_notice ( $ message , 'error ' );
410- return false ;
411- }
408+ /**
409+ * Safely fetches possible payment methods for the current cart.
410+ *
411+ * Handles known exceptions to prevent issues during cart calculation
412+ * (especially important for avoiding loops with plugins like Germanized).
413+ *
414+ * @return array|false Array of method configuration IDs if successful, false on failure.
415+ */
416+ protected function get_safe_possible_payment_methods_for_cart () {
417+ try {
418+ return WC_PostFinanceCheckout_Service_Transaction::instance ()->get_possible_payment_methods_for_cart ();
419+ } catch ( WC_PostFinanceCheckout_Exception_Invalid_Transaction_Amount $ e ) {
420+ WooCommerce_PostFinanceCheckout::instance ()->log ( $ e ->getMessage (), WC_Log_Levels::ERROR );
421+ } catch ( \PostFinanceCheckout \Sdk \ApiException $ e ) {
422+ WooCommerce_PostFinanceCheckout::instance ()->log ( $ e ->getMessage (), WC_Log_Levels::DEBUG );
423+ $ response_object = $ e ->getResponseObject ();
424+ $ is_client_error = ( $ response_object instanceof \PostFinanceCheckout \Sdk \Model \ClientError );
425+ if ( $ is_client_error ) {
426+ $ error_types = array ( 'CONFIGURATION_ERROR ' , 'DEVELOPER_ERROR ' );
427+ if ( in_array ( $ response_object ->getType (), $ error_types ) ) {
428+ $ message = esc_attr ( $ response_object ->getType () ) . ': ' . esc_attr ( $ response_object ->getMessage () );
429+ wc_print_notice ( $ message , 'error ' );
412430 }
413- return false ;
414- } catch ( Exception $ e ) {
415- WooCommerce_PostFinanceCheckout::instance ()->log ( $ e ->getMessage (), WC_Log_Levels::DEBUG );
416- return false ;
417- } finally {
418- $ this ->have_already_entered = false ;
419431 }
432+ } catch ( Exception $ e ) {
433+ WooCommerce_PostFinanceCheckout::instance ()->log ( $ e ->getMessage (), WC_Log_Levels::DEBUG );
420434 }
435+
436+ return false ;
437+ }
421438
422- $ possible = false ;
423- foreach ( $ possible_methods as $ possible_method ) {
424- if ( $ possible_method == $ this ->get_payment_method_configuration ()->get_configuration_id () ) {
425- $ possible = true ;
426- break ;
427- }
428- }
429- if ( ! $ possible ) {
430- return false ;
439+ /**
440+ * Safely fetches possible payment methods for a given WooCommerce order.
441+ *
442+ * Primarily used on the "order pay" endpoint where cart isn't available.
443+ *
444+ * @param WC_Order $order WooCommerce order object.
445+ * @return array|false Array of method configuration IDs if successful, false on failure.
446+ */
447+ protected function get_safe_possible_payment_methods_for_order ( $ order ) {
448+ try {
449+ return WC_Wallee_Service_Transaction::instance ()->get_possible_payment_methods_for_order ( $ order );
450+ } catch ( WC_Wallee_Exception_Invalid_Transaction_Amount $ e ) {
451+ WooCommerce_Wallee::instance ()->log ( $ e ->getMessage () . ' Order Id: ' . $ order ->get_id (), WC_Log_Levels::ERROR );
452+ } catch ( Exception $ e ) {
453+ WooCommerce_Wallee::instance ()->log ( $ e ->getMessage (), WC_Log_Levels::DEBUG );
431454 }
432-
433- // Store the availability information in the session.
434- $ gateway_available = WC ()->session ->get ( 'postfinancecheckout_payment_gateways ' );
435- $ gateway_available [ $ this ->pfc_payment_method_configuration_id ] = true ;
436- WC ()->session ->set ( 'postfinancecheckout_payment_gateways ' , $ gateway_available );
437- return true ;
455+ return false ;
438456 }
439457
440458 /**
0 commit comments