Add YITH Subscription support to your Woocommerce payment gateway

If you reading this you approached that WooCommerce plugin and you want to know if your Woocommerce payment gateway support it.

You are in the wrong place, I mean check that link because has a section for the various payment gateway that supports natively. Instead if you are a developer not someone that put code in the functions.php of your theme let’s move on.

If you look the official documentation at the time of writing is not so helpful. So I want to share what I did to add the support to the VivaWallet Payment gateway for a customer and my notes that I hope will help you.

So I looked on GitHub as I do usually not for repository but by code if searching specific keywords like ywsbs_pay_renew_order_with I will find something. Of course there was nothing…

The next step was to search how the YITH plugin add the support for those gateways and how the VivaWallet plugin add the support for WooCommerce subscription, that is after all the main competitor.
My first disappointment is that YITH plugin doesn’t have a wrapper to let the plugin to be compatible with that on it’s own. This because no one outside YITH support that plugin, so when you will find a plugin compatible with subscription is the official one from Woocommerce and not by YITH.

This means that the plugins works in a different way completely.

Replace the original gateway

After looking the various implementation inside the YITH plugin for the various gateway, that also them are completely different internally and also for the filters used. So as detective you need to see a pattern and the first things that you notice is: the payment gateway are extend and replace the original one.

Every Woocommerce payment is a class with some methods that need to extend another one. So when you need to add to them the support for something the most easy way is to extend the original class and replacing the class (using a filter) with a filter, in this way Woocommerce will use your version that extend the original one.

A little example from the (pseudo) code from the YITH plugin itself:

add_filter( 'woocommerce_payment_gateways', array( $this, 'add_eway_integration_gateway' ), 100 );
function add_eway_integration_gateway( $methods ) {
  if ( ( isset( $_GET['page'], $_GET['tab'] ) && $_GET['page'] === 'wc-settings' && $_GET['tab'] === 'checkout' ) ) {
    return $methods;
  }

  foreach ( $methods as $key => $method ) {
    if ( 'WC_Gateway_EWAY' === $method ) { // get the original gateway
      $methods[ $key ] = 'YWSBS_WC_EWAY'; // associate to that one the extended class
    }
  }

  return $methods;
}

As you can see is quite simple, the payment gateway get replaced with the one you want by code, at the same time as is an extended class the original code and feature are still there but now there is more.

In other cases some gateways also have a filter to load the right one if they natively support the Subscriptions plugins but we don’t care in this case. What we care is that the support for subscriptions is enabled in the gateway.

With the Vivawallet example we need also that the plugin detect the YITH otherwise will not load the extended version:

if ( class_exists( 'WC_Subscriptions_Order' ) && function_exists( 'wcs_create_renewal_order' ) || function_exists( 'ywsbs_register_failed_payment' ) ) {

This is an example of the code extended to check the YITH plugin.

Add support to the gateway

The next step is adding support to the keyword of the YITH plugin. In WordPress usually with support we are talking about keywords used internally to enable features, mainly with themes like for comments or sidebar support.

We need to add the new one for this plugin otherwise the plugin will have some issues (that I didn’t investigated).

You have two ways to achieve this, patch the original payment gateway plugin ,or use the woocommrce filters to add those new keywords.

add_filter( 'woocommerce_payment_gateway_supports', array( $this, 'add_subscription_support_to_paypal_standard' ), 10, 3 ); 
function add_subscription_support_to_paypal_standard( $support, $feature, $gateway ) {
  $gateway = is_object( $gateway ) ? $gateway->id : $gateway;
  if ( 'paypal' !== $gateway ) {
    return $support;
  }

  $supports = array( 'yith_subscriptions', 'yith_subscription_pause', 'yith_subscription_pay_method_customer' );

  return in_array( $feature, $supports, true ) ? true : $support;
}

Personally in my development I used this keywords:

'yith_subscriptions',
'yith_subscriptions_scheduling',
'yith_subscriptions_multiple',
'yith_subscriptions_payment_date',
'yith_subscriptions_recurring_amount',

As VivaWallet supports recurrent payments and works without issues but I avoided pause support.

Add the payment on subscription renewal

In your extended class in the construct you need to add the YITH filter that will let you to do the payment when there is a renewal.

if ( function_exists( 'ywsbs_register_failed_payment' ) ) {
  add_action( 'ywsbs_pay_renew_order_with_' . $this->id, array( $this, 'scheduled_subscription_payment_yith' ), 10, 1 );
}

public function scheduled_subscription_payment_yith( $renewal_order = null, $manually = false ) {
  if ( is_null( $renewal_order ) ) {
    return false;
  }

  $order_id        = $renewal_order->get_id();
  $is_a_renew      = $renewal_order->get_meta( 'is_a_renew' );
  $subscriptions   = $renewal_order->get_meta( 'subscriptions' );
  $subscription_id = $subscriptions ? $subscriptions[0] : false;
  $subscription    = ywsbs_get_subscription( $subscription_id );
  $order_id        = $renewal_order->get_id();

  if ( ! $subscription_id || 'yes' !== $is_a_renew ) {
    // translators: placeholder order id.
    yith_subscription_log( sprintf( __( 'Sorry, any subscription is found for this order: %s', 'yith-woocommerce-subscription' ), $order_id ), 'subscription_payment' );
    return false;
  }

  $result = $this->process_subscription_payment( $order_id, $renewal_order->get_total() );
  WC_Vivawallet_Logger::log( 'PROCESS_SUBSCRIPTION_PAYMENT ' . wp_json_encode( $result ) );
  if ( !$result ) {
    ywsbs_register_failed_payment( $renewal_order, 'Error: payment not processed' );
    WC_Vivawallet_Logger::log( 'FAILED SUBSCRIPTION - Subs_id: ' . wp_json_encode( $order_id ) . ' Parent order id: ' . $renewal_order->get_id() );
  }
}

In this pseudo code you can see some things.

We add the filter where $this->id is the gateway id we saw earlier that is part of the class.
Next in the function we check the status of the order, if there is an error we use the plugin log feature (just in case).

The next step is to process the payment process_subscription_payment, this one is for that payment gateway but any gateway can have something different that also use different parameters so is on you use/send the right stuff.

In the case of Vivawallet it was required also another change because save in every order the card token id that the gateway need to do the renewal, otherwise fails.

As before we needed to check if the YITH is available to load the extended gateway we need to add this meta if the order is a renew.

if ( function_exists( 'wcs_order_contains_subscription' ) ) {
  $subscriptions = wcs_get_subscriptions_for_order( $order );
  foreach ( $subscriptions as $subscription ) {
    // check if subscription already has an saved card related.
    $subscription_has_saved_method = get_post_meta( $subscription->get_id(), self::POST_META_VW_CARD_TOKEN_ID, true );

    update_post_meta( $subscription->get_id(), self::POST_META_VW_CARD_TOKEN, $new_token );
    update_post_meta( $subscription->get_id(), self::POST_META_VW_CARD_TOKEN_ID, $token->get_id() );

    if ( ! empty( $subscription_has_saved_method ) ) {
      WC_Subscriptions_Change_Payment_Gateway::update_payment_method( $subscription, 'vivawallet_native', $token->get_meta_data() );
    }
  }
} elseif ( function_exists( 'ywsbs_register_failed_payment' ) ) {
  $subscriptions   = $order->get_meta( 'subscriptions' );
  $subscription_id = $subscriptions ? $subscriptions[0] : false;
  $subscription    = ywsbs_get_subscription( $subscription_id );
  foreach ( $subscriptions as $subscription ) {
    // check if subscription already has an saved card related.
    $subscription_has_saved_method = get_post_meta( $subscription, self::POST_META_VW_CARD_TOKEN_ID, true );
    update_post_meta( $subscription, self::POST_META_VW_CARD_TOKEN, $new_token );
    update_post_meta( $subscription, self::POST_META_VW_CARD_TOKEN_ID, $token->get_id() );

    if ( ! empty( $subscription_has_saved_method ) ) {
      $order = wc_get_order( $subscription );
      $order->set_payment_method( 'vivawallet_native' );
      $order->save();
    }
  }
}

This is an example code from our changes that are executed by the gateway when an order is getting to save that token. Again, every gateway does this different so is just an hint about what to do.

We see an important things as Woocommerce since the 4 release is CRUD and do some things in a different way from WordPress. Usually in Wordpress we add a meta with the native wp functions but this can create incompatibilities in Woocommerce because now uses their own table and they have a layer of compatibility with WordPress for those cases.

We need to use the Woocommerce methods to get the order, set the payment method and save that change otherwise is not saved.
In any case uses always the Woocommerce methods instead of WP because they will do everything for you to avoid confusions.

Conclusion

Those are the changes with pseudo code I had to do in that payment gateway that showed some important things to remember.
Apart that this changes require a demo payment to avoid any troubles with bank accounts!

Remember any payment gateway is doing things differently for their reasons so methods but also flow is different. Some do a lot of ajax/rest requests to the website others instead when the checkout is confirmed. You need to test and read the code as I did with the 5 gateway supported natively by this YITH plugin.

Another hint is possible to re-execute the payment renewal inside the backend to test the payment to avoid a new checkout etc. This will speed up the things without set an expiration etc.
Liked it? Take a second to support Mte90 on Patreon!
Become a patron at Patreon!

Leave a Reply

Your email address will not be published. Required fields are marked *