<?php
/**
 * Credorax Payments For WooCommerce
 * https://www.credorax.com/
 *
 * @category Credorax
 * @package  WC_Credorax
 * @author   Girit-Interactive (https://www.girit-tech.com/)
 * @author   Developer: Pniel Cohen
 */

// phpcs:disable

use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Data\Payment_Methods;

if (! defined( 'ABSPATH')) {
     exit; // Exit if accessed directly.
 }

/**
 * WC_Credorax_Gateway class.
 *
 * @extends WC_Payment_Gateway
 */
class WC_Credorax_Gateway extends WC_Payment_Gateway_CC
{

    const GATEWAY_ID = Payment_Methods::NMC_SHIFT4_CARD;

    /**
     * Modes.
     */
    const MODE_SANDBOX = 'sandbox';
    const MODE_LIVE = 'live';

    /**
     * Payment Actions.
     */
    const ACTION_AUTHORIZE = 'authorize';
    const ACTION_AUTHORIZE_CAPTURE = 'authorize_capture';

    const KEY_RAW_TRANSACTION_DETAILS = '_wc_credorax_raw_transaction_details';

    const KEY_CC_SAVE = '_wc_credorax_cc_save';
    const KEY_CC_TYPE = '_wc_credorax_cc_type';
    const KEY_CC_LAST_4 = '_wc_credorax_cc_last_4';
    const KEY_CC_NUMBER = '_wc_credorax_cc_number';
    const KEY_CC_EXP_MONTH = '_wc_credorax_cc_exp_month';
    const KEY_CC_EXP_YEAR = '_wc_credorax_cc_exp_year';
    const KEY_CC_EXP_DATE = '_wc_credorax_cc_exp_date';
    const KEY_CC_OWNER = '_wc_credorax_cc_owner';
    const KEY_CC_TOKEN = '_wc_credorax_cc_token';
    const KEY_CC_TOKEN_ID = '_wc_credorax_cc_token_id';
    const KEY_CC_TEMP_TOKEN = '_wc_credorax_cc_temp_token';

    const KEY_CREDORAX_3DS_CAVV = '_wc_credorax_3ds_cavv';
    const KEY_CREDORAX_3DS_COMPIND = '_wc_credorax_3ds_compind';
    const KEY_CREDORAX_3DS_ECI = '_wc_credorax_3ds_eci';
    const KEY_CREDORAX_3DS_METHOD = '_wc_credorax_3ds_method';
    const KEY_CREDORAX_3DS_STATUS = '_wc_credorax_3ds_status';
    const KEY_CREDORAX_3DS_TRXID = '_wc_credorax_3ds_trxid';
    const KEY_CREDORAX_3DS_VERSION = '_wc_credorax_3ds_version';
    const KEY_CREDORAX_AUTH_CODE = '_wc_credorax_auth_code';
    const KEY_CREDORAX_BROWSER_LANG = '_wc_credorax_browser_lang';
    const KEY_CREDORAX_LAST_OPERATION_CODE = '_wc_credorax_last_operation_code';
    const KEY_CREDORAX_PAYMENT_RESPONSE_DATA = '_wc_credorax_payment_response_data';
    const KEY_CREDORAX_PKEY = '_wc_credorax_pkey';
    const KEY_CREDORAX_PKEY_DATA = '_wc_credorax_pkey_data';
    const KEY_CREDORAX_RESPONSE_ID = '_wc_credorax_response_id';
    const KEY_CREDORAX_RISK_SCORE = '_wc_credorax_risk_score';
    const KEY_CREDORAX_TRANSACTION_ID = '_wc_credorax_transaction_id';
    const KEY_CREDORAX_PAYMENT_STATUS = '_wc_credorax_payment_status';

    const CREDORAX_PAYMENT_STATUS_PENDING_CHALLENGE = 'pending_challenge';
    const CREDORAX_PAYMENT_STATUS_PENDNIG = 'pending';
    const CREDORAX_PAYMENT_STATUS_CAPTURED = 'captured';
    const CREDORAX_PAYMENT_STATUS_VOIDED = 'voided';
    const CREDORAX_PAYMENT_STATUS_REFUNDED = 'refunded';

    /**
     * @var string
     */
    public $mode = self::MODE_LIVE;

    /**
     * @var bool
     */
    public $is_sandbox_mode = false;

    /**
     * @var string
     */
    public $merchant_id;

    /**
     * @var string
     */
    public $signature_key;

    /**
     * @var string
     */
    public $static_key;

    /**
     * @var string
     */
    public $payment_action = self::ACTION_AUTHORIZE;

    /**
     * @var bool
     */
    public $is_3d_secure_enabled;

    /**
     * @var bool
     */
    public $is_using_smart_3d;

    /**
     * @var bool
     */
    public $save_cards;

    /**
     * @var string|null
     */
    public $billing_descriptor;

    /**
     * @var bool
     */
    public $min_order_total;

    /**
     * @var bool
     */
    public $max_order_total;

    /**
     * @var array
     */
    public $cctypes = [];

    /**
     * @var bool
     */
    public $use_js_loader;

    /**
     * @var bool
     */
    public $debug;

    /**
     * @var WC_Credorax_Model_Request_Factory
     */
    private $requestFactory;

    /**
     * @var WC_Credorax_Model_Response_Factory
     */
    private $responseFactory;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->id = self::GATEWAY_ID;
        $this->icon = WC_CREDORAX_ASSETS_URL . 'images/credit-card.svg'; // $this->icon = WC_CREDORAX_ASSETS_URL . 'images/credorax-logo.png';
        $this->has_fields = true;
        $this->method_title = __('Credorax Payment Solution', 'woocommerce-gateway-credorax');
        $this->method_description = __('Accept payments via Credorax\'s gateway', 'woocommerce-gateway-credorax');

        $this->supports = [
            'products',
            'refunds',
            'tokenization',
            'default_credit_card_form',
        ];

        // Load the form fields.
        $this->init_form_fields();

        // Load the settings.
        $this->init_settings();

        $this->enabled = $this->get_option('enabled');
        $this->title = $this->get_option('title');
        $this->description = $this->get_option('description');
        $this->mode = $this->get_option('mode');
        $this->is_sandbox_mode = $this->mode === self::MODE_SANDBOX;
        $this->merchant_id = $this->is_sandbox_mode ? $this->get_option('sandbox_merchant_id') : $this->get_option('merchant_id');
        $this->signature_key = $this->is_sandbox_mode ? $this->get_option('sandbox_signature_key') : $this->get_option('signature_key');
        $this->static_key = $this->is_sandbox_mode ? $this->get_option('sandbox_static_key') : $this->get_option('static_key');
        $this->payment_action = $this->get_option('payment_action');
        $this->is_3d_secure_enabled = $this->get_option('enable_3d_secure');
        $this->is_using_smart_3d = $this->get_option('use_smart_3d');
        $this->save_cards = $this->get_option('save_cards');
        $this->billing_descriptor = $this->get_option('billing_descriptor');
        $this->cctypes = $this->get_option('cctypes');
        $this->min_order_total = $this->get_option('min_order_total');
        $this->max_order_total = $this->get_option('max_order_total');
        $this->use_js_loader = $this->get_option('use_js_loader');
        $this->debug = $this->get_option('debug');

        // This action hook saves the settings
        add_action('woocommerce_update_options_payment_gateways_' . $this->id, [$this, 'process_admin_options']);

        // Add admin JS
        add_action('admin_enqueue_scripts', [$this, 'payment_settings_scripts']);

        // Add scripts & styles to frontend checkout
        add_action('wp_enqueue_scripts', [$this, 'payment_scripts']);

        // Add additional CC form fields
        add_filter('woocommerce_credit_card_form_fields', array( $this, 'credorax_credit_card_form_fields_filter' ), 10, 2);

        // Control order status changes
        add_filter('woocommerce_payment_complete_order_status', array( $this, 'credorax_payment_complete_order_status_filter' ), 10, 3);

        $this->requestFactory = new WC_Credorax_Model_Request_Factory();
        $this->responseFactory = new WC_Credorax_Model_Response_Factory();
    }

    /**
     * Initialise Gateway Settings Form Fields
     */
    public function init_form_fields()
    {
        $this->form_fields = WC_Credorax_Source_Settings::getFields();
    }

    /**
     * @return bool
     */
    public function isEnabled()
    {
        return $this->enabled === 'yes';
    }

    /**
     * Check if the gateway is available for use.
     *
     * @return bool
     */
    public function is_available()
    {
        return parent::is_available() &&
            ($this->supports('add_payment_method') || !is_add_payment_method_page()) &&
            count($this->cctypes) &&
            ($this->min_order_total <= 0 || $this->min_order_total <= WC_Cart::get_total()) &&
            ($this->max_order_total <= 0 || $this->max_order_total >= WC_Cart::get_total());
    }

    /**
     * Add the Credorax gateway to WooCommerce.
     */
    public function credorax_credit_card_form_fields_filter($default_fields, $gateway_id)
    {
        if ($gateway_id === $this->id) {
            $allowedCards = $this->cctypes ? $this->cctypes : [];
            foreach ($allowedCards as &$value) {
                $value = '<img src="' . WC_CREDORAX_ASSETS_URL . 'images/' . $value . '.svg" class="credorax-cc-type-icon credorax-' . $value . '-icon" alt="' . WC_Credorax_Source_Settings::getCcTypeName($value) . '" title="' . WC_Credorax_Source_Settings::getCcTypeName($value) . '" />';
            }

            $default_fields = array_merge(
                [
                    'allowed-cards' => $allowedCards ? '<div id="credorax-allowed-cards"><span>' . esc_html__('Accepted Cards: ', 'woocommerce-gateway-credorax') . '</span>' . implode('', $allowedCards) . '</div>' : '',
                ],
                $default_fields,
                [
                    'card-owner-field' => '<p class="form-row form-row-wide">
						<label for="' . esc_attr($this->id) . '-card-owner">' . esc_html__('Name On Card', 'woocommerce-gateway-credorax') . '&nbsp;<span class="required">*</span></label>
						<input id="' . esc_attr($this->id) . '-card-owner" class="input-text wc-credit-card-form-card-owner" type="text" minLength="5" autocomplete="on" autocorrect="no" autocapitalize="no" spellcheck="no" type="tel" placeholder="" name="' . esc_attr(self::KEY_CC_OWNER) . '" />
                        <span class="credorax-validation-message">' . esc_html__('Please enter at least 5 characters', 'woocommerce-gateway-credorax') . '</span>
					</p>',
                    'card-type-field' => '<input id="' . esc_attr($this->id) . '-card-type" type="hidden" name="' . esc_attr(self::KEY_CC_TYPE) . '" />',
                    'pkey-field' => '<input id="' . esc_attr($this->id) . '-pkey" type="hidden" name="' . esc_attr(self::KEY_CREDORAX_PKEY) . '" />',
                    'pkey-data-field' => '<input id="' . esc_attr($this->id) . '-pkey-data" type="hidden" name="' . esc_attr(self::KEY_CREDORAX_PKEY_DATA) . '" />',
                    '3ds-compind-field' => '<input id="' . esc_attr($this->id) . '-3ds-compind" type="hidden" name="' . esc_attr(self::KEY_CREDORAX_3DS_COMPIND) . '" />',
                    'browser-lang-field' => '<input id="' . esc_attr($this->id) . '-browser-lang" type="hidden" name="' . esc_attr(self::KEY_CREDORAX_BROWSER_LANG) . '" />',
                ]
            );
        }
        return $default_fields;
    }

    /**
     * Gets saved payment method HTML from a token.
     *
     * @since 2.6.0
     * @param  WC_Payment_Token $token Payment Token.
     * @return string Generated payment method HTML
     */
    public function get_saved_payment_method_option_html($token)
    {
        $html = sprintf(
            '<li class="woocommerce-SavedPaymentMethods-token">
				<input id="wc-%1$s-payment-token-%2$s" type="radio" name="wc-%1$s-payment-token" value="%2$s" style="width:auto;" class="woocommerce-SavedPaymentMethods-tokenInput" %4$s />
				<label for="wc-%1$s-payment-token-%2$s">%3$s</label>
			</li>',
            esc_attr($this->id),
            esc_attr($token->get_token()),
            esc_html($token->get_display_name()),
            checked($token->is_default(), true, false)
        );

        return apply_filters('woocommerce_payment_gateway_get_saved_payment_method_option_html', $html, $token, $this);
    }

    /**
     * @method credorax_payment_complete_order_status_filter
     * @param  string                                        $status
     * @param  int                                           $order_id
     * @param  WC_Order|null                                 $order
     * @return string
     */
    public function credorax_payment_complete_order_status_filter($status, $order_id, WC_Order $order = null)
    {
        if (!is_object($order) && $order_id) {
            $order = wc_get_order($order_id);
        }
        if ($order->get_payment_method() === $this->id && $order->get_meta(self::KEY_CREDORAX_PAYMENT_STATUS) === self::CREDORAX_PAYMENT_STATUS_PENDNIG) {
            $status = 'pending';
        }

        return $status;
    }

    /**
     * Builds our payment fields area - including tokenization fields for logged
     * in users, and the actual payment fields.
     */
    public function payment_fields()
    {
        if ($this->description) {
            echo apply_filters('wc_credorax_description', wpautop(wp_kses_post($this->description)));
        }

        if ($this->supports('tokenization') && is_checkout() && $this->save_cards) {
            $this->tokenization_script();
            $this->saved_payment_methods();
            $this->form();
            $this->save_payment_method_checkbox();
        } else {
            $this->form();
        }
		echo '<p>Powered by Northmill</p>';
    }

	public function getPostParam($key, $default = null, $type = 'string')
	{
		if ( \Flo_Checkout\Gateways::is_rest_checkout() ) {
			if ( 'wc-' . $this->id . '-new-payment-method' === $key ) {
				// Override since wc-' . $this->id . '-new-payment-method is forcibly changed in frontend.
				$key = 'wc-' . $this->id . '-new-payment-method-custom';
			}
			if (isset($_POST[$key])) {
				$value = $_POST[$key];

				switch ($type) {
					case 'json':
						json_decode($value, true);
						return json_last_error() === JSON_ERROR_NONE ? $value : $default;
					case 'string':
					default:
						return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
				}
			}

			return $default;
		} else {
			return isset($_POST["{$key}"]) ? filter_input(INPUT_POST, "{$key}") : $default;
		}
	}

    /*
     * CSS and JS for Credorax's settings page on admin
     */
    public function payment_settings_scripts($hook)
    {
        if ($hook === 'woocommerce_page_wc-settings') {
            wp_enqueue_script('woocommerce_credorax_settings', WC_CREDORAX_ASSETS_URL . 'js/credorax-settings.js', ['jquery']);
        }
    }

    /*
     * Custom CSS and JS, in most cases required only when you decided to go with a custom credit card form
     */
    public function payment_scripts()
    {
        if (!is_cart() && !is_checkout() && !isset($_GET['pay_for_order'])) {
            return;
        }

        if (!$this->isEnabled()) {
            return;
        }

        // no reason to enqueue JavaScript if API keys are not set
        if (empty($this->merchant_id) || empty($this->signature_key) || empty($this->static_key)) {
            return;
        }

        // do not work with card detailes without SSL unless your website is in a test mode
        if (!($this->debug || $this->is_sandbox_mode) && !is_ssl()) {
            return;
        }

        wp_enqueue_style('woocommerce_credorax_style', WC_CREDORAX_ASSETS_URL . 'css/credorax-styles.css');

        // and this is our custom JS in your plugin directory that works with token.js
        wp_register_script('woocommerce_credorax', WC_CREDORAX_ASSETS_URL . 'js/credorax.js', ['jquery', 'jquery-payment', 'wc-checkout']);

        // in most payment processors you have to use PUBLIC KEY to obtain a token
        wp_localize_script('woocommerce_credorax', 'credorax_config', [
            'merchant_id' => $this->merchant_id,
            'static_key' => $this->static_key,
            'is_3d_secure_enabled' => $this->is_3d_secure_enabled,
            'key_creation_url' => WC_Credorax_Model_Config::getCredoraxStoreUrl(),
            'fingetprint_iframe_url' => get_site_url(null, '?credorax_fingerprint_form'),
            'challenge_redirect_url' => get_site_url(null, '?credorax_fingerprint_notification'),
            'request_id' => md5(uniqid() . serialize(WC()->cart->get_cart())),
            'allowed_cctypes' => $this->cctypes,
            'use_js_loader' => $this->use_js_loader,
            'pkey_default_error_message' => __('Credorax transaction failed, please make sure that the payment details are correct.', 'woocommerce-gateway-credorax'),
        ]);

        wp_enqueue_script('woocommerce_credorax');
    }

    /**
     * @method assign_data_from_post
     * @param  WC_Order              $order
     * @return WC_Credorax_Gateway
     */
    private function assign_data_from_post(WC_Order $order)
    {
	    WC_Credorax_Model_Config::log('$_POST', 'error', $_POST);
	    WC_Credorax_Model_Config::log('pkey data', 'error', json_decode($this->getPostParam(self::KEY_CREDORAX_PKEY_DATA, null, 'json'), true));
        $order->add_meta_data(self::KEY_CC_TYPE, $this->getPostParam(self::KEY_CC_TYPE), true);
        $order->add_meta_data(self::KEY_CC_OWNER, $this->getPostParam(self::KEY_CC_OWNER), true);
        $order->add_meta_data(self::KEY_CREDORAX_PKEY_DATA, json_decode($this->getPostParam(self::KEY_CREDORAX_PKEY_DATA, null, 'json'), true), true);
        $order->add_meta_data(self::KEY_CREDORAX_BROWSER_LANG, trim($this->getPostParam(self::KEY_CREDORAX_BROWSER_LANG)) ?: 'en-US', true);

        //= Token data

        $ccToken = (($ccToken = $this->getPostParam('wc-' . $this->id . '-payment-token')) && $ccToken !== 'new' && WC_Credorax_Model_Config::isUsingVault())
            ? $ccToken
            : null;

        $ccSave = ($ccToken === null && ($ccSave = $this->getPostParam('wc-' . $this->id . '-new-payment-method')) && WC_Credorax_Model_Config::isUsingVault())
            ? (bool) $ccSave
            : false;

        $order->add_meta_data(self::KEY_CC_SAVE, $ccSave, true);
        $order->add_meta_data(self::KEY_CC_TOKEN, null, true);
        $order->add_meta_data(self::KEY_CC_TOKEN_ID, $ccToken, true);

        $order->add_meta_data(self::KEY_CREDORAX_3DS_METHOD, WC()->session->get(self::KEY_CREDORAX_3DS_METHOD), true);
        $order->add_meta_data(self::KEY_CREDORAX_3DS_TRXID, WC()->session->get(self::KEY_CREDORAX_3DS_TRXID), true);
        $order->add_meta_data(self::KEY_CREDORAX_3DS_COMPIND, WC()->session->get(self::KEY_CREDORAX_3DS_COMPIND) ?: null, true);

        if ($ccToken === null) {
            return $this;
        }
        $token = (array) WC_Payment_Tokens::get_tokens([
            'user_id'    => get_current_user_id(),
            'gateway_id' => WC_Credorax_Gateway::GATEWAY_ID,
            'type'       => WC_Credorax_Gateway::GATEWAY_ID,
            'token'      => (string)$ccToken,
        ]);
        if (!$token) {
            $order->add_meta_data(self::KEY_CC_TOKEN, null, true);
            throw new \Exception(
                __('Invalid CC Token', 'woocommerce-gateway-credorax')
            );
        }
        $token = array_pop($token);
        $order->add_meta_data(self::KEY_CC_TOKEN, $token->get_token(), true);
        $order->add_meta_data(self::KEY_CC_TYPE, $token->get_card_type(), true);
        $order->add_meta_data(self::KEY_CC_LAST_4, substr($token->get_last4(), -4), true);
        $order->add_meta_data(self::KEY_CC_NUMBER, $token->get_card_number(), true);
        $order->add_meta_data(self::KEY_CC_EXP_MONTH, $token->get_expiry_month(), true);
        $order->add_meta_data(self::KEY_CC_EXP_YEAR, $token->get_expiry_year(), true);
        $order->add_meta_data(self::KEY_CC_OWNER, $token->get_card_owner(), true);

        return $this;
    }

    /**
     * Validate payment post data.
     *
     * @return WC_Credorax_Gateway
     * @throws Exception
     */
    public function validate_post_data(WC_Order $order)
    {
        $credoraxPKeyData = $order->get_meta(self::KEY_CREDORAX_PKEY_DATA);
        if (!$credoraxPKeyData || !is_array($credoraxPKeyData)) {
            throw new \Exception(
                __('No response from Credorax gateway, please contact us or try again later.', 'woocommerce-gateway-credorax')
            );
        }
        if (!isset($credoraxPKeyData['PKey']) || !$credoraxPKeyData['PKey'] || (isset($credoraxPKeyData['z2']) && $credoraxPKeyData['z2'])) {
            $errMessage = (isset($credoraxPKeyData['z3']) && $credoraxPKeyData['z3']) ? $credoraxPKeyData['z3'] : 'Credorax transaction failed, please make sure that the payment details are correct.';
            throw new \Exception(
                __($errMessage, 'woocommerce-gateway-credorax')
            );
        }
        $order->add_meta_data(self::KEY_CREDORAX_PKEY, $credoraxPKeyData['PKey'], true);

        if (!in_array($order->get_meta(self::KEY_CC_TYPE), $this->cctypes)) {
            throw new \Exception(
                __('The provided credit card type is not supported. Supported types: ', 'woocommerce-gateway-credorax') . implode(', ', WC_Credorax_Source_Settings::getSupportedCcTypes())
            );
        }
        if (!($this->min_order_total <= 0 || $this->min_order_total <= $order->get_total())) {
            throw new \Exception(
                __(sprintf('This payment method is not available for orders where the total amount is less than %s', $this->min_order_total . get_woocommerce_currency_symbol($order->get_currency())), 'woocommerce-gateway-credorax')
            );
        }
        if (!($this->max_order_total <= 0 || $this->max_order_total >= $order->get_total())) {
            throw new \Exception(
                __(sprintf('This payment method is not available for orders where the total amount exceeds %s', $this->max_order_total . get_woocommerce_currency_symbol($order->get_currency())), 'woocommerce-gateway-credorax')
            );
        }

        return $this;
    }

    /**
     * @method getRequestMethod
     * @param  WC_Order         $order
     * @return string
     */
    private function getRequestMethod(WC_Order $order)
    {
        $token = $order->get_meta(self::KEY_CC_TOKEN);
        if ($token) {
            $method = WC_Credorax_Model_Request_AbstractPayment::PAYMENT_AUTH_USE_TOKEN_METHOD;
        } else {
            $method = $this->save_cards && $order->get_meta(self::KEY_CC_SAVE) ?
                WC_Credorax_Model_Request_AbstractPayment::PAYMENT_AUTH_TOKENIZATION_METHOD :
                WC_Credorax_Model_Request_AbstractPayment::PAYMENT_AUTH_METHOD;
        }
        if ($this->payment_action === self::ACTION_AUTHORIZE_CAPTURE) {
            if ($token) {
                $method = WC_Credorax_Model_Request_AbstractPayment::PAYMENT_SALE_USE_TOKEN_METHOD;
            } else {
                $method = $this->save_cards && $order->get_meta(self::KEY_CC_SAVE) ?
                    WC_Credorax_Model_Request_AbstractPayment::PAYMENT_SALE_TOKENIZATION_METHOD :
                    WC_Credorax_Model_Request_AbstractPayment::PAYMENT_SALE_METHOD;
            }
        }

        return $method;
    }

    /**
     * @method get_order_payment_note_info
     * @param  WC_Order                    $order
     * @return array
     */
    private function get_order_payment_note_info(WC_Order $order)
    {
        $data = [];

        if (($ccType = WC_Credorax_Source_Settings::getCcTypeName($order->get_meta(self::KEY_CC_TYPE)))) {
            $data[self::KEY_CC_TYPE] = [__('Credit card type', 'woocommerce-gateway-credorax'), $ccType];
        }
        if (($ccLast4 = $order->get_meta(self::KEY_CC_LAST_4))) {
            $data[self::KEY_CC_LAST_4] = [__('Credit card number', 'woocommerce-gateway-credorax'), sprintf('xxxx-%s', $ccLast4)];
        }
        if (($expMonth = $order->get_meta(self::KEY_CC_EXP_MONTH)) && ($expYear = $order->get_meta(self::KEY_CC_EXP_YEAR))) {
            $data[self::KEY_CC_EXP_DATE] = [__('Card expiration date', 'woocommerce-gateway-credorax'), $expMonth . '/' . $expYear];
        }
        if (($ccOwner = $order->get_meta(self::KEY_CC_OWNER))) {
            $data[self::KEY_CC_OWNER] = [__('Card holder name', 'woocommerce-gateway-credorax'), $ccOwner];
        }
        if (($transactionId = $order->get_meta(self::KEY_CREDORAX_TRANSACTION_ID))) {
            $data[self::KEY_CREDORAX_TRANSACTION_ID] = [__('Transaction Id (RRN)', 'woocommerce-gateway-credorax'), $transactionId];
        }
        if (($trxid = $order->get_meta(self::KEY_CREDORAX_3DS_TRXID))) {
            $data[self::KEY_CREDORAX_3DS_TRXID] = [__('3DS transaction ID', 'woocommerce-gateway-credorax'), $trxid];
        }

        return $data;
    }

    /**
     * @method get_order_payment_note_info
     * @param  WC_Order                    $order
     * @return array
     */
    private function get_order_payment_note_info_admin_only(WC_Order $order)
    {
        $data = [];

        if (($eci = $order->get_meta(self::KEY_CREDORAX_3DS_VERSION))) {
            $data[self::KEY_CREDORAX_3DS_VERSION] = [__('3DS version', 'woocommerce-gateway-credorax'), $eci];
        }
        if (($eci = $order->get_meta(self::KEY_CREDORAX_3DS_ECI))) {
            $data[self::KEY_CREDORAX_3DS_ECI] = [__('3DS ECI', 'woocommerce-gateway-credorax'), $eci];
        }
        if (($authCode = $order->get_meta(self::KEY_CREDORAX_AUTH_CODE))) {
            $data[self::KEY_CREDORAX_AUTH_CODE] = [__('Auth code', 'woocommerce-gateway-credorax'), $authCode];
        }
        if (($responseId = $order->get_meta(self::KEY_CREDORAX_RESPONSE_ID))) {
            $data[self::KEY_CREDORAX_RESPONSE_ID] = [__('Response ID', 'woocommerce-gateway-credorax'), $responseId];
        }
        if (($riskScore = $order->get_meta(self::KEY_CREDORAX_RISK_SCORE))) {
            $data[self::KEY_CREDORAX_RISK_SCORE] = [__('Risk score', 'woocommerce-gateway-credorax'), $riskScore];
        }

        return $data;
    }

    /**
     * @method prepare_payment_note_info
     * @param  array                     $info
     * @return string
     */
    private function prepare_payment_note_info($info = [])
    {
        $return = '';
        foreach ($info as $row) {
            $return .= sprintf("<strong>%s: </strong>%s.<br>\n", $row[0], $row[1]);
        }
        return $return;
    }

    private function add_payment_note_to_order(WC_Order $order)
    {
        if ($this->payment_action === self::ACTION_AUTHORIZE_CAPTURE) {
            $transactionNote = __(sprintf("<i>Captured amount of %s via Credorax.</i><br>\n", $order->get_total()), 'woocommerce-gateway-credorax');
        } else {
            $transactionNote = __(sprintf("<i>Authorized amount of %s via Credorax.</i><br>\n", $order->get_total()), 'woocommerce-gateway-credorax');
        }

        $order->add_order_note($transactionNote . $this->prepare_payment_note_info($this->get_order_payment_note_info($order)), true);

        if (($adminOnlyNote = $this->prepare_payment_note_info($this->get_order_payment_note_info_admin_only($order)))) {
            $order->add_order_note(__("<i>Additional info from Credorax:</i><br>\n", 'woocommerce-gateway-credorax') . $adminOnlyNote);
        }

        return $this;
    }

    /**
     * Can the order be refunded via this gateway?
     *
     * Should be extended by gateways to do their own checks.
     *
     * @param  WC_Order $order Order object.
     * @return bool If false, the automatic refund button is hidden in the UI.
     */
    public function can_refund_order($order)
    {
        if (
            !$order ||
            !$this->supports('refunds') ||
            $order->get_payment_method() !== $this->id ||
            !in_array($order->get_meta(self::KEY_CREDORAX_PAYMENT_STATUS), [self::CREDORAX_PAYMENT_STATUS_CAPTURED, self::CREDORAX_PAYMENT_STATUS_REFUNDED])
        ) {
            return false;
        }
        return true;
    }

    /**
     * Validate frontend fields.
     *
     * Validate payment fields on the frontend.
     *
     * @return bool
     */
    /*public function validate_fields()
    {
        return true;
    }*/

    /**
     * Process the payment
     * @param int  $order_id Reference.
     *
     * @throws Exception If payment will not be accepted.
     * @return array|void
     */
    public function process_payment($order_id)
    {
        try {
            $isChallengeCallback = isset($_GET['credorax_challenge_callback']);
            $order = wc_get_order($order_id);
            if (!($order && $order->get_id())) {
                throw new Exception(sprintf("Couldn't load order ID: %s", $order_id));
            }

            // Challenge Callback:
            if ($isChallengeCallback) {
                if (($payment3DchallengeData = WC()->session->get(self::KEY_CREDORAX_PAYMENT_RESPONSE_DATA))) {
                    $payment3DchallengeData = (array) unserialize($payment3DchallengeData);
                    if ($payment3DchallengeData['body']['h9'] !== $order_id) {
                        throw new Exception('Challenge order ID mismatch');
                    }
                    $payment3DchallengeData['body'] = array_merge(
                        (array) $payment3DchallengeData['body'],
                        $_REQUEST
                    );

                    if (!(isset($payment3DchallengeData['body']['3ds_status']) && in_array($payment3DchallengeData['body']['3ds_status'], ['Y','A']))) {
                        if (WC_Credorax_Model_Config::isDebugEnabled() && isset($payment3DchallengeData['body']['3ds_status'])) {
                            throw new Exception(sprintf('Your payment failed. Details: %s', WC_Credorax_Source_Messages::get3dStatusMessage($payment3DchallengeData['body']['3ds_status'])));
                        } else {
                            throw new Exception('Your payment failed');
                        }
                        wp_redirect(wc_get_checkout_url());
                    }
                }

                $response = $this->responseFactory->create(
                    $payment3DchallengeData['response_handler_type'],
                    $payment3DchallengeData,
                    $order
                )->process();
            } else {
                WC()->session->set(self::KEY_CREDORAX_PAYMENT_RESPONSE_DATA, null);
                $this->assign_data_from_post($order);
                $this->validate_post_data($order);

                $method = $this->getRequestMethod($order);

                /** @var WC_Credorax_Model_RequestInterface $request */
                $request = $this->requestFactory->create(
                    $method,
                    $order
                );
                $response = $request->process();

                if ($response->getResponseType() === WC_Credorax_Model_AbstractResponse::RESPONSE_TYPE_PAYMENT && $response->is3dsChallengeRequired()) {
                    WC()->session->set(self::KEY_CREDORAX_PAYMENT_RESPONSE_DATA, serialize($response->getResponseDataArray()));
                    $order->add_meta_data(WC_Credorax_Gateway::KEY_CREDORAX_PAYMENT_STATUS, self::CREDORAX_PAYMENT_STATUS_PENDING_CHALLENGE, true);
                    $order->add_order_note(__('Redirected to Credorax 3ds challenge.', 'woocommerce-gateway-credorax'));
                    $order->save();
                    // Redirect to the 3d challenge page
                    return [
                        'result' => 'success',
                        'redirect' => $response->get3dsAcsurl()
                    ];
                }
            }

            $order->payment_complete();
            $this->add_payment_note_to_order($order);

            // Remove cart.
            if (property_exists(WC(), "cart")) {
                WC()->cart->empty_cart();
            }

            WC()->session->set(self::KEY_CREDORAX_PAYMENT_RESPONSE_DATA, null);

            // Redirect to the thank you page
            if (!$isChallengeCallback) {
                return [
                    'result' => 'success',
                    'redirect' => $this->get_return_url($order)
                ];
            } else {
                wp_redirect($this->get_return_url($order));
                exit;
            }

            //return parent::process_payment($order_id);
        } catch (Exception $e) {
            try {
                if ($order && $order->get_id()) {
                    $order->update_status('failed');
                    $order->save();
                }
            } catch (\Exception $e) {
                // On exception, at least try to update the order status to 'failed'.
            }

            wc_add_notice(esc_html__($e->getMessage(), 'woocommerce-gateway-credorax'), 'error');
            WC_Credorax_Model_Config::log('WC_Credorax_Gateway::process_payment() - Exception: ' . $e->getMessage(), 'error', [$e->getTraceAsString()]);

            if (!$isChallengeCallback) {
                return [
                    'result'   => 'failure',
                    'messages' => esc_html__($e->getMessage(), 'woocommerce-gateway-credorax'),
                    'redirect' => '',
                ];
            } else {
                wp_redirect(wc_get_checkout_url());
                exit;
            }
        }
    }

    /**
     * Process refund.
     *
     * If the gateway declares 'refunds' support, this will allow it to refund.
     * a passed in amount.
     *
     * @param  int    $order_id Order ID.
     * @param  float  $amount Refund amount.
     * @param  string $reason Refund reason.
     * @return boolean True or false based on success, or a WP_Error object.
     */
    public function process_refund($order_id, $amount = null, $reason = '')
    {
        try {
            $order = wc_get_order($order_id);

            if (!$this->can_refund_order($order)) {
                return false;
            }

            /** @var RequestInterface $request */
            $request = $this->requestFactory->create(
                WC_Credorax_Model_Request_AbstractGateway::GATEWAY_REFUND_METHOD,
                $order,
                $amount
            );
            $request->process();

            parent::process_refund($order_id, $amount, $reason);

            $order->add_order_note(__(sprintf('Refunded %s via Credorax.', $amount), 'woocommerce-gateway-credorax'));
            $order->save();
        } catch (\Exception $e) {
            WC_Credorax_Model_Config::log('WC_Credorax_Gateway::process_refund() - Exception: ' . $e->getMessage(), 'error', [$e->getTraceAsString()]);
            return new WP_Error('wc_credorax_gateway_process_refund_error', 'WC_Credorax_Gateway::process_refund() - Exception: ' . $e->getMessage());
        }

        return true;
    }

    /**
     * Process capture.
     *
     * @param  int    $order_id Order ID.
     * @param  float  $amount Capture amount.
     * @return boolean True or false based on success, or a WP_Error object.
     */
    public function process_capture($order_id, $amount = null)
    {
        try {
            //parent::process_refund($order_id, $amount, $reason);
            $order = wc_get_order($order_id);

            if (! $order || $order->get_payment_method() !== $this->id || $order->get_meta(self::KEY_CREDORAX_PAYMENT_STATUS) !== self::CREDORAX_PAYMENT_STATUS_PENDNIG) {
                return false;
            }

            $amount = ($amount === null)? $order->get_total() : $amount;

            /** @var RequestInterface $request */
            $request = $this->requestFactory->create(
                WC_Credorax_Model_Request_AbstractGateway::GATEWAY_CAPTURE_METHOD,
                $order,
                $amount
            );
            $request->process();

            $order->add_order_note(__(sprintf(
                "<i>Captured amount of %s via Credorax.<i><br>\n",
                $order->get_total()
            ), 'woocommerce-gateway-credorax'));

            $order->set_status(apply_filters('woocommerce_payment_complete_order_status', $order->needs_processing() ? 'processing' : 'completed', $order->get_id(), $order));
            $order->save();
        } catch (\Exception $e) {
            WC_Credorax_Model_Config::log('WC_Credorax_Gateway::process_capture() - Exception: ' . $e->getMessage(), 'error', [$e->getTraceAsString()]);
            throw $e;
        }

        return true;
    }

    /**
     * Process void.
     *
     * @param  int    $order_id Order ID.
     * @return boolean True or false based on success, or a WP_Error object.
     */
    public function process_void($order_id)
    {
        try {
            //parent::process_refund($order_id, $amount, $reason);
            $order = wc_get_order($order_id);

            if (! $order || $order->get_payment_method() !== $this->id || $order->get_meta(self::KEY_CREDORAX_PAYMENT_STATUS) !== self::CREDORAX_PAYMENT_STATUS_PENDNIG) {
                return false;
            }

            /** @var RequestInterface $request */
            $request = $this->requestFactory->create(
                WC_Credorax_Model_Request_AbstractGateway::GATEWAY_VOID_METHOD,
                $order
            );
            $request->process();

            $order->add_order_note(__("<i>Payment voided via Credorax.<i><br>\n", 'woocommerce-gateway-credorax'));
            $order->update_status('cancelled');
            $order->save();
        } catch (\Exception $e) {
            WC_Credorax_Model_Config::log('WC_Credorax_Gateway::process_void() - Exception: ' . $e->getMessage(), 'error', [$e->getTraceAsString()]);
            throw $e;
        }

        return true;
    }
}
