<?php

use IpagWoocommerce\ArrUtils;

require_once plugin_dir_path(__FILE__) . '/support/ArrUtils.php';
require_once plugin_dir_path(__FILE__) . '../exceptions/IpagResponseException.php';

use IpagWoocommerce\MaskUtils;
use IpagWoocommerce\IpagResponseException;

class WC_Gateway_iPag_Credito extends WC_iPag_Loader
{
    public $interest_rate;
    public $smallest_installment;
    public $maximum_installment;
    public $interest_free_installment;
    public $accepted_cards;
    public $pagseguro_habilitado;
    public $pagseguro_email;
    public $pagseguro_token;
    public $pagseguro_ambiente;

    public $status_cancelado;
    public $status_aprovado;
    public $status_reprovado;
    public $status_capturado;
    public $brand;

    public $disable_card_cpf;
    public $disable_card_fill_preview;
    public $pass_interest;
    public $allow_saved_cards;
    public $tokenize_card;

    public $cardBrands = array(
        'visa' => 'Visa',
        'mastercard' => 'MasterCard',
        'diners' => 'Diners',
        'discover' => 'Discover',
        'elo' => 'Elo',
        'amex' => 'American Express',
        'hipercard' => 'Hipercard',
        'hiper' => 'Hiper',
        'jcb' => 'JCB',
        'aura' => 'Aura',
    );

    public function __construct()
    {

        $this->id = 'ipag-gateway';
        $this->has_fields = true;
        $this->method_title = __('iPag - Credit Card', 'ipag-gateway');
        $this->method_description = __('iPag Secure Payment', 'ipag-gateway');
        $this->supports = array(
            'products',
            'refunds',
            'subscriptions',
            // 'subscription_cancellation',
            'subscription_suspension',
            // 'subscription_reactivation',
            // 'subscription_amount_changes',
            // 'subscription_payment_method_change',
            // 'subscription_payment_method_change_customer',
            // 'subscription_payment_method_change_admin',
            // 'subscription_date_changes',
        );

        $this->init_form_fields();
        $this->init_settings();

        $this->enabled = $this->get_option('enabled');
        $this->title = $this->get_option('title');
        $this->identification = $this->get_option('identification');
        $this->apikey = $this->get_option('apikey');
        $this->accepted_cards = $this->get_option('accepted_cards');
        $this->pagseguro_habilitado = $this->get_option('psenabled', 'no');
        $this->pagseguro_ambiente = $this->get_option('psenvironment', '');
        $this->pagseguro_email = $this->get_option('pagseguro_email', '');
        $this->pagseguro_token = $this->get_option('pagseguro_token', '');
        $this->smallest_installment = $this->get_option('smallest_installment');
        $this->maximum_installment = $this->get_option('maximum_installment');
        $this->interest_rate = $this->get_option('interest_rate');
        $this->interest_free_installment = $this->get_option('interest_free_installment');
        $this->environment = $this->get_option('environment');
        $this->debug = $this->get_option('debug');
        $this->brand = $this->get_option('brand');
        $this->icon = $this->getIpagLogo($this);

        $this->disable_card_cpf = $this->get_option('disable_cpf_card');
        $this->disable_card_fill_preview = $this->get_option('disable_card_fill_preview');
        $this->pass_interest = $this->get_option('pass_interest');
        $this->allow_saved_cards = $this->get_option('allow_saved_cards');
        $this->tokenize_card = $this->get_option('tokenize_card');

        $this->status_aprovado = self::getStatusOption('status_aprovado', $this);
        $this->status_reprovado = self::getStatusOption('status_reprovado', $this);
        $this->status_capturado = self::getStatusOption('status_capturado', $this);
        $this->status_cancelado = self::getStatusOption('status_cancelado', $this);

        add_action('woocommerce_update_options_payment_gateways_' . $this->id, array($this, 'process_admin_options'));
        add_action('woocommerce_admin_order_data_after_shipping_address', array($this, 'orderInfo'));
        add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
        add_action('wp_enqueue_scripts', array($this, 'checkout_scripts'));
        add_action('admin_footer', array($this, 'capture_ajax'));
    }

    public function init_form_fields()
    {
        $environmentOptions = array(
            'test' => __('Test', 'ipag-gateway'),
            'production' => __('Production', 'ipag-gateway'),
        );

        if ((bool) getenv('ENVIRONMENT_LOCAL')) {
            $environmentOptions['local'] = __('Local', 'ipag-gateway');
        }

        $statuses = array('' => '-- Selecione um status --');
        $statuses = array_merge($statuses, wc_get_order_statuses());
        $this->form_fields = array(
            'enabled' => array(
                'title' => __('Enable iPag Payment Gateway', 'ipag-gateway'),
                'type' => 'checkbox',
                'label' => __('Enable', 'ipag-gateway'),
                'default' => 'no',
            ),
            'title' => array(
                'title' => __('iPag Title', 'ipag-gateway'),
                'type' => 'text',
                'description' => __('This controls the title that users will see during checkout.', 'ipag-gateway'),
                'default' => __('iPag - Credit Card', 'ipag-gateway'),
                'desc_tip' => true,
            ),
            'environment' => array(
                'title' => __('Environment', 'ipag-gateway'),
                'type' => 'select',
                'desc_tip' => false,
                'default' => 'production',
                'options' => $environmentOptions,
            ),
            'identification' => array(
                'title' => __('Store Identification', 'ipag-gateway'),
                'type' => 'text',
                'description' => __('Your store identification registered at iPag Panel.', 'ipag-gateway'),
                'default' => '',
                'desc_tip' => true,
            ),
            'apikey' => array(
                'title' => __('Ipag API KEY', 'ipag-gateway'),
                'type' => 'text',
                'description' => __('Your store API KEY at iPag Panel.', 'ipag-gateway'),
                'default' => '',
                'desc_tip' => true,
            ),
            'accepted_cards' => array(
                'title' => __('Accepted Card Brands', 'ipag-gateway'),
                'type' => 'multiselect',
                'description' => __('Select the card brands that will be accepted for payment.', 'ipag-gateway'),
                'desc_tip' => true,
                'class' => 'wc-enhanced-select',
                'default' => array('visa', 'mastercard'),
                'options' => $this->cardBrands,
            ),
            'pass_interest' => array(
                'title' => __('Utilizar parcelamento configurado na conta iPag', 'ipag-gateway'),
                'label' => __('Enable', 'ipag-gateway'),
                'description' => __('Se esta opção for ativada, serão levadas em consideração as configurações de parcelamento definidas na conta, fazendo com que os campos abaixo sejam ignorados.', 'ipag-gateway'),
                'type' => 'checkbox',
                'desc_tip' => true,
                'default' => 'no',
            ),
            'smallest_installment' => array(
                'title' => __('Smallest Installment', 'ipag-gateway'),
                'type' => 'text',
                'description' => __('Smallest value of each installment.', 'ipag-gateway'),
                'desc_tip' => true,
                'default' => '5.00',
                'class' => 'ipag-input-group-installment'
            ),
            'maximum_installment' => array(
                'title' => __('Installment Within', 'ipag-gateway'),
                'type' => 'select',
                'description' => __('Maximum number of installments for orders in your store.', 'ipag-gateway'),
                'desc_tip' => true,
                'class' => 'wc-enhanced-select ipag-input-group-installment',
                'default' => '6',
                'options' => array(
                    '1' => '1x',
                    '2' => '2x',
                    '3' => '3x',
                    '4' => '4x',
                    '5' => '5x',
                    '6' => '6x',
                    '7' => '7x',
                    '8' => '8x',
                    '9' => '9x',
                    '10' => '10x',
                    '11' => '11x',
                    '12' => '12x',
                ),
            ),
            'interest_rate' => array(
                'title' => __('Interest Rate(%)', 'ipag-gateway'),
                'type' => 'text',
                'description' => __('Percentage of interest that will be charged to the customer in the installment.', 'ipag-gateway'),
                'desc_tip' => true,
                'default' => '1.49',
                'class' => 'ipag-input-group-installment'
            ),
            'interest_free_installment' => array(
                'title' => __('Interest-Free Installment', 'ipag-gateway'),
                'type' => 'select',
                'description' => __('Number of interest-free installments', 'ipag-gateway'),
                'desc_tip' => true,
                'class' => 'wc-enhanced-select ipag-input-group-installment',
                'default' => '6',
                'options' => array(
                    '1' => '1x',
                    '2' => '2x',
                    '3' => '3x',
                    '4' => '4x',
                    '5' => '5x',
                    '6' => '6x',
                    '7' => '7x',
                    '8' => '8x',
                    '9' => '9x',
                    '10' => '10x',
                    '11' => '11x',
                    '12' => '12x',
                ),
            ),
            'tokenize_card' => array(
                'title' => __('Sempre tokenizar pagamentos com cartões de crédito'),
                'label' => __('Enable', 'ipag-gateway'),
                'type' => 'checkbox',
                'default' => 'no'
            ),
            'allow_saved_cards' => array(
                'class' => 'ipag-input-allow-saved-cards',
                'title' => __('Habilitar opção de pagamentos com cartões salvos para os clientes'),
                'label' => __('Enable', 'ipag-gateway'),
                'desc_tip' => __('Quando ativado, os clientes podem optar por salvar os detalhes de pagamento para agilizar compras futuras (obs: os detalhes do cartão não são salvos localmente).'),
                'type' => 'checkbox',
                'default' => 'no'
            ),
            'debug' => array(
                'title' => __('Enable Debug', 'ipag-gateway'),
                'label' => __('Enable', 'ipag-gateway'),
                'type' => 'checkbox',
                'desc_tip' => false,
                'default' => 'no',
            ),
            'brand' => array(
                'title' => __('Enable iPag Brand', 'ipag-gateway'),
                'label' => __('Enable', 'ipag-gateway'),
                'type' => 'checkbox',
                'desc_tip' => false,
                'default' => 'no',
            ),
            'disable_cpf_card' => array(
                'title' => __('Desativar o campo de CPF do portador do cartão', 'ipag-gateway'),
                'type' => 'checkbox',
                'label' => __('Sim, desativar!', 'ipag-gateway'),
                'default' => 'no',
            ),
            'disable_card_fill_preview' => array(
                'title' => __('Desativar o efeito de visualização no preenchimento dos dados do cartão de crédito', 'ipag-gateway'),
                'type' => 'checkbox',
                'label' => __('Sim, desativar!', 'ipag-gateway'),
                'default' => 'no',
            ),
            'status_aprovado' => array(
                'title' => __('Status Aprovado', 'ipag-gateway'),
                'type' => 'select',
                'desc_tip' => true,
                'description' => __('O pedido mudará automaticamente para este status em caso de aprovação no iPag', 'ipag-gateway'),
                'default' => 'wc-on-hold',
                'options' => $statuses,
            ),
            'status_reprovado' => array(
                'title' => __('Status Reprovado', 'ipag-gateway'),
                'type' => 'select',
                'desc_tip' => true,
                'description' => __('O pedido mudará automaticamente para este status em caso de reprova no iPag', 'ipag-gateway'),
                'default' => 'wc-failed',
                'options' => $statuses,
            ),
            'status_capturado' => array(
                'title' => __('Status Capturado', 'ipag-gateway'),
                'type' => 'select',
                'desc_tip' => true,
                'description' => __('O pedido mudará automaticamente para este status quando a transação for capturada no iPag', 'ipag-gateway'),
                'default' => 'wc-processing',
                'options' => $statuses,
            ),
            'status_cancelado' => array(
                'title' => __('Status Cancelado', 'ipag-gateway'),
                'type' => 'select',
                'desc_tip' => true,
                'description' => __('O pedido mudará automaticamente para este status quando a transação for cancelada no iPag', 'ipag-gateway'),
                'default' => 'wc-cancelled',
                'options' => $statuses,
            ),
            /**
            'psenabled' => array(
                'title' => __('Enable', 'ipag-gateway') . ' PagSeguro',
                'type' => 'checkbox',
                'label' => __('Enable', 'ipag-gateway'),
                'default' => 'no',
            ),
            'psenvironment' => array(
                'title' => 'PagSeguro ' . __('Environment', 'ipag-gateway'),
                'type' => 'select',
                'desc_tip' => false,
                'default' => 'production',
                'options' => array(
                    '0' => __('Test', 'ipag-gateway'),
                    '1' => __('Production', 'ipag-gateway'),
                ),
            ),
            'pagseguro_email' => array(
                'title' => __('PagSeguro Email', 'ipag-gateway'),
                'type' => 'text',
                'desc_tip' => true,
                'description' => __('E-mail para integração com PagSeguro', 'ipag-gateway'),
                'default' => '',
            ),
            'pagseguro_token' => array(
                'title' => __('PagSeguro Token', 'ipag-gateway'),
                'type' => 'text',
                'desc_tip' => true,
                'description' => __('Token para integração com PagSeguro', 'ipag-gateway'),
                'default' => '',
            ),
            */
            'ipag_user_public_key' => array(
                'title' => __('Identificador Público iPag', 'ipag-gateway'),
                'type' => 'text',
                'description' => __('Seu identificador público (Uuid) registrado no painel do iPag.', 'ipag-gateway'),
                'default' => '',
                'desc_tip' => true,
            ),
        );
    }

    public function getUserLoggedIn()
    {
        $user_id = get_current_user_id();

        return is_user_logged_in() && !empty($user_id) ? $user_id : null;
    }

    public function allow_saved_cards()
    {
        return $this->allow_saved_cards === 'yes';
    }

    public function tokenize_card()
    {
        return $this->tokenize_card === 'yes';
    }

    private function format_payment_error_message($message)
    {
        $raw_message = trim((string) $message);
        $normalized = strtoupper($raw_message);

        $mapped = [
            'DECLINED' => __('Pagamento recusado. Caso o erro persista, não tente novamente.', 'ipag-gateway'),
        ];

        return $mapped[$normalized] ?? $raw_message;
    }

    public function savedCards($user_id)
    {
        $classTokens = 'WC_Payment_Tokens';

        if (class_exists($classTokens)) {
            $tokens = [];

            foreach (WC_Payment_Tokens::get_customer_tokens($user_id) as $tokenCC) {
                $consentedToken = !empty($tokenCC->get_meta('user_consent'));

                if ($tokenCC->get_gateway_id() == $this->id && $consentedToken) {
                    $tokens[] = $tokenCC;
                }
            }

            return $tokens;
        }

        return [];
    }

    public function admin_enqueue_scripts($hook_suffix)
    {
        if ($hook_suffix === 'woocommerce_page_wc-settings') {
            $sectionParam = htmlspecialchars(filter_input(INPUT_GET, 'section') ?? '');

            if (strpos($sectionParam, 'ipag-gateway') === 0) {
                wp_enqueue_script('ipag-gateway-admin-script', plugins_url("../js/admin-script.js?vipag=" . WC_iPag_Gateway::IPAG_VERSION, __FILE__));
            }
        }
    }

    public function orderInfo()
    {
        if (self::getRequestId()) {
            $order_id = self::getRequestId();
            $order = new WC_Order($order_id);
            $method = self::getProp($order, 'payment_method');

            if ($method === 'ipag-gateway') {
                $this->order_info();
            }
        }
    }

    public function prepare_payment_fields_data()
    {
        $total = 0;

        if (function_exists('WC') && WC()->cart && isset(WC()->cart->total)) {
            $total = floatval(WC()->cart->total);
        }

        $juros = $this->interest_rate;
        $v_minimo = $this->smallest_installment;
        $maxparcelas = $this->maximum_installment;
        $disable_card_cpf = $this->disable_card_cpf;
        $disable_card_fill_preview = $this->disable_card_fill_preview;
        $pass_interest = $this->pass_interest;
        $pass_installments = null;
        $user_id = $this->getUserLoggedIn();
        $saved_cards = $this->allow_saved_cards() && !empty($user_id) ? $this->savedCards($user_id) : [];

        if (empty($total)) {
            $ped = new WC_Order(get_query_var('order-pay'));
            $total = $ped->get_total();
        }

        if (!empty($total) && !empty($v_minimo) && $total / $v_minimo < $maxparcelas) {
            $maxparcelas = (int) ($total / $v_minimo);
        }

        if (mb_strtolower($pass_interest) === 'yes') {
            $checkoutInstallments = $this->getCheckoutInstallments($total);

            if (!empty($checkoutInstallments)) {
                $pass_installments = $checkoutInstallments;
            }
        }

        $url = self::getEnvironment($this->environment);

        $params = array(
            'maximum_installment' => intval($maxparcelas),
            'disable_card_cpf' => $disable_card_cpf,
            'disable_card_fill_preview' => $disable_card_fill_preview,
            'pass_interest' => $pass_interest,
            'smallest_installment' => floatval($v_minimo),
            'interest_rate' => floatval($juros),
            'interest_free_installment' => intval($this->interest_free_installment),
            'parcelas_juros' => $this->valorParcelas($total, intval($maxparcelas), floatval($juros)),
            'pass_installments' => $pass_installments,
            'ipag_session_id' => $this->getIpagSessionId($this->identification, $this->apikey, $url),
            'static_path' => 'https://stc.pagseguro.uol.com.br',
            'saved_cards' => $saved_cards,
            'allow_saved_cards' => $this->allow_saved_cards(),
            'brand' => $this->brand,
            'assets_url' => WC_iPag_Gateway::plugin_url() . '/images/',
            'pagseguro_enabled' => $this->pagseguro_habilitado,
            'pagseguro_session' => $this->pagsegurosession($this->pagseguro_email, $this->pagseguro_token, $this->pagseguro_ambiente),
            'subscription_enabled' => class_exists('WC_Subscriptions_Cart') && WC_Subscriptions_Cart::cart_contains_subscription(),
            'total' => floatval($total),
            'ano' => date('Y'),
            'cardBrands' => array_keys($this->cardBrands),
            'accepted_cards' => $this->accepted_cards,
            'cart_total' => WC()->cart ? floatval(WC()->cart->total) : 0,
            'cart_products' => $this->getCartProductsData(),
            'ipag_test' => $this->environment == 'test' ? 'true' : 'false',
            'errors' => array(
                'invalid_card' => __('Invalid credit card number.', 'ipag-gateway'),
                'invalid_cvv' => __('Invalid CVV.', 'ipag-gateway'),
                'invalid_name' => __('Invalid name.', 'ipag-gateway'),
                'invalid_expiry' => __('Invalid expiry date.', 'ipag-gateway'),
                'expired_card' => __('Expired card.', 'ipag-gateway'),
                'invalid_cpf' => __('Invalid CPF.', 'ipag-gateway'),
                'invalid_installment' => __('Please choose an installment option.', 'ipag-gateway'),
            ),
        );

        return $params;
    }

    private function getCartProductsData()
    {
        $products = [];

        if (function_exists('WC') && WC()->cart) {
            foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
                $product = $cart_item['data'];

                $products[] = [
                    'name' => $product->get_name(),
                    'price_cents' => round($product->get_price() * 100),
                    'quantity' => $cart_item['quantity'],
                    'id' => $product->get_id(),
                    'sku' => $product->get_sku(),
                    'description' => $product->get_description(),
                ];
            }
        }

        return $products;
    }

    public function payment_fields()
    {
        $params = $this->prepare_payment_fields_data();

        wc_get_template(
            'ipag-gateway-template-credito.php',
            $params,
            '',
            WC_iPag_Gateway::get_templates_path()
        );
    }

    public function checkout_scripts()
    {
        if (is_checkout() && !is_order_received_page() && $this->enabled == 'yes') {
            $apiUrl = self::getEnvironment($this->environment);

            if (self::isLocal()) {
                if ($_SERVER['HTTPS'] === 'on') {
                    $apiUrl = str_replace('http://', 'https://', $apiUrl);
                }
            }

            $ipagUserPublicKey = $this->get_option('ipag_user_public_key');

            wp_enqueue_script('ipag-credito-js', $apiUrl . 'js/dist/ipag.js', array(), null, true);
            wp_enqueue_script('ipag-formatter-js', 'https://cdnjs.cloudflare.com/ajax/libs/formatter.js/0.1.5/formatter.min.js', array(), null, true);
            wp_enqueue_script('ipag-crypto-js', 'https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/md5.js', array(), null, true);

            if ($this->pagseguro_token) {
                wp_enqueue_script('pagseguro-credito-js', 'https://stc.' . ($this->pagseguro_ambiente ? '' : 'sandbox.') . 'pagseguro.uol.com.br/pagseguro/api/v2/checkout/pagseguro.directpayment.js', array(), null, true);
            }

            wp_enqueue_script('ipag-jquery.mask.min', plugins_url('js/jquery.mask.min.js', dirname(__FILE__)), array(), null, true);
            wp_enqueue_script('ipag-cartao-js', plugins_url('js/cartao.js', dirname(__FILE__)), array(), null, true);

            if (!empty($ipagUserPublicKey)) {
                wp_enqueue_script('checkout-script-manager-js', $apiUrl . 'service/v2/public/js/checkout-script-manager.js?id=' . $ipagUserPublicKey, array(), null, true);
            }

            wp_localize_script(
                'ipag-cartao-js',
                'errors',
                array(
                    'invalid_card' => __('Invalid credit card number.', 'ipag-gateway'),
                    'invalid_cvv' => __('Invalid CVV.', 'ipag-gateway'),
                    'invalid_name' => __('Invalid name.', 'ipag-gateway'),
                    'invalid_expiry' => __('Invalid expiry date.', 'ipag-gateway'),
                    'expired_card' => __('Expired card.', 'ipag-gateway'),
                    'invalid_cpf' => __('Invalid CPF.', 'ipag-gateway'),
                    'invalid_installment' => __('Please choose an installment option.', 'ipag-gateway'),
                )
            );
        }
    }

    public function expiryMesValido($mes, $ano)
    {
        $ano = substr(date('Y'), 0, 2) . $ano;
        if (strlen($mes) == 2) {
            $intMes = intval($mes);
            $currentAno = intval(date('Y'));
            $ano = intval($ano);
            if ($ano > $currentAno) {
                if ($intMes >= 1 && $intMes <= 12) {
                    return true;
                }
            } elseif ($ano == $currentAno) {
                $currentMes = intval(date('m'));
                if ($intMes >= $currentMes) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }
        return false;
    }

    public function expiryAnoValido($ano)
    {
        $valid = false;
        $ano = substr(date('Y'), 0, 2) . $ano;

        if (strlen($ano) == 4) {
            $intAno = intval($ano);
            $currentAno = intval(date('Y'));
            if ($intAno >= $currentAno) {
                return true;
            }
        }
        return $valid;
    }

    public function cartaoValido($number)
    {
        settype($number, 'string');
        $sumTable = array(
            array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
            array(0, 2, 4, 6, 8, 1, 3, 5, 7, 9)
        );
        $sum = 0;
        $flip = 0;
        for ($i = strlen($number) - 1; $i >= 0; $i--) {
            $sum += $sumTable[$flip++ & 0x1][$number[$i]];
        }
        return $sum % 10 === 0;
    }

    private function checkSaveCardRequest()
    {
        return isset($_POST['ipag_check_save_card']) && $_POST['ipag_check_save_card'] === 'yes';
    }

    private function getTokenCC($id)
    {
        $classTokenCC = 'WC_Payment_Token_CC';

        if (class_exists($classTokenCC)) {
            $tokenCC = new $classTokenCC($id);

            return !empty($tokenCC->get_token()) ? $tokenCC->get_token() : null;
        }

        return null;
    }

    private function saveTokenCC($card)
    {
        $classTokenCC = 'WC_Payment_Token_CC';

        $user_id = $this->getUserLoggedIn();
        $gateway_id = $this->id;

        $dataLog = array_merge(
            (array) MaskUtils::applyMaskRecursive($card),
            compact('user_id', 'gateway_id')
        );

        if (empty($user_id)) {
            self::addDataLog("tokenização do método não realizada (usuário não logado)", $dataLog, 'aviso');
            return false;
        }

        if (class_exists($classTokenCC)) {
            try {
                $tokenCC = new $classTokenCC();

                $tokenCC->set_user_id($user_id);
                $tokenCC->set_gateway_id($gateway_id);

                $tokenCC->set_token($card->token);
                $tokenCC->set_last4($card->last4);
                $tokenCC->set_expiry_year($card->expiry_year);
                $tokenCC->set_expiry_month($card->expiry_month);
                $tokenCC->set_card_type($card->card_type);

                $tokenCC->add_meta_data('user_consent', $card->user_consent);

                $tokenCC->save();

                self::addDataLog(
                    "tokenização do método realizada com sucesso",
                    array_merge(
                        ['token_id' => $tokenCC->get_id()],
                        $dataLog
                    ),
                    'info'
                );

                return true;
            } catch (\Throwable $e) {
                self::addDataLog(
                    "tokenização do método não realizada devido ao erro: {$e->getMessage()}",
                    array_merge(
                        ['exception' => strval($e)],
                        $dataLog
                    ),
                    'erro'
                );
            }
        }

        return false;
    }

    public function process_payment($order_id)
    {
        update_post_meta($order_id, '_ipag_payment_pending', 'yes');
        $result = $this->process_ipag_payment($order_id);
        update_post_meta($order_id, '_ipag_payment_pending', 'no');

        if (!$result['success']) {
            $friendly_message = $this->format_payment_error_message($result['message'] ?? '');
            wc_add_notice(__('Payment error: ' . $friendly_message, 'ipag-gateway'), 'error');
            self::addDataLog(">>> FIM_PROCESSO_PGTO_CC ORDER #{$order_id} <<<");

            $is_blocks = isset($_POST['ipag_woo_payments_blocks']) && $_POST['ipag_woo_payments_blocks'] === 'yes';

            if ($is_blocks) {
                throw new Exception('Payment error: ' . $friendly_message);
            }

            return array(
                'result' => 'fail',
                'message' => $friendly_message,
            );
        }

        global $woocommerce;
        $order = new WC_Order($order_id);
        $order->set_total($result['total']);

        version_compare($woocommerce->version, '3.0', '>=') ?
            wc_reduce_stock_levels($order_id) :
            $order->reduce_order_stock();

        $woocommerce->cart->empty_cart();

        // @NOTE: se o status do pagamento for 4 e existir url de autenticação, a transação é 3DS
        if (!empty($result['auth_url']) && $this->isEmAnalise(get_post_meta($order_id, '_status_payment', false)[0])) {
            self::addDataLog('iniciando autenticação de pagamento (3DS): redirecionando para a url recebida', [
                'status_pagamento' => get_post_meta($order_id, '_status_payment', false)[0],
                'url_autenticação' => $result['auth_url']
            ], 'info');

            self::addDataLog(">>> FIM_PROCESSO_PGTO_CC ORDER #{$order_id} <<<");

            return array(
                'result' => 'success',
                'redirect' => $result['auth_url'],
            );
        }

        self::addDataLog(">>> FIM_PROCESSO_PGTO_CC ORDER #{$order_id} <<<");

        return array(
            'result' => 'success',
            'redirect' => $this->get_return_url($order),
        );
    }

    public function process_ipag_payment($order_id)
    {
        $has_subscription = false;
        $order_subscription = null;

        $order = new WC_Order($order_id);

        $this->normalize_blocks_post_data();

        self::addDataLog(">>> INICIO_PROCESSO_PGTO_CC ORDER #{$order_id} <<<");

        $expiry = (isset($_POST['ipag_credito_card_expiry'])) ? $_POST['ipag_credito_card_expiry'] : '';
        $expiry_split = explode('/', $expiry);
        $acquirerToken = (isset($_POST['cartao_tokenpagseguro'])) ? $_POST['cartao_tokenpagseguro'] : '';
        $fingerprint_pagseguro = (isset($_POST['cartao_hashpagseguro'])) ? $_POST['cartao_hashpagseguro'] : '';
        $cc_nome = (isset($_POST['ipag_credito_card_name'])) ? $_POST['ipag_credito_card_name'] : '';

        $cc_numero = (isset($_POST['ipag_credito_card_num'])) ? $_POST['ipag_credito_card_num'] : '';
        $cc_numero = preg_replace('/\s+/', '', $cc_numero);
        $cc_val_mes = (isset($expiry_split[0])) ? trim($expiry_split[0]) : '';
        $cc_val_ano = (isset($expiry_split[1])) ? trim($expiry_split[1]) : '';
        if (strlen($cc_val_mes) > strlen($cc_val_ano)) {
            $cc_val_mes = str_pad($cc_val_mes, 4, "0", STR_PAD_LEFT);
            $cc_val_ano = substr($cc_val_mes, 2, 2);
            $cc_val_mes = substr($cc_val_mes, 0, 2);
        }

        $cc_cvv = (isset($_POST['ipag_credito_card_cvv'])) ? $_POST['ipag_credito_card_cvv'] : '';
        $cc_parcelas = (!empty($_POST['ipag_credito_installments'])) ? $_POST['ipag_credito_installments'] : 0;
        $cc_cpf = (isset($_POST['ipag_credito_card_cpf'])) ? $_POST['ipag_credito_card_cpf'] : '';
        $cc_cpf = preg_replace('/\D/', '', $cc_cpf);
        $ipagtoken = (isset($_POST['ipag_credito_card_token'])) ? $_POST['ipag_credito_card_token'] : '';
        $pass_interest = $this->pass_interest;

        if (isset($_POST['ipag_saved_card_option']) && $_POST['ipag_saved_card_option'] === self::OPTION_SAVED_CARD) {
            $cc_parcelas = !empty($_POST['ipag_saved_cards_installments']) ? intval($_POST['ipag_saved_cards_installments']) : $cc_parcelas;
            $idCC = !empty($_POST['ipag_saved_token_id']) ? intval($_POST['ipag_saved_token_id']) : null;
            $ipagtoken = $this->getTokenCC($idCC);
        }

        $maxparcelas = $this->maximum_installment;
        $cc_metodo = $this->getCardType($cc_numero);

        if (empty($cc_metodo) || $cc_metodo == 'NULL') {
            $cc_metodo = 'visa';
        } //@NOTE: Fallback

        if (($cc_parcelas + 1) > $this->interest_free_installment) {
            $total = $this->valorParcelas($order->get_total(), $maxparcelas, $this->interest_rate);
            $total = $total[$cc_parcelas] * ($cc_parcelas + 1);
        } else {
            $total = (float) $order->get_total();
        }

        if (mb_strtolower($pass_interest) === 'yes') {
            $cartTotal = WC()->cart->total;
            $ped = new WC_Order(get_query_var('order-pay'));

            $totalRef = !empty($cartTotal) ? $cartTotal : $ped->get_total();

            if (!empty($cc_parcelas)) {
                $checkoutInstallments = $this->getCheckoutInstallments($totalRef);

                if (!empty($checkoutInstallments)) {
                    $instFound = array_reduce($checkoutInstallments, function ($acc, $cur) use ($cc_parcelas) {
                        return !empty($acc) ? $acc : ($cur['installment'] == $cc_parcelas ? $cur : $acc);
                    }, null);

                    if (!empty($instFound)) {
                        $total = $instFound['amount'];
                        $cc_parcelas = intval($instFound['installment']) - 1;
                    }
                } else {
                    $cc_parcelas = 0;
                }
            } else {
                $total = $totalRef;
            }

            if (empty($total)) {
                self::addDataLog("não possível processar o parcelamento do pedido #{$order_id}", compact('total', 'cc_parcelas'), 'erro');
                return [
                    'success' => false,
                    'message' => __(' Não foi possível processar o parcelamento. Verifique os dados e tente novamente.', 'ipag-gateway'),
                ];
            }
        }

        $args = $this->getOrderData($order);
        $documento = empty($args['documento']) ? $cc_cpf : $args['documento'];
        $produtos = $this->getDescricaoPedido($order);
        $ano = date('Y');
        $mes = date('m');
        $ano2d = preg_replace('/^[\d]{2}/', '', $ano);

        // Subscription check
        if (class_exists('WC_Subscriptions_Cart')) {
            $has_subscription = wcs_order_contains_subscription($order);
            $countSubscriptions = count(wcs_get_subscriptions_for_order($order));

            if ($has_subscription && $countSubscriptions > 1) {
                $error_message = __('Apenas uma assinatura é permitida por pedido. Por favor, ajuste seu carrinho e tente novamente.', 'ipag-gateway');
                self::addDataLog("não possível processar o pedido #{$order_id} com múltiplas assinaturas", compact('has_subscription', 'countSubscriptions'), 'erro');
                return [
                    'success' => false,
                    'message' => $error_message,
                ];
            }
        }

        // Dados 3DS
        $ipag3Ds = isset($_POST['ipag_helper_3ds']) && is_array($_POST['ipag_helper_3ds']) ? $_POST['ipag_helper_3ds'] : [];

        // Dados AntiFraude
        $ipagAntifraud = isset($_POST['ipag_helper_antifraud']) && is_array($_POST['ipag_helper_antifraud']) ? $_POST['ipag_helper_antifraud'] : [];

        $anovalid1 = true;
        if ($cc_val_ano < $ano2d) {
            $anovalid1 = false;
        }
        if ($cc_val_ano == $ano2d && $cc_val_mes < $mes) {
            $anovalid1 = false;
        }
        $fingerprintPS = (isset($_POST['cartao_hashpagseguro'])) ? $_POST['cartao_hashpagseguro'] : '';
        $cardTokenPS = (isset($_POST['cartao_tokenpagseguro'])) ? $_POST['cartao_tokenpagseguro'] : '';

        $error_message = '';
        $validPayload =
            $this->cartaoValido($cc_numero) &&
            !empty($cc_nome) &&
            strlen($cc_cvv) > 2 &&
            !empty($cc_numero) &&
            $anovalid1 &&
            $this->expiryMesValido($cc_val_mes, $cc_val_ano);

        if ($validPayload || !empty($ipagtoken)) {
            $payload = array(
                'identificacao' => $this->identification,
                'metodo' => $cc_metodo,
                'operacao' => 'Pagamento',
                'pedido' => $order_id,
                'valor' => number_format($total, 2, '.', ''),
                'parcelas' => $cc_parcelas + 1,
                'acquirerToken' => $cardTokenPS,
                'fingerprint' => $fingerprintPS,
                'cpf_cartao' => $cc_cpf,
                'nome' => $args['billingName'],
                'gera_token_cartao' => $this->tokenize_card(),
                'documento' => preg_replace('/[\D]/', '', $documento),
                'email' => $args['billingEmail'],
                'fone' => preg_replace('/[\D]/', '', $args['billingPhone']),
                'endereco' => trim($args['billingAddress']),
                'numero_endereco' => str_replace(' ', '', $args['billingNumber']),
                'bairro' => empty($args['billingNeighborhood']) ? 'bairro' :
                    trim(preg_replace("/[^a-zA-Z ]/", "", $args['billingNeighborhood'])),
                'complemento' => empty($args['billingAddress2']) ? '' :
                    trim(preg_replace("/[^a-zA-Z ]/", "", $args['billingAddress2'])),
                'cidade' => $args['billingCity'],
                'estado' => substr($args['billingState'], 0, 2),
                'pais' => 'Brasil',
                'cep' => preg_replace('/[\D]/', '', $args['billingPostcode']),
                'endereco_entrega' => !empty(trim($args['shippingAddress'])) ? trim($args['shippingAddress']) : trim($args['billingAddress']),
                'numero_endereco_entrega' => !empty($args['shippingNumber']) ? str_replace(' ', '', $args['shippingNumber']) : str_replace(' ', '', $args['billingNumber']),
                'bairro_entrega' => empty($args['shippingNeighborhood']) ? 'bairro' :
                    trim(preg_replace("/[^a-zA-Z ]/", "", $args['shippingNeighborhood'])),
                'complemento_entrega' => empty($args['shippingAddress2']) ? '' :
                    trim(preg_replace("/[^a-zA-Z ]/", "", $args['shippingAddress2'])),
                'cidade_entrega' => !empty($args['shippingCity']) ? $args['shippingCity'] : $args['billingCity'],
                'estado_entrega' => !empty($args['shippingState']) ? substr($args['shippingState'], 0, 2) : substr($args['billingState'], 0, 2),
                'cep_entrega' => !empty($args['shippingPostcode']) ? preg_replace('/[\D]/', '', $args['shippingPostcode']) : preg_replace('/[\D]/', '', $args['billingPostcode']),
                'descricao_pedido' => $produtos,
                'retorno_tipo' => 'XML',
                'url_retorno' => self::isLocal() ? self::getLocalCallbackUrl() . "/?wc-api=wc_gateway_ipag&id={$order_id}" : home_url('/?wc-api=wc_gateway_ipag&id=' . $order_id),
                'url_redirect' => home_url('/?page_id=8&order-received=' . $order->get_id() . '&key=' . $order->get_order_key()),
                'ip' => $this->getClientIp($this->pagseguro_habilitado == 'yes'),
                'vencto' => ''
            );

            $payload = array_merge($payload, $ipag3Ds);
            $payload = array_merge($payload, $ipagAntifraud);

            if (empty($ipagtoken)) {
                $payload['nome_cartao'] = $cc_nome;
                $payload['num_cartao'] = $cc_numero;
                $payload['cvv_cartao'] = $cc_cvv;
                $payload['mes_cartao'] = $cc_val_mes;
                $payload['ano_cartao'] = $cc_val_ano;
            } else {
                $payload['token_cartao'] = $ipagtoken;
            }

            if ($has_subscription) {
                $order_subscription = array_first(wcs_get_subscriptions_for_order($order));
                $this->payloadWithSubscription($payload, $order_subscription);
            }

            $visitorid = '';
            if (isset($_COOKIE['_kdt'])) {
                $json = str_replace("\\", "", $_COOKIE['_kdt']);
                $cookie = json_decode($json, true);
                $visitorid = $cookie['i'];
                $payload['visitor_id'] = $visitorid;
            }

            $result = $this->processCreditCard($payload, $order, $order_subscription);

            if (!$result['success']) {
                return [
                    'success' => false,
                    'message' => $result['message'],
                ];
            }

            return [
                'success' => true,
                'message' => '',
                'total' => $total,
                'auth_url' => $result['auth_url'],
            ];
        } else {
            if (!$this->cartaoValido($cc_numero)) {
                $error_message = __(' Invalid credit card. Please choose another one!', 'ipag-gateway');
            } elseif (!$anovalid1 || !$this->expiryMesValido($cc_val_mes, $cc_val_ano)) {
                $error_message = __(' Validade do cartão expirada!', 'ipag-gateway');
            } elseif (empty($cc_numero)) {
                $error_message = __(' Preencha o número do cartão !', 'ipag-gateway');
            } elseif (empty($cc_nome)) {
                $error_message = __(' Preencha o nome do cartão!', 'ipag-gateway');
            } elseif (strlen($cc_cvv) < 3) {
                $error_message = __(' Preencha o CVV do cartão corretamente!', 'ipag-gateway');
            } elseif ($cc_val_ano == $ano && $cc_val_mes < $mes) {
                $error_message = __(' Validade do cartão expirada!', 'ipag-gateway');
            } else {
                $error_message = __(' This card isn\'t accepted. Please choose another one!', 'ipag-gateway');
            }
            update_post_meta($order_id, '_transaction_message', "Cartão inválido. Pagamento não enviado ao gateway.");
            $order->add_order_note("Cartão inválido. Pagamento não enviado ao gateway.");
            self::writeLog('Cartão inválido. Pagamento não enviado ao gateway.');
        }

        return [
            'success' => false,
            'message' => $error_message ?: __('Payment error processing credit card.', 'ipag-gateway'),
        ];
    }

    private function normalize_blocks_post_data()
    {
        if (empty($_POST['ipag_woo_payments_blocks']) || $_POST['ipag_woo_payments_blocks'] !== 'yes') {
            return;
        }

        foreach ($_POST as $key => $value) {
            if (strpos($key, 'ipag_helper_') !== 0) {
                continue;
            }

            $dash_pos = strpos($key, '-');
            if ($dash_pos === false) {
                continue;
            }

            $base = substr($key, 0, $dash_pos);
            $remainder = substr($key, $dash_pos + 1);

            if ($base === '' || $remainder === '') {
                continue;
            }

            $segments = explode('-', $remainder);
            if (empty($segments)) {
                continue;
            }

            if (!isset($_POST[$base]) || !is_array($_POST[$base])) {
                $_POST[$base] = [];
            }

            $current =& $_POST[$base];
            $last_index = count($segments) - 1;

            foreach ($segments as $index => $segment) {
                if ($segment === '') {
                    continue;
                }

                $is_last = $index === $last_index;

                if ($is_last) {
                    $current[$segment] = $value;
                    continue;
                }

                if (!isset($current[$segment]) || !is_array($current[$segment])) {
                    $current[$segment] = [];
                }

                $current =& $current[$segment];
            }
        }
    }

    private function payloadWithSubscription(&$payload, $subscription)
    {
        $subs_interval = self::getProp($subscription, 'billing_interval');
        $subs_trial_period = self::getProp($subscription, 'trial_period');
        $subs_period = self::getProp($subscription, 'billing_period');
        $sub_signup_fee = self::getProp($subscription, 'sign_up_fee');
        $subs_start_payment = $subscription->get_time('next_payment');

        if (empty($subs_start_payment) || $subs_start_payment <= 0) {
            $subs_start_payment = $subscription->get_time('start');
        }

        if (empty($subs_start_payment) || $subs_start_payment <= 0) {
            $subs_start_payment = time();
        }

        $subs_length = wcs_estimate_periods_between(
            $subscription->get_time('start'),
            $subscription->get_time('end'),
            $subs_period
        );

        $subs_trial_length = wcs_estimate_periods_between(
            $subscription->get_time('start'),
            $subscription->get_time('trial_end'),
            $subs_trial_period
        );

        $payload['valor_rec'] = number_format(self::getProp($subscription, 'total'), 2, '.', '');
        $payload['inicio'] = date('d/m/Y', $subs_start_payment);
        $payload['frequencia'] = $subs_interval;
        $payload['intervalo'] = $subs_period;
        $payload['gera_token_cartao'] = true;
        $payload['trial'] = '0';

        if (is_numeric($subs_length) && intval($subs_length) > 0) {
            $payload['ciclos'] = (int) $subs_length;
        }

        if (is_numeric($subs_trial_length) && intval($subs_trial_length) > 0 && floatval($sub_signup_fee) <= 0) {
            $payload['trial'] = '1';
            $inicio = strtotime("+" . intval($subs_trial_length) . " " . $subs_period, $subs_start_payment);

            if ($inicio !== false) {
                $payload['inicio'] = date('d/m/Y', $inicio);
            }
            $payload['trial_frequencia'] = $subs_period;
            $payload['trial_ciclos'] = (int) $subs_trial_length;
        }

        self::addDataLog("incorporado dados de assinatura ao payload do pedido", [
            'wc_linked_order_id' => $payload['pedido'],
            'wc_linked_subscription' => self::getProp($subscription, 'id'),
        ], 'info');
    }

    public function processCreditCard($payload, $order, $subscription = null)
    {
        global $woocommerce;
        $xml = '';

        $order_id = self::getProp($order, 'id');

        $url = self::buildRequestUrl("service/payment", $this->environment);

        $headers = $this->buildRequestHeaders([
            'Accept' => 'application/xml',
        ]);

        $args = $this->buildRequestPayload(
            array(
                'body' => $payload,
                'headers' => $headers,
            )
        );

        $parser = !$this->isPayloadV1($args) ? null : $this->findEndpointV2Parser($url);

        if ($parser) {
            $url = $this->transformParserRequestUrl($parser, $url) ?: $url;
            $args = $this->transformParserRequestPayload($parser, $args) ?: $args;
        }

        try {

            $result = $this->wp_remote_post_wrapper($url, $args);
            $result = $this->transformParserResponse($parser, $result);

            $xml = simplexml_load_string($result['body'], 'SimpleXMLElement');

            if (!$xml) {
                self::addDataLog("erro ao processar dados recebido do iPag referente ao pedido #{$order_id}", compact('xml'), 'erro');
                throw new Exception('Error processing payment');
            }

            $this->registerTransaction((string) $xml->id_transacao, $order_id);

            $newStatus = null;
            $statusPagamento = (string) $xml->status_pagamento;

            $mapStatus = [
                3 => $this->status_cancelado,
                5 => $this->status_aprovado,
                7 => $this->status_reprovado,
                8 => $this->status_capturado,
            ];

            if (array_key_exists($statusPagamento, $mapStatus)) {
                $newStatus = $mapStatus[$statusPagamento];
            }

            if ($newStatus) {
                self::addDataLog("mudando o status do objeto WC_Order", [
                    'old_status_wc_order' => $order->get_status(),
                    'new_status_wc_order' => $newStatus,
                ], 'info');

                self::mudaStatus($order, $newStatus, $this->get_status($statusPagamento));
            }

            if ($this->isAprovado($statusPagamento)) {
                version_compare($woocommerce->version, '3.0', ">=") ? wc_reduce_stock_levels($order_id) : $order->reduce_order_stock();
            }

            if (array_key_exists('token_cartao', $payload)) {
                $expiry_split = explode('/', $xml->cartao->vencimento);
                $cc_val_mes = (isset($expiry_split[0])) ? trim($expiry_split[0]) : '';
                $cc_val_ano = (isset($expiry_split[1])) ? trim($expiry_split[1]) : '';
                $payload['nome_cartao'] = $xml->cartao->titular;
                $payload['num_cartao'] = $xml->cartao->numero;
                $payload['mes_cartao'] = $cc_val_mes;
                $payload['ano_cartao'] = $cc_val_ano;
            }

            $card = $this->splitCard($payload['num_cartao']);
            update_post_meta($order_id, '_card_type', (string) $this->cardTypeClearSale($payload['metodo']));
            update_post_meta($order_id, '_card_bin', (string) $card['bin']);
            update_post_meta($order_id, '_card_end', (string) $card['end']);
            update_post_meta($order_id, '_card_masked', (string) $card['masked']);
            update_post_meta($order_id, '_card_exp_month', (string) $payload['mes_cartao']);
            update_post_meta($order_id, '_card_exp_year', (string) $payload['ano_cartao']);
            update_post_meta($order_id, '_card_name', (string) $payload['nome_cartao']);
            update_post_meta($order_id, '_card_cpf', (string) $payload['cpf_cartao']);
            update_post_meta($order_id, '_status_payment', $statusPagamento);
            update_post_meta($order_id, '_transaction_id', (string) $xml->id_transacao);
            update_post_meta($order_id, '_transaction_message', (string) $xml->mensagem_transacao);
            update_post_meta($order_id, '_operator_message', (string) $xml->operadora_mensagem);
            update_post_meta($order_id, '_installment_number', $payload['parcelas'] . 'x - Total: R$ ' . $payload['valor']);
            update_post_meta($order_id, '_metodo', (string) $xml->metodo);
            update_post_meta($order_id, '_auth_url', (string) $xml->url_autenticacao);

            $token_user_consent = (bool) $this->checkSaveCardRequest();

            if (!empty($payload['gera_token_cartao']) && $token_user_consent && !empty((string) $xml->token)) {
                update_post_meta($order_id, '_card_token', (string) $xml->token);

                if ($this->tokenize_card()) {
                    $this->saveTokenCC((object) [
                        'order_id' => $order_id,
                        'token' => (string) $xml->token,
                        'last4' => (string) $xml->last4,
                        'expiry_year' => (string) $xml->ano,
                        'expiry_month' => (string) $xml->mes,
                        'card_type' => (string) $xml->metodo,
                        'user_consent' => $token_user_consent,
                    ]);
                }
            }

            if ($subscription) {
                update_post_meta(self::getProp($subscription, 'id'), 'ipag_subscription_id', (string) $xml->assinatura->id);
            }

            if ($this->isReprovado($statusPagamento) || $this->isCancelado($statusPagamento)) {
                self::addDataLog("status da transação retornada como reprovada/cancelada do iPag (processamento de pagamento do pedido #{$order_id})", [
                    'mensagem_transacao' => (string) $xml->mensagem_transacao,
                    'status_transacao' => $statusPagamento,

                ], 'erro');

                return [
                    'success' => false,
                    'message' => (string) $xml->mensagem_transacao,
                ];
            }

            self::addDataLog("processo de pagamento do pedido #{$order_id} finalizado com sucesso", [
                'order_id' => $order_id,
                'id_transacao' => (string) $xml->id_transacao,
            ], 'info');

            return [
                'success' => true,
                'message' => '',
                'total' => $payload['valor'],
                'auth_url' => (string) $xml->url_autenticacao,
            ];
        } catch (Exception $e) {
            $dataLog = [
                'url' => $url,
                'exception' => strval($e)
            ];

            if ($e instanceof IpagResponseException) {
                $dataLog['response'] = $e->getResponse();
            }

            self::addDataLog("erro de comunicação com a api do iPag (processamento de pagamento do pedido #{$order_id}): {$e->getMessage()}", $dataLog, 'erro');

            $error_message = $e->getMessage();

            $error_message = wp_unslash($error_message);
            $decoded = json_decode($error_message, true);
            $error_message = $decoded;

            $error_message = html_entity_decode($error_message);

            $order->update_status('failed', __('Payment error: ' . $error_message, 'ipag-gateway'));

            return [
                'success' => false,
                'message' => $error_message,
            ];
        }
    }

    /**
     * Process a refund if supported.
     *
     * @param  int    $order_id Order ID.
     * @param  float  $amount Refund amount.
     * @param  string $reason Refund reason.
     * @return bool|WP_Error
     */
    public function process_refund($order_id, $amount = null, $reason = '')
    {
        $order = wc_get_order($order_id);
        $tid = get_post_meta($order_id, '_transaction_id', true);
        $status = get_post_meta($order_id, '_transaction_message', true);

        self::addDataLog(">>> INICIO_PROCESSO_CANCELAMENTO_CC ORDER #{$order_id} <<<");

        $id = $this->identification;
        $ambiente = $this->environment;
        $status_cancelado = $this->status_cancelado;

        $url = self::buildRequestUrl('service/cancel', $ambiente);

        $order = new WC_Order($order_id);

        $payload = array(
            'transId' => $tid
        );

        $headers = array(
            'Authorization' => 'Basic ' . base64_encode($this->identification . ':' . $this->apikey),
        );
        $args = $this->buildRequestPayload(
            array(
                'body' => $payload,
                'headers' => $headers,
            )
        );

        $parser = !$this->isPayloadV1($args) ? null : $this->findEndpointV2Parser($url);

        if ($parser) {
            $url = $this->transformParserRequestUrl($parser, $url) ?: $url;
            $args = $this->transformParserRequestPayload($parser, $args) ?: $args;
        }

        $result = $this->wp_remote_post_wrapper($url, $args);
        $result = $this->transformParserResponse($parser, $result);

        $xml = simplexml_load_string($result['body'], 'SimpleXMLElement');

        if (!empty($xml) && is_object($xml)) {
            if ($xml->status_pagamento == 3) {
                self::mudaStatus($order, $status_cancelado, self::get_status((string) $xml->status_pagamento));
            }

            update_post_meta($order_id, '_operator_message', (string) $xml->operadora_mensagem);
            update_post_meta($order_id, '_transaction_message', (string) $xml->mensagem_transacao);
            update_post_meta($order_id, '_status_payment', (string) $xml->status_pagamento);

            $order->add_order_note(self::get_status((string) $xml->status_pagamento) . ' - ' . (string) $xml->mensagem_transacao);

            if (!self::existsTransaction((string) $xml->id_transacao)) {
                self::registerTransaction((string) $xml->id_transacao, $order_id);
            } else {
                self::updateTransaction((string) $xml->id_transacao, (string) $xml->status_pagamento);
            }

            $retorno = array('status' => (string) $xml->status_pagamento, 'mensagem' => (string) $xml->mensagem_transacao);
        }

        self::addDataLog(">>> FIM_PROCESSO_CANCELAMENTO_CC ORDER #{$order_id} <<<");
        return isset($error) ? new WP_Error('error', 'Resposta iPag: ' . $error) : true;
    }

    public function getCheckoutInstallments($total)
    {
        $headers = $this->buildRequestHeaders(
            [
                'x-api-version' => 2,
                'Content-Type' => 'application/json',
            ]
        );

        $url = self::buildRequestUrl(
            "service/v2/checkout/installments",
            $this->environment,
            [
                'amount' => floatval($total),
            ]
        );

        $args = $this->buildRequestPayload(
            [
                'method' => 'GET',
                'headers' => $headers,
            ]
        );

        try {
            $result = $this->wp_remote_request_wrapper($url, $args);
            $result = $this->transformParserResponse(null, $result);

            $responseData = self::parseResponseData($result);

            return $responseData;
        } catch (\Exception $e) {
            $dataLog = [
                'exception' => strval($e)
            ];

            if ($e instanceof IpagResponseException) {
                $dataLog['response'] = $e->getResponse();
            }

            self::addDataLog(
                "Erro Requisição API: {$e->getMessage()}",
                $dataLog,
                'erro'
            );
        }

        return false;
    }

    /**
     * @param int $subscriptionId
     * @return false|array
     */
    public function getSubscription($subscriptionId, $logCallbackHandler = null)
    {
        $url = self::buildRequestUrl("service/resources/subscriptions?id={$subscriptionId}", $this->environment);

        $headers = $this->buildRequestHeaders(
            array(
                'x-api-version' => 2,
                'Content-Type' => 'application/json'
            )
        );

        $args = $this->buildRequestPayload(
            array(
                'method' => 'GET',
                'headers' => $headers,
            )
        );

        try {
            $result = $this->wp_remote_request_wrapper($url, $args, $logCallbackHandler);
            $result = $this->transformParserResponse(null, $result);

            $responseData = self::parseResponseData($result);

            return $responseData;
        } catch (\Exception $e) {
            $dataLog = [
                'exception' => strval($e)
            ];

            if ($e instanceof IpagResponseException) {
                $dataLog['response'] = $e->getResponse();
            }

            self::{$logCallbackHandler}(
                "erro de comunicação com a api do iPag (obter a assinatura #{$subscriptionId}): {$e->getMessage()}",
                $dataLog,
                'erro'
            );

            if (function_exists('wc_add_notice')) {
                wc_add_notice(__($e->getMessage(), 'ipag-gateway'), 'error');
            }

            return false;
        }
    }

    /**
     * @param int $subscriptionId
     * @return false|mixed
     */
    public function disableSubscription($subscriptionId)
    {
        $url = self::buildRequestUrl("service/resources/subscriptions", $this->environment);

        $headers = $this->buildRequestHeaders(
            array(
                'x-api-version' => 2,
                'Content-Type' => 'application/json'
            )
        );

        $payload = array(
            'is_active' => false
        );

        $query = array(
            'id' => $subscriptionId
        );

        $args = $this->buildRequestPayload(
            array(
                'method' => 'PUT',
                'body' => $payload,
                'headers' => $headers,
                'query' => $query
            )
        );

        try {
            $result = $this->wp_remote_request_wrapper($url, $args);
            $result = $this->transformParserResponse(null, $result);

            $responseData = self::parseResponseData($result);

            return $responseData;
        } catch (\Exception $e) {
            $dataLog = [
                'url' => $url,
                'exception' => strval($e)
            ];

            if ($e instanceof IpagResponseException) {
                $dataLog['response'] = $e->getResponse();
            }

            self::addDataLog("erro de comunicação com a api do iPag (desativar a assinatura #{$subscriptionId}): {$e->getMessage()}", $dataLog, 'erro');

            return false;
        }
    }

    /**
     * Cancela uma transação com o ID especificado.
     *
     * @param int $transactionId
     * @return false|mixed
     */
    public function cancelTransaction($transactionId, $orderId = null)
    {
        $url = self::buildRequestUrl("service/cancel", $this->environment);

        $query = [];

        if (!empty($transactionId)) {
            $query['tid'] = $transactionId;
        }

        if (!empty($orderId)) {
            $query['order_id'] = $orderId;
        }

        $headers = $this->buildRequestHeaders(
            array(
                'x-api-version' => 2,
                'Content-Type' => 'application/json'
            )
        );

        $args = $this->buildRequestPayload(
            array(
                'method' => 'POST',
                'headers' => $headers,
                'query' => $query,
            )
        );

        $argsLog = self::buildArgsLog($args);

        try {
            $result = $this->wp_remote_request_wrapper($url, $args);
            //@NOTE: enpoint já retorna resposta na lastest version, não é necessário passar por parser.
            $result = $this->transformParserResponse(null, $result);
            return self::parseResponseData($result);
        } catch (\Exception $e) {
            $dataLog = [
                'url' => $url,
                'request_params' => $argsLog,
                'exception' => strval($e)
            ];

            if ($e instanceof IpagResponseException)
                $dataLog['response'] = $e->getResponse();

            self::addDataLog("erro de comunicação com a api do iPag (processamento de pagamento do pedido #{$orderId}): {$e->getMessage()}", $dataLog, 'erro');

            $error_message = __($e->getMessage(), 'ipag-gateway');

            if (function_exists('wc_add_notice') && function_exists('wc') && wc() && isset(wc()->session) && is_object(wc()->session)) {
                wc_add_notice(__('API error: ' . $error_message, 'ipag-gateway'), 'error');
            }

            return false;
        }
    }

    public static function capturePayment()
    {
        $id = self::getRequestId();
        $order_id = $_REQUEST['order'];
        $transid = $_REQUEST['transid'];
        $ambiente = $_REQUEST['environment'];
        $status_capturado = $_REQUEST['status_capturado'];

        self::addDataLog(">>> INICIO_PROCESSO_CAPTURA_CC ORDER #{$order_id} <<<");

        $card = new WC_Gateway_iPag_Credito();
        $order = new WC_Order($order_id);

        $url = self::buildRequestUrl('service/capture', $ambiente);

        $headers = $card->buildRequestHeaders();

        $payload = array(
            'identificacao' => $id,
            'transId' => $transid,
            'url_retorno' => home_url('/wc-api/wc_gateway_ipag/?'),
            'tipo_retorno' => 'XML',
        );

        $args = $card->buildRequestPayload(
            array(
                'body' => $payload,
                'headers' => $headers,
            )
        );

        $parser = !$card->isPayloadV1($args) ? null : $card->findEndpointV2Parser($url);

        if ($parser) {
            $url = $card->transformParserRequestUrl($parser, $url) ?: $url;
            $args = $card->transformParserRequestPayload($parser, $args) ?: $args;
        }

        $result = $card->wp_remote_post_wrapper($url, $args);
        $result = $card->transformParserResponse($parser, $result);

        $xml = simplexml_load_string($result['body'], 'SimpleXMLElement');
        //fazer update dos post_metas
        if (!empty($xml) && is_object($xml)) {
            switch ($xml->status_pagamento) {
                case 8: //aprovado e capturado
                    self::mudaStatus($order, $status_capturado, self::get_status((string) $xml->status_pagamento));
                    break;
            }
            update_post_meta($order_id, '_operator_message', (string) $xml->operadora_mensagem);
            update_post_meta($order_id, '_transaction_message', (string) $xml->mensagem_transacao);
            update_post_meta($order_id, '_status_payment', (string) $xml->status_pagamento);
            $order->add_order_note(self::get_status((string) $xml->status_pagamento) . ' - ' . (string) $xml->mensagem_transacao);
            if (!self::existsTransaction((string) $xml->id_transacao)) {
                self::registerTransaction((string) $xml->id_transacao, $order_id);
            } else {
                self::updateTransaction((string) $xml->id_transacao, (string) $xml->status_pagamento);
            }
            $retorno = array(
                'status' => (string) $xml->status_pagamento,
                'mensagem' => (string) $xml->mensagem_transacao,
                'operator_message' => (string) $xml->operadora_mensagem
            );
        }
        echo json_encode($retorno);
        self::addDataLog(">>> FIM_PROCESSO_CAPTURA_CC ORDER #{$order_id} <<<");
        wp_die();
    }

    public function splitCard($card_number)
    {
        $card_len = strlen($card_number) - 10;
        preg_match('/[\d]{4}$/', $card_number, $card_end);
        $card_end = $card_end[0];
        preg_match('/^[\d]{6}/', $card_number, $card_bin);
        $card_bin = $card_bin[0];
        $masked = '';
        $masked .= $card_bin;
        for ($i = 0; $i < $card_len; $i++) {
            $masked .= '*';
        }

        $masked .= $card_end;

        return $retorno = array(
            'masked' => $masked,
            'bin' => $card_bin,
            'end' => $card_end,
        );
    }

    public function cardTypeClearSale($card_type)
    {
        $card = array(
            'mastercard' => 2,
            'visa' => 3,
            'amex' => 5,
            'discover' => 4,
            'diners' => 1,
            'elo' => 4,
            'hipercard' => 6,
            'hiper' => 6,
        );
        return $card[$card_type];
    }

    public function admin_options()
    {
        echo '<h2>' . _e('iPag Payment Gateway - Credit Card', 'ipag-gateway') . '</h2>';
        echo '<table class="form-table">';
        $this->generate_settings_html();
        echo '</table>';
    }

    public function capture_ajax()
    {
        global $theorder;
        if ($theorder) {
            if (!self::getRequestId()) {
                return;
            }

            $postId = self::getRequestId();
            $order = self::getOrder($postId);
            $orderId = self::getProp($order, 'id');
            $transid = get_post_meta($orderId, '_transaction_id', true);
            ?>
            <script type="text/javascript">
                var $j = window.$ || jQuery.noConflict();

                $j(document).ready(function () {
                    $j('#capt_payment').on('click', capturePayment);
                });

                function capturePayment() {
                    $j('#capt_payment').val('<?php _e('Capturing...', 'ipag-gateway') ?>');
                    $j.ajax({
                        method: 'POST',
                        url: ajaxurl,
                        data: {
                            action: 'capture',
                            order: '<?php echo $orderId; ?>',
                            transid: '<?php echo $transid; ?>',
                            id: '<?php echo $this->identification; ?>',
                            environment: '<?php echo $this->environment; ?>',
                            status_capturado: '<?php echo $this->status_capturado; ?>',
                        },
                        success: function (response) {
                            response = JSON.parse(response);
                            $j('#o_msg').html(response.operator_message);
                            $j('#t_msg').html(response.mensagem);
                            $j('#capt_payment').val('<?php _e('Captured', 'ipag-gateway') ?>');
                            $j('#capt_payment').removeClass('button-primary');
                            $j('#capt_payment').attr('disabled', 'disabled');
                        }
                    });
                }
            </script>
            <?php
        }
    }
}
