Skip to content

Configuring Vaulted Payments #2817

Description

@Caffe1neAdd1ct

Hey Team

We've had a dev try to programmatically disable vaulted payments via a custom plugin, breaking the customer's site as they didn't analyse the including methods of the PayPal Config inside the tutor plugin closely enough:

sample:

<?php
/**
 * Plugin Name: TVFE Tutor PayPal Vault Fix
 * Description: Disables PayPal payment-source vaulting in Tutor LMS checkout.
 * Version: 1.0.0
 * Author: Plugin Author
 */

defined( 'ABSPATH' ) || exit;

add_action(
	'plugins_loaded',
	function () {
		if (
			! class_exists( '\Tutor\PaymentGateways\PaypalGateway' ) ||
			! class_exists( '\Tutor\PaymentGateways\Configs\PaypalConfig' )
		) {
			return;
		}

		if ( ! class_exists( 'TVFE_Tutor_Paypal_Fix_Config' ) ) {
			class TVFE_Tutor_Paypal_Fix_Config extends \Tutor\PaymentGateways\Configs\PaypalConfig {
				public function createConfig(): void {
					parent::createConfig();
					$this->updateConfig( 'save_payment_method', false );
				}
			}
		}

		if ( ! class_exists( 'TVFE_Tutor_Paypal_Fix_Gateway' ) ) {
			class TVFE_Tutor_Paypal_Fix_Gateway extends \Tutor\PaymentGateways\PaypalGateway {
				public function get_config_class(): string {
					return TVFE_Tutor_Paypal_Fix_Config::class;
				}
			}
		}
	},
	20
);

function tvfe_tutor_paypal_fix_gateway( $gateways ) {
	if ( isset( $gateways['paypal'] ) && class_exists( 'TVFE_Tutor_Paypal_Fix_Gateway' ) ) {
		$gateways['paypal']['gateway_class'] = TVFE_Tutor_Paypal_Fix_Gateway::class;
		$gateways['paypal']['config_class']  = TVFE_Tutor_Paypal_Fix_Config::class;
	}

	return $gateways;
}

add_filter( 'tutor_payment_gateways_with_class', 'tvfe_tutor_paypal_fix_gateway', 20 );
add_filter( 'tutor_gateways_with_class', 'tvfe_tutor_paypal_fix_gateway', 20 );

Which obviously gives:

[18-Jun-2026 11:46:19 UTC] PHP Fatal error:  Uncaught Error: Class 'Ollyo\PaymentHub\Core\Payment\BaseConfig' not found in /app/wp-content/plugins/tutor/ecommerce/PaymentGateways/Configs/PaypalConfig.php:17
Stack trace:
#0 /app/wp-content/plugins/elementor/vendor/composer/ClassLoader.php(576): include()
#1 /app/wp-content/plugins/elementor/vendor/composer/ClassLoader.php(427): Composer\Autoload\{closure}()
#2 [internal function]: Composer\Autoload\ClassLoader->loadClass()
#3 [internal function]: spl_autoload_call()
#4 /app/wp-content/plugins/tvfe-tutor-paypal-fix/tvfe-tutor-paypal-fix.php(16): class_exists()
#5 /app/wp-includes/class-wp-hook.php(341): {closure}()
#6 /app/wp-includes/class-wp-hook.php(365): WP_Hook->apply_filters()
#7 /app/wp-includes/plugin.php(522): WP_Hook->do_action()
#8 /app/wp-settings.php(622): do_action()
#9 /app/wp-config.php(108): require_once('/app/wp-setting...')
#10 /app/wp-load.php(50): require_once('/app/wp-config....')
#11 /app/wp-blog-header.php(13): require_once('/app/wp-load.ph...')
#12 /app/index.php(17): require('/ap in /app/wp-content/plugins/tutor/ecommerce/PaymentGateways/Configs/PaypalConfig.php on line 17

We've fixed this attempt and the customer is back online.. so all good there, for future reference the working plugin code:

<?php
/**
 * Plugin Name: TVFE Tutor PayPal Vault Fix
 * Description: Disables PayPal payment-source vaulting in Tutor LMS checkout.
 * Version: 1.0.2
 * Author: Plugin Author
 */

defined( 'ABSPATH' ) || exit;

// We do NOT use the init hook or check class_exists() early. 
// We only hook directly into Tutor's gateway filter.
function tvfe_tutor_paypal_fix_gateway( $gateways ) {
    
    // If Tutor has reached this point, it has safely loaded its own files.
    if ( isset( $gateways['paypal'] ) ) {
        
        // Define the classes only right when they are needed
        if ( ! class_exists( 'TVFE_Tutor_Paypal_Fix_Config' ) ) {
            class TVFE_Tutor_Paypal_Fix_Config extends \Tutor\PaymentGateways\Configs\PaypalConfig {
                public function createConfig(): void {
                    parent::createConfig();
                    $this->updateConfig( 'save_payment_method', false );
                }
            }
        }

        if ( ! class_exists( 'TVFE_Tutor_Paypal_Fix_Gateway' ) ) {
            class TVFE_Tutor_Paypal_Fix_Gateway extends \Tutor\PaymentGateways\PaypalGateway {
                public function get_config_class(): string {
                    return TVFE_Tutor_Paypal_Fix_Config::class;
                }
            }
        }

        // Apply the custom gateway
        $gateways['paypal']['gateway_class'] = TVFE_Tutor_Paypal_Fix_Gateway::class;
        $gateways['paypal']['config_class']  = TVFE_Tutor_Paypal_Fix_Config::class;
    }

    return $gateways;
}

// These filters run late in the process, safely after Tutor is fully initialized
add_filter( 'tutor_payment_gateways_with_class', 'tvfe_tutor_paypal_fix_gateway', 20 );
add_filter( 'tutor_gateways_with_class', 'tvfe_tutor_paypal_fix_gateway', 20 );

We've quickly raised two PRs with separate approaches:

  1. Developer hook: feat(ecommerce): add filter for PayPal vaulting #2815
  2. UI Config: tutor lms paypal vaulting beta #2816

Hopefully these help, any changes or improvements needed please shout and i'll fire up the IDE myself 👍

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions