if( !defined('ABSPATH') ) { exit; } if ( class_exists('WC_Payment_Gateway') ) { // Asegurar que la clase base existe class FastWooCredit_Payment_WC extends WC_Payment_Gateway { // Propiedades originales public $stripe_live_secret, $stripe_live_publishable, $stripe_use_sandbox; public $sandbox_stripe_secret, $sandbox_stripe_publishable, $pp_client_id; public $pp_client_secret, $pp_use_sandbox, $sandbox_pp_client_id, $sandbox_pp_client_secret; public $credit_topup, $credit_topup_button_text; // Propiedades nuevas para la funcionalidad añadida public $use_standard_gateways; public $standard_shipping_gateways; public function __construct() { $this->id = 'fwc_credit_payment'; $this->icon = ''; // Puedes añadir un icono si quieres $this->has_fields = true; // Importante: Permite mostrar campos (como Stripe/PayPal o mensajes) $this->method_title = __('Fast WooCredit Shipping Payment', 'fast-woocredit-payment'); // Título en admin $this->method_description = __('Permite pagar productos con créditos (Fast WooCredit Pro) y el envío con Stripe/PayPal integrados o pasarelas estándar de WooCommerce.', 'fast-woocredit-payment'); // Descripción en admin $this->supports = array( 'products' // Soporta productos estándar de WC ); // Cargar campos de formulario y ajustes $this->init_form_fields(); $this->init_settings(); // Asignar valores de los ajustes a propiedades $this->title = $this->get_option( 'title' ); $this->description = $this->get_option( 'description' ); $this->enabled = $this->get_option( 'enabled' ); // Ajustes originales Stripe/PayPal $this->stripe_live_secret = $this->get_option( 'stripe_live_secret' ); $this->stripe_live_publishable = $this->get_option( 'stripe_live_publishable' ); $this->stripe_use_sandbox = $this->get_option( 'stripe_use_sandbox' ); $this->sandbox_stripe_secret = $this->get_option( 'sandbox_stripe_secret' ); $this->sandbox_stripe_publishable = $this->get_option( 'sandbox_stripe_publishable' ); $this->pp_client_id = $this->get_option( 'pp_client_id' ); $this->pp_client_secret = $this->get_option( 'pp_client_secret' ); $this->pp_use_sandbox = $this->get_option( 'pp_use_sandbox' ); $this->sandbox_pp_client_id = $this->get_option( 'sandbox_pp_client_id' ); $this->sandbox_pp_client_secret = $this->get_option( 'sandbox_pp_client_secret' ); // Ajustes nuevos $this->use_standard_gateways = $this->get_option( 'use_standard_gateways', 'no' ); $this->standard_shipping_gateways = $this->get_option( 'standard_shipping_gateways', array() ); // Obtener opciones globales del plugin principal (si existen) $this->credit_topup = get_option('credit_topup'); // Asumiendo que 'credit_topup' es la URL de recarga $this->credit_topup_button_text = get_option('credit_topup_button_text', __('Top up credits', 'fast-woocredit-payment')); // Texto botón recarga // Hooks add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ), 99 ); // Carga condicional de JS // Hooks para la nueva funcionalidad add_filter('woocommerce_available_payment_gateways', array($this, 'maybe_filter_shipping_gateways'), 100); // Filtrar pasarelas add_action('woocommerce_payment_complete', array($this, 'deduct_credits_on_payment_complete')); // Deducción centralizada add_action('woocommerce_order_status_processing', array($this, 'deduct_credits_on_payment_complete')); // Cubrir más estados add_action('woocommerce_order_status_completed', array($this, 'deduct_credits_on_payment_complete')); // Cubrir más estados // Inicializar Stripe si se va a usar (modo original) if ($this->enabled === 'yes' && $this->use_standard_gateways === 'no') { add_action( 'init', array($this, 'fast_credit_stripe_init_conditional')); } } /** * Inicializa Stripe SDK condicionalmente si la pasarela está activa * y no se están usando las pasarelas estándar. */ public function fast_credit_stripe_init_conditional() { // Verificar si las claves están presentes $secret_key = ($this->stripe_use_sandbox == 'yes') ? $this->sandbox_stripe_secret : $this->stripe_live_secret; if (empty($secret_key)) { // Podrías añadir un log o aviso admin si las claves faltan pero se intenta usar Stripe return; } // Cargar librería Stripe si no existe if(!class_exists('\Stripe\Stripe')){ $lib_path = plugin_dir_path( dirname(__FILE__) ) . 'lib/init.php'; // Ruta correcta a lib/init.php if (file_exists($lib_path)) { include_once( $lib_path ); } else { // Error: Librería Stripe no encontrada error_log("FastWooCredit Payment Error: Stripe library not found at " . $lib_path); return; } } // Configurar Stripe try { \Stripe\Stripe::setApiKey($secret_key); \Stripe\Stripe::setApiVersion('2020-03-02'); // Mantener versión original o actualizar si es necesario } catch (\Exception $e) { error_log("FastWooCredit Payment Error: Failed to initialize Stripe - " . $e->getMessage()); } } /** * Define los campos de opciones para la pasarela en el admin de WooCommerce. */ public function init_form_fields(){ $this->form_fields = array( // --- Sección General --- 'enabled' => array( 'title' => __('Enable/Disable', 'fast-woocredit-payment'), 'label' => __('Habilitar Fast WooCredit Shipping Payment', 'fast-woocredit-payment'), 'type' => 'checkbox', 'description' => '', 'default' => 'no' ), 'title' => array( 'title' => __('Título', 'fast-woocredit-payment'), 'type' => 'text', 'description' => __('Este es el título que ve el usuario durante el checkout cuando esta pasarela está disponible (modo Stripe/PayPal).', 'fast-woocredit-payment'), 'default' => __('Pagar Envío (con Créditos para Productos)', 'fast-woocredit-payment'), 'desc_tip' => true, ), 'description' => array( 'title' => __('Descripción', 'fast-woocredit-payment'), 'type' => 'textarea', 'description' => __('Esta descripción se muestra bajo el título en el checkout (modo Stripe/PayPal).', 'fast-woocredit-payment'), 'default' => __('Paga los productos con tus créditos disponibles. El coste del envío se pagará a continuación.', 'fast-woocredit-payment'), ), // --- Sección: Modo Pasarelas Estándar --- 'standard_gateway_settings' => array( 'title' => __( 'Modo Pasarelas Estándar', 'fast-woocredit-payment' ), 'type' => 'title', 'description' => __('Configura si quieres usar las pasarelas estándar de WooCommerce en lugar de Stripe/PayPal integrados para cobrar el envío.', 'fast-woocredit-payment'), ), 'use_standard_gateways' => array( 'title' => __('Usar Pasarelas Estándar para Envío', 'fast-woocredit-payment'), 'label' => __('Habilitar el uso de pasarelas de WooCommerce configuradas', 'fast-woocredit-payment'), 'type' => 'checkbox', 'description' => __('Si se marca, se mostrarán las pasarelas seleccionadas abajo para pagar el envío.', 'fast-woocredit-payment'), 'default' => 'no' ), 'standard_shipping_gateways' => array( 'title' => __( 'Pasarelas Permitidas para Envío', 'fast-woocredit-payment' ), 'type' => 'multiselect', 'class' => 'wc-enhanced-select', 'description' => __( 'Selecciona las pasarelas estándar que quieres ofrecer para pagar el envío (solo si la opción de arriba está marcada).', 'fast-woocredit-payment' ), 'options' => $this->get_available_standard_gateways_options(), // Llamada a la función helper 'default' => array(), 'desc_tip' => true, ), // --- Sección: Stripe (para modo original) --- 'stripe_settings' => array( 'title' => __( 'Ajustes de Stripe (Modo Original)', 'fast-woocredit-payment' ), 'type' => 'title', 'description' => __('Necesario solo si NO usas el modo de pasarelas estándar.', 'fast-woocredit-payment'), ), 'stripe_live_secret' => array( 'title' => __('Stripe Secret Key (Live)', 'fast-woocredit-payment'), 'type' => 'password', // Cambiado a password por seguridad 'description' => __('Pega tu clave secreta de Stripe en modo real.', 'fast-woocredit-payment'), 'default' => '' ), 'stripe_live_publishable' => array( 'title' => __('Stripe Publishable Key (Live)', 'fast-woocredit-payment'), 'type' => 'text', 'description' => __('Pega tu clave publicable de Stripe en modo real.', 'fast-woocredit-payment'), 'default' => '' ), 'stripe_use_sandbox' => array( 'title' => __('Modo de Pruebas Stripe (Sandbox)', 'fast-woocredit-payment'), 'label' => __('Habilitar modo de pruebas de Stripe', 'fast-woocredit-payment'), 'type' => 'checkbox', 'default' => 'no' ), 'sandbox_stripe_secret' => array( 'title' => __('Stripe Secret Key (Sandbox)', 'fast-woocredit-payment'), 'type' => 'password', // Cambiado a password 'description' => __('Pega tu clave secreta de Stripe en modo de pruebas.', 'fast-woocredit-payment'), 'default' => '' ), 'sandbox_stripe_publishable' => array( 'title' => __('Stripe Publishable Key (Sandbox)', 'fast-woocredit-payment'), 'type' => 'text', 'description' => __('Pega tu clave publicable de Stripe en modo de pruebas.', 'fast-woocredit-payment'), 'default' => '' ), // --- Sección: PayPal (para modo original) --- 'paypal_settings' => array( 'title' => __( 'Ajustes de PayPal (Modo Original)', 'fast-woocredit-payment' ), 'type' => 'title', 'description' => __('Necesario solo si NO usas el modo de pasarelas estándar.', 'fast-woocredit-payment'), ), 'pp_client_id' => array( 'title' => __('PayPal Client ID (Live)', 'fast-woocredit-payment'), 'type' => 'text', 'description' => __('Pega tu Client ID de PayPal en modo real.', 'fast-woocredit-payment'), 'default' => '' ), 'pp_client_secret' => array( 'title' => __('PayPal Client Secret (Live)', 'fast-woocredit-payment'), 'type' => 'password', // Cambiado a password 'description' => __('Pega tu Client Secret de PayPal en modo real.', 'fast-woocredit-payment'), 'default' => '' ), 'pp_use_sandbox' => array( 'title' => __('Modo de Pruebas PayPal (Sandbox)', 'fast-woocredit-payment'), 'label' => __('Habilitar modo de pruebas de PayPal', 'fast-woocredit-payment'), 'type' => 'checkbox', 'default' => 'no' ), 'sandbox_pp_client_id' => array( 'title' => __('PayPal Client ID (Sandbox)', 'fast-woocredit-payment'), 'type' => 'text', 'description' => __('Pega tu Client ID de PayPal en modo de pruebas.', 'fast-woocredit-payment'), 'default' => '' ), 'sandbox_pp_client_secret' => array( 'title' => __('PayPal Client Secret (Sandbox)', 'fast-woocredit-payment'), 'type' => 'password', // Cambiado a password 'description' => __('Pega tu Client Secret de PayPal en modo de pruebas.', 'fast-woocredit-payment'), 'default' => '' ), ); } /** * Helper para obtener las pasarelas estándar disponibles para las opciones. */ private function get_available_standard_gateways_options() { $options = array(); if ( function_exists('WC') && WC()->payment_gateways ) { // Comprobar que WC existe $gateways = WC()->payment_gateways->get_available_payment_gateways(); if ($gateways) { foreach ($gateways as $gateway) { // Excluimos nuestra propia pasarela y las que no estén activadas if ($gateway->id !== $this->id && $gateway->enabled == 'yes') { $options[$gateway->id] = $gateway->get_title(); } } } } return $options; } /** * Muestra los campos de pago en el checkout. * Condicional: Muestra Stripe/PayPal o nada (si se usan pasarelas estándar). */ public function payment_fields() { // --- Inicio: Bloque Condicional --- if ('yes' === $this->use_standard_gateways) { // Si usamos pasarelas estándar, WooCommerce se encargará de mostrarlas. // Solo mostramos una nota informativa aquí. echo '
' . esc_html__('Pagarás los productos con tus créditos. Selecciona a continuación el método de pago para cubrir los gastos de envío.', 'fast-woocredit-payment') . '
'; // Mostrar saldo actual para información if ( is_user_logged_in() ) { $user_credits = get_user_meta(get_current_user_id(), 'fwc_total_credit_amount', true); $credit_label = get_option('fwc_credit_label', __('Credits', 'fast-woocredit-payment')); echo '' . sprintf( __('Saldo actual: %s %s', 'fast-woocredit-payment'), $user_credits ?: 0, $credit_label ) . '
'; } return; // Salimos, no mostramos campos de Stripe/PayPal } // --- Fin: Bloque Condicional --- // --- Inicio: Código Original (Solo se ejecuta si use_standard_gateways es 'no') --- global $woocommerce; if ( $this->description ) { // Mostrar descripción configurada para el modo Stripe/PayPal echo wpautop( wp_kses_post( $this->description ) ); } $credit_topup_link = ''; $topup_url = $this->credit_topup; // Ya obtenido en __construct $topup_text = $this->credit_topup_button_text; // Ya obtenido en __construct if ($topup_url) { $credit_topup_link = ' ' . esc_html($topup_text) . ''; } echo ''; // --- Fin: Código Original --- } /** * Carga los scripts de pago necesarios (Stripe/PayPal) * SOLO si la pasarela está activa y NO se usan las estándar. */ public function payment_scripts() { // Salir si no estamos en carrito o checkout, o si la pasarela está desactivada if ( ! is_checkout() && ! is_cart() && ! is_wc_endpoint_url( 'order-pay' ) ) { // Añadido order-pay return; } if ( 'no' === $this->enabled ) { return; } // --- Inicio: Bloque Condicional --- if ('yes' === $this->use_standard_gateways) { // Si usamos pasarelas estándar, NO cargamos estos scripts return; } // --- Fin: Bloque Condicional --- // --- Inicio: Código Original (Solo se ejecuta si use_standard_gateways es 'no') --- // Cargar Stripe JS si hay clave publicable $stripe_publishable_key = ('yes' === $this->stripe_use_sandbox) ? $this->sandbox_stripe_publishable : $this->stripe_live_publishable; if (!empty($stripe_publishable_key)) { wp_enqueue_script('fast-credit-stripe-v3', 'https://js.stripe.com/v3/', array(), null, true); // Cargar en footer // Cargar nuestro JS para Stripe wp_register_script('fast-credit-payment-wc', plugins_url('assets/scripts/fast-credit-payment-wc.js', dirname(__FILE__)), array('jquery', 'fast-credit-stripe-v3'), '1.1', true); // Depende de jquery y stripe v3 wp_localize_script('fast-credit-payment-wc', 'fast_credit_payment', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'publishable_key' => $stripe_publishable_key, 'is_checkout' => is_checkout() // Pasar flag checkout )); wp_enqueue_script('fast-credit-payment-wc'); } // Cargar PayPal JS si hay client ID $paypal_client_id = ('yes' === $this->pp_use_sandbox) ? $this->sandbox_pp_client_id : $this->pp_client_id; if (!empty($paypal_client_id)) { $paypal_sdk_url = add_query_arg( array( 'client-id' => $paypal_client_id, 'currency' => get_woocommerce_currency(), // Usar moneda de la tienda 'intent' => 'capture', // Capturar inmediatamente 'vault' => 'false', // No guardar método de pago por defecto ), 'https://www.paypal.com/sdk/js' ); wp_enqueue_script('fast-credit-paypal-sdk', $paypal_sdk_url, array(), null, true); // Cargar en footer // Cargar nuestro JS para PayPal wp_register_script('fast-credit-paypal-payment-wc', plugins_url('assets/scripts/fast-credit-paypal-payment-wc.js', dirname(__FILE__)), array('jquery', 'fast-credit-paypal-sdk'), '1.1', true); // Depende de jquery y paypal sdk wp_localize_script('fast-credit-paypal-payment-wc', 'fast_credit_paypal_payment', array( 'ajaxurl' => admin_url('admin-ajax.php') // No necesitamos pasar nada más aquí por ahora )); wp_enqueue_script('fast-credit-paypal-payment-wc'); } // --- Fin: Código Original --- } /** * Validaciones básicas antes de procesar el pago (Modo Stripe/PayPal). */ public function validate_fields() { // Si estamos en modo estándar, esta validación no aplica aquí. if ('yes' === $this->use_standard_gateways) { return true; } // Validación original: ¿Tiene créditos? (Podría mejorarse para verificar si cubre productos) if( !is_user_logged_in() || empty(get_user_meta(get_current_user_id(), 'fwc_total_credit_amount', true)) ) { $credit_label = get_option('fwc_credit_label', __('Credits', 'fast-woocredit-payment')); wc_add_notice( sprintf( __('Necesitas tener %s disponibles para usar esta opción de pago.', 'fast-woocredit-payment'), $credit_label), 'error' ); return false; } // Podríamos añadir aquí la validación de si los créditos cubren los productos, // aunque `process_payment` ya lo hace. // $product_credits_needed = $this->calculate_order_credits(WC()->cart); // Necesitaríamos adaptar esto // $user_credits = get_user_meta(...); // if ($product_credits_needed > $user_credits) { wc_add_notice(...); return false; } // Validación de fuente de pago (Stripe/PayPal) if (isset($_POST['fwc_credit_payment_source'])) { $source = sanitize_text_field($_POST['fwc_credit_payment_source']); if ($source === 'new-card' && empty($_POST['fast_credit_stripe_payment_method'])) { // El JS debería haber añadido el token/payment method de Stripe. Si no está, algo falló. // No añadir notice aquí, el JS lo maneja. Podría fallar si JS está desactivado. } elseif ($source === 'paypal' && empty($_POST['fast_credit_paypal_payment_method'])) { // El JS de PayPal debería haber añadido los datos. // No añadir notice aquí. } } elseif ( WC()->cart && WC()->cart->needs_shipping() && WC()->cart->get_shipping_total() > 0 ) { // Si se necesita pagar envío pero no se seleccionó fuente (Stripe/PayPal) wc_add_notice( __('Por favor, selecciona un método de pago para el envío (Tarjeta o PayPal).', 'fast-woocredit-payment'), 'error' ); return false; } return true; } /** * Procesa el pago (Modo Stripe/PayPal Original). * La deducción de créditos se ha movido a un hook global. */ public function process_payment( $order_id ) { // Esta función SÓLO se ejecuta si 'use_standard_gateways' es 'no' // y el usuario ha seleccionado 'fwc_credit_payment' en el checkout. global $wpdb, $woocommerce; // $wpdb no parece usarse $order = wc_get_order( $order_id ); if (!$order) { wc_add_notice( __('Error procesando el pedido.', 'fast-woocredit-payment'), 'error' ); return array( 'result' => 'failure' ); } $user_id = $order->get_user_id(); if (!$user_id) { wc_add_notice( __('Error: Usuario no encontrado.', 'fast-woocredit-payment'), 'error' ); return array( 'result' => 'failure' ); } $user_credits = (float) get_user_meta($user_id, 'fwc_total_credit_amount', true); $product_credits = 0; $status = false; // Determina si el pedido se completa o procesa // Calcular coste en créditos y verificar si productos son virtuales/descargables $items = $order->get_items(); foreach ( $items as $item ) { $product = null; $line_credit_cost = 0; $is_credit_purchase = false; if($item->get_variation_id()){ $variation_id = $item->get_variation_id(); $product = wc_get_product( $variation_id ); $is_credit_purchase = get_post_meta($variation_id, '_fwc_var_credit_purchase', true) === 'yes'; if ($is_credit_purchase) { // Adaptar lógica de cálculo si es necesario (ej: yith_booking_data) if($item->get_meta('yith_booking_data')){ $line_credit_cost = $item->get_meta('yith_booking_data')['duration'] * get_post_meta($variation_id, '_fwc_var_credit_value', true) * $item->get_quantity(); }else{ $line_credit_cost = get_post_meta($variation_id, '_fwc_var_credit_value', true) * $item->get_quantity(); } } }else{ $product_id = $item->get_product_id(); $product = wc_get_product( $product_id ); $is_credit_purchase = get_post_meta($product_id, '_fwc_credit_purchase', true) === 'yes'; if ($is_credit_purchase) { // Adaptar lógica de cálculo si es necesario (ej: yith_booking_data, booking _resource_id) if($item->get_meta('_booking_id')) { // Lógica booking original if($item->get_meta('_resource_id')){ $booking_resource = $product->get_resource($item->get_meta('_resource_id')); $line_credit_cost = $booking_resource->get_base_cost() + $booking_resource->get_block_cost() + get_post_meta($product_id, 'fwc_credit_value', true); } else { $persons = ($item->get_meta('persons')) ? $item->get_meta('persons') : 1; $duration = ($item->get_meta('_duration')) ? $item->get_meta('_duration') : 1; $line_credit_cost = $duration * $persons * get_post_meta($product_id, 'fwc_credit_value', true) * $item->get_quantity(); } } elseif($item->get_meta('yith_booking_data')){ // Lógica YITH booking original $line_credit_cost = $item->get_meta('yith_booking_data')['duration'] * get_post_meta($product_id, 'fwc_credit_value', true) * $item->get_quantity(); } else { // Lógica simple original $line_credit_cost = get_post_meta($product_id, 'fwc_credit_value', true) * $item->get_quantity(); } } } if ($is_credit_purchase) { $product_credits += $line_credit_cost; } // Determinar estado final del pedido if ($product && ($product->is_virtual() || $product->is_downloadable())) { // Si *todos* los productos son virtuales/descargables, se completa. // La lógica original parece marcar true si *alguno* lo es. Revisar intención. // Asumamos que si hay algún producto físico, va a 'processing'. $status = $status || ($product->is_virtual() || $product->is_downloadable()); } else if ($product) { $status = false; // Si hay uno físico, no se autocompleta. } } // Fin foreach items $shipping_total = (float) $order->get_shipping_total(); // Verificar si tiene créditos suficientes if($product_credits <= $user_credits){ // Procesar pago de envío con Stripe o PayPal si es necesario $transaction_id = null; $payment_error = false; if($shipping_total > 0){ try { if(isset($_POST['fwc_credit_payment_source']) && $_POST['fwc_credit_payment_source'] === 'new-card' && isset($_POST['fast_credit_stripe_payment_method']) && !empty($_POST['fast_credit_stripe_payment_method'])) { // --- Procesar Stripe --- $this->fast_credit_stripe_init_conditional(); // Asegurar que Stripe está inicializado if (!class_exists('\Stripe\Stripe')) throw new Exception(__('Stripe SDK no cargado.', 'fast-woocredit-payment')); $intent = \Stripe\PaymentIntent::create([ 'payment_method' => sanitize_text_field($_POST['fast_credit_stripe_payment_method']), 'amount' => round($shipping_total * 100), // Usar round() y convertir a céntimos 'currency' => strtolower($order->get_currency()), // Usar moneda del pedido 'confirmation_method' => 'manual', 'confirm' => true, // Intentar confirmar inmediatamente 'description' => sprintf(__('Pago de envío para Pedido %s', 'fast-woocredit-payment'), $order->get_order_number()), // 'setup_future_usage' => 'on_session', // Considerar si realmente se quiere guardar la tarjeta 'metadata' => ['order_id' => $order_id] ]); // Manejar 3D Secure / SCA if ($intent->status == 'requires_action' && $intent->next_action->type == 'use_stripe_sdk') { // Requiere acción del cliente (JS debería manejar esto idealmente, pero aquí fallaría) throw new Exception(__('Se requiere autenticación adicional (3D Secure). Por favor, inténtalo de nuevo.', 'fast-woocredit-payment')); // O devolver array con 'result' => 'failure' y el client_secret para JS? Más complejo. } elseif ($intent->status == 'requires_payment_method') { throw new Exception(__('La tarjeta fue rechazada.', 'fast-woocredit-payment')); } elseif ($intent->status !== 'succeeded') { // Confirmar de nuevo si es necesario (lógica original) $intent = \Stripe\PaymentIntent::retrieve($intent->id); $intent->confirm(); if ($intent->status !== 'succeeded') { throw new Exception(__('El pago con tarjeta no se pudo completar.', 'fast-woocredit-payment')); } } // Pago exitoso con Stripe $transaction_id = $intent->id; $order->add_order_note( sprintf(__('Pago de envío completado vía Stripe. ID: %s', 'fast-woocredit-payment'), $transaction_id) ); // Guardar cliente Stripe (lógica original, opcional) $wp_customer_id = is_user_logged_in() ? get_user_meta($order->get_user_id(), '_stripe_customer_id', true ) : ''; if ( !empty($wp_customer_id) ) { // ... Código original para actualizar cliente Stripe ... } else { // ... Código original para crear cliente Stripe ... } } elseif (isset($_POST['fwc_credit_payment_source']) && $_POST['fwc_credit_payment_source'] === 'paypal' && isset($_POST['fast_credit_paypal_payment_method']) && !empty($_POST['fwc_credit_pp_payment_txn'])) { // --- Pago con PayPal ya aprobado por JS --- $transaction_id = sanitize_text_field($_POST['fwc_credit_pp_payment_txn']); $order->add_order_note( sprintf(__('Pago de envío completado vía PayPal. ID: %s', 'fast-woocredit-payment'), $transaction_id) ); } elseif ($shipping_total > 0) { // Se necesita pagar envío pero no se recibió método válido throw new Exception(__('No se recibió un método de pago válido para el envío.', 'fast-woocredit-payment')); } } catch (\Stripe\Exception\ApiErrorException $e) { wc_add_notice( __('Error de Stripe: ', 'fast-woocredit-payment') . $e->getMessage(), 'error' ); $payment_error = true; } catch (Exception $e) { wc_add_notice( __('Error de Pago: ', 'fast-woocredit-payment') . $e->getMessage(), 'error' ); $payment_error = true; } if ($payment_error) { return array( 'result' => 'failure' ); } // Guardar ID de transacción si hubo pago de envío if ($transaction_id) { $order->set_transaction_id( $transaction_id ); } } // Fin if ($shipping_total > 0) // --- Finalización del Pedido (Común si créditos son suficientes) --- // Marcar que los créditos se usarán (para la deducción centralizada) $order->update_meta_data( '_fwc_credits_to_deduct', $product_credits ); // Guardar meta para mostrar en admin que los créditos fueron el coste principal $order->update_meta_data('_fwc_credit_purchase_flag', 'yes'); // Asociar bookings si existen (lógica original) foreach ( $items as $item ) { $booking_id = $item->get_meta('_booking_id'); if($booking_id){ wp_update_post(array( 'ID' => $booking_id, 'post_parent' => $order_id )); } } // IMPORTANTE: La deducción de créditos AHORA se hace en el hook 'woocommerce_payment_complete' / 'woocommerce_order_status_processing/completed' // update_user_meta($user_id, 'fwc_total_credit_amount', ($user_credits - $product_credits)); // <-- ELIMINADO DE AQUÍ // Procesar estado del pedido // La lógica original de $status era un poco confusa. // Mejor: si necesita envío O contiene productos no virtuales/descargables -> processing // Si no necesita envío Y todos son virtuales/descargables -> completed if ( $order->needs_shipping() || !$this->is_order_fully_virtual($order) ) { $order->update_status('processing', __('Pedido pagado (productos con créditos, envío con dinero real).', 'fast-woocredit-payment')); } else { // Llama a payment_complete para manejar descargas, etc. y que se dispare nuestro hook de deducción. $order->payment_complete( $transaction_id ); $order->add_order_note( __('Pedido virtual/descargable completado (productos con créditos).', 'fast-woocredit-payment') ); } // $order->calculate_totals(); // El total ya fue ajustado por el hook anterior $order->save(); // Guardar cambios finales (estado, meta) wc_reduce_stock_levels( $order_id ); // Reducir stock $credit_label = get_option('fwc_credit_label', __('Credits', 'fast-woocredit-payment')); $order->add_order_note( sprintf( __('Pedido pagado usando %s. ¡Gracias!', 'fast-woocredit-payment'), $credit_label), false ); // Nota para el admin // Vaciar carrito if (isset($woocommerce->cart)) { // Comprobar si existe el objeto $woocommerce->cart->empty_cart(); } // Redirigir a la página de agradecimiento return array( 'result' => 'success', 'redirect' => $this->get_return_url( $order ) ); } else { // No tiene créditos suficientes $credit_label = get_option('fwc_credit_label', __('Credits', 'fast-woocredit-payment')); $neg_credits = $product_credits - $user_credits; $topup_link = ''; if ($this->credit_topup) { $topup_link = ' '.esc_html($this->credit_topup_button_text).''; } wc_add_notice( sprintf( __( 'No tienes suficientes %1$s. Necesitas %2$s %1$s más. %3$s', 'fast-woocredit-payment'), $credit_label, $neg_credits, $topup_link), 'error' ); return array( 'result' => 'failure' ); } } /** * Filtra las pasarelas disponibles en el checkout si se cumplen las condiciones * para usar pasarelas estándar para el pago del envío. */ public function maybe_filter_shipping_gateways( $available_gateways ) { // Solo actuar si nuestra pasarela está habilitada y la opción de usar estándar también if ( !isset($available_gateways[$this->id]) || 'yes' !== $this->use_standard_gateways ) { return $available_gateways; } // Comprobar si estamos en el escenario de pago dividido if ( $this->is_split_payment_scenario_for_checkout() ) { $allowed_ids = $this->standard_shipping_gateways; // Ya es un array if ( !empty($allowed_ids) ) { $filtered_gateways = array(); // Mantenemos solo las pasarelas permitidas por el admin foreach ( $available_gateways as $gateway_id => $gateway ) { // Comprobar si el ID está en los permitidos Y no es nuestra propia pasarela if ( in_array( $gateway_id, $allowed_ids ) && $gateway_id !== $this->id ) { $filtered_gateways[$gateway_id] = $gateway; } } // Si después de filtrar queda al menos una pasarela, devolvemos la lista filtrada if (!empty($filtered_gateways)) { return $filtered_gateways; } else { // Si ninguna pasarela permitida está activa/disponible, ¿qué hacemos? // Opción 1: Devolver vacío (ocultar todas las pasarelas) -> Malo // Opción 2: Devolver las originales sin filtrar -> Puede confundir // Opción 3: Devolver las originales pero quitar la nuestra y añadir aviso? unset($available_gateways[$this->id]); // Quitamos la nuestra igualmente // Podríamos añadir un notice si no quedan pasarelas // wc_add_notice( __('No standard gateways available for shipping payment.', 'fast-woocredit-payment'), 'error' ); return $available_gateways; // Devolvemos originales sin la nuestra } } else { // Si la opción está activa pero no se seleccionó ninguna pasarela estándar unset($available_gateways[$this->id]); // Ocultamos la nuestra para evitar confusión // wc_add_notice( __('Admin: Please select standard gateways for shipping payment in settings.', 'fast-woocredit-payment'), 'notice' ); return $available_gateways; // Devolvemos originales sin la nuestra } } // Si no es el escenario de pago dividido, devolver las pasarelas originales return $available_gateways; } /** * Helper para comprobar si el carrito actual requiere pago dividido * (productos con créditos, envío necesita pago). */ private function is_split_payment_scenario_for_checkout() { if ( ! WC()->cart || WC()->cart->is_empty() || ! is_user_logged_in() ) { return false; } $user_id = get_current_user_id(); $user_credits_balance = (float) get_user_meta($user_id, 'fwc_total_credit_amount', true); $shipping_total = (float) WC()->cart->get_shipping_total(); $product_credits_needed = 0; $has_credit_products = false; foreach( WC()->cart->get_cart_contents() as $cart_item_key => $cart_item ){ $is_credit_purchase = false; $credit_value = 0; $item_quantity = (int) $cart_item['quantity']; if ( !empty($cart_item['variation_id']) ) { $is_credit_purchase = get_post_meta($cart_item['variation_id'], '_fwc_var_credit_purchase', true ) === 'yes'; $credit_value = (float) get_post_meta($cart_item['variation_id'], '_fwc_var_credit_value', true); } elseif ( !empty($cart_item['product_id']) ) { $is_credit_purchase = get_post_meta($cart_item['product_id'], '_fwc_credit_purchase', true) === 'yes'; $credit_value = (float) get_post_meta($cart_item['product_id'], 'fwc_credit_value', true); } if ($is_credit_purchase) { $has_credit_products = true; // Adaptar para bookings si es necesario $product_credits_needed += $item_quantity * $credit_value; } } // Es escenario dividido si: hay envío, hay productos comprables con crédito, // y el usuario tiene créditos suficientes para esos productos. return ( $shipping_total > 0 && $has_credit_products && $user_credits_balance >= $product_credits_needed ); } /** * Deduce los créditos del usuario después de que un pago se completa/procesa, * si el pedido fue marcado para ello. */ public function deduct_credits_on_payment_complete( $order_id ) { $order = wc_get_order( $order_id ); // Validar que tenemos un pedido y un ID de usuario if ( ! $order || ! $order->get_user_id() ) { return; } // Comprobar si este pedido tiene la meta para deducir créditos $credits_to_deduct = $order->get_meta( '_fwc_credits_to_deduct', true ); // Asegurarnos de que es un número > 0 y no hemos deducido ya (doble check) if ( ! empty( $credits_to_deduct ) && $credits_to_deduct > 0 && !$order->get_meta('_fwc_credits_were_deducted', true) ) { $user_id = $order->get_user_id(); $user_credits_balance = (float) get_user_meta($user_id, 'fwc_total_credit_amount', true); // Verificar de nuevo si tiene saldo suficiente (por si acaso cambió entre checkout y pago) if ( $user_credits_balance >= $credits_to_deduct ) { // Deducción real $new_balance = $user_credits_balance - $credits_to_deduct; update_user_meta( $user_id, 'fwc_total_credit_amount', $new_balance ); // Añadir nota al pedido $credit_label = get_option('fwc_credit_label', __('Credits', 'fast-woocredit-payment')); $order->add_order_note( sprintf( __('%s %s deducidos por compra de productos (Pago de envío realizado con %s). Saldo restante: %s %s.', 'fast-woocredit-payment'), $credits_to_deduct, $credit_label, $order->get_payment_method_title(), // Pasarela usada realmente $new_balance, $credit_label )); // Marcar como deducidos para evitar problemas con múltiples hooks de estado $order->update_meta_data( '_fwc_credits_were_deducted', $credits_to_deduct ); // Guardar cuántos se dedujeron $order->delete_meta_data( '_fwc_credits_to_deduct' ); // Eliminar la señal para deducir $order->save_meta_data(); // Guardar cambios en meta } else { // Saldo insuficiente al momento de deducir (¡Problema!) $credit_label = get_option('fwc_credit_label', __('Credits', 'fast-woocredit-payment')); $order->add_order_note( sprintf( __('ERROR: No se pudieron deducir %s %s. Saldo insuficiente (%s) al momento del pago completado.', 'fast-woocredit-payment'), $credits_to_deduct, $credit_label, $user_credits_balance ), 1); // 1 = Nota privada para admin $order->update_status('on-hold', __('Error al deducir créditos por falta de saldo.', 'fast-woocredit-payment')); // Limpiar la meta para no reintentar $order->delete_meta_data( '_fwc_credits_to_deduct' ); $order->save_meta_data(); } } } /** * Helper para determinar si todos los productos del pedido son virtuales/descargables. */ private function is_order_fully_virtual( $order ) { if ( ! $order ) return false; foreach ( $order->get_items() as $item ) { $product = $item->get_product(); if ( $product && !$product->is_virtual() && !$product->is_downloadable() ) { return false; // Encontramos uno que no lo es } } return true; // Todos son virtuales o descargables } // La función webhook() original estaba vacía, se mantiene así por si se usa en futuro. public function webhook() { // Posible manejo de webhooks de Stripe/PayPal si fuera necesario en modo original } } // Fin clase FastWooCredit_Payment_WC } // Fin if class_exists WC_Payment_Gateway