Plugin Directory

Changeset 3351633


Ignore:
Timestamp:
08/28/2025 03:10:21 AM (7 months ago)
Author:
felixmp
Message:

1.6.0 - Improve recurrent payment feature

Location:
metrepay/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • metrepay/trunk/metrepay.php

    r3299938 r3351633  
    44Plugin URI: https://wordpress.org/plugins/metrepay
    55Description: Pasarela de pago para integracion con MetrePay y Woocomerce
    6 Version: 1.5.1
     6Version: 1.6.0
    77Author: Rugertek
    88Author URI: https://rugertek.com/
     
    9696            $this->custom_id_1 = sanitize_text_field($this->get_option('custom_id_1'));
    9797            $this->custom_id_2 = 'yes' === sanitize_text_field($this->get_option('custom_id_2'));
     98            $this->subscription_installments = intval($this->get_option('subscription_installments', 12));
    9899            $this->header_token = $this->mpay_local_generate_string();
    99100
     
    154155                    'title'       => __('Débito automático', 'metrepay'),
    155156                    'label'       => __('Permitir pagos en cuotas por débitos automáticos', 'metrepay'),
    156                     'description' => __('Aplica cuando el carrito tiene solamente 1 producto con atributo "cuotas" y valor numérico en el mismo. Si se activa, los links de pago tienen login requerido.', 'metrepay'),
     157                    'description' => __('Aplica cuando el carrito tiene solamente 1 producto con etiqueta de suscripción. Si se activa, los links de pago tienen login requerido.', 'metrepay'),
    157158                    'type'        => 'checkbox',
    158159                    'default'     => 'no'
     160                ),
     161                'subscription_installments' => array(
     162                    'title'       => __('Cantidad de pagos para suscripciones', 'metrepay'),
     163                    'type'        => 'number',
     164                    'description' => __('Número de pagos (cuotas) que se aplicarán para productos con etiqueta de suscripción en débito automático.', 'metrepay'),
     165                    'default'     => '12',
     166                    'custom_attributes' => array(
     167                        'min' => '1',
     168                        'max' => '60'
     169                    ),
    159170                ),
    160171                'links_login_required' => array(
     
    235246        {
    236247            return $this->custom_id_2;
     248        }
     249
     250        public function get_subscription_installments()
     251        {
     252            return $this->subscription_installments;
    237253        }
    238254       
     
    334350
    335351            try {
     352                $this->logger->info('Sending payment request to MetrePay API', array_merge($this->context, array('order_id' => $order_id)));
    336353                $response = $this->mpay_local_data_post($data);
    337354                $result = json_decode($response, true);
    338355
    339                 error_log(json_encode($data));
    340                 error_log(json_encode($result));
     356                $this->logger->info('Payment request successful', array_merge($this->context, array(
     357                    'order_id' => $order_id,
     358                    'payment_url' => isset($result['publicPayUrl']) ? $result['publicPayUrl'] : 'N/A'
     359                )));
    341360
    342361                // Delete the cookie
     
    348367                );
    349368            } catch (\Throwable $th) {
     369                $this->logger->error('Payment processing failed: ' . $th->getMessage(), array_merge($this->context, array('order_id' => $order_id)));
    350370                $this->mpay_local_show_error_to_user($th);
    351                 $this->logger->log('error', __('Error al procesar el pago', 'metrepay'), $this->context);
     371                $this->logger->error(__('Error al procesar el pago', 'metrepay'), $this->context);
    352372            }
    353373        }
     
    368388
    369389            if (!isset(WC()->cart)) {
     390                $this->logger->info('Cart not available, skipping subscription check', $this->context);
    370391                return;
    371392            }
    372393
    373394            $cart = WC()->cart->get_cart();
    374             // For RP just 1 item is allowed
     395            $this->logger->info('Checking cart for subscription eligibility. Cart items count: ' . count($cart), $this->context);
     396           
     397            // New conditions for automatic debit (subscription):
     398            // 1. Cart has only 1 product
     399            // 2. Product quantity is 1
     400            // 3. Product can be any type (not just variable)
     401            // 4. Product has a subscription tag
     402            // 5. Automatic debit is enabled in settings
    375403            if (count($cart) == 1 && $this->get_active_recurrent_payment()) {
    376404                foreach ( $cart as $cart_item_key => $cart_item ) {
    377405                    $product = $cart_item['data'];
    378                     $installments_quantity = $product->get_attribute('cuotas');
    379                     if (isset($installments_quantity)
    380                         && is_numeric($installments_quantity)) {
    381                             $installments_quantity = intval($installments_quantity);
    382                             if ($installments_quantity > 0) {
    383                                 $can_be_recurrent_payment = true;
    384                             }
    385                     }   
     406                    $quantity = $cart_item['quantity'];
     407                   
     408                    $this->logger->debug('Analyzing product: ID=' . $product->get_id() . ', Type=' . $product->get_type() . ', Quantity=' . $quantity, $this->context);
     409                   
     410                    // Check if quantity is exactly 1
     411                    if ($quantity == 1) {
     412                        // Check if product has subscription tags
     413                        if ($this->has_subscription_tag($product)) {
     414                            $installments_quantity = $this->get_subscription_installments();
     415                            $can_be_recurrent_payment = true;
     416                            $this->logger->debug('Product qualifies for subscription: installments=' . $installments_quantity, $this->context);
     417                        } else {
     418                            $this->logger->debug('Product does not have subscription tags', $this->context);
     419                        }
     420                    } else {
     421                        $this->logger->debug('Product quantity is not 1, cannot be subscription', $this->context);
     422                    }
    386423                }
    387             }
    388 
    389             error_log('can_be_recurrent_payment: ' . $can_be_recurrent_payment);
     424            } else {
     425                if (count($cart) != 1) {
     426                    $this->logger->debug('Cart has more than 1 item, cannot be subscription', $this->context);
     427                }
     428                if (!$this->get_active_recurrent_payment()) {
     429                    $this->logger->debug('Automatic debit is disabled in settings', $this->context);
     430                }
     431            }
     432
     433            $this->logger->debug('Final subscription check result: can_be_recurrent_payment=' . ($can_be_recurrent_payment ? 'true' : 'false'), $this->context);
     434           
    390435            if ($can_be_recurrent_payment) {
    391436                // We setup the cookie
    392437                $this->mpay_local_set_cookie('mp_installments_quantity', $installments_quantity);
    393438                $this->mpay_local_set_cookie('mp_payment_method', 'RECURRENT_PAYMENT');
    394                 // Known warning -> PHP Warning:  Cannot modify header information - headers already sent by blablabla
    395                 // Another file was outputting information before this php file.
     439                $this->logger->debug('Set cookies for recurrent payment: installments=' . $installments_quantity, $this->context);
    396440            } else {
    397441                // The cart is only for "SINGLE PAYMENT"
     
    399443                $this->mpay_local_delete_cookie('mp_installments_quantity');
    400444                $this->mpay_local_set_cookie('mp_payment_method', 'SINGLE_PAYMENT');
     445                $this->logger->debug('Set cookies for single payment', $this->context);
    401446            }
    402447       
    403448            if (isset($_COOKIE['mp_installments_quantity'])) {
    404                 error_log('mp_installments_quantity: ' . sanitize_text_field($_COOKIE['mp_installments_quantity']));
     449                $this->logger->info('Final cookie mp_installments_quantity: ' . sanitize_text_field($_COOKIE['mp_installments_quantity']), $this->context);
    405450            } else {
    406                 error_log('mp_installments_quantity: NULL');
    407             }
     451                $this->logger->info('Final cookie mp_installments_quantity: NULL', $this->context);
     452            }
     453        }
     454
     455        /**
     456         * Check if a product has subscription tags
     457         * Looks for tags like: suscripción, suscripcion, subscription, etc.
     458         */
     459        private function has_subscription_tag($product)
     460        {
     461            $product_id = $product->get_id();
     462            $tags = wp_get_post_terms($product_id, 'product_tag');
     463           
     464            if (is_wp_error($tags) || empty($tags)) {
     465                $this->logger->debug('Product ID ' . $product_id . ' has no tags', $this->context);
     466                return false;
     467            }
     468
     469            // Palabras clave "base", sin duplicar mayúsculas ni tildes
     470            $subscription_keywords = array('suscripcion', 'subscription');
     471
     472            // Función helper para normalizar strings (case + quitar tildes)
     473            $normalize = function($string) {
     474                $string = mb_strtolower($string, 'UTF-8');
     475                $unwanted = array('á'=>'a','é'=>'e','í'=>'i','ó'=>'o','ú'=>'u','ü'=>'u','ñ'=>'n');
     476                return strtr($string, $unwanted);
     477            };
     478
     479            foreach ($tags as $tag) {
     480                $tag_name = $normalize($tag->name);
     481                $this->logger->debug('Checking tag: ' . $tag->name, $this->context);
     482
     483                foreach ($subscription_keywords as $keyword) {
     484                    if (strpos($tag_name, $keyword) !== false) {
     485                        $this->logger->debug(
     486                            'Found subscription tag: ' . $tag->name . ' (matched keyword: ' . $keyword . ')',
     487                            $this->context
     488                        );
     489                        return true;
     490                    }
     491                }
     492            }
     493
     494            $this->logger->debug('No subscription tags found for product ID ' . $product_id, $this->context);
     495            return false;
    408496        }
    409497
     
    453541        function mpay_local_data_post($data) {
    454542            $url = $this->mpay_local_get_mp_url();
    455             error_log(json_encode($url));
    456             error_log('MP API REQ BODY: ' . json_encode($data));
     543
     544            // Obtenemos el token y mostramos solo los primeros 6 caracteres
     545            $authToken = $this->get_auth_token();
     546            $authTokenPreview = substr($authToken, 0, 6) . '...';
     547
     548            // Log de depuración
     549            $request_log = array(
     550                'url'     => $url,
     551                'headers' => array(
     552                    'Api-Token' => $authTokenPreview, // no mostramos todo
     553                    'Content-Type' => 'application/json',
     554                ),
     555                'body'    => $data, // payload completo
     556            );
     557
     558            $this->logger->info('MP API request', array_merge($this->context, $request_log));
    457559
    458560            $args = array(
     
    464566                'headers'     => array(
    465567                    'Content-Type' => 'application/json',
    466                     'Api-Token'    => $this->get_auth_token()
     568                    'Api-Token'    => $authToken
    467569                ),
    468570            );
     571
    469572            $response = wp_remote_post($url, $args);
    470573
    471574            if (is_wp_error($response)) {
    472                 error_log('Error: ' . $response->get_error_message());
     575                $this->logger->error('WP Error: ' . $response->get_error_message(), $this->context);
    473576                throw new Exception(__("Error procesando petición a MetrePay", 'metrepay'), 1);
    474577            }
    475578
    476             // Verifica el código de estado de la respuesta
    477579            $http_code = wp_remote_retrieve_response_code($response);
    478580
     
    480582                $error_msg = "HTTP Error Code: $http_code - Error message from server: "
    481583                    . wp_remote_retrieve_body($response);
    482                 error_log($error_msg);
    483                 $this->logger->log('error', $error_msg, $this->context);
     584                $this->logger->error($error_msg, $this->context);
    484585                throw new Exception(__("Petición HTTP falló con código ", 'metrepay') . $http_code, $http_code);
    485586            }
     
    487588            return wp_remote_retrieve_body($response);
    488589        }
     590
    489591
    490592        function mpay_local_set_cookie($name, $value) {
  • metrepay/trunk/readme.txt

    r3299938 r3351633  
    33Tags: metrepay, payment
    44Requires at least: 5.3
    5 Tested up to: 6.8.1
    6 Stable tag: 1.5.1
     5Tested up to: 6.8.2
     6Stable tag: 1.6.0
    77Requires PHP: 7.3.5
    88License: GPLv3
     
    6060* Fixed the amount value formatting: it now includes decimal places when needed (e.g. 10.01), and uses integers when the value is whole (e.g. 10).
    6161* Removed the "provider slug" setting field, as it's no longer required for commerce configuration.
     62
     63= 1.6.0 =
     64* Updated automatic debit detection to use product tags instead of "cuotas" attribute.
     65* Improved subscription detection based on tags like "suscripción", "subscription", etc.
     66* Added configurable subscription installments setting (default: 12 payments).
     67* Automatic debits now support any product type, not just variable products.
     68* Enhanced logging system with WooCommerce logger.
Note: See TracChangeset for help on using the changeset viewer.