5

In the WooCommerce plugin, the PayPal gateway has recently been updated to capture payment on status change TO either "Processing" or "Completed".

We want to ONLY allow capture after order status has been updated to COMPLETED.

In order to do this, I have identified that I need to remove an action. The action is location in class-wc-gateway-paypal.php and the code is below:

class WC_Gateway_Paypal extends WC_Payment_Gateway {

/** @var bool Whether or not logging is enabled */
public static $log_enabled = false;

/** @var WC_Logger Logger instance */
public static $log = false;

/**
 * Constructor for the gateway.
 */
public function __construct() {
    $this->id                 = 'paypal';
    $this->has_fields         = false;
    $this->order_button_text  = __( 'Proceed to PayPal', 'woocommerce' );
    $this->method_title       = __( 'PayPal', 'woocommerce' );
    $this->method_description = sprintf( __( 'PayPal Standard sends customers to PayPal to enter their payment information. PayPal IPN requires fsockopen/cURL support to update order statuses after payment. Check the <a href="%s">system status</a> page for more details.', 'woocommerce' ), admin_url( 'admin.php?page=wc-status' ) );
    $this->supports           = array(
        'products',
        'refunds',
    );

    // Load the settings.
    $this->init_form_fields();
    $this->init_settings();

    // Define user set variables.
    $this->title          = $this->get_option( 'title' );
    $this->description    = $this->get_option( 'description' );
    $this->testmode       = 'yes' === $this->get_option( 'testmode', 'no' );
    $this->debug          = 'yes' === $this->get_option( 'debug', 'no' );
    $this->email          = $this->get_option( 'email' );
    $this->receiver_email = $this->get_option( 'receiver_email', $this->email );
    $this->identity_token = $this->get_option( 'identity_token' );

    self::$log_enabled    = $this->debug;

    add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
    add_action( 'woocommerce_order_status_on-hold_to_processing', array( $this, 'capture_payment' ) );

The final line is the action I want to remove.

add_action( 'woocommerce_order_status_on-hold_to_processing', array( $this, 'capture_payment' ) ); 

I have my own plugin that I use to call various functions with. Before I screw something up, I am hoping someone can validate (or correct) the code I plan to use:

add_action( 'woocommerce_order_status_on-hold_to_completed','remove_PaypalCapture_action' );
function remove_PaypalCapture_action(){ 
global $WC_Gateway_Paypal; //get access to the class object instance
remove_action('woocommerce_order_status_on-hold_to_processing', array($WC_Gateway_Paypal, 'capture_payment' )); //DO NOT capture payment upon order change to processing
} 

Will this work, or am I missing something?

  • You should read this post and use this referenced library, easy to use and really helpful in your context. – bueltge Jan 3 '18 at 9:28
1

There are two things that you'll need in order to remove the action.

  1. The exact instance of WC_Gateway_Paypal
  2. Knowing when the action is added and when it is executed

Without these, it will be difficult, but impossible to remove the action. Getting the instance of WC_Gateway_Paypal should be straightforward.

//* Make sure class-wc-payment-gateways.php is included once
include_once PATH_TO . class-wc-payment-gateways.php;

//* Get the gateways
$gateways = \WC_Payment_Gateways::instance();

//* The class instances are located in the payment_gateways property
$gateways->payment_gateways

The Paypal gateway instance should be in this array. I don't have WooCommerce installed on this machine right now, so I don't know exactly how it's stored. But you should be able to figure it out.

[Added: Thanks to @willington-vega for finding out how WC stores the payment gateways in the array. I've updated the get_wc_paypal_payment_gateway() function below with this.]

Fortunately, the action is added at priority 10 to the woocommerce_order_status_on-hold_to_completed hook. What we can do is hook in at an earlier priority and remove the hook.

add_action( 'woocommerce_order_status_on-hold_to_completed', 'remove_PaypalCapture_action', 9 );
function remove_PaypalCapture_action() {
  //* This is where we'll remove the action

  //* Get the instance
  $WC_Paypal_Payment_Gateway = get_wc_paypal_payment_gateway();
  remove_action( 'woocommerce_order_status_on-hold_to_completed', [ $WC_Paypal_Payment_Gateway , 'capture_payment' ] );
}
function get_wc_paypal_payment_gateway() {

  //* Make sure class-wc-payment-gateways.php is included once
  include_once PATH_TO . class-wc-payment-gateways.php;

  //* Return the paypal gateway
  return \WC_Payment_Gateways::instance()->payment_gateways[ 'paypal' ];
}
  • Ok, looking at class-wc-payment-gateways.php, I see : public function init() { $load_gateways = array( 'WC_Gateway_BACS', 'WC_Gateway_Cheque', 'WC_Gateway_COD', 'WC_Gateway_Paypal', ); So how do I modify your code to add this into the function get_wc_paypal_payment_gateway()? – Cameron Scott Jan 3 '18 at 0:26
  • Based on this: github.com/woocommerce/woocommerce/blob/master/includes/…, you could access the gateway instance using $all_gateways = \WC_Payment_Gateways::instance()->payment_gateways(); $paypal_gateway = $all_gateways['paypal'];. The get_wc_paypal_payment_gateway() function should return the $paypal_gateway variable. – Willington Vega Jan 3 '18 at 13:31
  • For your reference, the action I wish to remove is 'woocommerce_order_status_on-hold_to_processing' and NOT 'woocommerce_order_status_on-hold_to_completed' – Cameron Scott Jan 5 '18 at 18:23
  • So I have tried everything and using this code does NOT work. When adding and removing hooks WordPress uses the _wp_filter_build_unique_id function which generates a unique ID for each plugin. Since the hooked function is an object it creates a unique string ID. I am at a loss as to what to try next... – Cameron Scott Jan 7 '18 at 4:53
0

If WooCommerce plugin declares $WC_Gateway_Paypal as global variable:

global $WC_Gateway_Paypal;

and instantiates WC_Gateway_Paypal class as:

$WC_Gateway_Paypal = new WC_Gateway_Paypal;

then, yes, the code below:

global $WC_Gateway_Paypal;
remove_action('woocommerce_order_status_on-hold_to_processing', array($WC_Gateway_Paypal, 'capture_payment'), 10);

will properly remove this action.

Note: remove_action() must be called inside a function and cannot be called directly in your plugin or theme ( from Codex ).

  • Thanks...unfortunately, that doesn't get me any closer because I don't believe the plugin declares that global variable or instantiates the class. I modified my code: add_action( 'woocommerce_order_status_on-hold_to_completed','remove_PaypalCapture_action' ); function remove_PaypalCapture_action(){ global $WC_Gateway_Paypal; //get access to the class object instance remove_action('woocommerce_order_status_on-hold_to_processing', array($WC_Gateway_Paypal, 'capture_payment' )); //DO NOT capture payment upon order change to processing } – Cameron Scott Dec 31 '17 at 21:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.