SILENT KILLERPanel

Current Path: > home > codekrsu > > ameliagraphics.com > wp-content > plugins > > wp-user-frontend > Lib > Gateway


Operation   : Linux premium131.web-hosting.com 4.18.0-553.44.1.lve.el8.x86_64 #1 SMP Thu Mar 13 14:29:12 UTC 2025 x86_64
Software     : Apache
Server IP    : 162.0.232.56 | Your IP: 216.73.216.111
Domains      : 1034 Domain(s)
Permission   : [ 0755 ]

Files and Folders in: /home/codekrsu//ameliagraphics.com/wp-content/plugins//wp-user-frontend/Lib/Gateway

NameTypeSizeLast ModifiedActions
Bank.php File 4515 bytes May 12 2025 14:47:56.
Paypal.php File 88715 bytes June 30 2025 16:27:10.

Reading File: /home/codekrsu//ameliagraphics.com/wp-content/plugins//wp-user-frontend/Lib/Gateway/Paypal.php

<?php

namespace WeDevs\Wpuf\Lib\Gateway;

use WeDevs\Wpuf\Frontend\Payment;
use WeDevs\Wpuf\Traits\TaxableTrait;

/**
 * WP User Frontend PayPal gateway
 *
 * @since 0.8
 * @updated 4.1.5
 */
class Paypal {
    use TaxableTrait;

    private $gateway_url;
    private $test_mode;
    private $webhook_id;
    private $api_version = '2.0';
    private $settings_url;
    private $setup_guide_url;
    private $paypal_icon_url;

    public function __construct() {
        $this->gateway_url = 'https://www.paypal.com/webscr/?';
        $this->test_mode = 'test' === wpuf_get_option( 'paypal_test_mode', 'wpuf_payment' );
        $this->webhook_id = wpuf_get_option( 'paypal_webhook_id', 'wpuf_payment' );

        // Initialize URL properties
        $this->settings_url = admin_url( 'admin.php?page=wpuf-settings&tab=wpuf_payment' );
        $this->setup_guide_url = 'https://wedevs.com/docs/wp-user-frontend-pro/settings/paypal-payement-gateway/';
        $this->paypal_icon_url = WPUF_ASSET_URI . '/images/wpuf_paypal.png';

        // Initialize hooks
        add_action( 'wpuf_gateway_paypal', [ $this, 'prepare_to_send' ] );
        add_filter( 'wpuf_options_payment', [ $this, 'payment_options' ] );
        add_action( 'init', [ $this, 'check_paypal_return' ], 20 );
        add_action( 'wpuf_cancel_payment_paypal', [ $this, 'cancel_subscription' ] );
        add_action( 'wpuf_cancel_subscription_paypal', [ $this, 'cancel_subscription' ] );
        add_action( 'init', [ $this, 'handle_pending_payment' ] );
        add_action( 'wpuf_paypal_webhook', [ $this, 'process_webhook' ] );
        // Add webhook endpoint handler
        add_action( 'init', [ $this, 'register_webhook_endpoint' ] );
        add_action( 'template_redirect', [ $this, 'handle_webhook_request' ] );

        // Add admin notice for PayPal settings update
        add_action( 'admin_notices', [ $this, 'paypal_settings_update_notice' ] );
        add_action( 'wp_ajax_wpuf_dismiss_paypal_notice', [ $this, 'dismiss_paypal_notice' ] );

        // Add CSS for webhook configuration section
        add_action( 'admin_head', [ $this, 'inject_webhook_css' ] );
    }

    /**
     * Inject CSS for webhook configuration
     */
    public function inject_webhook_css() {
        // Only inject on WPUF settings page
        $screen = get_current_screen();
        if ( ! $screen || false === strpos( $screen->base, 'wpuf' ) ) {
            return;
        }
        ?>
        <style>
        .wpuf-paypal-webhook-container {
            padding: 10px 15px 15px 15px !important;
            border: 1px solid #ddd !important;
            background-color: #f9f9f9 !important;
            border-radius: 4px !important;
            box-sizing: border-box !important;
        }
        </style>
        <?php
    }

    /**
     * Display admin notice for PayPal settings update
     */
    public function paypal_settings_update_notice() {
        // Only show to administrators
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        // Check if notice has been dismissed
        if ( get_option( 'wpuf_paypal_settings_notice_dismissed' ) ) {
            return;
        }

        // Only show in admin area
        if ( ! is_admin() ) {
            return;
        }

        ?>
        <div class="notice notice-info is-dismissible wpuf-paypal-notice" id="wpuf-paypal-settings-notice">
            <div class="wpuf-notice-content" style="display: flex; align-items: center; padding: 10px 0;">
                <div class="wpuf-notice-icon" style="margin-right: 15px;">
                    <img src="<?php echo esc_url( $this->paypal_icon_url ); ?>" alt="PayPal" style="width: 45px; height: 45px;">
                </div>
                <div class="wpuf-notice-message" style="flex: 1;">
                    <h3 style="margin: 0 0 5px 0;"><?php esc_html_e( 'WPUF PayPal Integration Enhanced', 'wp-user-frontend' ); ?></h3>
                    <p style="margin: 0;">
                        <?php
                        echo wp_kses_post(
                            sprintf(
                                /* translators: %s: PayPal setup guide URL */
                                __( "We've enhanced <strong>WPUF's PayPal integration</strong> to be more secure and reliable with webhook support. If you're using PayPal, please follow our %s to update your settings and ensure uninterrupted payments.", 'wp-user-frontend' ),
                                '<a href="' . esc_url( $this->setup_guide_url ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'PayPal setup guide', 'wp-user-frontend' ) . '</a>'
                            )
                        );
                        ?>
                    </p>
                </div>
                <div class="wpuf-notice-action" style="margin-left: 15px;">
                    <a href="<?php echo esc_url( $this->settings_url ); ?>" class="button button-primary">
                        <?php esc_html_e( 'Update PayPal Settings', 'wp-user-frontend' ); ?>
                    </a>
                </div>
            </div>
        </div>

        <script type="text/javascript">
        jQuery(document).ready(function($) {
            $(document).on('click', '.wpuf-paypal-notice .notice-dismiss', function() {
                $.ajax({
                    url: ajaxurl,
                    type: 'POST',
                    data: {
                        action: 'wpuf_dismiss_paypal_notice',
                        nonce: '<?php echo wp_create_nonce( 'wpuf_dismiss_paypal_notice' ); ?>'
                    },
                    success: function(response) {
                        // Handle success if needed
                    },
                    error: function(xhr, status, error) {
                    console.error('Failed to dismiss notice:', error);
                    }
                });
            });
        });
        </script>

        <style>
        .wpuf-paypal-notice {
            border-left-color: #0073aa;
        }
        .wpuf-paypal-notice .wpuf-notice-content {
            padding: 10px 0;
        }
        .wpuf-paypal-notice h3 {
            color: #23282d;
            font-size: 14px;
            font-weight: 600;
        }
        .wpuf-paypal-notice p {
            color: #666;
            font-size: 13px;
            line-height: 1.5;
        }
        .wpuf-paypal-notice a {
            color: #0073aa;
            text-decoration: none;
        }
        .wpuf-paypal-notice a:hover {
            text-decoration: underline;
        }
        .wpuf-paypal-notice strong {
            font-weight: 600;
            color: #23282d;
        }
        </style>
        <?php
    }

    /**
     * Handle dismissal of PayPal settings notice
     */
    public function dismiss_paypal_notice() {
        // Verify nonce
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wpuf_dismiss_paypal_notice' ) ) {
            wp_die( 'Security check failed' );
        }

        // Check user capabilities
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( 'Insufficient permissions' );
        }

        // Mark notice as dismissed
        update_option( 'wpuf_paypal_settings_notice_dismissed', true );

        wp_die();
    }

    /**
     * Main webhook processor
     */
    public function process_webhook( $raw_input ) {
        try {
            // Set headers for webhook acknowledgment
            header( 'HTTP/1.1 200 OK' );
            header( 'Content-Type: application/json' );

            // Verify webhook signature
            if ( ! $this->verify_webhook_signature_from_input( $raw_input ) ) {
                http_response_code( 401 );
                echo wp_json_encode(
                    [
						'status' => 'error',
						'message' => 'Unauthorized',
					]
                );
                exit;
            }

            // Decode the webhook data
            $event = json_decode( $raw_input, true );
            if ( JSON_ERROR_NONE !== json_last_error() ) {
                throw new \Exception( 'Invalid JSON in webhook data' );
            }

            \WP_User_Frontend::log( 'PayPal: Processing webhook event: ' . $event['event_type'] );

            switch ( $event['event_type'] ) {
                case 'BILLING.SUBSCRIPTION.CANCELLED':
                    if ( isset( $event['resource'] ) ) {
                        $this->handle_subscription_cancelled( $event['resource'] );
                    }
                    break;

                case 'BILLING.SUBSCRIPTION.CREATED':
                    if ( isset( $event['resource'] ) ) {
                        $this->handle_subscription_created( $event['resource'] );
                    }
                    break;

                case 'BILLING.SUBSCRIPTION.ACTIVATED':
                    if ( isset( $event['resource'] ) ) {
                        // Handle when a subscription becomes active (after trial)
                        $this->handle_subscription_activated( $event['resource'] );
                    }
                    break;

                case 'PAYMENT.SALE.COMPLETED':
                    if ( isset( $event['resource'] ) ) {
                        $payment = $event['resource'];
                        if ( isset( $payment['billing_agreement_id'] ) ) {
                            $this->process_subscription_payment( $payment );
                        }
                    }
                    break;

                case 'PAYMENT.CAPTURE.COMPLETED':
                    if ( isset( $event['resource'] ) ) {
                        $this->process_payment_capture( $event['resource'] );
                    }
                    break;
            }

            echo wp_json_encode(
                [
					'status' => 'success',
					'message' => 'Webhook processed successfully',
				]
            );
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Webhook processing failed: ' . $e->getMessage() );
            echo wp_json_encode(
                [
					'status' => 'error',
					'message' => $e->getMessage(),
				]
            );
        }
    }

    /**
     * Process payment capture
     */
    private function process_payment_capture( $payment ) {
        global $wpdb;

        try {
            // Get custom data from custom_id
            if ( ! isset( $payment['custom_id'] ) ) {
                throw new \Exception( 'Missing custom_id in payment' );
            }

            $custom_data = json_decode( $payment['custom_id'], true );
            if ( ! $custom_data ) {
                throw new \Exception( 'Invalid custom data' );
            }

            // Verify payment amount
            $payment_amount = number_format( $payment['amount']['value'], 2, '.', '' );
            $expected_amount = number_format( $custom_data['subtotal'], 2, '.', '' );

            if ( $payment['amount']['value'] < 0 ) {
                throw new \Exception( 'Invalid payment amount: negative value' );
            }

            if ( $payment_amount !== $expected_amount ) {
                throw new \Exception( 'Payment amount mismatch' );
            }

            // Check if transaction already exists
            $existing = $wpdb->get_var(
                $wpdb->prepare(
                    "SELECT id FROM {$wpdb->prefix}wpuf_transaction
                WHERE transaction_id = %s",
                    $payment['id']
                )
            );

            if ( $existing ) {
                return; // Exit if transaction already processed
            }

            // Get user
            $user = get_user_by( 'id', $custom_data['user_id'] );
            if ( ! $user ) {
                throw new \Exception( 'Invalid user' );
            }

            // Calculate tax
            $tax_amount = 0;
            if ( $this->wpuf_tax_enabled() ) {
                $tax_rate = $this->wpuf_current_tax_rate();
                $payment_amount = $payment['amount']['value'];
                // Calculate tax from total amount
                $tax_amount = ( $payment_amount * $tax_rate ) / ( 100 + $tax_rate );
                $subtotal = $payment_amount - $tax_amount;
            } else {
                $subtotal = $payment['amount']['value'];
            }

            // Create payment record
            $data = [
                'user_id' => $custom_data['user_id'],
                'status' => 'completed',
                'subtotal' => $subtotal,
                'tax' => $tax_amount,
                'cost' => $payment['amount']['value'],
                'post_id' => ( 'post' === $custom_data['type'] ) ? $custom_data['item_number'] : 0,
                'pack_id' => ( 'pack' === $custom_data['type'] ) ? $custom_data['item_number'] : 0,
                'payer_first_name' => $user->first_name,
                'payer_last_name' => $user->last_name,
                'payer_email' => $user->user_email,
                'payment_type' => 'paypal',
                'transaction_id' => $payment['id'],
                'created' => gmdate( 'Y-m-d H:i:s' ),
            ];

            // Insert payment record
            Payment::insert_payment( $data, $payment['id'], false );

            // Handle coupon if present
            if ( ! empty( $custom_data['coupon_id'] ) ) {
                $this->update_coupon_usage( $custom_data['coupon_id'] );
            }
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Payment capture processing failed: ' . $e->getMessage() );
            throw $e;
        }
    }

    /**
     * Get payer information
     */
    private function get_payer_info( $payment, $user ) {
        $payer_info = [
            'first_name' => $user->first_name,
            'last_name' => $user->last_name,
            'email' => $user->user_email,
        ];

        if ( isset( $payment['payer'] ) ) {
            if ( isset( $payment['payer']['name'] ) ) {
                $name_parts = explode( ' ', $payment['payer']['name'] );
                $payer_info['first_name'] = $name_parts[0];
                $payer_info['last_name'] = count( $name_parts ) > 1 ? implode( ' ', array_slice( $name_parts, 1 ) ) : '';
            }
            if ( isset( $payment['payer']['email_address'] ) ) {
                $payer_info['email'] = $payment['payer']['email_address'];
            }
        }

        return $payer_info;
    }

    /**
     * Handle additional payment features
     */
    private function handle_payment_features( $custom_data, $payment ) {
        // Handle coupon
        if ( ! empty( $custom_data['coupon_id'] ) ) {
            $this->update_coupon_usage( $custom_data['coupon_id'] );
        }

        // Verify payment amount
        if ( $payment['amount']['value'] !== number_format( $custom_data['subtotal'], 2, '.', '' ) ) {
            throw new \Exception( 'Payment amount mismatch' );
        }
    }


    /**
     * Register webhook endpoint
     */
    public function register_webhook_endpoint() {
        // Add rewrite rule for webhook endpoint
        add_rewrite_rule(
            '^webhook_triggered/?$',
            'index.php?action=webhook_triggered=1',
            'top'
        );

        // Add query var filter
        add_filter(
            'query_vars', function ( $vars ) {
				$vars[] = 'action';
				return $vars;
			}
        );

        // Flush rewrite rules only once
        if ( ! get_option( 'wpuf_paypal_webhook_flushed' ) ) {
            flush_rewrite_rules();
            update_option( 'wpuf_paypal_webhook_flushed', true );
            \WP_User_Frontend::log( 'PayPal: Webhook endpoint registered and rewrite rules flushed' );
        }

        // Verify webhook configuration
        if ( empty( $this->webhook_id ) ) {
            \WP_User_Frontend::log( 'PayPal: Warning - Webhook ID is not configured' );
        } else {
            \WP_User_Frontend::log( 'PayPal: Webhook endpoint registered at: ' . home_url( '/?action=webhook_triggered' ) );
        }
    }

    /**
     * Handle webhook request
     */
    public function handle_webhook_request() {
        if ( 'action' === get_query_var( 'action' ) &&
            isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] &&
            isset( $_SERVER['HTTP_PAYPAL_TRANSMISSION_ID'] ) ) {
            $raw_input = file_get_contents( 'php://input' );
            $acknowledged = false;

            try {
                // Log and basic checks
                \WP_User_Frontend::log( 'PayPal: Webhook received' );
                if ( empty( $raw_input ) ) {
                    throw new \Exception( 'Empty webhook payload' );
                }

                // Verify signature
                if ( ! $this->verify_webhook_signature_from_input( $raw_input ) ) {
                    throw new \Exception( 'Invalid webhook signature' );
                }

                // Decode and validate
                $webhook_data = json_decode( $raw_input, true );
                if ( JSON_ERROR_NONE !== json_last_error() ) {
                    throw new \Exception( 'Invalid JSON' );
                }
                if ( ! isset( $webhook_data['event_type'] ) ) {
                    throw new \Exception( 'Missing event type' );
                }

                // Log the event type
                \WP_User_Frontend::log( 'PayPal: Received webhook event: ' . $webhook_data['event_type'] );

                // Process the webhook
                $this->process_webhook( $raw_input );

                $acknowledged = true;
            } catch ( \Exception $e ) {
                \WP_User_Frontend::log( 'PayPal: Webhook error: ' . $e->getMessage() );
                // Optionally, save the failed payload for manual review
            }

            // Always acknowledge to PayPal
            http_response_code( 200 );
            echo wp_json_encode( [ 'status' => $acknowledged ? 'ok' : 'error' ] );
            exit;
        }
    }

    /**
     * Verify webhook signature from raw input
     */
    private function verify_webhook_signature_from_input( $raw_input ) {
        try {
            // Get all headers, lowercased
            $headers = function_exists( 'getallheaders' ) ?
                array_change_key_case( getallheaders(), CASE_LOWER ) :
                array_change_key_case( $_SERVER, CASE_LOWER );

            // Required PayPal webhook headers
            $required_headers = [
                'paypal-transmission-id',
                'paypal-transmission-time',
                'paypal-transmission-sig',
                'paypal-cert-url',
                'paypal-auth-algo',
            ];

            foreach ( $required_headers as $header ) {
                if ( empty( $headers[ $header ] ) ) {
                    return false;
                }
            }

            $access_token = $this->get_access_token();
            if ( ! $access_token ) {
                \WP_User_Frontend::log( 'PayPal: Failed to get access token' );
                return false;
            }

            $verification_url = $this->test_mode ?
                'https://api.sandbox.paypal.com/v1/notifications/verify-webhook-signature' :
                'https://api.paypal.com/v1/notifications/verify-webhook-signature';

            $verification_data = [
                'transmission_id'    => $headers['paypal-transmission-id'],
                'transmission_time'  => $headers['paypal-transmission-time'],
                'cert_url'           => $headers['paypal-cert-url'],
                'webhook_id'         => $this->webhook_id,
                'webhook_event'      => json_decode( $raw_input, false ),
                'transmission_sig'   => $headers['paypal-transmission-sig'],
                'auth_algo'          => $headers['paypal-auth-algo'],
            ];

            $response = wp_remote_post(
                $verification_url, [
					'headers' => [
						'Content-Type'  => 'application/json',
						'Authorization' => 'Bearer ' . $access_token,
					],
					'body'    => wp_json_encode( $verification_data ),
					'timeout' => 30,
				]
            );

            if ( is_wp_error( $response ) ) {
                \WP_User_Frontend::log( 'PayPal: Verification request failed: ' . $response->get_error_message() );
                return false;
            }

            $body = json_decode( wp_remote_retrieve_body( $response ), true );

            $is_verified = isset( $body['verification_status'] ) && $body['verification_status'] === 'SUCCESS';

            \WP_User_Frontend::log( 'PayPal: Webhook verification ' . ( $is_verified ? 'successful' : 'failed' ) );
            \WP_User_Frontend::log( 'PayPal: ===== Webhook Verification End =====' );

            return $is_verified;
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Webhook verification error: ' . $e->getMessage() );
            \WP_User_Frontend::log( 'PayPal: Stack trace: ' . $e->getTraceAsString() );
            return false;
        }
    }

    /**
     * Get PayPal access token
     */
    private function get_access_token() {
        $client_id = wpuf_get_option( 'paypal_client_id', 'wpuf_payment' );
        $client_secret = wpuf_get_option( 'paypal_client_secret', 'wpuf_payment' );

        $token_url = $this->test_mode ?
            'https://api-m.sandbox.paypal.com/v1/oauth2/token' :
            'https://api-m.paypal.com/v1/oauth2/token';

        $response = wp_remote_post(
            $token_url, [
				'headers' => [
					'Authorization' => 'Basic ' . base64_encode( $client_id . ':' . $client_secret ),
					'Content-Type' => 'application/x-www-form-urlencoded',
				],
				'body' => 'grant_type=client_credentials',
			]
        );

        if ( is_wp_error( $response ) ) {
            throw new \Exception( 'Failed to get access token: ' . esc_html( $response->get_error_message() ) );
        }

        $body = json_decode( wp_remote_retrieve_body( $response ), true );

        return $body['access_token'];
    }

    /**
     * Handle subscription creation
     */
    private function handle_subscription_created( $subscription ) {
        try {
            \WP_User_Frontend::log( 'PayPal: Processing subscription creation: ' . print_r( $subscription, true ) );

            // Extract custom data
            $custom_data = [];
            if ( isset( $subscription['custom_id'] ) ) {
                $custom_data = json_decode( $subscription['custom_id'], true );
            }

            if ( ! $custom_data || ! isset( $custom_data['user_id'] ) ) {
                throw new \Exception( 'Invalid custom data in subscription' );
            }

            $user_id = $custom_data['user_id'];
            $subscription_id = $subscription['id']; // This is the PayPal subscription ID
            $trial_period_days = isset( $custom_data['trial_period_days'] ) ? $custom_data['trial_period_days'] : 0;
            $is_trial = $trial_period_days > 0;

            // Check if we're in a trial period
            $is_in_trial = false;
            if ( isset( $subscription['billing_info']['cycle_executions'] ) ) {
                foreach ( $subscription['billing_info']['cycle_executions'] as $cycle ) {
                    if ( 'TRIAL' === $cycle['tenure_type'] && $cycle['cycles_completed'] < $cycle['cycles_remaining'] ) {
                        $is_in_trial = true;
                        break;
                    }
                }
            }

            // Get the pack details
            $pack = get_post( $custom_data['item_number'] );
            $pack_meta = array_map(
                function ( $value ) {
					return maybe_unserialize( $value[0] );
				}, get_post_meta( $pack->ID )
            );

            // Get subscription period and interval from pack meta
            $period = isset( $pack_meta['_cycle_period'] ) ? $pack_meta['_cycle_period'] : 'month';
            $interval = isset( $pack_meta['_billing_cycle_number'] ) ? intval( $pack_meta['_billing_cycle_number'] ) : 1;

            // Get tax rate if enabled
            $tax_rate = 0;
            if ( $this->wpuf_tax_enabled() ) {
                $tax_rate = $this->wpuf_current_tax_rate();
            }

            // Create subscription data structure with all necessary meta
            $subscription_data = [
                'pack_id' => $custom_data['item_number'],
                'posts' => isset( $pack_meta['_post_types'] ) ? $pack_meta['_post_types'] : [],
                'total_feature_item' => isset( $pack_meta['_total_feature_item'] ) ? $pack_meta['_total_feature_item'] : '-1',
                'remove_feature_item' => isset( $pack_meta['_remove_feature_item'] ) ? $pack_meta['_remove_feature_item'] : '-1',
                'status' => 'completed',
                'expire' => isset( $pack_meta['expire'] ) ? $pack_meta['expire'] : '',
                'profile_id' => $subscription_id,
                'recurring' => 'yes',
                'cycle_period' => $period,
                'cycle_number' => $interval,
                'postnum_rollback_on_delete' => isset( $pack_meta['_postnum_rollback_on_delete'] ) ? $pack_meta['_postnum_rollback_on_delete'] : '',
                '_enable_post_expiration' => isset( $pack_meta['_enable_post_expiration'] ) ? $pack_meta['_enable_post_expiration'] : 'no',
                '_post_expiration_time' => isset( $pack_meta['_post_expiration_time'] ) ? $pack_meta['_post_expiration_time'] : '',
                '_expired_post_status' => isset( $pack_meta['_expired_post_status'] ) ? $pack_meta['_expired_post_status'] : 'publish',
                '_enable_mail_after_expired' => isset( $pack_meta['_enable_mail_after_expired'] ) ? $pack_meta['_enable_mail_after_expired'] : 'no',
                '_post_expiration_message' => isset( $pack_meta['_post_expiration_message'] ) ? $pack_meta['_post_expiration_message'] : '',
                'subscription_id' => $subscription_id,
                'trial' => $is_trial ? 'yes' : 'no',
                'created' => gmdate( 'Y-m-d H:i:s' ),
            ];

            // If posts meta is empty, set default values
            if ( empty( $subscription_data['posts'] ) ) {
                $subscription_data['posts'] = [
                    'post' => '-1',
                    'page' => '-1',
                    'user_request' => '-1',
                    'wp_block' => '-1',
                    'wp_template' => '-1',
                    'wp_template_part' => '-1',
                    'wp_global_styles' => '-1',
                    'wp_navigation' => '-1',
                    'wp_font_family' => '-1',
                    'wp_font_face' => '-1',
                ];
            }

            \WP_User_Frontend::log( 'PayPal: Current pack: ' . print_r( $subscription_data, true ) );

            // Update user meta with complete subscription data
            update_user_meta( $user_id, '_wpuf_subscription_pack', $subscription_data );

            // Create a trial payment record if this is a trial
            if ( $is_in_trial ) {
                $this->create_trial_payment_record( $user_id, $custom_data['item_number'], $subscription_id );
            }
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Subscription creation handling failed: ' . $e->getMessage() );
            throw $e;
        }
    }

    /**
     * Create a trial payment record with zero cost
     */
    private function create_trial_payment_record( $user_id, $pack_id, $subscription_id ) {
        $user = get_user_by( 'id', $user_id );

        if ( ! $user ) {
            \WP_User_Frontend::log( 'PayPal: Invalid user ID for trial payment: ' . $user_id );
            return;
        }

        // Create payment data with zero cost for trial
        $payment_data = [
            'user_id' => $user_id,
            'status' => 'completed',
            'subtotal' => 0,
            'tax' => 0,
            'cost' => 0,
            'post_id' => 0,
            'pack_id' => $pack_id,
            'payer_first_name' => $user->first_name,
            'payer_last_name' => $user->last_name,
            'payer_email' => $user->user_email,
            'payment_type' => 'PayPal',
            'transaction_id' => $subscription_id . '_trial',
            'created' => gmdate( 'Y-m-d H:i:s' ),
        ];

        Payment::insert_payment( $payment_data, $subscription_id . '_trial', true );
        \WP_User_Frontend::log( 'PayPal: Trial payment record created for user: ' . $user_id );
    }

    /**
     * Handle subscription cancellation
     */
    public function cancel_subscription( $data ) {
        \WP_User_Frontend::log( 'PayPal: ===== Subscription Cancellation Debug Start =====' );
        \WP_User_Frontend::log( 'PayPal: Raw input data: ' . print_r( $data, true ) );

        try {
            global $wpdb;
            // Extract user_id from the input
            $user_id = null;

            // Get user ID from form data or current user
            if ( isset( $data['user_id'] ) ) {
                $user_id = $data['user_id'];
            } else {
                $user_id = get_current_user_id();
            }

            if ( ! $user_id || ! is_numeric( $user_id ) ) {
                \WP_User_Frontend::log( 'PayPal: Invalid user ID provided for cancellation: ' . print_r( $data, true ) );
                throw new \Exception( 'Invalid user ID provided for cancellation' );
            }

            $subscription = get_user_meta( $user_id, '_wpuf_subscription_pack', true );
            $subscription_id = get_user_meta( $user_id, '_wpuf_paypal_subscription_id', true );

            // If we have a profile ID and subscription is recurring
            if ( ! empty( $subscription_id ) && $subscription['recurring'] === 'yes' ) {
                try {
                    // Get access token
                    $access_token = $this->get_access_token();

                    $plan_id_url = ( $this->test_mode ?
                        'https://api-m.sandbox.paypal.com' :
                        'https://api-m.paypal.com' ) . '/v1/billing/subscriptions/' . $subscription_id;
                    $plan_id_response = wp_remote_get(
                        $plan_id_url, [
							'headers' => [
								'Authorization' => 'Bearer ' . $access_token,
							],
						]
                    );
                    if ( is_wp_error( $plan_id_response ) ) {
                        \WP_User_Frontend::log( 'PayPal: API Error: ' . $plan_id_response->get_error_message() );
                        throw new \Exception( 'Failed to get plan ID from PayPal: ' . $plan_id_response->get_error_message() );
                    }

                    $plan_response_body = json_decode( wp_remote_retrieve_body( $plan_id_response ), true );
                    if ( ! $plan_response_body || ! isset( $plan_response_body['plan_id'] ) ) {
                        \WP_User_Frontend::log( 'PayPal: Invalid plan response: ' . wp_remote_retrieve_body( $plan_id_response ) );
                        throw new \Exception( 'Invalid plan response from PayPal' );
                    }

                    $plan_id = $plan_response_body['plan_id'];

                    if ( empty( $plan_id ) ) {
                        \WP_User_Frontend::log( 'PayPal: Plan ID not found for subscription: ' . $subscription_id );
                        throw new \Exception( 'Plan ID not found for subscription: ' . $subscription_id );
                    }

                    // Cancel the subscription in PayPal
                    $cancel_url = ( $this->test_mode ?
                        'https://api-m.sandbox.paypal.com' :
                        'https://api-m.paypal.com' ) . '/v1/billing/plans/' . $plan_id . '/deactivate';

                    \WP_User_Frontend::log( 'PayPal: Attempting to cancel subscription in PayPal with URL: ' . $cancel_url );

                    $response = wp_remote_post(
                        $cancel_url, [
							'headers' => [
								'Authorization' => 'Bearer ' . $access_token,
								'Content-Type' => 'application/json',
								'Prefer' => 'return=representation',
							],
							'body' => wp_json_encode(
                                [
									'reason' => 'Customer requested cancellation',
                                ]
                            ),
						]
                    );

                    if ( is_wp_error( $response ) ) {
                        \WP_User_Frontend::log( 'PayPal: API Error: ' . $response->get_error_message() );
                        throw new \Exception( 'Failed to cancel subscription in PayPal: ' . $response->get_error_message() );
                    }

                    $response_code = wp_remote_retrieve_response_code( $response );
                    $response_body = wp_remote_retrieve_body( $response );

                    if ( $response_code !== 204 ) {
                        throw new \Exception( 'Unexpected response from PayPal: ' . $response_body );
                    }
                } catch ( \Exception $e ) {
                    \WP_User_Frontend::log( 'PayPal: PayPal API error: ' . $e->getMessage() );
                    // Continue with local cancellation even if PayPal fails
                }
            }

            // Update local subscription status regardless of PayPal API result
            $updated_subscription = [
                'profile_id' => $subscription_id,
                'status' => 'cancel',
                'updated' => gmdate( 'Y-m-d H:i:s' ),
            ];

            update_user_meta( $user_id, '_wpuf_subscription_pack', $updated_subscription );
            update_user_meta( $user_id, '_wpuf_paypal_subscription_status', 'cancel' );
            \WP_User_Frontend::log( 'PayPal: Updated subscription meta: ' . print_r( $updated_subscription, true ) );

            // Update subscriber table
            $update_result = $wpdb->update(
                $wpdb->prefix . 'wpuf_subscribers',
                [
                    'subscribtion_status' => 'cancel',
                    'expire' => gmdate( 'd-m-Y' ),
                ],
                [
                    'user_id' => $user_id,
                    'gateway' => 'paypal',
                ],
                [ '%s', '%s' ],
                [ '%d', '%s' ]
            );

            if ( $update_result === false ) {
                \WP_User_Frontend::log( 'PayPal: Database error updating subscribers table: ' . $wpdb->last_error );
            } else {
                \WP_User_Frontend::log( 'PayPal: Updated subscribers table. Rows affected: ' . $update_result );
            }

            \WP_User_Frontend::log( 'PayPal: Subscription cancel successfully for user: ' . $user_id );
            \WP_User_Frontend::log( 'PayPal: ===== Subscription Cancellation Debug End =====' );
            return true;
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Subscription cancellation failed: ' . $e->getMessage() );
            \WP_User_Frontend::log( 'PayPal: Stack trace: ' . $e->getTraceAsString() );
            throw $e;
        }
    }

    /**
     * Handle subscription payment
     */
    private function process_subscription_payment( $payment ) {
        global $wpdb;

        try {
            // Get transaction ID - could be in different locations based on event type
            $transaction_id = isset( $payment['id'] ) ? $payment['id'] : '';

            // Get subscription ID
            $subscription_id = isset( $payment['billing_agreement_id'] ) ? $payment['billing_agreement_id'] : '';

            // If no subscription ID or transaction ID, exit
            if ( empty( $subscription_id ) || empty( $transaction_id ) ) {
                \WP_User_Frontend::log( 'PayPal: Missing subscription ID or transaction ID in payment data' );
                return;
            }

            // Check if transaction already exists
            $existing = $wpdb->get_var(
                $wpdb->prepare(
                    "SELECT id FROM {$wpdb->prefix}wpuf_transaction
                WHERE transaction_id = %s",
                    $transaction_id
                )
            );

            if ( $existing ) {
                \WP_User_Frontend::log( 'PayPal: Transaction already processed: ' . $transaction_id );
                // Even if transaction exists, clean up any transients
                $this->clean_up_transients( $subscription_id );
                return; // Exit if transaction already processed
            }

            // Get payment amount
            $amount = 0;
            if ( isset( $payment['amount']['total'] ) ) {
                $amount = $payment['amount']['total'];
            } elseif ( isset( $payment['amount']['value'] ) ) {
                $amount = $payment['amount']['value'];
            }

            // Get currency
            $currency = '';
            if ( isset( $payment['amount']['currency'] ) ) {
                $currency = $payment['amount']['currency'];
            } elseif ( isset( $payment['amount']['currency_code'] ) ) {
                $currency = $payment['amount']['currency_code'];
            }

            // Initialize custom data
            $custom_data = [];

            // Get custom data
            if ( isset( $payment['custom'] ) ) {
                $custom_data = json_decode( $payment['custom'], true );
            } elseif ( isset( $payment['custom_id'] ) ) {
                $custom_data = json_decode( $payment['custom_id'], true );
            }

            // If JSON decode failed, initialize as empty array
            if ( ! is_array( $custom_data ) ) {
                $custom_data = [];
            }

            // Check for pending subscription in transient
            $pending_subscription = get_transient( 'wpuf_paypal_pending_' . $subscription_id );
            $user_id = null;
            $pack_id = 0;

            // If we have pending subscription data, use it
            if ( $pending_subscription && isset( $pending_subscription['user_id'] ) ) {
                $user_id = $pending_subscription['user_id'];

                if ( isset( $pending_subscription['pack_id'] ) ) {
                    $pack_id = $pending_subscription['pack_id'];
                }
            } else {
                // Get user ID from subscription if not found in transient
                $user_id = $this->get_user_id_by_subscription( $subscription_id );
            }

            // Try to get user ID from custom data if still not found
            if ( ! $user_id && isset( $custom_data['user_id'] ) ) {
                $user_id = $custom_data['user_id'];
            }

            if ( ! $user_id ) {
                \WP_User_Frontend::log( 'PayPal: User not found for subscription: ' . $subscription_id );
                // Clean up any transients even if user not found
                $this->clean_up_transients( $subscription_id );
                return;
            }

            // Get user data
            $user = get_user_by( 'id', $user_id );
            if ( ! $user ) {
                \WP_User_Frontend::log( 'PayPal: Invalid user ID: ' . $user_id );
                // Clean up any transients even if user invalid
                $this->clean_up_transients( $subscription_id );
                return;
            }

            // If pack ID not found in transient, try to get it from custom data or meta
            if ( 0 === $pack_id ) {
                if ( isset( $custom_data['item_number'] ) && isset( $custom_data['type'] ) && $custom_data['type'] === 'pack' ) {
                    $pack_id = $custom_data['item_number'];
                } else {
                    // Try to get pack ID from user meta
                    $pack_id = $this->get_pack_id_by_subscription( $user_id, $subscription_id );
                }
            }

            // Check if a subscriber record already exists for this subscription
            $existing_subscriber = $wpdb->get_var(
                $wpdb->prepare(
                    "SELECT id FROM {$wpdb->prefix}wpuf_subscribers
                WHERE user_id = %d AND transaction_id = %s",
                    $user_id, $subscription_id
                )
            );

            // If no subscriber record exists yet and we have all the data, create one
            if ( ! $existing_subscriber && $pack_id > 0 ) {
                // Update user subscription status in WordPress - this should be the only record
                wpuf_get_user( $user_id )->subscription()->add_pack( $pack_id, $subscription_id, true, 'recurring' );
                update_user_meta( $user_id, '_wpuf_paypal_subscription_status', 'completed' );
                // Log subscription creation
                \WP_User_Frontend::log( 'PayPal: User subscription pack added for user: ' . $user_id . ', pack: ' . $pack_id );
            }

            // Prepare payment data
            $data = [
                'user_id'           => $user_id,
                'status'            => 'completed',
                'subtotal'          => $amount,
                'tax'               => 0,
                'cost'              => $amount,
                'post_id'           => 0,
                'pack_id'           => $pack_id,
                'payer_first_name'  => $user->first_name,
                'payer_last_name'   => $user->last_name,
                'payer_email'       => $user->user_email,
                'payment_type'      => 'paypal',
                'transaction_id'    => $transaction_id,
                'created'           => gmdate( 'Y-m-d H:i:s' ),
            ];

            // Add custom meta information if available
            if ( isset( $payment['time'] ) ) {
                $data['created'] = gmdate( 'Y-m-d H:i:s', strtotime( $payment['time'] ) );
            }

            // Insert payment record
            Payment::insert_payment( $data, $transaction_id, true );

            // Final cleanup of any transients after successful processing
            $this->clean_up_transients( $subscription_id );
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Subscription payment processing failed: ' . $e->getMessage() );

            // Even in case of error, try to clean up transients
            if ( isset( $subscription_id ) ) {
                $this->clean_up_transients( $subscription_id );
            }
        }
    }

    /**
     * Clean up all transients related to a subscription
     */
    private function clean_up_transients( $subscription_id ) {
        // Delete the specific transient
        delete_transient( 'wpuf_paypal_pending_' . $subscription_id );

        // Log cleanup
        \WP_User_Frontend::log( 'PayPal: Cleaned up transients for subscription: ' . $subscription_id );
    }

    /**
     * Get pack ID by subscription ID
     */
    private function get_pack_id_by_subscription( $user_id, $subscription_id ) {
        global $wpdb;

        // First try to get from subscribers table
        $pack_id = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT subscribtion_id FROM {$wpdb->prefix}wpuf_subscribers
            WHERE user_id = %d AND transaction_id = %s",
                $user_id, $subscription_id
            )
        );

        if ( $pack_id ) {
            return $pack_id;
        }

        // Then try to get from user meta
        $subscription = get_user_meta( $user_id, '_wpuf_subscription_pack', true );
        if ( $subscription && isset( $subscription['pack_id'] ) ) {
            return $subscription['pack_id'];
        }

        return 0;
    }

    /**
     * Update payment options
     */
    public function payment_options( $options ) {
        // Existing options
        $options[] = [
            'name'  => 'paypal_email',
            'label' => __( 'PayPal Email', 'wp-user-frontend' ),
        ];

        $options[] = [
            'name'    => 'gate_instruct_paypal',
            'label'   => __( 'PayPal Instruction', 'wp-user-frontend' ),
            'type'    => 'wysiwyg',
            'default' => "Pay via PayPal; you can pay with your credit card if you don't have a PayPal account",
        ];

        // New REST API options
        $options[] = [
            'name' => 'paypal_client_id',
            'label' => __( 'PayPal Client ID', 'wp-user-frontend' ),
            'desc' => __( 'Enter your PayPal Client ID from your PayPal developer dashboard.', 'wp-user-frontend' ),
        ];

        $options[] = [
            'name' => 'paypal_client_secret',
            'label' => __( 'PayPal Client Secret', 'wp-user-frontend' ),
            'desc' => __( 'Enter your PayPal Client Secret from your PayPal developer dashboard.', 'wp-user-frontend' ),
        ];

        // Webhook URL field (read-only, auto-generated)
        $webhook_url = home_url( '/?action=webhook_triggered' );
        $options[] = [
            'name' => 'paypal_webhook_url',
            'label' => __( 'PayPal Webhook URL', 'wp-user-frontend' ),
            'type' => 'text',
            'default' => $webhook_url,
            'attr' => 'disabled',
            'readonly' => true,
            'desc' => sprintf(
                /* translators: %s: webhook URL */
                __( 'Copy this URL and add it to your PayPal webhook configuration in your PayPal developer dashboard. This URL is: <code>%s</code>', 'wp-user-frontend' ),
                esc_html( $webhook_url )
            ),
            'css' => 'background-color: #f9f9f9; color: #666;',
        ];

        $options[] = [
            'name' => 'paypal_webhook_id',
            'label' => __( 'PayPal Webhook ID', 'wp-user-frontend' ),
            'desc' => sprintf(
                /* translators: %1$s: live dashboard link, %2$s: sandbox dashboard link */
                __( 'Enter the Webhook ID provided by PayPal after creating the webhook. This is required for webhook verification. Find your webhook ID in your <a href="%1$s" target="_blank" rel="noopener noreferrer">PayPal dashboard</a>', 'wp-user-frontend' ),
                'https://developer.paypal.com/'
            ),
        ];

        // Legacy API options (keep for backward compatibility)
        $options[] = [
            'name'  => 'paypal_api_username',
            'label' => __( 'PayPal API username', 'wp-user-frontend' ),
            'desc' => sprintf(
                /* translators: %1$s: live dashboard link, %2$s: sandbox dashboard link */
                __( '(Legacy) <a href="%1$s" target="_blank" rel="noopener noreferrer">PayPal API username</a> for older integrations.', 'wp-user-frontend' ),
                'https://developer.paypal.com/'
            ),
        ];
        $options[] = [
            'name'  => 'paypal_api_password',
            'label' => __( 'PayPal API password', 'wp-user-frontend' ),
            'desc' => __( '(Legacy) PayPal API password for older integrations.', 'wp-user-frontend' ),
        ];
        $options[] = [
            'name'  => 'paypal_api_signature',
            'label' => __( 'PayPal API signature', 'wp-user-frontend' ),
            'desc' => __( '(Legacy) PayPal API signature for older integrations.', 'wp-user-frontend' ),
        ];

        // Replace sandbox mode checkbox with test mode radio button
        $options[] = [
            'name' => 'paypal_test_mode',
            'label' => __( 'PayPal Mode', 'wp-user-frontend' ),
            'type' => 'radio',
            'default' => 'live',
            'options' => [
                'live' => __( 'Live Mode', 'wp-user-frontend' ),
                'test' => __( 'Test Mode (Sandbox)', 'wp-user-frontend' ),
            ],
            'desc' => __( 'Choose whether to process real payments or test payments. Test mode uses PayPal Sandbox environment. Make sure to configure separate webhook URLs for each environment.', 'wp-user-frontend' ),
        ];

        // Add error page setting
        $options[] = [
            'name' => 'paypal_error_page',
            'label' => __( 'Payment Error Page', 'wp-user-frontend' ),
            'type' => 'select',
            'options' => $this->get_pages_dropdown(),
            'desc' => __( 'Select the page where users will be redirected when payment errors occur. If not set, users will be redirected to home page with error message.', 'wp-user-frontend' ),
        ];

        // Add webhook events information
        $options[] = [
            'name' => 'paypal_webhook_events_info',
            'label' => '', // Empty label since we have both headings in the description
            'type' => 'html',
            'desc' => $this->get_webhook_events_notice(),
        ];

        return $options;
    }

    /**
     * Get pages dropdown for settings
     */
    private function get_pages_dropdown() {
        $pages = get_pages();
        $options = [ '' => __( 'Select a page', 'wp-user-frontend' ) ];
        
        foreach ( $pages as $page ) {
            $options[ $page->ID ] = $page->post_title;
        }
        
        return $options;
    }

    /**
     * Get error page URL
     */
    private function get_error_page_url( $error_message = '' ) {
        $error_page_id = wpuf_get_option( 'paypal_error_page', 'wpuf_payment' );
        
        if ( ! empty( $error_page_id ) && is_numeric( $error_page_id ) ) {
            $error_url = get_permalink( $error_page_id );
            if ( ! empty( $error_message ) ) {
                $error_url = add_query_arg( 'wpuf_paypal_error', rawurlencode( $error_message ), $error_url );
            }
            return $error_url;
        }
        
        // Fallback to home URL with error parameter
        if ( ! empty( $error_message ) ) {
            return home_url( '/?wpuf_paypal_error=' . rawurlencode( $error_message ) );
        }
        
        return home_url();
    }

    /**
     * Get webhook events notice HTML
     */
    private function get_webhook_events_notice() {
        ob_start();
        ?>
        <div class="wpuf-paypal-webhook-container">
            <p><?php esc_html_e( 'Setup Guide:', 'wp-user-frontend' ); ?>
            <a href="<?php echo esc_url( $this->setup_guide_url ); ?>" target="_blank" rel="noopener noreferrer">
                <?php esc_html_e( 'Follow our PayPal integration guide', 'wp-user-frontend' ); ?>
            </a>
            <?php esc_html_e( 'for step-by-step webhook configuration instructions.', 'wp-user-frontend' ); ?></p>
        </div>

        <?php
        return ob_get_clean();
    }

    public function subscription_cancel( $user_id ) {
        try {
            $this->cancel_subscription( [ 'user_id' => $user_id ] );
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Failed to cancel subscription for user ' . $user_id . ': ' . $e->getMessage() );
            // Update local meta even if PayPal API call fails
            $sub_meta = 'cancel';
            wpuf_get_user( $user_id )->subscription()->update_meta( $sub_meta );
        }
    }

    /**
     * Get PayPal allowed redirect hosts
     *
     * @return array Array of PayPal domains
     */
    private function get_paypal_allowed_hosts() {
        return [
            'www.paypal.com',
            'paypal.com',
            'www.sandbox.paypal.com',
            'sandbox.paypal.com',
        ];
    }

    /**
     * Update coupon usage count
     *
     * @param int $coupon_id
     */
    private function update_coupon_usage( $coupon_id ) {
        if ( empty( $coupon_id ) ) {
            return;
        }

        $pre_usage = get_post_meta( $coupon_id, '_coupon_used', true );
        $pre_usage = empty( $pre_usage ) ? 0 : $pre_usage;
        $new_use = $pre_usage + 1;

        update_post_meta( $coupon_id, '_coupon_used', $new_use );
    }


    /**
     * Prepare and send payment to PayPal
     *
     * @param array $data Payment data
     */
    public function prepare_to_send( $data ) {
        try {
            $user_id = $data['user_info']['id'];
            $return_url = add_query_arg(
                [
					'action' => 'wpuf_paypal_success',
					'type' => $data['type'],
					'item_number' => $data['item_number'],
					'wpuf_payment_method' => 'paypal',
					'_wpnonce' => wp_create_nonce( 'wpuf_paypal_return' ),
                ], wpuf_payment_success_page(
                    [
						'type' => isset( $_GET['type'] ) ? sanitize_text_field( wp_unslash( $_GET['type'] ) ) : 'pack',
						'item_number' => isset( $_GET['item_number'] ) ? sanitize_text_field( wp_unslash( $_GET['item_number'] ) ) : '',
						'wpuf_payment_method' => 'paypal',
					]
                )
            );

            $cancel_url = $return_url;

            $billing_amount = empty( $data['price'] ) ? 0 : $data['price'];
            $tax_amount = 0;

            // Handle tax if enabled
            if ( $this->wpuf_tax_enabled() ) {
                $tax_rate = $this->wpuf_current_tax_rate();
                $tax_amount = $billing_amount * ( $tax_rate / 100 );
                $billing_amount = $billing_amount + $tax_amount;
            }

            // Handle coupon if present
            if ( isset( $_POST['coupon_id'] ) && ! empty( $_POST['coupon_id'] ) &&
                isset( $_POST['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'wpuf_payment_coupon' ) &&
                is_numeric( sanitize_text_field( wp_unslash( $_POST['coupon_id'] ) ) ) ) {
                $coupon_id = absint( sanitize_text_field( wp_unslash( $_POST['coupon_id'] ) ) );
                $billing_amount = wpuf_pro()->coupon->discount( $billing_amount, $coupon_id, $data['item_number'] );
            } else {
                $coupon_id = '';
            }

            $data['subtotal'] = $billing_amount - $tax_amount;
            $data['tax'] = $tax_amount;
            $billing_amount = apply_filters( 'wpuf_payment_amount', $billing_amount );

            // Handle free payments
            if ( $billing_amount == 0 ) {
                wpuf_get_user( $user_id )->subscription()->add_pack( $data['item_number'], null, false, 'Free' );
                wp_safe_redirect( $return_url );
                exit();
            }

            // Get access token
            $access_token = $this->get_access_token();

            // Check if this is a recurring payment
            if ( 'pack' === $data['type'] && isset( $data['custom']['recurring_pay'] ) && wpuf_is_checkbox_or_toggle_on( $data['custom']['recurring_pay'] ) ) {
                \WP_User_Frontend::log( 'PayPal: Setting up recurring payment subscription' );

                // Get subscription details from pack
                $pack = get_post( $data['item_number'] );

                $flattened_subscription_meta = array_map(
                    function ( $value ) {
						return maybe_unserialize( $value[0] );
					}, get_post_meta( $pack->ID )
                );

                if ( empty( $flattened_subscription_meta ) ) {
                    throw new \Exception( 'Invalid subscription pack' );
                }

                // Get subscription period and interval
                $period = isset( $flattened_subscription_meta['_cycle_period'] ) ? $flattened_subscription_meta['_cycle_period'] : 'month';
                $interval = isset( $flattened_subscription_meta['_billing_cycle_number'] ) ? intval( $flattened_subscription_meta['_billing_cycle_number'] ) : 1;

                // Handle trial period
                $trial_period_days = 0;

                if ( isset( $data['custom']['trial_status'] ) && wpuf_is_checkbox_or_toggle_on( $data['custom']['trial_status'] ) ) {
                    $trial_duration_type = $data['custom']['trial_duration_type'];
                    $trial_duration = absint( $data['custom']['trial_duration'] );

                    switch ( $trial_duration_type ) {
                        case 'week':
                            $trial_period_days = $trial_duration * 7;
                            break;
                        case 'month':
                            $trial_period_days = $trial_duration * 30;
                            break;
                        case 'year':
                            $trial_period_days = $trial_duration * 365;
                            break;
                        case 'day':
                        default:
                            $trial_period_days = $trial_duration;
                            break;
                    }

                    // Set trial meta for once per user
                    if ( ! get_user_meta( $user_id, '_wpuf_used_trial', true ) ) {
                        update_user_meta( $user_id, '_wpuf_used_trial', 'yes' );
                    }
                }

                // Create a plan if not exists
                $plan_id = $this->get_or_create_plan( $pack, $billing_amount, $period, $interval, $trial_period_days );

                if ( ! $plan_id ) {
                    throw new \Exception( 'Failed to create or get subscription plan' );
                }

                // Get tax rate if enabled
                $tax_rate = 0;
                if ( $this->wpuf_tax_enabled() ) {
                    $tax_rate = $this->wpuf_current_tax_rate();
                }

                // Prepare subscription data
                $subscription_data = [
                    'plan_id' => $plan_id,
                    'application_context' => [
                        'brand_name' => get_bloginfo( 'name' ),
                        'locale' => 'en-US',
                        'shipping_preference' => 'NO_SHIPPING',
                        'user_action' => 'SUBSCRIBE_NOW',
                        'return_url' => $return_url,
                        'cancel_url' => $cancel_url,
                    ],
                    'custom_id' => wp_json_encode(
                        [
							'type' => $data['type'],
							'user_id' => $user_id,
							'item_number' => $data['item_number'],
							'subtotal' => $data['subtotal'],
							'tax_rate' => $tax_rate,
							'tax' => $data['tax'],
							'coupon_id' => $coupon_id,
							'trial_period_days' => $trial_period_days,
						]
                    ),
                ];

                // Create subscription
                $response = wp_remote_post(
                    ( $this->test_mode ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com' ) . '/v1/billing/subscriptions',
                    [
                        'headers' => [
                            'Authorization' => 'Bearer ' . $access_token,
                            'Content-Type' => 'application/json',
                            'Prefer' => 'return=representation',
                        ],
                        'body' => wp_json_encode( $subscription_data ),
                    ]
                );

                if ( is_wp_error( $response ) ) {
                    throw new \Exception( 'Failed to create PayPal subscription: ' . $response->get_error_message() );
                }

                $body = json_decode( wp_remote_retrieve_body( $response ), true );
                \WP_User_Frontend::log( 'PayPal: Subscription response: ' . print_r( $body, true ) );

                if ( ! isset( $body['id'] ) ) {
                    throw new \Exception( 'Invalid response from PayPal - no subscription ID' );
                }

                // Store subscription details in transient instead of creating initial record
                set_transient(
                    'wpuf_paypal_pending_' . $body['id'],
                    [
                        'user_id' => $user_id,
                        'pack_id' => $data['item_number'],
                        'subscription_id' => $body['id'],
                        'status' => 'pending',
                        'created' => gmdate( 'Y-m-d H:i:s' ),
                        'trial_period_days' => $trial_period_days,
                    ],
                    HOUR_IN_SECONDS * 24 // Expire after 24 hours
                );

                // Find approval URL
                $approval_url = '';
                foreach ( $body['links'] as $link ) {
                    if ( 'approve' === $link['rel'] ) {
                        $approval_url = $link['href'];
                        break;
                    }
                }

                if ( empty( $approval_url ) ) {
                    throw new \Exception( 'Approval URL not found in PayPal response' );
                }

                // Add PayPal to allowed hosts just before redirect
                add_filter(
                    'allowed_redirect_hosts',
                    function( $hosts ) {
                        return array_merge( $hosts, $this->get_paypal_allowed_hosts() );
                    },
                    10,
                    1
                );
                
                // Redirect to PayPal
                \WP_User_Frontend::log( 'PayPal: Redirecting to PayPal subscription approval URL: ' . $approval_url );
                wp_safe_redirect( $approval_url );
                exit();
            } else {
                $payment_data = [
                    'intent' => 'CAPTURE',
                    'purchase_units' => [
						[
							'amount' => [
								'currency_code' => $data['currency'],
								'value' => number_format( $billing_amount, 2, '.', '' ),
							],
							'description' => isset( $data['custom']['post_title'] ) ? $data['custom']['post_title'] : $data['item_name'],
							'custom_id' => wp_json_encode(
                                [
									'type' => $data['type'],
									'user_id' => $user_id,
									'coupon_id' => $coupon_id,
									'subtotal' => $data['subtotal'],
									'tax' => $data['tax'],
									'item_number' => $data['item_number'],
									'first_name' => $data['user_info']['first_name'],
									'last_name' => $data['user_info']['last_name'],
									'email' => $data['user_info']['email'],
								]
                            ),
						],
					],
                    'application_context' => [
                        'return_url' => $return_url,
                        'cancel_url' => $cancel_url,
                        'brand_name' => get_bloginfo( 'name' ),
                        'landing_page' => 'LOGIN',
                        'user_action' => 'PAY_NOW',
                        'shipping_preference' => 'NO_SHIPPING',
                    ],
                ];

                // Add debug logging
                            // Create order
                $response = wp_remote_post(
                    $this->test_mode ? 'https://api-m.sandbox.paypal.com/v2/checkout/orders' : 'https://api-m.paypal.com/v2/checkout/orders',
                    [
                        'headers' => [
                            'Authorization' => 'Bearer ' . $access_token,
                            'Content-Type' => 'application/json',
                        ],
                        'body' => wp_json_encode( $payment_data ),
                    ]
                );

                if ( is_wp_error( $response ) ) {
                    throw new \Exception( 'Failed to create PayPal order: ' . $response->get_error_message() );
                }

                $body = json_decode( wp_remote_retrieve_body( $response ), true );
                \WP_User_Frontend::log( 'PayPal: PayPal response: ' . print_r( $body, true ) );

                if ( ! isset( $body['id'] ) ) {
                    throw new \Exception( 'Invalid response from PayPal - no order ID' );
                }

                // Find approval URL
                $approval_url = '';
                foreach ( $body['links'] as $link ) {
                    if ( 'approve' === $link['rel'] ) {
                        $approval_url = $link['href'];
                        break;
                    }
                }

                if ( empty( $approval_url ) ) {
                    throw new \Exception( 'Approval URL not found in PayPal response' );
                }
                
                \WP_User_Frontend::log( 'PayPal: Redirecting to PayPal for payment approval' );
                
                // Add PayPal to allowed hosts just before redirect
                add_filter(
                    'allowed_redirect_hosts',
                    function( $hosts ) {
                        return array_merge( $hosts, $this->get_paypal_allowed_hosts() );
                    },
                    10,
                    1
                );
                
                wp_safe_redirect( $approval_url );
                exit();
            }
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Payment preparation failed: ' . $e->getMessage() );
            wp_die( $e->getMessage() );
        }
    }

    /**
     * Get or create a PayPal subscription plan
     */
    private function get_or_create_plan( $pack, $amount, $period, $interval, $trial_period_days = 0 ) {
        try {
            $access_token = $this->get_access_token();
            $plan_name = 'WPUF-' . $pack->post_title . '-' . uniqid();
            $plan_id = get_post_meta( $pack->ID, '_paypal_plan_id', true );

            // If plan exists and is active, return it
            if ( $plan_id ) {
                $plan_url = ( $this->test_mode ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com' ) .
                            '/v1/billing/plans/' . $plan_id;

                $response = wp_remote_get(
                    $plan_url, [
						'headers' => [
							'Authorization' => 'Bearer ' . $access_token,
							'Content-Type' => 'application/json',
						],
					]
                );

                if ( ! is_wp_error( $response ) ) {
                    $body = json_decode( wp_remote_retrieve_body( $response ), true );
                    if ( isset( $body['status'] ) && 'ACTIVE' === $body['status'] ) {
                        return $plan_id;
                    }
                }
            }

            // Create new plan
            $plan_data = [
                'product_id' => $this->get_or_create_product( $pack ),
                'name' => $plan_name,
                'description' => $pack->post_title,
                'status' => 'ACTIVE',
                'billing_cycles' => [
					[
						'frequency' => [
							'interval_unit' => strtoupper( $period ),
							'interval_count' => $interval,
						],
						'tenure_type' => 'REGULAR',
						'sequence' => 1,
						'total_cycles' => 0, // Unlimited
						'pricing_scheme' => [
							'fixed_price' => [
								'value' => number_format( $amount, 2, '.', '' ),
								'currency_code' => wpuf_get_option( 'currency', 'wpuf_payment', 'USD' ),
							],
						],
					],
				],
                'payment_preferences' => [
                    'auto_bill_outstanding' => true,
                    'setup_fee' => [
                        'value' => '0',
                        'currency_code' => wpuf_get_option( 'currency', 'wpuf_payment', 'USD' ),
                    ],
                    'setup_fee_failure_action' => 'CONTINUE',
                    'payment_failure_threshold' => 3,
                ],
            ];

            // Add trial period if specified
            if ( $trial_period_days > 0 ) {
                $plan_data['payment_preferences']['setup_fee_failure_action'] = 'CONTINUE';

                // Add trial period as a billing cycle before the regular one
                array_unshift(
                    $plan_data['billing_cycles'], [
						'frequency' => [
							'interval_unit' => 'DAY',
							'interval_count' => $trial_period_days,
						],
						'tenure_type' => 'TRIAL',
						'sequence' => 1,
						'total_cycles' => 1,
						'pricing_scheme' => [
							'fixed_price' => [
								'value' => '0',
								'currency_code' => wpuf_get_option( 'currency', 'wpuf_payment', 'USD' ),
							],
						],
					]
                );

                // Update the regular billing cycle sequence
                $plan_data['billing_cycles'][1]['sequence'] = 2;
            }

            $response = wp_remote_post(
                ( $this->test_mode ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com' ) . '/v1/billing/plans',
                [
                    'headers' => [
                        'Authorization' => 'Bearer ' . $access_token,
                        'Content-Type' => 'application/json',
                        'Prefer' => 'return=representation',
                    ],
                    'body' => wp_json_encode( $plan_data ),
                ]
            );

            if ( is_wp_error( $response ) ) {
                throw new \Exception( 'Failed to create PayPal plan: ' . $response->get_error_message() );
            }

            $body = json_decode( wp_remote_retrieve_body( $response ), true );

            if ( ! isset( $body['id'] ) ) {
                throw new \Exception( 'Invalid response from PayPal - no plan ID' );
            }

            // Store plan ID
            update_post_meta( $pack->ID, '_paypal_plan_id', $body['id'] );

            return $body['id'];
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Plan creation failed: ' . $e->getMessage() );
            return false;
        }
    }

    /**
     * Get or create a PayPal product
     */
    private function get_or_create_product( $pack ) {
        try {
            $access_token = $this->get_access_token();
            $product_id = get_post_meta( $pack->ID, '_paypal_product_id', true );

            // If product exists, return it
            if ( $product_id ) {
                return $product_id;
            }

            // Create new product
            $product_data = [
                'name' => $pack->post_title,
                'description' => $pack->post_excerpt ? $pack->post_excerpt : $pack->post_title,
                'type' => 'SERVICE',
                'category' => 'SERVICES',
            ];

            $response = wp_remote_post(
                ( $this->test_mode ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com' ) . '/v1/catalogs/products',
                [
                    'headers' => [
                        'Authorization' => 'Bearer ' . $access_token,
                        'Content-Type' => 'application/json',
                    ],
                    'body' => wp_json_encode( $product_data ),
                ]
            );

            if ( is_wp_error( $response ) ) {
                throw new \Exception( 'Failed to create PayPal product: ' . $response->get_error_message() );
            }

            $body = json_decode( wp_remote_retrieve_body( $response ), true );

            if ( ! isset( $body['id'] ) ) {
                throw new \Exception( 'Invalid response from PayPal - no product ID' );
            }

            // Store product ID
            update_post_meta( $pack->ID, '_paypal_product_id', $body['id'] );

            return $body['id'];
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Product creation failed: ' . $e->getMessage() );
            return false;
        }
    }

    /**
     * Get user ID by subscription ID
     */
    private function get_user_id_by_subscription( $subscription_id ) {
        global $wpdb;

        // First try from subscribers table
        $user_id = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT user_id FROM {$wpdb->prefix}wpuf_subscribers
            WHERE transaction_id = %s",
                $subscription_id
            )
        );

        if ( $user_id ) {
            return $user_id;
        }

        // Then try from usermeta table
        $user_id = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT user_id FROM {$wpdb->usermeta}
            WHERE meta_key = '_wpuf_subscription_pack'
            AND meta_value LIKE %s",
                '%' . $wpdb->esc_like( $subscription_id ) . '%'
            )
        );

        return $user_id;
    }

    /**
     * Handle subscription return from PayPal
     */
    public function handle_subscription_return() {
        try {
            $subscription_id = isset( $_GET['subscription_id'] ) ? sanitize_text_field( wp_unslash( $_GET['subscription_id'] ) ) : '';
            $ba_token = isset( $_GET['ba_token'] ) ? sanitize_text_field( wp_unslash( $_GET['ba_token'] ) ) : '';
            $token = isset( $_GET['token'] ) ? sanitize_text_field( wp_unslash( $_GET['token'] ) ) : '';
            
            // If we have a token but no subscription_id, the token might be the subscription ID
            if ( empty( $subscription_id ) && ! empty( $token ) ) {
                $subscription_id = $token;
            }
            
            // Log the subscription return
            \WP_User_Frontend::log( 'PayPal: Subscription return - subscription_id: ' . $subscription_id . ', ba_token: ' . $ba_token . ', token: ' . $token );
            
            // Redirect to success page for subscriptions without requiring nonce
            $success_url = add_query_arg(
                [
                    'action' => 'wpuf_paypal_success',
                    'type' => isset( $_GET['type'] ) ? sanitize_text_field( wp_unslash( $_GET['type'] ) ) : 'pack',
                    'item_number' => isset( $_GET['item_number'] ) ? sanitize_text_field( wp_unslash( $_GET['item_number'] ) ) : '',
                    'wpuf_payment_method' => 'paypal',
                    'payment_status' => 'subscription_created',
                    'subscription_id' => $subscription_id,
                    'skip_nonce_check' => '1', // Skip nonce check for subscription success page
                ], wpuf_payment_success_page(
                    [
                        'type' => isset( $_GET['type'] ) ? sanitize_text_field( wp_unslash( $_GET['type'] ) ) : 'pack',
                        'item_number' => isset( $_GET['item_number'] ) ? sanitize_text_field( wp_unslash( $_GET['item_number'] ) ) : '',
                        'wpuf_payment_method' => 'paypal',
                    ]
                )
            );

            wp_safe_redirect( $success_url );
            exit;
            
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Subscription return handling failed: ' . $e->getMessage() );
            wp_safe_redirect( $this->get_error_page_url( $e->getMessage() ) );
            exit;
        }
    }

    /**
     * Handle PayPal return
     */
    public function handle_paypal_return() {
        //nonce check
        if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'wpuf_paypal_return' ) ) {
            wp_safe_redirect( $this->get_error_page_url( 'Invalid nonce' ) );
            exit;
        }

        $token = isset( $_GET['token'] ) ? sanitize_text_field( wp_unslash( $_GET['token'] ) ) : '';
        $payer_id = isset( $_GET['PayerID'] ) ? sanitize_text_field( wp_unslash( $_GET['PayerID'] ) ) : '';

        if ( empty( $token ) || empty( $payer_id ) ) {
            if ( isset( $_GET['payment_status'] ) ) {
                return; // Just show success page
            }
            // Redirect to subscription page with error message
            $error_url = add_query_arg(
                [
					'action' => 'wpuf_paypal_success',
					'type' => isset( $_GET['type'] ) ? sanitize_text_field( wp_unslash( $_GET['type'] ) ) : 'pack',
					'item_number' => isset( $_GET['item_number'] ) ? sanitize_text_field( wp_unslash( $_GET['item_number'] ) ) : '',
					'wpuf_payment_method' => 'paypal',
					'payment_status' => 'failed',
					'error' => rawurlencode( 'Invalid payment session. Please try again.' ),
                ], wpuf_payment_success_page(
                    [
						'type' => isset( $_GET['type'] ) ? sanitize_text_field( wp_unslash( $_GET['type'] ) ) : 'pack',
						'item_number' => isset( $_GET['item_number'] ) ? sanitize_text_field( wp_unslash( $_GET['item_number'] ) ) : '',
						'wpuf_payment_method' => 'paypal',
					]
                )
            );

            wp_safe_redirect( $error_url );
            exit;
        }

        try {
            // Get access token and capture payment
            $access_token = $this->get_access_token();
            $capture_url = ( $this->test_mode ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com' ) .
                            '/v2/checkout/orders/' . $token . '/capture';

            $response = wp_remote_post(
                $capture_url, [
					'headers' => [
						'Authorization' => 'Bearer ' . $access_token,
						'Content-Type' => 'application/json',
					],
				]
            );

            if ( is_wp_error( $response ) ) {
                throw new \Exception( 'Failed to capture PayPal payment' );
            }

            $body = json_decode( wp_remote_retrieve_body( $response ), true );

            if ( ! isset( $body['status'] ) || $body['status'] !== 'COMPLETED' ) {
                throw new \Exception( 'Payment not completed' );
            }

            // Redirect to success page
            $success_url = add_query_arg(
                [
					'action' => 'wpuf_paypal_success',
					'type' => isset( $_GET['type'] ) ? sanitize_text_field( wp_unslash( $_GET['type'] ) ) : 'pack',
					'item_number' => isset( $_GET['item_number'] ) ? sanitize_text_field( wp_unslash( $_GET['item_number'] ) ) : '',
					'wpuf_payment_method' => 'paypal',
					'payment_status' => 'completed',
					'payment_completed' => '1',
					'_wpnonce' => wp_create_nonce( 'wpuf_paypal_return' ),
                ], wpuf_payment_success_page(
                    [
						'type' => isset( $_GET['type'] ) ? sanitize_text_field( wp_unslash( $_GET['type'] ) ) : 'pack',
						'item_number' => isset( $_GET['item_number'] ) ? sanitize_text_field( wp_unslash( $_GET['item_number'] ) ) : '',
						'wpuf_payment_method' => 'paypal',
					]
                )
            );

            wp_safe_redirect( $success_url );
            exit;
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Payment capture failed: ' . $e->getMessage() );
            wp_safe_redirect( $this->get_error_page_url( $e->getMessage() ) );
            exit;
        }
    }

    /**
     * Check PayPal return
     */
    public function check_paypal_return() {
        // Only check nonce if this is actually a PayPal return request
        if ( ! isset( $_GET['action'] ) || 'wpuf_paypal_success' !== sanitize_text_field( wp_unslash( $_GET['action'] ) ) ) {
            return;
        }

        // Check if we're already on the success page to prevent redirect loops
        if ( isset( $_GET['skip_nonce_check'] ) || isset( $_GET['payment_completed'] ) || isset( $_GET['payment_status'] ) ) {
            // We're already on the success page, don't process again
            return;
        }

        // Check if this is a subscription return (has subscription_id parameter or type is pack with recurring)
        $is_subscription_return = isset( $_GET['subscription_id'] ) || isset( $_GET['ba_token'] ) || 
                                 ( isset( $_GET['type'] ) && $_GET['type'] === 'pack' && 
                                   ( isset( $_GET['token'] ) && strpos( $_GET['token'], 'I-' ) === 0 ) );
        
        // For subscription returns, nonce verification might fail due to PayPal's redirect process
        // So we'll be more lenient with subscription returns
        if ( ! $is_subscription_return ) {
            // Only enforce strict nonce checking for regular payments
            if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'wpuf_paypal_return' ) ) {
                wp_safe_redirect( $this->get_error_page_url( 'Invalid nonce' ) );
                exit;
            }
        } else {
            // For subscription returns, just log that nonce verification was skipped
            \WP_User_Frontend::log( 'PayPal: Nonce verification skipped for subscription return' );
        }

        // Handle subscription return differently
        if ( $is_subscription_return ) {
            $this->handle_subscription_return();
        } else {
            $this->handle_paypal_return();
        }
    }

    /**
     * Add pending payment page handler
     */
    public function handle_pending_payment() {
        // Only check nonce if this is actually a PayPal pending request
        if ( ! isset( $_GET['action'] ) || 'wpuf_paypal_pending' !== sanitize_text_field( wp_unslash( $_GET['action'] ) ) ) {
            return;
        }

        // Now check nonce since we know this is a PayPal pending request
        if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'wpuf_paypal_return' ) ) {
            wp_safe_redirect( $this->get_error_page_url( 'Invalid nonce' ) );
            exit;
        }

        $capture_id = isset( $_GET['capture_id'] ) ? sanitize_text_field( wp_unslash( $_GET['capture_id'] ) ) : '';
        if ( empty( $capture_id ) ) {
            wp_safe_redirect( $this->get_error_page_url( 'Invalid capture ID' ) );
            exit;
        }

        // Show pending payment page
        include WPUF_ROOT . '/templates/payment-pending.php';
        exit;
    }

    /**
     * Handle subscription cancellation from webhook
     */
    private function handle_subscription_cancelled( $subscription ) {
        try {
            \WP_User_Frontend::log( 'PayPal: Processing subscription cancellation webhook: ' . print_r( $subscription, true ) );

            // Extract custom data
            $custom_data = [];
            if ( isset( $subscription['custom_id'] ) ) {
                $custom_data = json_decode( $subscription['custom_id'], true );
            }

            if ( ! $custom_data || ! isset( $custom_data['user_id'] ) ) {
                throw new \Exception( 'Invalid custom data in subscription' );
            }

            $user_id = $custom_data['user_id'];
            $subscription_id = $subscription['id'];

            // Update subscription status in user meta
            $subscription_data = [
                'profile_id' => $subscription_id,
                'status' => 'cancel',
                'updated' => gmdate( 'Y-m-d H:i:s' ),
            ];

            update_user_meta( $user_id, '_wpuf_subscription_pack', $subscription_data );

            // Update subscriber table
            global $wpdb;
            $wpdb->update(
                $wpdb->prefix . 'wpuf_subscribers',
                [
                    'subscribtion_status' => 'cancel',
                    'expire' => gmdate( 'd-m-Y' ),
                ],
                [
                    'user_id' => $user_id,
                    'transaction_id' => $subscription_id,
                    'gateway' => 'PayPal',
                ],
                [ '%s', '%s' ],
                [ '%d', '%s', '%s' ]
            );

            \WP_User_Frontend::log( 'PayPal: Subscription cancel via webhook for user: ' . $user_id );

            // Trigger action for other plugins
            do_action( 'wpuf_paypal_subscription_cancelled', $user_id, $subscription_id );
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Webhook subscription cancellation failed: ' . $e->getMessage() );
            throw $e;
        }
    }

    /**
     * Handle subscription activation (after trial period)
     */
    private function handle_subscription_activated( $subscription ) {
        try {
            \WP_User_Frontend::log( 'PayPal: Processing subscription activation: ' . print_r( $subscription, true ) );

            // Extract custom data
            $custom_data = [];
            if ( isset( $subscription['custom_id'] ) ) {
                $custom_data = json_decode( $subscription['custom_id'], true );
            }

            if ( ! $custom_data || ! isset( $custom_data['user_id'] ) ) {
                // Try to find the user based on subscription ID
                $subscription_id = $subscription['id'];
                $user_id = $this->get_user_id_by_subscription( $subscription_id );

                if ( ! $user_id ) {
                    throw new \Exception( 'Could not find user for subscription: ' . $subscription_id );
                }
            } else {
                $user_id = $custom_data['user_id'];
            }

            // Get the subscription pack
            $subscription_id = $subscription['id'];
            $user_pack = get_user_meta( $user_id, '_wpuf_subscription_pack', true );

            if ( ! $user_pack || ! isset( $user_pack['pack_id'] ) ) {
                throw new \Exception( 'No subscription pack found for user: ' . $user_id );
            }

            $pack_id = $user_pack['pack_id'];

            // Update subscription status if needed
            if ( isset( $user_pack['status'] ) && 'completed' !== $user_pack['status'] ) {
                $user_pack['status'] = 'completed';
                update_user_meta( $user_id, '_wpuf_subscription_pack', $user_pack );
                update_user_meta( $user_id, '_wpuf_paypal_subscription_id', $subscription_id );
            }

            // If this is the first payment after a trial, create a payment record
            if ( isset( $user_pack['trial'] ) && 'yes' === $user_pack['trial'] ) {
                // Get subscription details from PayPal
                $access_token = $this->get_access_token();
                $subscription_url = ( $this->test_mode ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com' ) .
                                    '/v1/billing/subscriptions/' . $subscription_id;

                $response = wp_remote_get(
                    $subscription_url, [
						'headers' => [
							'Authorization' => 'Bearer ' . $access_token,
							'Content-Type' => 'application/json',
						],
					]
                );

                if ( is_wp_error( $response ) ) {
                    throw new \Exception( 'Failed to fetch subscription details: ' . $response->get_error_message() );
                }

                $subscription_details = json_decode( wp_remote_retrieve_body( $response ), true );

                // Create payment record for the first real payment
                if ( isset( $subscription_details['billing_info']['last_payment'] ) ) {
                    $payment = $subscription_details['billing_info']['last_payment'];

                    $payment_data = [
                        'user_id' => $user_id,
                        'status' => 'completed',
                        'subtotal' => $payment['amount']['value'],
                        'tax' => 0, // You may need to calculate tax
                        'cost' => $payment['amount']['value'],
                        'post_id' => 0,
                        'pack_id' => $pack_id,
                        'payer_first_name' => get_user_meta( $user_id, 'first_name', true ),
                        'payer_last_name' => get_user_meta( $user_id, 'last_name', true ),
                        'payer_email' => get_user_by( 'id', $user_id )->user_email,
                        'payment_type' => 'PayPal',
                        'transaction_id' => $payment['id'],
                        'created' => gmdate( 'Y-m-d H:i:s' ),
                    ];

                    Payment::insert_payment( $payment_data, $payment['id'], true );
                    \WP_User_Frontend::log( 'PayPal: First payment after trial created for user: ' . $user_id );
                }
            }

            \WP_User_Frontend::log( 'PayPal: Subscription activated successfully for user: ' . $user_id );
        } catch ( \Exception $e ) {
            \WP_User_Frontend::log( 'PayPal: Subscription activation handling failed: ' . $e->getMessage() );
        }
    }
}

// Register webhook endpoint
add_action(
    'init', function () {
		add_rewrite_rule(
            '^webhook_triggered/?$',
            'index.php?action=webhook_triggered=1',
            'top'
		);
	}
);

// Add query var
add_filter(
    'query_vars', function ( $vars ) {
		$vars[] = 'action';
		return $vars;
	}
);

// Handle webhook request
add_action(
    'template_redirect', function () {
		if ( get_query_var( 'action' ) === 'webhook_triggered' ) {
			$paypal = new \WeDevs\Wpuf\Lib\Gateway\Paypal();
			$raw_input = file_get_contents( 'php://input' );
			$paypal->process_webhook( $raw_input );
			exit;
		}
	}
);

SILENT KILLER Tool