<?php

class ControllerExtensionPaymentMonoPay extends Controller {
	public function __construct($registry) {
		parent::__construct($registry);
		
		require_once DIR_SYSTEM . 'library/mono_pay/mono_pay.php';
		
		$this->load->model('checkout/order');
		$this->load->model('extension/payment/mono_pay');
		$this->load->language('extension/payment/mono_pay');
		
		$this->secretkey = $this->config->get('mono_pay_token');
		$this->currency_code = $this->config->get('mono_pay_pay_cur');
		
		if ($this->request->server['HTTPS']) {
			$this->server = $this->config->get('config_ssl');
		} else {
			$this->server = $this->config->get('config_url');
		}
	}
	
	public function index() {
		
		$order_id = !empty($this->session->data['order_id']) ? $this->session->data['order_id'] : false;
		
		$data['text_title'] = $this->language->get('text_title');
		$data['text_basket'] = $this->language->get('text_basket');
		$data['text_checkout'] = $this->language->get('text_checkout');
		$data['text_loading'] = $this->language->get('text_loading');
		
		if($order_id) {
			$mono_pay = new MonoPay();
			$mono_pay->setSecretKey($this->secretkey);
			
			$order_info = $this->model_extension_payment_mono_pay->getOrder($order_id);
			
			if(!empty($mono_pay->currencies[$order_info['currency_code']])) {
				$currency_ccy = $mono_pay->currencies[$order_info['currency_code']];
				$currency = $order_info['currency_code'];
			} elseif(!empty($mono_pay->currencies[$this->currency_code])) {
				$currency_ccy = $mono_pay->currencies[$this->currency_code];
				$currency = $this->currency_code;
			} else {
				$currency_ccy = 980;
				$currency = 'UAH';
			}
			
			if($this->config->get('mono_pay_custom_callback')) {
				$callback_url = $this->config->get('mono_pay_custom_callback');
			} else {
				$callback_url = $this->server . 'index.php?route=extension/payment/mono_pay/callback';
			}
			
			$order_product_result = $this->model_extension_payment_mono_pay->getOrderProduct($order_id, $currency);
			
			if($this->config->get('mono_pay_totals')) {
				$total_cost = $this->model_extension_payment_mono_pay->getTotalCost($order_id);
				
				if($total_cost) {
					$order_info['total'] = $order_info['total'] - $total_cost;
				}
			}
			
			$order_product = $this->prepareTotalFiscalization($order_info['total'], $order_product_result, $currency);
			
			$cart_total = $this->calculateBasketTotal($order_product);

			if($cart_total != round($this->currency->format($order_info['total'], $currency, $this->currency->getValue($currency), false) * 100)) {
				$amount = round($cart_total / 100, 2) * 100;
			} else {
				$amount = round($this->currency->format($order_info['total'], $currency, $this->currency->getValue($currency), false) * 100);
			}
			
			$merchantPaymInfo = array(
				'reference'		=> (string)$order_id,
				'destination'	=> sprintf($this->language->get('text_order_description'), $order_id),
				'basketOrder'	=> $order_product,
			);
			
			if(!empty($order_info['email'])) {
				$merchantPaymInfo['customerEmails'] = array($order_info['email']);
			}
			
			$api_data = array(
				'amount'            => $amount,
				'ccy'				=> (int)$currency_ccy,
				'merchantPaymInfo'	=> $merchantPaymInfo,
				'redirectUrl'		=> 'https://erotikon.com.ua/index.php?route=checkout/success',
				'webHookUrl'        => $callback_url,
				'validity'			=> (int)$this->config->get('mono_pay_validity_time'),
				'paymentType'		=> in_array($this->config->get('mono_pay_type'), $mono_pay->payment_type) ? $this->config->get('mono_pay_type') : '',
				'api_uri'			=> 'invoice/create',
			);
			
			$data['iframe'] = false;
			
			if($this->config->get('mono_pay_mode') == 'iframe') {
				$api_data['displayType'] = 'iframe';
				
				$data['iframe'] = true;
			}
			
			$this->setLog($api_data, 'Дані що йдуть на платіжну систему');
			
			$result_data = $mono_pay->getInvoice($api_data);
		
			$data['error'] = $mono_pay->logError($result_data);
			
			if(!empty($result_data['pageUrl'])) {
				$this->setLog($result_data, 'Дані що прийшли з платіжки');
				
				$data['payment_url'] = $result_data['pageUrl'];
				
				$this->model_extension_payment_mono_pay->setMonoOrder($order_id, $currency, $result_data);
			} else {
				$data['payment_url'] = ''; 
			}
			
            if($this->config->get('mono_pay_forced_referrer')) {
                $data['forced_referrer'] = true;
            } else {
                $data['forced_referrer'] = false;
            }
			
			$data['referrer'] = $this->config->get('mono_pay_status_referrer');

			return $this->load->view('extension/payment/mono_pay', $data);
		}
	}
	
	public function callback() {
		$calback_info = (array)json_decode(file_get_contents("php://input"));
		
		//перевірка підпису
		if(empty($this->request->server['HTTP_X_SIGN']) || !$this->checkSign($this->request->server['HTTP_X_SIGN'], $calback_info)) {
			$this->setLog(array('text' => 'Callback verification fail!'), 'Помилка при верифікації підпису колбеку');
			
			return false;
		}
		
		if(!$calback_info) {
			$this->setLog(array('text' => 'Callback data empty!'), 'Помилка при поверненні колбеку');
			
			return false;
		}
		
		//Log
		$this->setLog(json_encode($calback_info), 'Колбек від платіжної системи');
		
		$invoice_info = $this->getInvoiceInfo($calback_info['invoiceId']);
		
		if(!$invoice_info) {
			$this->setLog(array('text' => 'Callback data empty!'), 'Помилка при перевірці поточного статусу інвойсу');
			
			return false;
		}
		
		$mono_pay = new MonoPay();
		
		if($invoice_info['status'] == 'success' && $this->config->get('mono_pay_type') == 'hold') {
			$status_id = $this->config->get('mono_pay_order_hold_status_id');
		} elseif(in_array($invoice_info['status'], $mono_pay->succes_payment_type)) {
			$status_id = $this->config->get('mono_pay_order_success_status_id');
		} elseif($invoice_info['status'] == 'failure') {
			$status_id = $this->config->get('mono_pay_order_failure_status_id');
		} elseif($invoice_info['status'] == 'reversed') {
			$status_id = $this->config->get('mono_pay_order_return_status_id');
		} else {
			$status_id = false;
		}
		
		if($status_id) {			
			$this->pushHistory($invoice_info['reference'], $status_id);
		}
	}
	
	public function response() {		
		//Фікс щодо втрати сессії та номеру замовлення при поверненні
		if(empty($this->session->data['order_id']) && !empty($this->request->get['orderid'])) {
			$this->session->data['order_id'] = (int)$this->request->get['orderid'];
		}
		
		$order_id = !empty($this->session->data['order_id']) ? $this->session->data['order_id'] : false;
		
		if(!$order_id) {
			$this->setLog(array('text' => 'Order ID empty!'), 'Помилка при поверненні з платіжної системи');
			
			return false;
		}
		
		$data['text_message'] = $this->language->get('text_message');
		$data['text_check_pay'] = $this->language->get('text_check_pay');
		
		$data['lang'] = $this->language->get('code');
		$data['redirect_success'] = $this->url->link('checkout/success', '', 'SSL');
		$data['redirect_fail'] = $this->url->link('checkout/mono_fail', '', 'SSL');
		$data['query_url'] = $this->server . 'index.php?route=extension/payment/mono_pay/checkStatusInvoice&order_id=' . $order_id;
		
		$this->response->setOutput($this->load->view('extension/payment/mono_pay_check', $data));
	}
	
	public function checkStatusInvoice() {
		$json = array();
		
		$order_id = !empty($this->session->data['order_id']) ? $this->session->data['order_id'] : false;
		
		$invoice_info = $this->model_extension_payment_mono_pay->getInvoiceByOrderId($order_id);
		
		if($invoice_info) {
			$mono_pay = new MonoPay();
			$mono_pay->setSecretKey($this->secretkey);
			$mono_pay->setTypeMethod('GET');
			
			$mono_pay_data = array(
				'invoiceId' => $invoice_info['invoiceId'],
				'api_uri' 	=> 'invoice/status?invoiceId=' . $invoice_info['invoiceId']
			);
			
			$result_data = $mono_pay->getTransactionInfo($mono_pay_data);
		}
	
		if(!empty($invoice_info) && in_array($result_data['status'], $mono_pay->succes_payment_type)) {
			$json['status'] = true;
			
			if($result_data['status'] == 'success' && $this->config->get('mono_pay_type') == 'hold') {
				$status_id = $this->config->get('mono_pay_order_hold_status_id');
			} elseif(in_array($result_data['status'], $mono_pay->succes_payment_type)) {
				$status_id = $this->config->get('mono_pay_order_success_status_id');
			} else {
				$status_id = 0;
			}
			
			if($status_id) {
				$this->pushHistory($order_id, $status_id);
			}
		} else {
			$json['status'] = false;
		}
		
		$this->response->addHeader('Content-Type: application/json');
		$this->response->setOutput(json_encode($json));
	}
	
	public function referrer() {
		$order_id = $this->session->data['order_id'];
		
		$this->pushHistory($order_id, $this->config->get('mono_pay_order_status_id'));
	}
	
	private function pushHistory($order_id, $status) {
		$order_info = $this->model_extension_payment_mono_pay->getOrder($order_id);
		
		if($order_info['order_status_id'] != $status) {
			$this->model_checkout_order->addOrderHistory($order_id, $status);
		}
	}
	
	private function setLog($data, $text) {
		if($this->config->get('mono_pay_status_log')) {
			$log = new Log('mono.log');
			$log->write('--------- ' . $text . ': ПОЧАТОК ---------');
			$log->write(json_encode($data));
			$log->write('--------- ' . $text . ': КІНЕЦЬ ---------');
		}
	}
	
	private function getInvoiceInfo($invoiceId) {
		$mono_pay = new MonoPay();
		$mono_pay->setSecretKey($this->secretkey);
		$mono_pay->setTypeMethod('GET');
		
		$mono_pay_data = array(
			'invoiceId' => $invoiceId,
			'api_uri' 	=> 'invoice/status?invoiceId=' . $invoiceId
		);
		
		$result_data = $mono_pay->getTransactionInfo($mono_pay_data);
		
		return $result_data;
	}
	
	private function prepareTotalFiscalization($orderTotal, $products, $currency) {
		$formattedOrderTotal = (int)round($this->currency->format($orderTotal, $currency, $this->currency->getValue($currency), false) * 100);

		$totalProductSum = 0;

		foreach ($products as &$product) {
			$product['sum'] = (int)round($product['sum']);
			$totalProductSum += $product['sum'] * $product['qty']; 
		}
		unset($product);

		$totalDifference = $formattedOrderTotal - $totalProductSum;

		if ($totalDifference != 0 && !empty($products)) {
			$lastProductIndex = count($products) - 1;

			$products[$lastProductIndex]['sum'] += (int)round($totalDifference / $products[$lastProductIndex]['qty']);
			$products[$lastProductIndex]['sum'] = (int)round($products[$lastProductIndex]['sum']);
		}

		return $products;
	}
	
	private function calculateBasketTotal($products) {
		$totalSum = 0;

		foreach ($products as $product) {
			$productSum = (int)round($product['sum']) * (int)$product['qty'];
			$totalSum += $productSum;
		}

		return $totalSum;
	}
	
	private function checkSign($xsign, $data) {
		$public_key = '';
		
		if(file_exists(DIR_SYSTEM . 'mono_key.pub')) {
			$public_key = file_get_contents(DIR_SYSTEM . 'mono_key.pub');
		}
		
		if(!$public_key) {
			$public_key = $this->getSignKey();
		}
		
		$result = $this->verify($xsign, $public_key, $data);
		
		if(!$result) {
			//Якщо ключ вже не дійсний, робимо запит на новий і повторюємо валідацію
			
			$public_key = $this->getSignKey();
			
			$result = $this->verify($xsign, $public_key, $data);
		}
		
		return $result;
	}
	
	private function verify($xsign, $public_key, $data) {
		$public_key = openssl_get_publickey(base64_decode($public_key));
		
		$binarySignature = base64_decode($xsign);
		
		$data = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
		
		$ok = openssl_verify($data, $binarySignature, $public_key, OPENSSL_ALGO_SHA256);

		if ($ok === 1) {
			return true;
		} elseif ($ok === 0) {
			return false;
		} else {
			return false;
		}
	}

	private function getSignKey() {
		$mono_pay = new MonoPay();
		$mono_pay->setSecretKey($this->secretkey);
		$mono_pay->setTypeMethod('GET');
		
		$mono_pay_data = array(
			'api_uri' 	=> 'pubkey'
		);
		
		$result_data = $mono_pay->getPubkey($mono_pay_data);
			
		if(!empty($result_data['key'])) {		
			file_put_contents(DIR_SYSTEM . 'mono_key.pub', $result_data['key']);

			return $result_data['key'];
		}
		
		return false;
	}
}