<?php
class ControllerPaymentCieloSi extends Controller {

	public function index() {
		$this->language->load('payment/cielo_si');

		$data['button_confirm'] = $this->language->get('button_confirm');
		$data['text_instruction'] = $this->language->get('text_instruction');
		$data['text_loading'] = $this->language->get('text_loading');

		if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/payment/cielo_si.tpl')) {
			return $this->load->view($this->config->get('config_template') . '/template/payment/cielo_si.tpl', $data);
		} else {
			return $this->load->view('default/template/payment/cielo_si.tpl', $data);
		}
	}

	public function confirm() {
		if ($this->session->data['payment_method']['code'] == 'cielo_si') {
			$this->load->model('checkout/order');

			$comment = '';

			$this->model_checkout_order->addOrderHistory($this->session->data['order_id'], $this->config->get('cielo_si_order_aguardando'), $comment, true);
		}
	}

	private function mbsubstr($str, $start = 0, $length = 10) {
		$mb_substr = (function_exists("mb_substr")) ? true : false;

		if ($mb_substr) {
			$str = mb_substr($str, $start, $length, 'UTF-8');
		} else {
			$str = utf8_encode(substr(utf8_decode($str), $start, $length));
		}

		return $str;
	}

	public function enviar() {
		// pega o id do pedido
		$order_id = isset($this->session->data['order_id']) ? $this->session->data['order_id'] : '';

		if (!is_numeric($order_id)) {
			$this->response->redirect($this->url->link('checkout/checkout', 'm1&e10', 'SSL'));
			exit;
		}

		// busca o pedido no banco de dados
		$this->load->model('checkout/order');
		$order_info = $this->model_checkout_order->getOrder($order_id);

		if (!$order_info) {
			$this->response->redirect($this->url->link('checkout/checkout', 'm1&e20', 'SSL'));
			exit;
		}

		// requer idioma portugues
		$query = $this->db->query("SELECT language_id FROM " . DB_PREFIX . "language WHERE directory = 'portuguese-br'");
		if ($query->num_rows) {
			$id_idioma_portugues = $query->row['language_id'];
		} else {
			$this->response->redirect($this->url->link('checkout/checkout', 'm1&e30', 'SSL'));
			exit;
		}

		// requer unidade de peso gramas
		$query = $this->db->query("SELECT weight_class_id FROM " . DB_PREFIX . "weight_class_description WHERE unit = 'g' AND language_id = '" . (int)$id_idioma_portugues . "'");
		if ($query->num_rows) {
			$id_peso_gramas = $query->row['weight_class_id'];
		} else {
			$this->response->redirect($this->url->link('checkout/checkout', 'm1&e40', 'SSL'));
			exit;
		}

		$this->language->load('payment/cielo_si');

		// define os campos para enviar no POST
		$campos = array();

		$campos['merchant_id'] = $this->config->get('cielo_si_merchant_id');
		$campos['order_number'] = $order_id;
		// $campos['soft_descriptor'] = $order_id;

		// dados do cliente
		$campos['customer_name'] = self::mbsubstr(trim($order_info['payment_firstname'].' '.$order_info['payment_lastname']), 0, 288);

		if (strlen($order_info['email']) <= 64) {
			$campos['customer_email'] = $order_info['email'];
		}

		$campos['customer_phone'] = self::mbsubstr(preg_replace("/[^0-9]/", '', $order_info['telephone']), 0, 11);

		$customer_cpf = isset($order_info['custom_field']['cpf']) ? $order_info['custom_field']['cpf'] : '';
		$customer_cnpj = isset($order_info['custom_field']['cnpj']) ? $order_info['custom_field']['cnpj'] : '';

		if ($this->validaCPF($customer_cpf)) {
			$campos['customer_identity'] = preg_replace ('/[^0-9]/', '', $customer_cpf);
		} elseif ($this->validaCNPJ($customer_cnpj)) {
			$campos['customer_identity'] = preg_replace ('/[^0-9]/', '', $customer_cnpj);
		}

		// dados do frete
		$shipping_title = '';
		$shipping_value = 0;
		$shipping_postcode = self::mbsubstr(preg_replace("/[^0-9]/", '', $order_info['shipping_postcode']), 0, 8);

		$query = $this->db->query("SELECT title, value FROM " . DB_PREFIX . "order_total WHERE order_id = '" . (int)$order_id . "' AND code = 'shipping'");
		if ($query->num_rows) {
			$shipping_title = $query->row['title'];
			$shipping_value = (float)$query->row['value'];
		}

		$campos['shipping_type'] = 5; // Sem cobrança de frete (serviços ou produtos digitais)

		// tipo de frete e endereço de entrega
		if ($this->cart->hasShipping()) {

			$campos['shipping_type'] = 2; // Frete fixo

			if ($shipping_value == 0) {
				if (
					mb_stripos($shipping_title, 'free', 0, 'UTF-8') !== false ||
					mb_stripos($shipping_title, 'grátis', 0, 'UTF-8') !== false ||
					mb_stripos($shipping_title, 'gratis', 0, 'UTF-8') !== false
				) {
					$campos['shipping_type'] = 3; // Frete grátis
				} else {
					$campos['shipping_type'] = 4; // Retirar em mãos/loja
				}
			} else {
				$campos['shipping_1_name'] = $shipping_title;
				$campos['shipping_1_price'] = number_format($shipping_value, 2, '', '');
			}

			if ($campos['shipping_type'] != 4) {
				$shipping_numero = isset($order_info["shipping_custom_field"]['numero']) ? $order_info["shipping_custom_field"]['numero'] : '';
				$shipping_complemento = isset($order_info["shipping_custom_field"]['complemento']) ? $order_info["shipping_custom_field"]['complemento'] : '';

				$campos['shipping_address_name']       = self::mbsubstr($order_info['shipping_address_1'], 0, 256);
				$campos['shipping_address_number']     = $shipping_numero;
				$campos['shipping_address_complement'] = $shipping_complemento;
				$campos['shipping_address_district']   = self::mbsubstr($order_info['shipping_address_2'], 0, 64);
				$campos['shipping_address_city']       = self::mbsubstr($order_info['shipping_city'], 0, 64);
				$campos['shipping_address_state']      = '';
				$campos['shipping_zipcode']            = $shipping_postcode;

				$this->load->model('localisation/zone');
				$zone = $this->model_localisation_zone->getZone($order_info['shipping_zone_id']);
				if (isset($zone['code'])) {
					$campos['shipping_address_state'] = self::mbsubstr($zone['code'], 0, 2);
				}
			}
		}

		// lista de produtos
		$produtos = array();

		foreach ($this->cart->getProducts() as $produto) {
			$option_data = array();

			foreach ($produto['option'] as $option) {
				$option_data[] = array(
					'name'	=> $option['name'],
					'value'	=> $option['value']
				);
			}

			$produtos[] = array(
				'product_id' => $produto['product_id'],
				'name'       => $produto['name'],
				'model'      => $produto['model'],
				'value'      => $this->currency->format($produto['price'], $order_info['currency_code'], false, false),
				'quantity'   => $produto['quantity'],
				'shipping'   => $produto['shipping'],
				'weight'     => $this->weight->convert($produto['weight'], $produto['weight_class_id'], $id_peso_gramas),
				'option'     => $option_data
			);
		}

		// lista de vales presentes
		$vouchers = isset($this->session->data['vouchers']) ? $this->session->data['vouchers'] : array();

		$total_voucher = 0;
		$count_voucher = 0;

		foreach ($vouchers as $voucher_id => $voucher) {
			$count_voucher++;

			$produtos[] = array(
				'product_id' => 'voucher_'.$count_voucher,
				'name'       => $voucher['description'],
				'model'      => '',
				'value'      => $this->currency->format($voucher['amount'], $order_info['currency_code'], false, false),
				'quantity'   => 1,
				'shipping'   => 0,
				'weight'     => 0,
				'option'     => array()
			);

			$total_voucher += $this->currency->convert($voucher['amount'], $this->config->get('config_currency'), $order_info['currency_code']);
		}

		// total extra do pedido (frete, descontos e taxas) pode ser um valor positivo ou negativo
		$total_extra = $this->currency->format($order_info['total'] - $this->cart->getSubTotal() - $total_voucher - $shipping_value, $order_info['currency_code'], false, false);

		// se o extra for positivo adiciona como produto
		if ($total_extra > 0) {
			$produtos[] = array(
				'product_id' => 'extra_amount',
				'name'       => $this->language->get('text_extra_amount'),
				'model'      => '',
				'value'      => $total_extra,
				'quantity'   => 1,
				'shipping'   => 0,
				'weight'     => 0,
				'option'     => array()
			);
		}
		// se o extra for negativo envia como desconto
		elseif ($total_extra < 0) {
			$campos['discount_type'] = 1; // em valor
			$campos['discount_value'] = number_format(abs($total_extra), 2, '', ''); // precisa ser um valor positivo
		}

		// inclui os produtos no post
		$i = 1;
		foreach ($produtos as $produto) {
			$options_names = '';
			foreach ($produto['option'] as $option) {
				$options_names .= ' / '.$option['value'];
			}

			$model = ($produto['model'] != '') ? $produto['model'].' - ' : '';

			$campos["cart_{$i}_code"]        = trim($produto['product_id']);
			$campos["cart_{$i}_name"]        = trim($model.$produto['name'].$options_names);
			$campos["cart_{$i}_description"] = '';
			$campos["cart_{$i}_quantity"]    = trim($produto['quantity']);
			$campos["cart_{$i}_unitprice"]   = number_format($produto['value'], 2, '', '');

			if ($produto['shipping'] == '1') {
				$campos["cart_{$i}_type"]   = 1; // produto físico
				$campos["cart_{$i}_weight"] = number_format($produto['weight'], 0, '', '');
				$campos["cart_{$i}_zipcode"] = $shipping_postcode;
			} else {
				$campos["cart_{$i}_type"] = 2; // produto virtual/digital
			}

			$i++;
		}

		$data['campos'] = $campos;
		$data['action'] = 'https://cieloecommerce.cielo.com.br/Transactional/Order/Index';

		// carrega o template
		if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/payment/cielo_si_enviar.tpl')) {
			$this->response->setOutput($this->load->view($this->config->get('config_template') . '/template/payment/cielo_si_enviar.tpl', $data));
		} else {
			$this->response->setOutput($this->load->view('default/template/payment/cielo_si_enviar.tpl', $data));
		}
	}

	public function retorno() {
		$this->response->redirect($this->url->link('checkout/success'));
	}

	public function notificacao() {
		$order_id = isset($_POST['order_number']) ? $_POST['order_number'] : 0;

		if ($order_id == 0) {
			$this->log->write('Checkout Cielo: Número do pedido inválido. ID: '.$order_id);
			$this->response->redirect($this->url->link('payment/cielo_si/success', '', 'SSL'));
			exit;
		}

		$tid = isset($_POST['tid']) ? $_POST['tid'] : '';
		$amount = isset($_POST['amount']) ? intval($_POST['amount']) : 0;

		// payment_status:
		$arr_payment_status = array(
			1 => 'Pendente',       // (Para todos os meios de pagamento)
			2 => 'Pago',           // (Para todos os meios de pagamento)
			3 => 'Negado',         // (Somente para Cartão Crédito)
			5 => 'Cancelado',      // (Somente para Cartão Crédito)
			6 => 'Não Finalizado', // (Para todos os meios de pagamento)
			7 => 'Autorizado'      // (Somente para Cartão Crédito)
		);
		$payment_status = isset($_POST['payment_status']) ? intval($_POST['payment_status']) : 0;

		// payment_method_type:
		$arr_payment_method_type = array(
			1 => 'Cartão de Crédito',
			2 => 'Boleto Bancário',
			3 => 'Débito Online',
			4 => 'Cartão de Débito'
		);
		$payment_method_type = isset($_POST['payment_method_type']) ? intval($_POST['payment_method_type']) : 0;

		// payment_method_brand:
		$arr_payment_method_brand = array(
			1 => 'Visa',
			2 => 'Mastercard',
			3 => 'Amex',
			4 => 'Diners',
			5 => 'Elo',
			6 => 'Aura',
			7 => 'JCB'
		);
		$payment_method_brand = isset($_POST['payment_method_brand']) ? intval($_POST['payment_method_brand']) : 0;

		// payment_method_bank:
		$arr_payment_method_bank = array(
			1 => 'Banco do Brasil',
			2 => 'Bradesco'
		);
		$payment_method_bank = isset($_POST['payment_method_bank']) ? intval($_POST['payment_method_bank']) : 0;

		$payment_maskedcredicard = isset($_POST['payment_maskedcredicard']) ? $_POST['payment_maskedcredicard'] : '';
		$payment_installments = isset($_POST['payment_installments']) ? intval($_POST['payment_installments']) : 0;
		$payment_boletonumber = isset($_POST['payment_boletonumber']) ? $_POST['payment_boletonumber'] : 0;
		$payment_boletoexpirationdate = isset($_POST['payment_boletoexpirationdate']) ? $_POST['payment_boletoexpirationdate'] : 0;

		// busca o pedido
		$this->load->model('checkout/order');
		$order_info = $this->model_checkout_order->getOrder($order_id);

		if (!is_array($order_info)) {
			// se nao encontrar o pedido grava log de erro
			$this->log->write('Checkout Cielo: Pedido nao encontrado. ID: '.$order_id);
			$this->response->redirect($this->url->link('payment/cielo_si/success', '', 'SSL'));
			exit;
		}

		// comentarios que serao adicionados no historico do pedido
		$comment = '';

		if (array_key_exists($payment_status, $arr_payment_status)) {
			$comment .= "\nStatus da transação na Cielo: ". mb_strtoupper($arr_payment_status[$payment_status], 'UTF-8');
		}

		if (array_key_exists($payment_method_type, $arr_payment_method_type)) {
			$comment .= "\nTipo de pagamento escolhido: ".mb_strtoupper($arr_payment_method_type[$payment_method_type], 'UTF-8');

			if ($payment_method_type == 1 || $payment_method_type == 4) {
				if (array_key_exists($payment_method_brand, $arr_payment_method_brand)) {
					$comment .= " / ".mb_strtoupper($arr_payment_method_brand[$payment_method_brand], 'UTF-8');
				}
				if ($payment_installments != '') {
					$comment .= " em ".$payment_installments.'x';
				}
			}

			if ($payment_method_type == 2 || $payment_method_type == 3) {
				if (array_key_exists($payment_method_bank, $arr_payment_method_bank)) {
					$comment .= " / ".mb_strtoupper($arr_payment_method_bank[$payment_method_bank], 'UTF-8');
				}
			}
		}

		if ($payment_method_type == 1 || $payment_method_type == 4) {
			if ($payment_maskedcredicard != '') {
				$comment .= "\nNúmero do cartão: ".$payment_maskedcredicard;
			}
		}

		if ($payment_method_type == 2) {
			if ($payment_boletonumber != '') {
				$comment .= "\nNúmero do boleto: ".$payment_boletonumber;
			}
			if ($payment_boletoexpirationdate != '') {
				$comment .= "\nVencimento do boleto: ".$payment_boletoexpirationdate;
			}
		}

		if ($tid != '') {
			$comment .= "\nTID: ".$tid;
		}

		if ($amount > 0) {
			$comment .= "\nValor: R$ ". number_format($amount / 100, 2, ',', '.');
		}

		$comment = trim($comment);
		$status_novo = $order_info['order_status_id'];

		// verifica se precisa notificar o cliente
		$update_status_alert = false;
		if ($this->config->get('cielo_si_update_status_alert')) {
			$update_status_alert = true;
		}

		switch ($payment_status) {
			case 1: // Pendente
				$status_novo = $this->config->get('cielo_si_order_pendente');
				break;

			case 2: // Pago
				$status_novo = $this->config->get('cielo_si_order_pago');
				break;

			case 3: // Negado
				$status_novo = $this->config->get('cielo_si_order_negado');
				break;

			case 5: // Cancelado
				$status_novo = $this->config->get('cielo_si_order_cancelado');
				break;

			case 6: // Não Finalizado
				$status_novo = $this->config->get('cielo_si_order_nao_finalizado');
				break;

			case 7: // Autorizado
				$status_novo = $this->config->get('cielo_si_order_autorizado');
				break;
		}

		if (is_numeric($status_novo) && $status_novo > 0) {
			if ($order_info['order_status_id'] == $status_novo) {
				$update_status_alert = false;
			}

			$this->model_checkout_order->addOrderHistory($order_id, $status_novo, $comment, $update_status_alert);
		}

		echo('<status>OK</status>');
		exit;
	}

	public function status() {
		$order_id = isset($_POST['order_number']) ? $_POST['order_number'] : 0;

		if ($order_id == 0) {
			$this->log->write('Checkout Cielo: Número do pedido inválido. ID: '.$order_id);
			$this->response->redirect($this->url->link('payment/cielo_si/success', '', 'SSL'));
			exit;
		}

		$amount = isset($_POST['amount']) ? intval($_POST['amount']) : 0;

		// payment_status:
		$arr_payment_status = array(
			1 => 'Pendente',       // (Para todos os meios de pagamento)
			2 => 'Pago',           // (Para todos os meios de pagamento)
			3 => 'Negado',         // (Somente para Cartão Crédito)
			5 => 'Cancelado',      // (Somente para Cartão Crédito)
			6 => 'Não Finalizado', // (Para todos os meios de pagamento)
			7 => 'Autorizado'      // (Somente para Cartão Crédito)
		);
		$payment_status = isset($_POST['payment_status']) ? intval($_POST['payment_status']) : 0;

		// busca o pedido
		$this->load->model('checkout/order');
		$order_info = $this->model_checkout_order->getOrder($order_id);

		if (!is_array($order_info)) {
			// se nao encontrar o pedido grava log de erro
			$this->log->write('Checkout Cielo: Pedido não encontrado. ID: '.$order_id);
			$this->response->redirect($this->url->link('payment/cielo_si/success', '', 'SSL'));
			exit;
		}

		// comentarios que serao adicionados no historico do pedido
		$comment = '';

		if (array_key_exists($payment_status, $arr_payment_status)) {
			$comment .= "\nStatus da transação na Cielo: ". mb_strtoupper($arr_payment_status[$payment_status], 'UTF-8');
		}

		if ($amount > 0) {
			$comment .= "\nValor: R$ ". number_format($amount / 100, 2, ',', '.');
		}

		$comment = trim($comment);
		$status_novo = $order_info['order_status_id'];

		// verifica se precisa notificar o cliente
		$update_status_alert = false;
		if ($this->config->get('cielo_si_update_status_alert')) {
			$update_status_alert = true;
		}

		switch ($payment_status) {
			case 1: // Pendente
				$status_novo = $this->config->get('cielo_si_order_pendente');
				break;

			case 2: // Pago
				$status_novo = $this->config->get('cielo_si_order_pago');
				break;

			case 3: // Negado
				$status_novo = $this->config->get('cielo_si_order_negado');
				break;

			case 5: // Cancelado
				$status_novo = $this->config->get('cielo_si_order_cancelado');
				break;

			case 6: // Não Finalizado
				$status_novo = $this->config->get('cielo_si_order_nao_finalizado');
				break;

			case 7: // Autorizado
				$status_novo = $this->config->get('cielo_si_order_autorizado');
				break;
		}

		if (is_numeric($status_novo) && $status_novo > 0) {
			if ($order_info['order_status_id'] == $status_novo) {
				$update_status_alert = false;
			}

			$this->model_checkout_order->addOrderHistory($order_id, $status_novo, $comment, $update_status_alert);
		}

		echo('<status>OK</status>');
		exit;
	}

	private function validaCPF($strCPF) {
		// validacao: apenas numeros ou no formato 000.000.000-00
		if (!preg_match('/^[0-9]{11}$/', $strCPF) && !preg_match('/^[0-9]{3}\.[0-9]{3}\.[0-9]{3}-[0-9]{2}$/', $strCPF)) { return false; }

		// remove caracteres nao numericos (pontuacao)
		$strCPF = preg_replace('/[^0-9]/', '', $strCPF);

		// nao aceita sequencias de numeros iguais (00000000000, 11111111111, etc)
		for ($a = 0; $a < 10; $a++) {
			if (str_repeat($a, 11) == $strCPF) { return false; }
		}

		// digito verificador informado
		$strCPF_dv = substr($strCPF, 9, 2);

		// separa os digitos
		$digito = str_split($strCPF);

		// calcula o digito verificador
		$mult = 10; $soma = 0;
		for ($a = 0; $a < 9; $a++) { $soma += $digito[$a] * $mult--; }
		$soma = $soma % 11;
		$digito[9] = ($soma < 2) ? 0 : 11 - $soma;

		$mult = 11; $soma = 0;
		for ($a = 0; $a < 10; $a++){ $soma += $digito[$a] * $mult--; }
		$soma = $soma % 11;
		$digito[10] = ($soma < 2) ? 0 : 11 - $soma;

		// verifica se o digito verificador informado e igual ao calculado
		$digitoVerificador = $digito[9] .''. $digito[10];
		if ($digitoVerificador != $strCPF_dv) { return false; }

		return true;
	}

	private function validaCNPJ($strCNPJ) {
		// validacao: apenas numeros ou no formato 00.000.000/0000-00
		if (!preg_match('/^[0-9]{14,15}$/', $strCNPJ) && !preg_match('/^[0-9]{2,3}\.[0-9]{3}\.[0-9]{3}\/[0-9]{4}-[0-9]{2}$/', $strCNPJ)) { return false; }

		// remove caracteres nao numericos (pontuacao)
		$strCNPJ = preg_replace('/[^0-9]/', '', $strCNPJ);

		if (strlen($strCNPJ) == 15) {
			if (substr($strCNPJ,0,1) != 0) { return false; }
			$strCNPJ = substr($strCNPJ, 1, 14);
		}

		// nao aceita sequencias de numeros iguais (00000000000000, 11111111111111, etc)
		for ($a = 0; $a < 10; $a++) {
			if (str_repeat($a, 14) == $strCNPJ) { return false; }
		}

		// digito verificador informado
		$strCNPJ_dv = substr($strCNPJ, 12, 2);

		// separa os digitos
		$digito = str_split($strCNPJ);

		// calcula o digito verificador
		$mult = 5; $soma = 0;
		for ($a = 0; $a < 12; $a++) { $soma += $digito[$a] * $mult--; if ($mult < 2) { $mult = 9; }}
		$soma = $soma % 11;
		$digito[12] = ($soma < 2) ? 0 : 11 - $soma;

		$mult = 6; $soma = 0;
		for ($a = 0; $a < 13; $a++){ $soma += $digito[$a] * $mult--; if ($mult < 2) { $mult = 9; }}
		$soma = $soma % 11;
		$digito[13] = ($soma < 2) ? 0 : 11 - $soma;

		// verifica se o digito verificador informado e igual ao calculado
		$digitoVerificador = $digito[12] .''. $digito[13];
		if ($digitoVerificador != $strCNPJ_dv) { return false; }

		return true;
	}
}
