Plugin Directory

Changeset 3626543


Ignore:
Timestamp:
07/29/2026 12:15:19 AM (3 months ago)
Author:
scottpaterson
Message:

3.1.1

  • 7/28/26
  • Fix - Security fixes related to PayPal IPN and PDT.
  • Fix - Resolved the 'Translation loading for the WooCommerce domain was triggered too early' notice in WordPress 6.7+, caused by loading WooCommerce's PayPal gateway on plugins_loaded.
Location:
restore-paypal-standard-for-woocommerce
Files:
34 added
6 edited

Legend:

Unmodified
Added
Removed
  • restore-paypal-standard-for-woocommerce/trunk/includes/admin/migration.php

    r3292178 r3626543  
    328328        wp_die( '', '', array( 'response' => 403 ) );
    329329    }
    330    
     330
    331331    // Get the notice count from the AJAX request
    332332    $notice_count = isset( $_REQUEST['notice_count'] ) ? intval( $_REQUEST['notice_count'] ) : 0;
    … …  
    355355            wp_die( esc_html__( 'Security check failed', 'restore-paypal-standard-for-woocommerce' ) );
    356356        }
    357        
     357
    358358        // Mark notice as permanently dismissed
    359359        update_option( 'rpsfw_migration_notice_dismissed_permanently', 'yes' );
    … …  
    397397            wp_die( esc_html__( 'Security check failed', 'restore-paypal-standard-for-woocommerce' ) );
    398398        }
    399        
     399
    400400        // Run the migration
    401401        rpsfw_migrate_native_paypal_settings();
    … …  
    417417            wp_die( esc_html__( 'Security check failed', 'restore-paypal-standard-for-woocommerce' ) );
    418418        }
    419        
     419
    420420        // Reset dismissal options to force showing the notice
    421421        delete_option( 'rpsfw_migration_notice_dismissed_until' );
  • restore-paypal-standard-for-woocommerce/trunk/includes/functions.php

    r3421414 r3626543  
    152152   
    153153    return $links;
     154}
     155
     156/**
     157 * Set the "_should_load" flag on WooCommerce's native PayPal Standard gateway.
     158 *
     159 * The value is written straight to the gateway's option rather than by calling
     160 * WC_Gateway_Paypal::update_option(), which would mean instantiating the gateway.
     161 * That constructor calls __() on the 'woocommerce' text domain, and on plugins_loaded
     162 * that is before 'init' - which makes WordPress 6.7+ emit a "Translation loading for
     163 * the woocommerce domain was triggered too early" notice. Writing the option has the
     164 * same effect: WC_Settings_API::update_option() simply stores the value in
     165 * get_option_key(), which for this gateway is 'woocommerce_paypal_settings'.
     166 *
     167 * @param string $value Either 'yes' or 'no'.
     168 * @return void
     169 */
     170function rpsfw_set_native_paypal_should_load( $value ) {
     171    // Nothing to toggle if WooCommerce or its PayPal Standard gateway is not present.
     172    if ( ! class_exists( 'WC_Gateway_Paypal' ) ) {
     173        return;
     174    }
     175
     176    $settings = get_option( 'woocommerce_paypal_settings', array() );
     177
     178    if ( ! is_array( $settings ) ) {
     179        $settings = array();
     180    }
     181
     182    $settings['_should_load'] = $value;
     183
     184    update_option( 'woocommerce_paypal_settings', $settings, 'yes' );
    154185}
    155186
  • restore-paypal-standard-for-woocommerce/trunk/includes/paypal-standard/paypal-standard-ipn-handler.php

    r3292178 r3626543  
    176176
    177177    /**
    178      * Check receiver email from PayPal. If the receiver email in the IPN is different than what is stored in our settings, process order but add a note.
     178     * Check receiver email from PayPal. If the receiver email in the IPN is different than what is stored in our settings, the payment was not made to this store's PayPal account, so put the order on-hold and stop - exactly as the currency and amount checks above do.
    179179     *
    180180     * @param WC_Order $order          Order object.
    … …  
    184184        if ( strcasecmp( trim( $receiver_email ), trim( $this->receiver_email ) ) !== 0 ) {
    185185            rpsfw_Gateway_PayPal_Standard::log( 'IPN Response: Receiver email mismatch - ' . $receiver_email . ' vs ' . $this->receiver_email );
    186            
     186
    187187            /* translators: %s: receiver email */
    188188            $order->update_status('on-hold', sprintf(__('Validation error: PayPal IPN response from a different email address (%s).', 'restore-paypal-standard-for-woocommerce'), $receiver_email));
    189            
    190             return false;
    191         }
    192         return true;
     189
     190            exit;
     191        }
    193192    }
    194193
  • restore-paypal-standard-for-woocommerce/trunk/includes/paypal-standard/paypal-standard-pdt-handler.php

    r3292178 r3626543  
    155155     */
    156156    protected function validate_transaction_details( $order, $details ) {
    157         // Validate receiver email (if provided)
    158         if ( isset( $details['receiver_email'] ) && !empty( $this->receiver_email ) ) {
     157        // PDT runs again every time the buyer loads the order-received URL with a ?tx= token,
     158        // including for orders the IPN handler has already paid and verified. These checks exist
     159        // to stop an order being completed, never to reverse a completion that already happened,
     160        // so skip them once the order is paid.
     161        $needs_verification = ! $order->has_status( wc_get_is_paid_statuses() );
     162
     163        // Validate receiver email. When a receiving account is configured the response has to
     164        // name it - a missing receiver_email means the payment cannot be confirmed as having
     165        // reached this store, which is not the same as it having passed.
     166        if ( $needs_verification && !empty( $this->receiver_email ) ) {
     167            if ( ! isset( $details['receiver_email'] ) ) {
     168                rpsfw_Gateway_PayPal_Standard::log('PDT Response: no receiver_email returned, cannot confirm the payment reached this store. Order #' . $order->get_id());
     169
     170                $order->update_status('on-hold', __('Validation error: the PayPal PDT response did not identify the receiving account, so the payment could not be verified.', 'restore-paypal-standard-for-woocommerce'));
     171
     172                return false;
     173            }
     174
    159175            if ( strtolower( $details['receiver_email'] ) !== strtolower( $this->receiver_email ) ) {
    160176                rpsfw_Gateway_PayPal_Standard::log('PDT Response: Receiver email mismatch - ' . $details['receiver_email'] . ' vs ' . $this->receiver_email);
    161                
     177
    162178                /* translators: %1$s: receiver email, %2$s: order ID */
    163                 $order->update_status('on-hold', sprintf(__('Validation error: PayPal PDT response from a different email address (%1$s). Order #%2$s', 'restore-paypal-standard-for-woocommerce'), 
     179                $order->update_status('on-hold', sprintf(__('Validation error: PayPal PDT response from a different email address (%1$s). Order #%2$s', 'restore-paypal-standard-for-woocommerce'),
    164180                    $details['receiver_email'],
    165181                    $order->get_id()
    166182                ));
    167                
     183
    168184                return false;
    169185            }
    … …  
    185201                    // If the order is already completed, just add a note
    186202                    if ( $order->has_status( wc_get_is_paid_statuses() ) ) {
    187                         $order->add_order_note( 
    188                             sprintf( 
     203                        $order->add_order_note(
     204                            sprintf(
    189205                                __( 'PayPal PDT: Payment verified (ID: %s)', 'restore-paypal-standard-for-woocommerce' ),
    190                                 $transaction_id 
    191                             ) 
     206                                $transaction_id
     207                            )
    192208                        );
    193209                    } else {
     210                        // The amount and currency are carried in client side form fields on the way
     211                        // to PayPal, so a buyer can pay less than the order total. Check what was
     212                        // actually paid before completing the order, the same way the IPN handler
     213                        // does. The IPN handler aborts early on an already paid order, so this is
     214                        // the only amount check on a synchronous PDT completion. On a mismatch the
     215                        // order is left on-hold for manual review instead of being marked paid.
     216                        if ( ! isset( $details['mc_gross'], $details['mc_currency'] )
     217                            || $order->get_currency() !== $details['mc_currency']
     218                            || number_format( $order->get_total(), 2, '.', '' ) !== number_format( (float) $details['mc_gross'], 2, '.', '' ) ) {
     219
     220                            rpsfw_Gateway_PayPal_Standard::log( 'PDT Response: payment could not be verified for order #' . $order->get_id()
     221                                . ' (order total ' . $order->get_total() . ' ' . $order->get_currency()
     222                                . ' | returned gross ' . ( isset( $details['mc_gross'] ) ? $details['mc_gross'] : '(missing)' )
     223                                . ' ' . ( isset( $details['mc_currency'] ) ? $details['mc_currency'] : '(missing)' ) . ')' );
     224
     225                            /* translators: %s: Amount. */
     226                            $order->update_status( 'on-hold', sprintf( __( 'Validation error: PayPal PDT amounts do not match (gross %s).', 'restore-paypal-standard-for-woocommerce' ), isset( $details['mc_gross'] ) ? $details['mc_gross'] : '' ) );
     227
     228                            return false;
     229                        }
     230
    194231                        // If order is still pending, complete it
    195232                        $order->payment_complete( $transaction_id );
    196                         $order->add_order_note( 
    197                             sprintf( 
     233                        $order->add_order_note(
     234                            sprintf(
    198235                                __( 'PayPal PDT: Payment completed (ID: %s)', 'restore-paypal-standard-for-woocommerce' ),
    199                                 $transaction_id 
    200                             ) 
     236                                $transaction_id
     237                            )
    201238                        );
    202239                    }
  • restore-paypal-standard-for-woocommerce/trunk/readme.txt

    r3540267 r3626543  
    55Tested up to: 7.0
    66Requires PHP: 5.6
    7 Stable tag: 3.1.0
     7Stable tag: 3.1.1
    88License: GPLv3
    99License URI: https://www.gnu.org/licenses/gpl-3.0.html
    … …  
    4242
    4343== Changelog ==
     44
     45= 3.1.1 =
     46* 7/28/26
     47* Fix - Security fixes related to PayPal IPN and PDT.
     48* Fix - Resolved the 'Translation loading for the WooCommerce domain was triggered too early' notice in WordPress 6.7+, caused by loading WooCommerce's PayPal gateway on plugins_loaded.
    4449
    4550= 3.1.0 =
  • restore-paypal-standard-for-woocommerce/trunk/restore-paypal-standard-for-woocommerce.php

    r3421414 r3626543  
    33 * Plugin Name: Restore Paypal Standard For WooCommerce
    44 * Description: Restore PayPal Standard payment gateway for WooCommerce
    5  * Version: 3.1.0
     5 * Version: 3.1.1
    66 * Author: Scott Paterson
    77 * Author URI: https://wpplugin.org
    … …  
    2323
    2424// Define plugin constants
    25 define( 'RPSFW_VERSION', '3.1.0' );
     25define( 'RPSFW_VERSION', '3.1.1' );
    2626define( 'RPSFW_PLUGIN_FILE', __FILE__ );
    2727define( 'RPSFW_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    … …  
    6565  add_action( 'plugins_loaded',function(){
    6666    // It enable PayPal Standard for WooCommerce.
    67     $paypal = class_exists( 'WC_Gateway_Paypal' ) ? new WC_Gateway_Paypal() : null;
    68     if( $paypal ) {
    69       $paypal->update_option( '_should_load', 'yes' );
    70     }
     67    rpsfw_set_native_paypal_should_load( 'yes' );
    7168    add_filter( 'woocommerce_should_load_paypal_standard','__return_true',9999999999999 );
    7269  } );
    … …  
    7471  // Ensure native PayPal is disabled after migration
    7572  add_action( 'plugins_loaded', function() {
    76     if (class_exists( 'WC_Gateway_Paypal' )) {
    77       $paypal = new WC_Gateway_Paypal();
    78       $paypal->update_option( '_should_load', 'no' );
    79     }
     73    rpsfw_set_native_paypal_should_load( 'no' );
    8074  }, 5 );
    8175}
Note: See TracChangeset for help on using the changeset viewer.