<?php
/* Biblioteca de integração com a API 3.0 da Cielo*/



  use Cielo\API30\Merchant;

  use Cielo\API30\Ecommerce\Environment;
  use Cielo\API30\Ecommerce\Sale;
  use Cielo\API30\Ecommerce\CieloEcommerce;
  use Cielo\API30\Ecommerce\Payment;
  use Cielo\API30\Ecommerce\Items;
  use Cielo\API30\Ecommerce\Address;
  use Cielo\API30\Ecommerce\Browser;
  use Cielo\API30\Ecommerce\MerchantDefinedFields;

  use Cielo\API30\Ecommerce\Request\CieloRequestException;

class cielo_api extends RestApi{

  public $registry;
  public $load;
  public $config;
  public $session;
  public $db;
  public $log;
  public $logError;

  public function __construct($registry) {
    $this->registry = $registry;
    $this->load     = $registry->get('load');
		$this->config   = $registry->get('config');
		$this->session  = $registry->get('session');
		$this->db       = $registry->get('db');
		$this->logError = $registry->get('log');

    $this->log = new BaseLog($registry);
	}

  public function getMerchant(){
    return new Merchant($this->config->get('cielo_api_numero'), $this->config->get('cielo_api_chave'));
  }

  public function getMerchantPix(){
    return new Merchant($this->config->get('cielo_api_pix_numero'), $this->config->get('cielo_api_pix_chave'));
  }

  public function getEnvironment(){
    if($this->config->get('cielo_api_ambiente_teste')){
      return Environment::sandbox();
    }else{
      return Environment::production();
    }
  }

  public function getToken3DS(){
    $header =  array(
      'Content-Type: application/json',
      'Authorization: Basic '.base64_encode($this->config->get('cielo_api_client_id').':'.$this->config->get('cielo_api_client_secret'))
    );

    $dataSend = array(
         'EstablishmentCode' => $this->config->get('cielo_api_codigoestabelecimento'),
         'MerchantName' => $this->config->get('cielo_api_nome_estabelecimento'),
         'MCC' => $this->config->get('cielo_api_codigo_categoria')
    );

    if($this->config->get('cielo_api_ambiente_teste')){
      $url3Ds = 'https://mpisandbox.braspag.com.br/v2/auth/token';
    }else{
      $url3Ds = 'https://mpi.braspag.com.br/v2/auth/token';
    }
    $data = parent::callAPI($url3Ds, 'POST', json_encode($dataSend), $header, false, $retorno, Type_DadosSend::TpDados_Identificar);

    return $data;
  }

  public function setHeader($merchant) {
    return [
        'MerchantKey: ' . $merchant->getKey(),
        'MerchantId: ' . $merchant->getId(),
        'Content-Type: application/json',
        'Accept: application/json'
    ];
  }

  public function cieloPagamentoPix($dadosTransacao) {
    $merchant = $this->getMerchantPix();
    $environment = $this->getEnvironment();

    $this->load->type('tipocieloapi');
    $this->load->type('tipocomum');

    $headers = $this->setHeader($merchant);

    $dataSend = json_encode([
      'MerchantOrderId' => $dadosTransacao['id_pedido'],
      'Payment' => [
        'Type' => ucfirst(Type_PaymentOption::Tp_Pix),
        'Amount' => $dadosTransacao['valor'],
        "Provider" => Type_CieloApiPixProviders::getNickFromValue($this->config->get('cielo_api_pix_provider')),
        "QrCode" => [
              "Expiration" => ($this->config->get('cielo_api_pix_expires') ? ($this->config->get('cielo_api_pix_expires') * 60) : 86400)
          ]
      ],
      'Customer' => [
        'Name' => $dadosTransacao['custommer']['nome_cliente'],
        'Identity' => $dadosTransacao['custommer']['documento'],
        'IdentityType' => $dadosTransacao['custommer']['tipo_documento']
      ]
    ]);

    $data = parent::callAPI($environment->getApiUrl().'1/sales/', 'POST', $dataSend, $headers, false, $retorno, Type_DadosSend::TpDados_Identificar);
    $data = is_string($data) ? json_decode($data, true) : $data;
    
    if (!$data || $retorno != 201) {
      $error = isset($data[0]->Message) ? $data[0]->Message : 'Unknown error';
      $this->logError->write('Error in Pix payment: ' . json_encode($error, JSON_UNESCAPED_UNICODE));
    }
    return $data;
  }

  public function getPagamentoPix($pixId) {
    $merchant = $this->getMerchantPix();
    $environment = $this->getEnvironment();

    $headers = $this->setHeader($merchant);

    $data = parent::callAPI($environment->getApiQueryURL().'1/sales/'.$pixId, 'GET', null, $headers, false, $retorno, Type_DadosSend::TpDados_Identificar);
    $data = is_string($data) ? json_decode($data, true) : $data;
    
    if (!$data || $retorno != 200) {
      $error = isset($data[0]->Message) ? $data[0]->Message : 'Unknown error';
      $this->logError->write('Error getting Pix payment: ' . json_encode($error, JSON_UNESCAPED_UNICODE));
    }
    return $data;
  }

  public function cieloPagamentoCartaoCredito($dadosTransacao){
    $merchant = $this->getMerchant();
    $environment = $environment = $this->getEnvironment();

    $this->load->type('tipocieloapi');
    $this->load->type('tipocomum');

    try{
      $sale     = new Sale($dadosTransacao['id_pedido']);
      $payment  = $sale->payment($dadosTransacao['total_pedido'],$dadosTransacao['cartao_parcelas']);

      $payment->setType(Payment::PAYMENTTYPE_CREDITCARD)
              ->creditCard($dadosTransacao['cartao_csv'], ucfirst($dadosTransacao['cartao_bandeira']))
              ->setExpirationDate($dadosTransacao['cartao_dataexp'])
              ->setCardNumber($dadosTransacao['cartao_numero'])
              ->setHolder($dadosTransacao['cartao_nomeimpresso']);

      $customer = $sale->customer($dadosTransacao['custommer']['nome_cliente']);

      if ( false ){
        $customer->setIdentity($dadosTransacao['custommer']['cpf_cnpj'])
          ->setIdentityType($dadosTransacao['custommer']['tipo_documento'])
          ->setEmail($dadosTransacao['custommer']['email']);

        if ($dadosTransacao['custommer']['data_nascimento']){
          $customer->setBirthDate($dadosTransacao['custommer']['data_nascimento']);
        }

        $customer->Address()->setZipCode($dadosTransacao['custommer']['payment_address']['postcode'])
          ->setCountry('BR')
          ->setState($dadosTransacao['custommer']['payment_address']['estado'])
          ->setCity($dadosTransacao['custommer']['payment_address']['cidade'])
          ->setDistrict($dadosTransacao['custommer']['payment_address']['bairro'])
          ->setStreet($dadosTransacao['custommer']['payment_address']['rua'])
          ->setNumber($dadosTransacao['custommer']['payment_address']['numero']);

        $customer->BillingAddress()->setZipCode($dadosTransacao['custommer']['payment_address']['postcode'])
          ->setCountry('BR')
          ->setState($dadosTransacao['custommer']['payment_address']['estado'])
          ->setCity($dadosTransacao['custommer']['payment_address']['cidade'])
          ->setDistrict($dadosTransacao['custommer']['payment_address']['bairro'])
          ->setStreet($dadosTransacao['custommer']['payment_address']['rua'])
          ->setNumber($dadosTransacao['custommer']['payment_address']['numero']);

        $customer->DeliveryAddress()->setZipCode($dadosTransacao['custommer']['shipping_address']['postcode'])
          ->setCountry('BR')
          ->setState($dadosTransacao['custommer']['shipping_address']['estado'])
          ->setCity($dadosTransacao['custommer']['shipping_address']['cidade'])
          ->setDistrict($dadosTransacao['custommer']['shipping_address']['bairro'])
          ->setStreet($dadosTransacao['custommer']['shipping_address']['rua'])
          ->setNumber($dadosTransacao['custommer']['shipping_address']['numero']);


        $itens = array();

        foreach ($dadosTransacao['products'] as $produto){
          $item = new Items;
          //Critérios de relevancia para a analise de fraude (Normal é o padrão)>>
          $item->setHostHedge('Normal');
          $item->setNonSensicalHedge('Normal');
          $item->setObscenitiesHedge('Normal');
          $item->setPhoneHedge('Normal');
          $item->setTimeHedge('Normal');
          $item->setRisk('Normal');
          //<<Critérios de relevancia para a analise de fraude (Normal é o padrão)
          $item->setName($produto['name']);
          $item->setQuantity($produto['quantity']);
          $item->setSku($produto['product_id']);
          $item->setUnitPrice(number_format($produto['price'], 2, '', ''));
          $itens[] = $item;
        }

        //https://developercielo.github.io/manual/cielo-ecommerce#tabela-20-payment.fraudanalysis.merchantdefinedfields
        $merchantDefinedFieldsList = array(
          "1" => $dadosTransacao['custommer']['email'], //Cliente efetuou Login
          "2" => $dadosTransacao['custommer']['dias_cadastrado'], //Quantidade em dias que o cliente é seu cliente,
          "3" => $dadosTransacao['cartao_parcelas'], //Quantidade de parcelas,
          "4" => "Web", //Canal
          "6" => $dadosTransacao['custommer']['dias_ultimo_pedido'], //Quantidade em dias desde a última compra realizada pelo cliente
          "7" => $this->config->get("config_owner"),
          "21" => number_format($dadosTransacao['frete_total'], 2, '', ''),//Valor do frete
          "23" => substr($dadosTransacao['cartao_numero'], -4),//Sufixo (4 últimos dígitos) do cartão
          "24" => $dadosTransacao['custommer']['dias_primeiro_pedido'],// Quantidade de dias desde a primeira compra realizada pelo cliente
          "26" => substr($dadosTransacao['cartao_numero'], 0,6),//Bin (6 primeiros dígitos) do cartão de crédito
          "41" => $dadosTransacao['custommer']['tipo_documento'],
          "44" => $dadosTransacao['custommer']['qtd_pedidos'],//Quantidade histórica de compras realizadas pelo cliente
          "46" => $dadosTransacao['cartao_nomeimpresso'],
          "49" => number_format($dadosTransacao['custommer']['media_compras'], 2, '', ''),	//Média das compras realizadas nos últimos 6 meses
          "84" => "PROPRIA"
        );
        if (isset($dadosTransacao['frete'])){
          if(substr_count(strtolower($dadosTransacao['frete']), 'sedex')>0){
            $merchantDefinedFieldsList['37'] = 'Sedex';
          }
        }
        if ($dadosTransacao['custommer']['data_nascimento']){
          $dataNascimento = new DateTime($dadosTransacao['custommer']['data_nascimento']);
          $dataAtual = new DateTime(date("Y-m-d"));
          $diferenca = $dataNascimento->diff($dataAtual);
          $merchantDefinedFieldsList['42'] = $diferenca->y;
        }
        if($dadosTransacao['cupom_desconto']){
          $merchantDefinedFieldsList['5'] = $dadosTransacao['cupom_desconto'];
        }

        $merchantDefinedFields = array();
        foreach ($merchantDefinedFieldsList as $key => $value){
          $merchantField = new MerchantDefinedFields;
          $merchantField->setId($key);
          $merchantField->setValue($value);
          $merchantDefinedFields[] = $merchantField;
        }

        $browser = new Browser;
        $browser->setFingerPrintId($this->config->get('cielo_api_numero').$dadosTransacao['browser_info']['finger_print'])
          ->setCookiesAccepted($dadosTransacao['browser_info']['cookies'])

          ->setIpAddress($dadosTransacao['browser_info']['ip'])
          ->setType($dadosTransacao['browser_info']['type']);

        $payment->FraudAnalysis()
          ->setProvider("Cybersource")
          ->setSequence($this->config->get('cielo_api_anti_fraude_sequence'))
          ->setSequenceCriteria($this->config->get('cielo_api_anti_fraude_sequence_criteria'))
          ->setCaptureOnLowRisk( $this->config->get('cielo_api_anti_fraude_capturelowrisk') ? true : false )
          ->setVoidOnHighRisk( $this->config->get('cielo_api_anti_fraude_voidhightrisk') ? true : false )
          ->setTotalOrderAmount($dadosTransacao['total_pedido'])
          ->setBrowser($browser)
          ->setMerchantDefinedFields($merchantDefinedFields)
          ->cart()->setItems($itens);

      }

      $saleResult = (new CieloEcommerce($merchant, $environment))->createSale($sale);

      $paymentId = $saleResult->getPayment()->getPaymentId();
	    $tid    = $saleResult->getPayment()->getTid();
      $status = $saleResult->getPayment()->getStatus();
      $codRetorno = $saleResult->getPayment()->getReturnCode();
      $msgRetorno = $saleResult->getPayment()->getReturnMessage();
      $numAutorizacao = $saleResult->getPayment()->getProofOfSale();
      $codAutorizacao = $saleResult->getPayment()->getAuthorizationCode();
      $cartaoMask = $saleResult->getPayment()->getCreditCard()->getCardNumber();
      $dataPagamento = $saleResult->getPayment()->getReceivedDate();

      if (( in_array($status,[Type_CieloApiStatus::Tp_Aguardando,Type_CieloApiStatus::Tp_AptoAFinalizar]  ))
          &&($this->config->get('cielo_api_anti_fraude')==Type_AntiFraudeCheckout::Tp_SemVerificacao)){
        /* O antifraude cielo tem opção propria para captura ou cancelamento de acordo com configuração */
        $saleResult = (new CieloEcommerce($merchant, $environment))->captureSale($paymentId, $dadosTransacao['total_pedido'], 0);
        $tid    = $saleResult->getTid();
        $status = $saleResult->getStatus();
        $codRetorno = $saleResult->getReturnCode();
        $msgRetorno = $saleResult->getReturnMessage();
        $numAutorizacao = $saleResult->getProofOfSale();
        $codAutorizacao = $saleResult->getAuthorizationCode();
      }

      $retorno = array(
        'paymentId' => $paymentId,
        'tid'       => $tid,
        'numAutorizacao' => $numAutorizacao,
        'codAutorizacao' => $codAutorizacao,
        'cartaoMask'=> $cartaoMask,
        'codRetorno'=> $codRetorno,
        'msgRetorno'=> $msgRetorno,
        'status'    => $status,
        'dataPagamento' => $dataPagamento
      );

      if (false){

        $resultFraude = $saleResult->getPayment()->getFraudAnalysis()->getReplyData();

        $retorno['anti_fraude'] = array(
          'fraud_analysis_id' => $saleResult->getPayment()->getFraudAnalysis()->getId() ,
          'status' => $saleResult->getPayment()->getFraudAnalysis()->getStatus(),
          'reason_code' => $saleResult->getPayment()->getFraudAnalysis()->getFraudAnalysisReasonCode(),
          'address_info_code' => $resultFraude->getAddressInfoCode(),
          'factor_code' => $resultFraude->getFactorCode(),
          'score' => $resultFraude->getScore(),
          'bin_country' => $resultFraude->getBinCountry(),
          'card_issuer' => $resultFraude->getCardIssuer(),
          'card_scheme' => $resultFraude->getCardScheme(),
          'host_severity' => $resultFraude->getHostSeverity(),
          'internet_info_code' => $resultFraude->getInternetInfoCode(),
          'ip_routing_method' => $resultFraude->getIpRoutingMethod(),
          'score_model_used' => $resultFraude->getScoreModelUsed(),
          'case_priority' => $resultFraude->getCasePriority(),
          'provider_transaction_id' => $resultFraude->getProviderTransactionId(),
          'hot_list_info_code' => $resultFraude->getHotListInfoCode(),
          'identity_info_code' => $resultFraude->getIdentityInfoCode(),
          'ip_city' => $resultFraude->getIpCity(),
          'ip_country' => $resultFraude->getIpCountry(),
          'ip_state' => $resultFraude->getIpState(),
          'velocity_infocode' => $resultFraude->getVelocityinfocode(),
        );

      }else{
        $retorno['anti_fraude'] = false;
      }
      return $retorno;
    } catch (CieloRequestException $e) {
        $log = new Log(Date('Y-m-d') . '_payment_error.txt');
        if(is_null($e->getCieloError())){
            $error = new stdClass();
            $error->tag = get_class($this);
            $error->code = $e->getCode();
            $error->message = $e->getMessage();
        } else {
            $errorCielo = $e->getCieloError();
            $error = new stdClass();
            $error->tag = get_class($this);
            $error->code = $errorCielo->getCode();
            $error->message = $errorCielo->getMessage();
        }
        $log->write(json_encode($error, JSON_UNESCAPED_UNICODE));
        $retorno['error'] = array(
            'code' => $error->code,
            'message' => $error->message
        );

        if (isset($saleResult)) {
            $this->log->baseLog->setTipoEnviado(Type_LogEnviado::Tp_LogEnviado);
            $this->log->baseLog->setOrigem(__METHOD__);
            $this->log->baseLog->setTipoLog(Type_Log::Tp_LogPedido);
            $this->log->baseLog->setTipoId(Type_LogId::Tp_LogIdPedido);
            $this->log->baseLog->setEntidadeId($dadosTransacao['id_pedido']);
            $this->log->baseLog->setUrl($environment->getApiUrl().'1/sales/');
            $this->log->baseLog->setMetodo('POST');
            $this->log->baseLog->setRetorno($saleResult);
            $this->log->baseLog->setDataHora($this->db->dataAtual());
            $this->log->saveBaseLog();
        }

        return $retorno;
    }
  }

  public function cieloCapturaPagamento($paymentId,$total_pedido){
    try{
      $merchant = $this->getMerchant();
      $environment = $environment = $this->getEnvironment();
      $sale = (new CieloEcommerce($merchant, $environment))->captureSale($paymentId,$total_pedido, 0);

	    $tid    = $sale->getTid();
      $status = $sale->getStatus();
      $codRetorno = $sale->getReturnCode();
      $msgRetorno = $sale->getReturnMessage();
      $numAutorizacao = $sale->getProofOfSale();
      $codAutorizacao = $sale->getAuthorizationCode();

      $retorno = array(
        'tid'       => $tid,
        'numAutorizacao' => $numAutorizacao,
        'codAutorizacao' => $codAutorizacao,
        'codRetorno'=> $codRetorno,
        'msgRetorno'=> $msgRetorno,
        'status'    => $status
      );

      return $retorno;

    } catch (CieloRequestException $e) {
        $logData = [
            'tag'      => '[' . get_class($this) . ']',
            'message'  => 'Erro cieloAPI',
            'exception'=> [
                'type'    => get_class($e),
                'message' => $e->getMessage(),
                'code'    => $e->getCode(),
                'file'    => $e->getFile(),
                'line'    => $e->getLine(),
                'trace'   => $e->getTraceAsString()
            ],
            'datetime' => date('c'), // data/hora no padrão ISO
        ];
      $log = new Log(Date('Y-m-d') . '_payment_error.txt');
      $log->write(json_encode($logData, JSON_UNESCAPED_UNICODE));
      $error = $e->getCieloError();
      $retorno['error'] = array(
        'code' => $error->getCode(),
        'message' => $error->getMessage()
      );
      return $retorno;
    }
  }

  public function cieloPagamentoCartaoDebito($dadosTransacao){
    $merchant = $this->getMerchant();
    $environment = $environment = $this->getEnvironment();

    $sale     = new Sale($dadosTransacao['id_pedido']);
    $customer = $sale->customer($dadosTransacao['nome_cliente']);
    $payment  = $sale->payment($dadosTransacao['total_pedido']);

    $payment->setReturnUrl($urlRetorno);//Url para voltar a loja (Success)


    $payment->setType(Payment::PAYMENTTYPE_DEBITCARD)
            ->debitCard($dadosTransacao['cartao_csv'], $dadosTransacao['cartao_bandeira'])
            ->setExpirationDate($dadosTransacao['cartao_dataexp'])
            ->setCardNumber($dadosTransacao['cartao_numero'])
            ->setHolder($dadosTransacao['cartao_nomeimpresso']);
    try {
      $sale = (new CieloEcommerce($merchant, $environment))->createSale($sale);
      $paymentId = $sale->getPayment()->getPaymentId();
      $authenticationUrl = $sale->getPayment()->getAuthenticationUrl();
      $status = $sale->getPayment()->getStatus();
      $codRetorno = $sale->getPayment()->getReturnCode();

      $retorno = array(
        'paymentId' => $paymentId,
        'urlAut'    => $authenticationUrl,
        'codRetorno'=> $codRetorno,
        'status'    => $status
      );

      return $retorno;

    } catch (CieloRequestException $e) {
      $error = $e->getCieloError();

    }
  }

  public function cieloPagamentoTransferencia($dadosTransacao){
    $merchant = $this->getMerchant();
    $environment = $environment = $this->getEnvironment();

    $sale     = new Sale($dadosTransacao['id_pedido']);
    $customer = $sale->customer($dadosTransacao['nome_cliente']);
    $payment  = $sale->payment($dadosTransacao['total_pedido']);

    $payment->setReturnUrl($urlRetorno);//Url para voltar a loja (Success)


    $payment->setType(Payment::PAYMENTTYPE_ELECTRONIC_TRANSFER)
            ->setProvider($dadosTransacao['banco']);

    try {
      $sale = (new CieloEcommerce($merchant, $environment))->createSale($sale);
      $paymentId = $sale->getPayment()->getPaymentId();
      $authenticationUrl = $sale->getPayment()->getAuthenticationUrl();

      $retorno = array(
        'paymentId' => $paymentId,
        'urlAut'    => $authenticationUrl,
        'status'    => $status
      );

    } catch (CieloRequestException $e) {
      $error = $e->getCieloError();
    }
  }

  public function cieloPagamentoBoleto($dadosTransacao,$dadosCliente,$dadosBoleto){
    $merchant = $this->getMerchant();
    $environment = $environment = $this->getEnvironment();

    $sale     = new Sale($dadosTransacao['id_pedido']);
    $customer = $sale->customer($dadosCliente['nome'])
                      ->setIdentity($dadosCliente['cpf'])
                      ->setIdentityType('CPF')
                      ->address()->setZipCode($dadosCliente['cep'])
                                 ->setCountry('BRA')
                                 ->setState($dadosCliente['UF'])
                                 ->setCity($dadosCliente['cidade'])
                                 ->setDistrict($dadosCliente['bairro'])
                                 ->setStreet($dadosCliente['rua'])
                                 ->setNumber($dadosCliente['numero']);


    $payment = $sale->payment($dadosTransacao['total_pedido'])
                    ->setType(Payment::PAYMENTTYPE_BOLETO)
                    ->setProvider($dadosBoleto['banco'])
                    ->setAddress($dadosBoleto['rua'])
                    ->setBoletoNumber($dadosBoleto['numero'])
                    ->setAssignor($dadosBoleto['empresa_cedente'])
                    ->setDemonstrative($dadosBoleto['demonstrativo'])
                    ->setExpirationDate($dadosBoleto['data_exp'])
                    ->setIdentification($dadosBoleto['identificacao_cedente'])
                    ->setInstructions($dadosBoleto['instrucoes']);


    try {
      $sale = (new CieloEcommerce($merchant, $environment))->createSale($sale);
      $paymentId = $sale->getPayment()->getPaymentId();
      $boletoURL = $sale->getPayment()->getUrl();

      printf("URL Boleto: %s\n", $boletoURL);
    } catch (CieloRequestException $e) {
      $error = $e->getCieloError();
    }
  }

  public function cieloValidaMerchant($cielo_api_numero = '', $cielo_api_chave = ''){
    $merchant = new Merchant($cielo_api_numero, $cielo_api_chave);

    $environment = $environment = $this->getEnvironment();

    $sale = (new CieloEcommerce($merchant, $environment))->getSale('0');

    return $sale;
  }

  public function cieloCancelaPagamento($paymentId, $totalPedido){
    $merchant = $this->getMerchant();
    $environment = $environment = $this->getEnvironment();

    try{
      $sale = (new CieloEcommerce($merchant, $environment))->cancelSale($paymentId, $totalPedido);

      $status     = $sale->getStatus();
      $codRetorno = $sale->getReturnCode();
      $msgRetorno = $sale->getReturnMessage();

      $retorno = array(
        'status'      => $status,
        'codRetorno'  => $codRetorno,
        'msgRetorno'  => $msgRetorno
      );
      return $retorno;
    } catch (CieloRequestException $e) {
      $error = $e->getCieloError();
      $retorno['error'] = array(
        'code' => $error->getCode(),
        'message' => $error->getMessage()
      );
      return $retorno;
    }

  }



}
