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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions