Parece que nada foi encontrado neste local.
cookie_name ] ) ) { $this->session_id = sanitize_key( $_COOKIE[ $this->cookie_name ] ); } else { $this->session_id = md5( uniqid( 'ytiw_ds_', true ) . rand() ); // Proteção contra "Headers Already Sent". // Só tentamos definir o cookie se o output ainda não começou. if ( ! headers_sent() ) { setcookie( $this->cookie_name, $this->session_id, time() + ( 2 * DAY_IN_SECONDS ), COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true ); $_COOKIE[ $this->cookie_name ] = $this->session_id; } } // Carrega os dados do carrinho do banco de dados (Transient) $this->load_cart(); } /** * Carrega os dados do carrinho do transient. */ private function load_cart() { if ( ! empty( $this->session_id ) ) { $saved_cart = get_transient( 'ytiw_ds_cart_' . $this->session_id ); if ( $saved_cart !== false && is_array( $saved_cart ) ) { $this->cart_data = $saved_cart; } else { $this->cart_data = array( 'items' => array(), 'total' => 0.0, 'subtotal' => 0.0, 'discount_total' => 0.0, 'coupon' => '', ); } } } /** * Salva os dados do carrinho no transient. */ private function save_cart() { if ( ! empty( $this->session_id ) && is_array( $this->cart_data ) ) { // Salva por 7 dias, renovando a cada alteração set_transient( 'ytiw_ds_cart_' . $this->session_id, $this->cart_data, 7 * DAY_IN_SECONDS ); } } /** * Adiciona um produto ao carrinho. * Atualizado para processar Variações (Smart Product) e calcular SKUs. * * @param int $product_id O ID do produto a ser adicionado. * @param int $quantity A quantidade do produto (padrão 1). * @param array $variation_args Array contendo 'license_type' e 'period'. * @param string $renewal_key (Opcional) A chave de licença para renovação. * @return bool True se o produto foi adicionado, False caso contrário. */ public function add_to_cart( $product_id, $quantity = 1, $variation_args = array(), $renewal_key = '' ) { // Garante que a sessão está iniciada if ( empty( $this->session_id ) ) { $this->init_session(); } $product_id = absint( $product_id ); $quantity = absint( $quantity ); $renewal_key = sanitize_text_field( $renewal_key ); // 1. Validação do Produto Base (Correção de Compliance: Aceita WooCommerce e Digital Store) $product = get_post( $product_id ); if ( ! $product || ! in_array( $product->post_type, array( 'product', 'ytiw_digital_product' ), true ) || $quantity < 1 ) { return false; } // 2. Validação da Variação e Preço (Via Factory) // license_type e period são obrigatórios na nova arquitetura $license_type = isset( $variation_args['license_type'] ) ? sanitize_text_field( $variation_args['license_type'] ) : ''; $period = isset( $variation_args['period'] ) ? sanitize_text_field( $variation_args['period'] ) : ''; if ( empty( $license_type ) || empty( $period ) ) { return false; } // Obtém os dados completos (SKU, Preço, Label) // Isso já utiliza a estrutura plana corrigida da Factory $variation_data = YTIW_DS_Product_Factory::get_variation_data( $product_id, $license_type, $period ); if ( is_wp_error( $variation_data ) ) { return false; } // Define a Chave Única do Item no Carrinho (SKU) // Isso permite ter o mesmo produto com licenças diferentes no carrinho $item_key = isset( $variation_data['sku'] ) ? $variation_data['sku'] : $product_id . '-' . $license_type . '-' . $period; // 3. Adiciona ou atualiza o item if ( isset( $this->cart_data['items'][ $item_key ] ) ) { // Se já existe essa variação exata, incrementa a quantidade if ( isset( $this->cart_data['items'][ $item_key ]['quantity'] ) ) { $this->cart_data['items'][ $item_key ]['quantity'] += $quantity; } else { $this->cart_data['items'][ $item_key ]['quantity'] = $quantity; } // Atualiza chave de renovação se fornecida if ( ! empty( $renewal_key ) ) { $this->cart_data['items'][ $item_key ]['renewal_key'] = $renewal_key; } } else { // Novo item no carrinho $this->cart_data['items'][ $item_key ] = array( 'product_id' => $product_id, 'item_key' => $item_key, // SKU 'price' => (float) $variation_data['price'], 'quantity' => $quantity, 'name' => $variation_data['name'], // Nome formatado (ex: Plugin - Personal Mensal) // Dados Padrão (Arquitetura Nova) 'license_type' => $license_type, 'period' => $period, // CORREÇÃO CRÍTICA v3.3.9: Dados de Compatibilidade (Arquitetura Legada/Checkout) // Isso garante que o Checkout Handler encontre 'scope' e 'cycle' 'scope' => $license_type, 'cycle' => $period, 'renewal_key' => $renewal_key, ); } $this->calculate_totals(); $this->save_cart(); // Persiste no DB return true; } /** * Atualiza a quantidade de um item no carrinho. * * @param string $item_key O SKU/Key do item a ser atualizado. * @param int $quantity A nova quantidade. * @return bool */ public function update_cart_item_quantity( $item_key, $quantity ) { if ( empty( $this->session_id ) ) { $this->init_session(); } $item_key = sanitize_text_field( $item_key ); $quantity = absint( $quantity ); if ( ! isset( $this->cart_data['items'][ $item_key ] ) ) { return false; } if ( $quantity <= 0 ) { // Se a quantidade for 0 ou menor, remove o item $this->remove_from_cart( $item_key ); } else { $this->cart_data['items'][ $item_key ]['quantity'] = $quantity; $this->calculate_totals(); $this->save_cart(); } return true; } /** * Remove um item do carrinho. * * @param string $item_key O SKU/Key do item a ser removido. */ public function remove_from_cart( $item_key ) { if ( empty( $this->session_id ) ) { $this->init_session(); } $item_key = sanitize_text_field( $item_key ); if ( isset( $this->cart_data['items'][ $item_key ] ) ) { unset( $this->cart_data['items'][ $item_key ] ); $this->calculate_totals(); $this->save_cart(); } } /** * Aplica um cupom ao carrinho. * Lógica Ativa e Validação v3.9.6 * @param string $coupon_code O código do cupom. * @return array array('success' => bool, 'message' => string) */ public function apply_coupon( $coupon_code ) { if ( empty( $this->session_id ) ) { $this->init_session(); } $code = sanitize_text_field( $coupon_code ); if ( empty( $code ) ) { return array('success' => false, 'message' => __('Por favor, insira um código de cupom.', 'ytiw-digital-store')); } // Validação Mestre: Verifica se o YTI&W Affiliate Nexus está ativo para resolver o cupom $is_valid = false; $discount_type = ''; $discount_value = 0; // Simula verificação. Se o Nexus estiver instalado e ativo, buscaremos os dados do afiliado dono do cupom. // A regra do Nexus (Lido no Prompt Mestre): O cupom DEVE bater com o 'ytiw_nexus_coupon_code' do usuário ou ser igual ao 'user_login'. $users_by_meta = get_users( array( 'meta_key' => 'ytiw_nexus_coupon_code', 'meta_value' => $code, 'number' => 1, 'fields' => 'ID' ) ); $affiliate_id = 0; if ( ! empty( $users_by_meta ) ) { $affiliate_id = $users_by_meta[0]; } else { $user_by_login = get_user_by( 'login', $code ); if ( $user_by_login ) { $affiliate_id = $user_by_login->ID; } } if ( $affiliate_id > 0 ) { $is_valid = true; // Para integração Nexus V1, o desconto oferecido ao cliente pelo cupom do afiliado // é definido globalmente nas configurações do Nexus, ou se não houver, aplicamos 10% padrão de incentivo. $global_discount = get_option( 'ytiw_nexus_coupon_discount', 10 ); $discount_type = 'percent'; $discount_value = (float) $global_discount; } if ( $is_valid ) { $this->cart_data['coupon'] = array( 'code' => $code, 'type' => $discount_type, 'value' => $discount_value ); $this->calculate_totals(); $this->save_cart(); return array('success' => true, 'message' => __('Cupom aplicado com sucesso!', 'ytiw-digital-store')); } return array('success' => false, 'message' => __('Cupom inválido ou expirado.', 'ytiw-digital-store')); } /** * Remove o cupom atual. */ public function remove_coupon() { if ( empty( $this->session_id ) ) { $this->init_session(); } $this->cart_data['coupon'] = ''; $this->calculate_totals(); $this->save_cart(); return array('success' => true, 'message' => __('Cupom removido.', 'ytiw-digital-store')); } /** * Retorna os dados do cupom aplicado na sessão. * @return string|array Vazio ou array com os dados do cupom */ public function get_applied_coupon() { if ( empty( $this->session_id ) ) { $this->init_session(); } // Retorna a array complexa se existir, se for legado (string), converte para array vazio if ( isset( $this->cart_data['coupon'] ) && is_array( $this->cart_data['coupon'] ) ) { return $this->cart_data['coupon']; } return ''; } /** * Retorna o desconto total aplicado. * @return float */ public function get_discount_total() { if ( empty( $this->session_id ) ) { $this->init_session(); } return isset( $this->cart_data['discount_total'] ) ? (float) $this->cart_data['discount_total'] : 0.0; } /** * Retorna o subtotal (antes dos descontos). * @return float */ public function get_subtotal() { if ( empty( $this->session_id ) ) { $this->init_session(); } return isset( $this->cart_data['subtotal'] ) ? (float) $this->cart_data['subtotal'] : 0.0; } /** * Retorna o conteúdo completo do carrinho. * * @return array */ public function get_cart_contents() { if ( empty( $this->session_id ) ) { $this->init_session(); } // Verificação de sanidade antes de retornar if ( ! isset( $this->cart_data['items'] ) || ! is_array( $this->cart_data['items'] ) ) { return array(); } return $this->cart_data['items']; } /** * Retorna o valor total do carrinho. * * @return float */ public function get_cart_total() { if ( empty( $this->session_id ) ) { $this->init_session(); } return isset( $this->cart_data['total'] ) ? $this->cart_data['total'] : 0.0; } /** * Limpa o carrinho completamente. * Usado após a finalização da compra ou manualmente. */ public function empty_cart() { if ( ! empty( $this->session_id ) ) { // Remove o transient do banco delete_transient( 'ytiw_ds_cart_' . $this->session_id ); // Reseta a variável local $this->cart_data = array( 'items' => array(), 'total' => 0.0, 'subtotal' => 0.0, 'discount_total' => 0.0, 'coupon' => '', ); } } /** * (Re)calcula o valor total do carrinho. * Deve ser chamado sempre que o carrinho é modificado. * Implementa AUTO-CURA para remover itens inválidos e MOTOR DE CUPONS v3.9.6. */ private function calculate_totals() { $subtotal = 0.0; $cleaned_items = array(); if ( ! empty( $this->cart_data['items'] ) && is_array( $this->cart_data['items'] ) ) { foreach ( $this->cart_data['items'] as $item_key => $item ) { // AUTO-CURA: Verifica integridade da estrutura // Deve ser array e ter price e quantity numéricos if ( is_array( $item ) && isset( $item['price'], $item['quantity'] ) && is_numeric( $item['price'] ) && is_numeric( $item['quantity'] ) ) { $subtotal += (float) $item['price'] * (int) $item['quantity']; $cleaned_items[ $item_key ] = $item; } else { // Item corrompido ou legado: não incluído no $cleaned_items (Remoção Silenciosa) // Útil para limpar sessões com estrutura antiga } } } // Lógica de Desconto (Motor de Cupons v3.9.6) $discount_total = 0.0; $total = $subtotal; $coupon = $this->get_applied_coupon(); if ( is_array( $coupon ) && ! empty( $coupon['code'] ) && $subtotal > 0 ) { if ( 'percent' === $coupon['type'] ) { $discount_total = ( $subtotal * (float) $coupon['value'] ) / 100; } elseif ( 'fixed' === $coupon['type'] ) { $discount_total = (float) $coupon['value']; } // Garante que o desconto não seja maior que o subtotal if ( $discount_total > $subtotal ) { $discount_total = $subtotal; } $total = $subtotal - $discount_total; } // Arredondamento Bancário Seguro $total = round( $total, 2 ); $subtotal = round( $subtotal, 2 ); $discount_total = round( $discount_total, 2 ); // Atualiza a memória $this->cart_data['items'] = $cleaned_items; $this->cart_data['subtotal'] = (float) $subtotal; $this->cart_data['discount_total'] = (float) $discount_total; $this->cart_data['total'] = (float) $total; // Limpa o cupom se o carrinho esvaziar if ( empty( $cleaned_items ) ) { $this->cart_data['coupon'] = ''; } }}/** * Função para retornar a instância principal do carrinho. * @return YTIW_DS_Cart */function YTIW_DS_Cart() { return YTIW_DS_Cart::instance();} __( 'Sessão expirada. Recarregue a página.', 'ytiw-digital-store' ) ) ); } $product_id = isset( $_POST['product_id'] ) ? absint( $_POST['product_id'] ) : 0; $quantity = isset( $_POST['quantity'] ) ? absint( $_POST['quantity'] ) : 1; // 2. Captura Robusta de Variações (Scope/Cycle) // O JS envia 'scope'/'cycle' e também 'license_type'/'period' para redundância. $scope = ''; if ( ! empty( $_POST['scope'] ) ) { $scope = sanitize_text_field( $_POST['scope'] ); } elseif ( ! empty( $_POST['license_type'] ) ) { $scope = sanitize_text_field( $_POST['license_type'] ); } $cycle = ''; if ( ! empty( $_POST['cycle'] ) ) { $cycle = sanitize_text_field( $_POST['cycle'] ); } elseif ( ! empty( $_POST['period'] ) ) { $cycle = sanitize_text_field( $_POST['period'] ); } // 3. Preparação dos Argumentos para o Carrinho $variation_args = array( 'license_type' => $scope, // Ex: 'p', 'b', 'a' 'period' => $cycle // Ex: 'm', 's', 'a' ); // Carrega instância do carrinho if ( ! class_exists( 'YTIW_DS_Cart' ) ) { include_once YTIW_DS_PLUGIN_DIR . 'includes/class-ytiw-ds-cart.php'; } $cart = YTIW_DS_Cart::instance(); // 4. Adição ao Carrinho $added = $cart->add_to_cart( $product_id, $quantity, $variation_args ); if ( $added ) { // Sucesso: Retorna totais atualizados e URL de redirecionamento (se configurada) $settings = get_option( 'ytiw_ds_general_settings' ); $redirect = ''; if ( isset( $settings['redirect_after_add'] ) && 'yes' === $settings['redirect_after_add'] ) { $cart_page_id = isset( $settings['cart_page_id'] ) ? $settings['cart_page_id'] : 0; $redirect = $cart_page_id ? get_permalink( $cart_page_id ) : ''; } wp_send_json_success( array( 'message' => __( 'Produto adicionado!', 'ytiw-digital-store' ), 'cart_total' => 'R$ ' . number_format( $cart->get_cart_total(), 2, ',', '.' ), 'item_count' => count( $cart->get_cart_contents() ), 'redirect' => $redirect ) ); } else { wp_send_json_error( array( 'message' => __( 'Não foi possível adicionar este produto. Verifique as opções selecionadas.', 'ytiw-digital-store' ) ) ); } } /** * AJAX: Atualizar quantidade de um item. */ public function update_item_quantity() { // Verifica Nonce check_ajax_referer( 'ytiw_ds_cart_nonce', 'security' ); $item_key = isset( $_POST['item_key'] ) ? sanitize_text_field( $_POST['item_key'] ) : ''; $quantity = isset( $_POST['quantity'] ) ? absint( $_POST['quantity'] ) : 1; if ( empty( $item_key ) ) { wp_send_json_error( array( 'message' => __( 'Item inválido.', 'ytiw-digital-store' ) ) ); } if ( ! class_exists( 'YTIW_DS_Cart' ) ) { include_once YTIW_DS_PLUGIN_DIR . 'includes/class-ytiw-ds-cart.php'; } $cart = YTIW_DS_Cart::instance(); $item_subtotal = ''; // Variável para armazenar o novo subtotal da linha // Se quantidade for 0 ou menor, remove o item if ( $quantity <= 0 ) { $cart->remove_from_cart( $item_key ); $message = __( 'Item removido.', 'ytiw-digital-store' ); } else { $updated = $cart->update_cart_item_quantity( $item_key, $quantity ); if ( ! $updated ) { wp_send_json_error( array( 'message' => __( 'Erro ao atualizar item.', 'ytiw-digital-store' ) ) ); } $message = __( 'Carrinho atualizado.', 'ytiw-digital-store' ); // Calcula o novo subtotal da linha para atualização visual $items = $cart->get_cart_contents(); if ( isset( $items[ $item_key ] ) ) { $price = isset( $items[ $item_key ]['price'] ) ? (float) $items[ $item_key ]['price'] : 0; $qty = isset( $items[ $item_key ]['quantity'] ) ? (int) $items[ $item_key ]['quantity'] : 1; $item_subtotal = 'R$ ' . number_format( $price * $qty, 2, ',', '.' ); } } // Retorna novos totais e o subtotal da linha wp_send_json_success( array( 'message' => $message, 'cart_total' => 'R$ ' . number_format( $cart->get_cart_total(), 2, ',', '.' ), 'total_html' => 'R$ ' . number_format( $cart->get_cart_total(), 2, ',', '.' ), 'item_count' => count( $cart->get_cart_contents() ), 'item_subtotal' => $item_subtotal // Dado essencial para correção visual ) ); } /** * AJAX: Remover item do carrinho. */ public function remove_item() { check_ajax_referer( 'ytiw_ds_cart_nonce', 'security' ); $item_key = isset( $_POST['item_key'] ) ? sanitize_text_field( $_POST['item_key'] ) : ''; if ( empty( $item_key ) ) { wp_send_json_error( array( 'message' => __( 'Item inválido.', 'ytiw-digital-store' ) ) ); } if ( ! class_exists( 'YTIW_DS_Cart' ) ) { include_once YTIW_DS_PLUGIN_DIR . 'includes/class-ytiw-ds-cart.php'; } $cart = YTIW_DS_Cart::instance(); $cart->remove_from_cart( $item_key ); wp_send_json_success( array( 'message' => __( 'Item removido.', 'ytiw-digital-store' ), 'cart_total' => 'R$ ' . number_format( $cart->get_cart_total(), 2, ',', '.' ), 'item_count' => count( $cart->get_cart_contents() ) ) ); } /** * AJAX: Aplicar Cupom * @since 3.9.6 */ public function apply_coupon() { check_ajax_referer( 'ytiw_ds_cart_nonce', 'security' ); $coupon_code = isset( $_POST['coupon_code'] ) ? sanitize_text_field( $_POST['coupon_code'] ) : ''; if ( ! class_exists( 'YTIW_DS_Cart' ) ) { include_once YTIW_DS_PLUGIN_DIR . 'includes/class-ytiw-ds-cart.php'; } $cart = YTIW_DS_Cart::instance(); $result = $cart->apply_coupon( $coupon_code ); if ( $result['success'] ) { wp_send_json_success( array( 'message' => $result['message'], 'cart_total' => 'R$ ' . number_format( $cart->get_cart_total(), 2, ',', '.' ) ) ); } else { wp_send_json_error( array( 'message' => $result['message'] ) ); } } /** * AJAX: Remover Cupom * @since 3.9.6 */ public function remove_coupon() { check_ajax_referer( 'ytiw_ds_cart_nonce', 'security' ); if ( ! class_exists( 'YTIW_DS_Cart' ) ) { include_once YTIW_DS_PLUGIN_DIR . 'includes/class-ytiw-ds-cart.php'; } $cart = YTIW_DS_Cart::instance(); $result = $cart->remove_coupon(); if ( $result['success'] ) { wp_send_json_success( array( 'message' => $result['message'], 'cart_total' => 'R$ ' . number_format( $cart->get_cart_total(), 2, ',', '.' ) ) ); } else { wp_send_json_error( array( 'message' => $result['message'] ) ); } } /** * AJAX: Processar Checkout. */ public function process_checkout() { if ( ! WP_DEBUG || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) { @error_reporting( 0 ); } try { $this->load_checkout_dependencies(); if ( ! class_exists( 'YTIW_DS_Checkout_Handler' ) ) { throw new Exception( __( 'Erro interno: Handler de Checkout não carregado.', 'ytiw-digital-store' ) ); } YTIW_DS_Checkout_Handler::process_checkout(); } catch ( Exception $e ) { wp_send_json_error( array( 'message' => $e->getMessage() ) ); } } /** * AJAX: Resetar contador e validade de download (Admin). * Permite que o suporte renove o acesso do cliente ao arquivo. */ public function reset_download() { // 1. Verificação de Permissão if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Sem permissão.', 'ytiw-digital-store' ) ) ); } $token = isset( $_POST['token'] ) ? sanitize_text_field( $_POST['token'] ) : ''; $order_id = isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0; // 2. Verificação de Segurança (Nonce Específico) check_ajax_referer( 'ytiw_reset_dl_' . $token, 'security' ); if ( ! $order_id || empty( $token ) ) { wp_send_json_error( array( 'message' => __( 'Dados inválidos.', 'ytiw-digital-store' ) ) ); } // 3. Lógica de Reset $items = get_post_meta( $order_id, '_ytiw_ds_order_items', true ); $found = false; if ( is_array( $items ) ) { // Busca configurações globais para nova expiração $settings = get_option( 'ytiw_ds_general_settings' ); $new_expiry_hours = isset( $settings['download_expiry'] ) ? absint( $settings['download_expiry'] ) : 48; $new_expiry_date = date( 'Y-m-d H:i:s', strtotime( "+{$new_expiry_hours} hours" ) ); foreach ( $items as &$item ) { if ( isset( $item['download_token'] ) && $item['download_token'] === $token ) { // Zera contador $item['download_count'] = 0; // Renova data $item['download_expiry'] = $new_expiry_date; $found = true; break; } } if ( $found ) { update_post_meta( $order_id, '_ytiw_ds_order_items', $items ); // Registra nota no pedido if ( class_exists( 'YTIW_DS_Order_Manager' ) ) { YTIW_DS_Order_Manager::add_order_note( $order_id, sprintf( __( 'Download resetado manualmente pelo Admin. Validade estendida até %s.', 'ytiw-digital-store' ), $new_expiry_date ) ); } wp_send_json_success( array( 'message' => __( 'Download resetado com sucesso! O cliente pode baixar o arquivo novamente.', 'ytiw-digital-store' ) ) ); } else { wp_send_json_error( array( 'message' => __( 'Token não encontrado neste pedido.', 'ytiw-digital-store' ) ) ); } } } /** * Auxiliar: Carrega classes necessárias para o checkout. */ private function load_checkout_dependencies() { $includes_dir = YTIW_DS_PLUGIN_DIR . 'includes/'; if ( ! class_exists( 'YTIW_DS_Cart' ) ) { require_once $includes_dir . 'class-ytiw-ds-cart.php'; } if ( ! class_exists( 'YTIW_DS_Order_Manager' ) ) { require_once $includes_dir . 'class-ytiw-ds-order-manager.php'; } if ( ! class_exists( 'YTIW_DS_Checkout_Handler' ) ) { require_once $includes_dir . 'class-ytiw-ds-checkout-handler.php'; } } /** * Helper: Formata moeda. */ private function format_money( $amount ) { return 'R$ ' . number_format( (float) $amount, 2, ',', '.' ); }}
Parece que nada foi encontrado neste local.