<?php
    /**
     *  wsComPagoSF_VP_v40_CA.PHP
     * 
     *  Autor: Felipe Faccinetto
     *  Fecha: Marzo/2019
     * 
     *  DESCRIPCIÓN:
     * 
     *  Valida, crea y timbra un CFDI . 
     * 
     *  PARAMETROS:
     * 
     *  pDatPAC         Cadena codificada en base64 que contiene un arreglo JSON con los datos del PAC
     *  pDatGen         Cadena codificada en base64 que contiene un arreglo JSON con los datos generales del CFDI
     *  pDatEmi         Cadena codificada en base64 que contiene un arreglo JSON con los datos del emisor del CFDI
     *  pDatRec         Cadena codificada en base64 que contiene un arreglo JSON con los datos del receptor del CFDI
     *  pDatArt         Cadena codificada en base64 que contiene un arreglo JSON con los datos de los articulos del CFDI
     *  pDatDoc         Cadena codificada en base64 que contiene un arreglo JSON con los datos de los dcoumentos del CFDI
	 *  pRecordId		Identidicador del registro a actualizar en Zoho Creator
	 *  pFormatoPDF		Nombre del formato que se utilizara para generar el PDF
     *  pDatRel         Cadena codificada en base64 que contiene un arreglo JSON con los datos de los CFDI Relacionados
     *  pAppLnkNam      Identificador para la aplicación de llamado.
     *  pAppOwner       Especifica el propietario de la aplicación que se actualizara.
     * 
    */
    
    header('Content-Type: text/html; charset=UTF-8'); 
    include("qrlib/qrlib.php");
    require('oauth.php');

    $bDatPAC 	= "";
    $bDatGen 	= "";
    $bDatEmi 	= "";
    $bDatRec 	= "";
    $bDatArt 	= "";
	$bDatDoc  	= "";
    $bDatRel 	= "";
	$bRecId  	= "";
	$FormatoPDF = "wsComPagoPDF_VP_v40";
	$applnkname = 'cfdi'; 
    $TipPDFGen  = 0;
    $appOwner   = "";
    

    ## Cargamos los parametros enviados al WebService
    if (isset($_REQUEST['pDatPAC']))
    {
        $bDatPAC = $_REQUEST['pDatPAC'];        // Parametros para la conexión con el PAC
    }

    if (isset($_REQUEST['pDatGen']))
    {
        $bDatGen = $_REQUEST['pDatGen'];        // Datos Generales del CFDI
    }

    if (isset($_REQUEST['pDatEmi']))
    {
        $bDatEmi = $_REQUEST['pDatEmi'];        // Datos del Emisor del CFDI
    }

    if (isset($_REQUEST['pDatRec']))
    {
        $bDatRec = $_REQUEST['pDatRec'];        // Datos del Receptor del CFDI
    }

    if (isset($_REQUEST['pDatArt']))
    {
        $bDatArt = $_REQUEST['pDatArt'];        // Datos de los articulos del CFDI
    }

    // if (isset($_REQUEST['pDatDoc']))
    // {
    //     $bDatDoc = $_REQUEST['pDatDoc'];        // Datos de los documentos del CFDI
    // }

    if (isset($_REQUEST['pRecordId']))
    {
        $bRecId = $_REQUEST['pRecordId'];        // Identidicador del registro
    }

    if (isset($_REQUEST['pFormatoPDF']))
    {
        $FormatoPDF = $_REQUEST['pFormatoPDF'];        // Nombre del formato para el PDF
    }

    if (isset($_REQUEST['pDatRel']))
    {
        $bDatRel = $_REQUEST['pDatRel'];     	// Datos de los CFDI ReElacionados
    }

    if (isset($_REQUEST['pAppLnkNam']))
    {
        $applnkname = $_REQUEST['pAppLnkNam'];     	// Identificador de la aplicación en Zoho Creator
    }
	 
    if (isset($_REQUEST['pTipPDFGen']))
    {
        $TipPDFGen = $_REQUEST['pTipPDFGen'];        // Parametros para el tipo de formato
    }

    if (isset($_REQUEST['pAppOwner']))
    {
        $appOwner = $_REQUEST['pAppOwner'];        // Parametros para el propietario de la aplicación
    }

    if($appOwner == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsInvoiceSF] Parametros incompletos para el Servicio Web, faltan el Propoietario de la Aplicación");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    if($bRecId == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsComPagoSF_VP_v33] Parametros incompletos para el Servicio Web, faltan el ID del CFDI a actualizar");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    if($bDatPAC == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsComPagoSF_VP_v33] Parametros incompletos para el Servicio Web, faltan los Datos del PAC");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    if($bDatGen == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsInvoiceSF] Parametros incompletos para el Servicio Web, faltan los Datos Generales");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    if($bDatEmi == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsComPagoSF_VP_v33] Parametros incompletos para el Servicio Web, faltan los Datos del Emisor");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    if($bDatRec == "" or $bDatArt == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsComPagoSF_VP_v33] Parametros incompletos para el Servicio Web, faltan los Datos del Receptor");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    if($bDatArt == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsComPagoSF_VP_v33] Parametros incompletos para el Servicio Web, faltan los Datos de los Articulos");
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    // if($bDatDoc == "")
    // {
	// 	$j_array = array('code' => "400", "message" => "ERROR - [wsComPagoSF_VP_v33] Parametros incompletos para el Servicio Web, faltan los Datos de los Documentos");
	// 	$Resultado = json_encode($j_array);
	// 	echo $Resultado;
    //     return;
    // }

    ### 0. EXTRACCION DE PARAMETROS PARA EL CFDI ######################################################
    #== Primero, extraemos el JSON del string en base 64
    $bdDatPAC = base64_decode($bDatPAC);
    $bdDatGen = base64_decode($bDatGen);
    $bdDatEmi = base64_decode($bDatEmi);
    $bdDatRec = base64_decode($bDatRec);
    $bdDatArt = base64_decode($bDatArt);
	$bdDatDoc = base64_decode($bDatDoc); 
	if($bDatRel <> "")
	{
		$bdDatRel = base64_decode($bDatRel);
	}

    #== Segundo, decodificamos el JSON a un arreglo
    $abdDatPAC = json_decode($bdDatPAC,true);
    $abdDatGen = json_decode($bdDatGen,true);
    $abdDatEmi = json_decode($bdDatEmi,true);
    $abdDatRec = json_decode($bdDatRec,true);
    $abdDatArt = json_decode($bdDatArt,true);
    $abdDatDoc = json_decode($bdDatDoc,true);
	if($bDatRel <> "")
	{
		$abdDatRel = json_decode($bdDatRel,true);
	}

    print_r($abdDatDoc);
	
   
    #== Datos y Variables para OAuth Token
	$coa_ClientId 		= $abdDatGen["C_OAuth_client_id"];
	$coa_ClientSecret 	= $abdDatGen["C_OAuth_client_secret"];
	$coa_RefreshToken 	= $abdDatGen["C_OAuth_refresh_token"];
    $coa_GrantType      = $abdDatGen["C_OAuth_grant_type"];
    $coa_RedirectUri    = $abdDatGen["C_OAuth_redirect_uri"];
	$coa_AuthUrl 		= "https://accounts.zoho.com/oauth/v2/token";

    #----------------------------------------------------------------
  	# JFA: 2021-06-12
	# Obtenemos el access_token
  	#----------------------------------------------------------------
    $access_token = oauth($appOwner, 'ZCreator', $coa_RefreshToken, $coa_ClientId, $coa_ClientSecret, $coa_RedirectUri, $coa_GrantType, $coa_AuthUrl);  
    
   #== Conexión a Zoho Creator para el detalle del CFDI
   // Added by FFR para el manejo de Complementos de Pago con 1 o multiples documentos relacionados 
   $request_url =  'https://creator.zoho.com/api/v2/'.$appOwner.'/'.$applnkname.'/report/cfdi_cp_query/'.$bRecId;

   $ch = curl_init();
   curl_setopt($ch, CURLOPT_URL, $request_url);
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
   curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
   curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Zoho-oauthtoken ' . $access_token)); 

   $r = curl_exec($ch);
   $array = json_decode($r);
   $bdDatDoc =  $array->data->jsonDoctosRel;
   $abdDatDoc = json_decode($bdDatDoc,true);
   
   $dirBase = realpath("../");

    ### CÓDIGO FUENTE, FACTURACIÓN ELECTRÓNICA CFDI VERSIÓN 3.2 ACORDE A LOS REQUIRIMIENTOS DEL SAT, ANEXO 20.

    ### 1. CONFIGURACIÓN INICIAL ######################################################

    # 1.1 Configuración de zona horaria
    date_default_timezone_set('America/Mexico_City'); // 

    # 1.2 Muestra la zona horaria predeterminada del servidor (opcional a mostrar)

    ### 2. DEFINICIÓN DE CONSTANTES ###################################################
    $SendaPEMS  = "archs_pem/";   
    $SendaCFDI  = "archs_cfdi/";  
    $SendaGRAFS = "archs_graf/";  
    $SendaXSD   = "archs_xsd/";   
    
    // 2.1 Datos de acceso del usuario (proporcionados por el PAC).
    if($abdDatPAC["tipoTim"] == 1){
        ## Timbrado en producción
        $urlPAC   = "https://solucionfactible.com/ws/services/Timbrado";
    }
    else
    {
        ## Timbrado en pruebas
        $urlPAC   = "https://testing.solucionfactible.com/ws/services/Timbrado?wsdl";
    }
    
    $username = $abdDatPAC["username"];
    $password = $abdDatPAC["password"];

    $valorNod = '';
    $metodoDePago  = "";
    
    ### MUESTRA LOS DATOS DEL USUARIO QUE ESTÁ TIMBRANDO (OPCIONAL A MOSTRAR) ######
   
    
    ### 3. DEFINICIÓN DE VARIABLES INICIALES ##########################################
    $noCertificado = $abdDatGen["Certificado_SAT"];
    $file_cer      = $abdDatGen["Archivo_CER"];
    $file_key      = $abdDatGen["Archivo_KEY"];
	
	$organi_id_ZB  = $abdDatGen["organization_id_ZB"];
	$authtoken_ZB  = $abdDatGen["authtoken_ZB"];
	$authtoken_ZC  = $abdDatGen["authtoken_ZC"];

    // #== Datos y Variables para OAuth Token
	// $coa_ClientId 		= $abdDatGen["C_OAuth_client_id"];
	// $coa_ClientSecret 	= $abdDatGen["C_OAuth_client_secret"];
	// $coa_RefreshToken 	= $abdDatGen["C_OAuth_refresh_token"];
    // $coa_GrantType      = $abdDatGen["C_OAuth_grant_type"];
    // $coa_RedirectUri    = $abdDatGen["C_OAuth_redirect_uri"];
	// $coa_AuthUrl 		= "https://accounts.zoho.com/oauth/v2/token";

    // #----------------------------------------------------------------
  	// # JFA: 2021-06-12
	// # Obtenemos el access_token
  	// #----------------------------------------------------------------
    // $access_token = oauth($appOwner, 'ZCreator', $coa_RefreshToken, $coa_ClientId, $coa_ClientSecret, $coa_RedirectUri, $coa_GrantType, $coa_AuthUrl);

    //echo $access_token;
	
    ### 4. DESTINATARIOS DE E-MAILS, PERSONAS A QUIENES LES LLEGARÁ DE MANERA ADJUNTA LA FACTURA ELECTRÓNICA EN ARCHIVOS .XML Y .PDF
    $CadenaEmails = $abdDatGen["CadenaEmails"];
    $CadenaEmails = "ffaccinetto@aptus-legal.com";
    
    
    ### 5. DATOS GENERALES DE LA FACTURA ##############################################
    $fact_serie        	= $abdDatGen["comprobante_serie"];
    $fact_folio        	= $abdDatGen["comprobante_folio"];
    $NoFac             	= $fact_serie.$fact_folio;
    $fact_tipcompr     	= $abdDatGen["comprobante_tipo"];
    $fact_exportacion   = $abdDatGen["comprobante_exportacion"]; 
    $subTotal          	= $abdDatGen["subTotal"];
    $total             	= $abdDatGen["Total"];
    $fecha_fact        	= $abdDatGen["comprobante_fecha"];
    $condicionesDePago 	= $abdDatGen["condicionesDePago"];
    $LugarExpedicion   	= utf8_decode($abdDatGen["LugarExpedicion"]);
    $moneda            	= $abdDatGen["moneda"];
	$invoiceNumber		= utf8_decode($abdDatGen["invoiceNumber"]);
	$invoiceID     		= $abdDatGen["invoiceID"];		
	$TipoRelacion	    = $abdDatGen["TipoRelacion"];

    ### 5.1 DATOS GENERALES DEL PAGO ##############################################
	$FechaPago 		   	= $abdDatGen["FechaPago"];
	$FormaDePagoP		= $abdDatGen["FormaDePagoP"];
	$MonedaP			= $abdDatGen["MonedaP"];
	$Monto				= number_format($abdDatGen["Monto"],2,'.','');
	$TipoCambioP		= number_format($abdDatGen["TipoCambioP"],4,'.','');
	$NumOperacion		= utf8_decode($abdDatGen["NumOperacion"]);
	$RfcEmisorCtaOrd	= utf8_decode($abdDatGen["RfcEmisorCtaOrd"]);
	$NomBancoOrdExt		= utf8_decode($abdDatGen["NomBancoOrdExt"]);
	$CtaOrdenante		= utf8_decode($abdDatGen["CtaOrdenante"]);
	$RfcEmisorCtaBen	= utf8_decode($abdDatGen["RfcEmisorCtaBen"]);
	$CtaBeneficiario	= utf8_decode($abdDatGen["CtaBeneficiario"]);
	$TipoCadPago		= utf8_decode($abdDatGen["TipoCadPago"]);
	$CertPago			= $abdDatGen["CertPago"];
	$CadPago			= $abdDatGen["CadPago"];
	$SelloPago			= $abdDatGen["SelloPago"];

	### 6. MUESTRA LA ZONA HORARIA PREDETERMINADA DEL SERVIDOR (OPCIONAL A MOSTRAR) ######
    ### 7. ARRAYS QUE CONTIENEN LOS ARTICULOS QUE FORMAN PARTE DE LA VENTA ############
                            
    ### 8. DATOS GENERALES DEL EMISOR #################################################    
    $emisor_rs       = utf8_decode($abdDatEmi["emisor_rs"]);
    $emisor_rfc      = utf8_decode($abdDatEmi["emisor_rfc"]);
    $emisor_regfis   = utf8_decode($abdDatEmi["emisor_regfis"]);

    ### 9. DATOS GENERALES DEL RECEPTOR (CLIENTE) #####################################
    $RFC_Recep    = utf8_decode($abdDatRec["RFC_Recep"]);
    if (strlen($RFC_Recep)==12){$RFC_Recep = " ".$RFC_Recep; }else{$RFC_Recep = $RFC_Recep;}
    $receptor_rfc       = $RFC_Recep;
    $receptor_rs        = utf8_decode($abdDatRec["receptor_rs"]);
	$receptor_uso       = utf8_decode($abdDatGen["usoCFDI"]);
    $receptor_regfis    = utf8_decode($abdDatRec["receptor_regfis"]);
    $receptor_cp        = utf8_decode($abdDatRec["receptor_cp"]);

    if($TipPDFGen == 1)
    {
        $receptor_cal = utf8_decode($abdDatRec["receptor_cal"]);
        $receptor_ne  = utf8_decode($abdDatRec["receptor_ne"]);
        $receptor_ni  = utf8_decode($abdDatRec["receptor_ni"]);
        $receptor_col = utf8_decode($abdDatRec["receptor_col"]);
        $receptos_loc = utf8_decode($abdDatRec["receptor_loc"]);
        $receptor_del = utf8_decode($abdDatRec["receptor_del"]);
        $receptor_edo = utf8_decode($abdDatRec["receptor_edo"]);
        $receptor_pai = utf8_decode($abdDatRec["receptor_pai"]);
        $receptor_cp  = utf8_decode($abdDatRec["receptor_cp"]);

        $sDomicilio = $receptor_cal;
        if($receptor_ne != "")
        {
            $sDomicilio = $sDomicilio." ".$receptor_ne;
        } 
        if($receptor_ni != "")
        {
            $sDomicilio = $sDomicilio." ".$receptor_ni;
        } 
        if($receptor_col != "")
        {
            $sDomicilio = $sDomicilio.", ".$receptor_col;
        } 
        if($receptos_loc != "")
        {
            $sDomicilio = $sDomicilio.", ".$receptos_loc;
        } 
        if($receptor_del != "")
        {
            $sDomicilio = $sDomicilio.", ".$receptor_del;
        } 
        if($receptor_edo != "")
        {
            $sDomicilio = $sDomicilio.", ".$receptor_edo;
        } 
        if($receptor_pai != "")
        {
            $sDomicilio = $sDomicilio.", ".$receptor_pai;
        } 
        if($receptor_cp != "")
        {
            $sDomicilio = $sDomicilio." ".$receptor_cp;
        } 
        $sDireRecep = base64_encode(utf8_encode($sDomicilio));
    }

     // Calculamos el Monto Total de los Pagos relacionados
    $TotalRetencionesIVA            = 0.00;
    $TotalRetencionesISR            = 0.00;
    $TotalRetencionesIEPS           = 0.00;
    $TotalTrasladosBaseIVA16        = 0.00;
    $TotalTrasladosImpuestoIVA16    = 0.00;
    $TotalTrasladosBaseIVA8         = 0.00;
    $TotalTrasladosImpuestoIVA8     = 0.00;
    $TotalTrasladosBaseIVA0         = 0.00;
    $TotalTrasladosImpuestoIVA0     = 0.00;
    $TotalTrasladosBaseIVAExento    = 0.00;
    $MontoTotalPagos                = 0.00;

    for ($i=0; $i<count($abdDatDoc); $i++){
      $importePago     = $abdDatDoc[$i]["ImpPagado"] * $abdDatDoc[$i]["TipoCambioDR"];
      $importePago     = round($importePago,2,PHP_ROUND_HALF_UP);
      $MontoTotalPagos = $MontoTotalPagos + $importePago;

      // Varificamos si aplican los totales de impuestos
      if($abdDatDoc[$i]["ObjetoImpDR"] == "02")
      {
        // Retenciones
        if($abdDatDoc[$i]["RDR_ImpuestoDR"] == "001")
        {
            $TotalRetencionesISR = $TotalRetencionesISR + $abdDatDoc[$i]["RDR_ImporteDR"];
        }
        if($abdDatDoc[$i]["RDR_ImpuestoDR"] == "002")
        {
            $TotalRetencionesIVA = $TotalRetencionesIVA + $abdDatDoc[$i]["RDR_ImporteDR"];
        }
        // Traslados
        if($abdDatDoc[$i]["TDR_ImpuestoDR"] == "002")
        {
            if($abdDatDoc[$i]["TDR_TasaOCuotaDR"] == 0.16)
            {
                $TotalTrasladosBaseIVA16        = $TotalTrasladosBaseIVA16 + $abdDatDoc[$i]["TDR_BaseDR"];
                $TotalTrasladosImpuestoIVA16    = $TotalTrasladosImpuestoIVA16 + $abdDatDoc[$i]["TDR_ImporteDR"];
            }
            if($abdDatDoc[$i]["TDR_TasaOCuotaDR"] == 0.08)
            {
                $TotalTrasladosBaseIVA8         = $TotalTrasladosBaseIVA8 + $abdDatDoc[$i]["TDR_BaseDR"];
                $TotalTrasladosImpuestoIVA8     = $TotalTrasladosImpuestoIVA8 + $abdDatDoc[$i]["TDR_ImporteDR"];
            }
            if($abdDatDoc[$i]["TDR_TasaOCuotaDR"] == 0.00)
            {
                $TotalTrasladosBaseIVA0         = $TotalTrasladosBaseIVA0 + $abdDatDoc[$i]["TDR_BaseDR"];
                $TotalTrasladosImpuestoIVA0     = $TotalTrasladosImpuestoIVA0 + $abdDatDoc[$i]["TDR_ImporteDR"];
            }
        }
      }
	 }
  
    ### 10. CREACIÓN Y ALMACENAMIENTO DEL ARCHIVO .XML (CFDI) ANTES DE SER TIMBRADO ###################
    
    #== 10.1 Creación de la variable de tipo DOM, aquí se conforma el XML a timbrar posteriormente.
    $xml = new DOMdocument('1.0', 'UTF-8'); 
    $root = $xml->createElement("cfdi:Comprobante");
    $root = $xml->appendChild($root); 
    
    $cadena_original='||';
    $noatt=  array();
    
    #== 10.2 Se crea e inserta el primer nodo donde se declaran los namespaces ======
    cargaAtt($root, array("xsi:schemaLocation"=>"http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd",
            "xmlns:cfdi"=>"http://www.sat.gob.mx/cfd/4",
            "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
            "xmlns:pago20" => "http://www.sat.gob.mx/Pagos20"
        )
    );

    #== 10.3 Rutina de integración de nodos =========================================
    cargaAtt($root, array(
             "Version"=>"4.0", 
             "Serie"=>$fact_serie,
             "Folio"=>$fact_folio,
             "Fecha"=>date("Y-m-d")."T".date("H:i:s"),
             "NoCertificado"=>$noCertificado,
			 "SubTotal"=>$subTotal,
             "Moneda"=>$moneda,
             "Total"=>$total,
			 "TipoDeComprobante"=>$fact_tipcompr,
             "Exportacion"=>$fact_exportacion,
             "LugarExpedicion"=>$LugarExpedicion,
             "CondicionesDePago"=>$condicionesDePago,
          )
       );

    $emisor = $xml->createElement("cfdi:Emisor");
    $emisor = $root->appendChild($emisor);
    cargaAtt($emisor, array("Rfc"=>$emisor_rfc,
                            "Nombre"=>$emisor_rs,
                            "RegimenFiscal"=>$emisor_regfis
                             )
                        );
                    
    $receptor = $xml->createElement("cfdi:Receptor");
    $receptor = $root->appendChild($receptor);
    cargaAtt($receptor, array("Rfc"=>$receptor_rfc,
                    "Nombre"=>$receptor_rs,
                    "DomicilioFiscalReceptor"=>$receptor_cp,
                    "RegimenFiscalReceptor"=>$receptor_regfis,
                    "UsoCFDI"=>$receptor_uso
                )
            );
    
    // Adicionado por FFR en Marzo 30, 2019
	// Para el manejo de los CFDI Relacionados 
	if($bDatRel <> "")
	{
		$cfdiRelacionados = $xml->createElement("cfdi:CfdiRelacionados");
		$cfdiRelacionados = $root->appendChild($cfdiRelacionados);
        cargaAtt($cfdiRelacionados, array("TipoRelacion"=>utf8_decode($TipoRelacion)));
		for ($i=0; $i < count($abdDatRel); $i++)
		{
			$cfdiRelacionado = $xml->createElement("cfdi:CfdiRelacionado");
			$cfdiRelacionado = $cfdiRelacionados->appendChild($cfdiRelacionado);
			cargaAtt($cfdiRelacionado, array("UUID"=>utf8_decode($abdDatRel[$i]["UUID"])));
		}
	}


    $conceptos = $xml->createElement("cfdi:Conceptos");
    $conceptos = $root->appendChild($conceptos);
    
    #== 10.4 Ciclo "for", recopilación de datos de artículos e integración de sus respectivos nodos =
    for ($i=0; $i<count($abdDatArt); $i++){
        $concepto = $xml->createElement("cfdi:Concepto");
        $concepto = $conceptos->appendChild($concepto);
        cargaAtt($concepto, array(
               "ClaveProdServ"=>utf8_decode($abdDatArt[$i]["claveProd"]),
               "Cantidad"=>$abdDatArt[$i]["cantidad"],
               "ClaveUnidad"=>utf8_decode($abdDatArt[$i]["unidad"]),
               "Descripcion"=>utf8_decode($abdDatArt[$i]["descripcion"]),
               "ValorUnitario"=>$abdDatArt[$i]["valorUnitario"],
               "Importe"=>$abdDatArt[$i]["importe"],
               "ObjetoImp"=>utf8_decode($abdDatArt[$i]["ObjetoImp"])
            )
        );
	}
	
    $complemento = $xml->createElement("cfdi:Complemento");
    $complemento = $root->appendChild($complemento);

    $Pagos = $xml->createElement("pago20:Pagos");
    $Pagos = $complemento->appendChild($Pagos);
	
    cargaAtt($Pagos, array("xmlns:pago20"=>"http://www.sat.gob.mx/Pagos20", 
					"Version"=>"2.0"
                  )
            );

    $totales = $xml->createElement("pago20:Totales");
    $total = $Pagos->appendChild($totales);
    cargaAtt($total, array(
            "TotalRetencionesIVA"=>number_format($TotalRetencionesIVA,2,'.',''),
            "TotalRetencionesISR"=>number_format($TotalRetencionesISR,2,'.',''),
            "TotalRetencionesIEPS"=>number_format($TotalRetencionesIEPS,2,'.',''),
            "TotalTrasladosBaseIVA16"=>number_format($TotalTrasladosBaseIVA16,2,'.',''),
            "TotalTrasladosImpuestoIVA16"=>number_format($TotalTrasladosImpuestoIVA16,2,'.',''),
            "TotalTrasladosBaseIVA8"=>number_format($TotalTrasladosBaseIVA8,2,'.',''),
            "TotalTrasladosImpuestoIVA8"=>number_format($TotalTrasladosImpuestoIVA8,2,'.',''),
            "TotalTrasladosBaseIVA0"=>number_format($TotalTrasladosBaseIVA0,2,'.',''),
            "TotalTrasladosImpuestoIVA0"=>number_format($TotalTrasladosImpuestoIVA0,2,'.',''),
            "TotalTrasladosBaseIVAExento"=>number_format($TotalTrasladosBaseIVAExento,2,'.',''),
            "MontoTotalPagos"=>number_format($MontoTotalPagos,2,'.','')
            )
        );
           
    $pago = $xml->createElement("pago20:Pago");
    $pago = $Pagos->appendChild($pago);

    cargaAtt($pago, array(
					"FechaPago"=>$FechaPago,
					"FormaDePagoP"=>$FormaDePagoP,
					"MonedaP"=>$MonedaP,
					"TipoCambioP"=>number_format($TipoCambioP,6,'.',''),
					"Monto"=>number_format($Monto,2,'.',''),
					"NumOperacion"=>$NumOperacion,
					"RfcEmisorCtaOrd"=>$RfcEmisorCtaOrd,
					"NomBancoOrdExt"=>$NomBancoOrdExt,
					"CtaOrdenante"=>$CtaOrdenante,
					"RfcEmisorCtaBen"=>$RfcEmisorCtaBen,
					"CtaBeneficiario"=>$CtaBeneficiario,
					"TipoCadPago"=>$TipoCadPago,
					"CertPago"=>$CertPago,
					"CadPago"=>$CadPago,
					"SelloPago"=>$SelloPago,
					"xmlns:pago20"=>"http://www.sat.gob.mx/Pagos20"
                )
            );
	
    $impuestosP         = false;

    $RDR_ImpuestoDR     = "";
    $RDR_ImporteDR      = 0.00;

    $TDR_BaseDR         = 0.00;
    $TDR_ImpuestoDR     = "";
    $TDR_TipoFactorDR   = "";
    $TDR_TasaOCuotaDR   = 0.00; 
    $TDR_ImporteDR      = 0.00;

    #== Ciclo "for", recopilación de datos de documento e integración de sus respectivos nodos =
    for ($i=0; $i<count($abdDatDoc); $i++){
		$doctoRel = $xml->createElement("pago20:DoctoRelacionado");
		$doctoRel = $pago->appendChild($doctoRel);
		if($abdDatDoc[$i]["MonedaDR"] == $MonedaP)
		{
			cargaAtt($doctoRel, array(
				   "IdDocumento"=>utf8_decode($abdDatDoc[$i]["IdDocumento"]),
				   "Serie"=>$abdDatDoc[$i]["Serie"],
				   "Folio"=>$abdDatDoc[$i]["Folio"],
				   "MonedaDR"=>utf8_decode($abdDatDoc[$i]["MonedaDR"]),
				   "EquivalenciaDR"=>$abdDatDoc[$i]["TipoCambioDR"],
				   "NumParcialidad"=>$abdDatDoc[$i]["NumParcialidad"],
				   "ImpSaldoAnt"=>number_format($abdDatDoc[$i]["ImpSaldoAnt"],2,'.',''),
				   "ImpPagado"=>number_format($abdDatDoc[$i]["ImpPagado"],2,'.',''),
				   "ImpSaldoInsoluto"=>number_format($abdDatDoc[$i]["ImpSaldoInsoluto"],2,'.',''),
				   "ObjetoImpDR"=>$abdDatDoc[$i]["ObjetoImpDR"]
				)
			);
		}
		else
		{
			cargaAtt($doctoRel, array(
				   "IdDocumento"=>utf8_decode($abdDatDoc[$i]["IdDocumento"]),
				   "Serie"=>$abdDatDoc[$i]["Serie"],
				   "Folio"=>$abdDatDoc[$i]["Folio"],
				   "MonedaDR"=>utf8_decode($abdDatDoc[$i]["MonedaDR"]),
				   "EquivalenciaDR"=>$abdDatDoc[$i]["TipoCambioDR"],
				   "NumParcialidad"=>$abdDatDoc[$i]["NumParcialidad"],
				   "ImpSaldoAnt"=>number_format($abdDatDoc[$i]["ImpSaldoAnt"],2,'.',''),
				   "ImpPagado"=>number_format($abdDatDoc[$i]["ImpPagado"],2,'.',''),
				   "ImpSaldoInsoluto"=>number_format($abdDatDoc[$i]["ImpSaldoInsoluto"],2,'.',''),
				   "ObjetoImpDR"=>$abdDatDoc[$i]["ObjetoImpDR"]
				)
			);
		}

        // Verificamos si aplica desglose de impuestos o no
        if($abdDatDoc[$i]["ObjetoImpDR"] == "02")
        {
            $impuestosP = true;

            $impuestosDR = $xml->createElement("pago20:ImpuestosDR");
            $impuestosDR = $doctoRel->appendChild($impuestosDR);

            if($abdDatDoc[$i]["RDR_ImpuestoDR"] == "001")
            {
                $retencionesDR = $xml->createElement("pago20:RetencionesDR");
                $retencionesDR = $impuestosDR->appendChild($retencionesDR);

                $retencionDR = $xml->createElement("pago20:RetencionDR");
                $retencionDR = $retencionesDR->appendChild($retencionDR);
                
                $RDR_ImpuestoDR     = $abdDatDoc[$i]["RDR_ImpuestoDR"];
                $RDR_ImporteDR      = $RDR_ImporteDR + $abdDatDoc[$i]["RDR_ImporteDR"];

                cargaAtt($retencionDR, array(
                    "BaseDR"=>number_format($abdDatDoc[$i]["RDR_BaseDR"],2,'.',''),
                    "ImpuestoDR"=>$abdDatDoc[$i]["RDR_ImpuestoDR"],
                    "TipoFactorDR"=>$abdDatDoc[$i]["RDR_TipoFactorDR"],
                    "TasaOCuotaDR"=>number_format($abdDatDoc[$i]["RDR_TasaOCuotaDR"],6,'.',''),
                    "ImporteDR"=>number_format($abdDatDoc[$i]["RDR_ImporteDR"],2,'.','')
                    )
                );
            }

            if($abdDatDoc[$i]["TDR_ImpuestoDR"] == "002")
            {
                $trasladosDR = $xml->createElement("pago20:TrasladosDR");
                $trasladosDR = $impuestosDR->appendChild($trasladosDR);

                $trasaldoDR = $xml->createElement("pago20:TrasladoDR");
                $trasaldoDR = $trasladosDR->appendChild($trasaldoDR);

                $TDR_BaseDR         = $TDR_BaseDR + $abdDatDoc[$i]["TDR_BaseDR"];
                $TDR_ImpuestoDR     = $abdDatDoc[$i]["TDR_ImpuestoDR"];
                $TDR_TipoFactorDR   = $abdDatDoc[$i]["TDR_TipoFactorDR"];
                $TDR_TasaOCuotaDR   = $abdDatDoc[$i]["TDR_TasaOCuotaDR"];
                $TDR_ImporteDR      = $TDR_ImporteDR + $abdDatDoc[$i]["TDR_ImporteDR"];

                cargaAtt($trasaldoDR, array(
                    "BaseDR"=>number_format($abdDatDoc[$i]["TDR_BaseDR"],2,'.',''),
                    "ImpuestoDR"=>$abdDatDoc[$i]["TDR_ImpuestoDR"],
                    "TipoFactorDR"=>$abdDatDoc[$i]["TDR_TipoFactorDR"],
                    "TasaOCuotaDR"=>number_format($abdDatDoc[$i]["TDR_TasaOCuotaDR"],6,'.',''),
                    "ImporteDR"=>number_format($abdDatDoc[$i]["TDR_ImporteDR"],2,'.','')
                    )
                );
            }
        }
	}

    // Resumen de Impuestos en caso de Aplicar.
    if($impuestosP == true)
    {
		$impuestosP = $xml->createElement("pago20:ImpuestosP");
		$impuestosP = $pago->appendChild($impuestosP);

        if($RDR_ImpuestoDR == "001")
        {
            $retencionesP = $xml->createElement("pago20:RetencionesP");
            $retencionesP = $impuestosP->appendChild($retencionesP);

            $retencionP = $xml->createElement("pago20:RetencionP");
            $retencionP = $retencionesP->appendChild($retencionP);

            cargaAtt($retencionP, array(
                "ImpuestoP"=>$RDR_ImpuestoDR,
                "ImporteP"=>number_format($RDR_ImporteDR,2,'.','')
                )
            );
        }

        if($TDR_ImpuestoDR == "002")
        {
            $trasladosP = $xml->createElement("pago20:TrasladosP");
            $trasladosP = $impuestosP->appendChild($trasladosP);

            $trasladoP = $xml->createElement("pago20:TrasladoP");
            $trasladoP = $trasladosP->appendChild($trasladoP);

            cargaAtt($trasladoP, array(
                "BaseP"=>number_format($TDR_BaseDR,2,'.',''),
                "ImpuestoP"=>$TDR_ImpuestoDR,
                "TipoFactorP"=>$TDR_TipoFactorDR,
                "TasaOCuotaP"=>number_format($TDR_TasaOCuotaDR,6,'.',''),
                "ImporteP"=>number_format($TDR_ImporteDR,2,'.','')
                )
            );
        }
    }

    #== 10.7 Termina de conformarse la "Cadena original" con doble ||
    $cadena_original .= "|";   
	
    #=== Muestra la cadena original (opcional a mostrar) =======================
    
    #== 10.8 Proceso para obtener el sello digital del archivo .pem.key =========
    $keyid = openssl_get_privatekey(file_get_contents($SendaPEMS.$file_key));
    openssl_sign($cadena_original, $crypttext, $keyid, OPENSSL_ALGO_SHA256);
    openssl_free_key($keyid);

    #== 10.9 Se convierte la cadena digital a Base 64 ===========================
    $sello = base64_encode($crypttext);    
    
    #=== Muestra el sello (opcional a mostrar) =================================
    
    #== 10.10 Proceso para extraer el certificado del sello digital ==================
    $file = $SendaPEMS.$file_cer;      // Ruta al archivo
    $datos = file($file);
    $certificado = ""; 
    $carga=false;  
    for ($i=0; $i<sizeof($datos); $i++){
        if (strstr($datos[$i],"END CERTIFICATE")) $carga=false;
        if ($carga) $certificado .= trim($datos[$i]);

        if (strstr($datos[$i],"BEGIN CERTIFICATE")) $carga=true;
    } 
    
    #=== Muestra el certificado del sello digital (opcional a mostrar) =========
    
    #== 10.11 Se continua con la integración de nodos ===========================   
    $root->setAttribute("sello",$sello);
    $root->setAttribute("certificado",$certificado);   # Certificado.
	
    #== Fin de la integración de nodos =========================================
    
    
    #=== 10.12 Se guarda el archivo .XML antes de ser timbrado =======================
	
    $NomArchCFDI = $SendaCFDI."PreCFDI-33_".$NoFac.".xml";
    //$NomArchPDF1 = $SendaCFDI."VP_CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT)."_".$invoiceNumber.".pdf";
	
    $NomArchXML = "PreCFDI-33_".$NoFac.".xml";
    $NomArchPDF = "VP_CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT)."_CP.pdf";
	
//    $cfdi = $xml->saveXML();
    $xml->formatOutput = true;             
    $xml->save($NomArchCFDI); // Guarda el archivo .XML (sin timbrar) en el directorio predeterminado.
    unset($xml);
    
    #=== 10.13 Se dan permisos de escritura al archivo .xml. =========================
    chmod($NomArchCFDI, 0777); 

    ### 13. CREACION DE PDF Y ENVIO DE CORREO CON XML Y PDF ###########################
    # 13.1 Creación del Archivo PDF
    //$url = 'http://64.235.39.50/aptusCFDIRF/wsComPagoPDF_VP_v33.php?NomArchXML='.$NomArchXML.'&NomArchPDF='.$NomArchPDF;
    if($TipPDFGen == 1)
    {
        $url = 'https://aptuslegal.app/aptusCFDIRF/'.$FormatoPDF.'.php?NomArchXML='.$NomArchXML.'&NomArchPDF='.$NomArchPDF.'&pDirRecep='.$sDireRecep;
    }
    else
    {
        $url = 'https://aptuslegal.app/aptusCFDIRF/'.$FormatoPDF.'.php?NomArchXML='.$NomArchXML.'&NomArchPDF='.$NomArchPDF;
    }
    $objCurl = curl_init();
    curl_setopt($objCurl, CURLOPT_URL, $url);
    curl_setopt($objCurl, CURLOPT_HEADER, 0);
    curl_setopt($objCurl, CURLOPT_RETURNTRANSFER, true);
    curl_exec($objCurl);
    curl_close($objCurl);

    # 13.3 Envio del Archivos a Invoice de Zoho Creator
	# Primero el PDF
    $file_name_with_full_path = '/var/www/html/aptusCFDIRF/archs_cfdi/'.$NomArchPDF;
    // echo $file_name_with_full_path;
    // echo '<br>';
    //$request_url = 'https://creator.zoho.com/api/xml/fileupload/scope=creatorapi';
    # Actualización a API V2 y OAuth JFA y FFR 2021-06-18
    $request_url = 'https://creator.zoho.com/api/v2/'.$appOwner.'/'.$applnkname.'/report/CFDI_Pago_v33_Borradores/'.$bRecId.'/File_PDF_VP/upload';

    if (function_exists('curl_file_create')) { // php 5.6+
      $cFile = curl_file_create($file_name_with_full_path);
    } 
    else 
	{  
      $cFile = '@' . realpath($file_name_with_full_path);
    }

    $post = array(
    // 'authtoken' => $authtoken_ZC,							
    // 'applinkname' => $applnkname,
    // 'formname' => 'ComplementoPagov33',
	// 'fieldname' => 'File_PDF_VP',
    // 'recordId' => $bRecId,
    // 'filename' => $NomArchPDF,
    'file'=> $cFile);

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $request_url);
    //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
    curl_setopt($ch, CURLOPT_POST, true); 
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post); 
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    #JFA: Cambiamos el método de Autenticación a OAuth
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Zoho-oauthtoken ' . $access_token));

    $r = curl_exec($ch);
    curl_close ($ch);

    #== Se elimina los archivos de trabajo
    //unlink($NomArchCFDI);
    //unlink($NomArchPDF1);

	
    $j_array = array('code' => "200", "message" => "Proceso de creacion de CFDI fue exitoso", 'file_upload' => $NomArchPDF);
    $Resultado = json_encode($j_array);
	echo $Resultado;
	return;
	
    
    ### 14. FUNCIONES DEL MÓDULO #########################################################
    # 14.1 Función que integra los nodos al archivo .XML y forma la "Cadena original".
    function cargaAtt(&$nodo, $attr){
        global $xml, $cadena_original;
        $quitar = array('sello'=>1, 'noCertificado'=>1, 'certificado'=>1, 'xmlns:pago20'=>1);
        foreach ($attr as $key => $val){
            $val = preg_replace('/\s\s+/', ' ', $val);
            $val = trim($val);
            if (strlen($val)>0){
                 $val = utf8_encode(str_replace("|","/",$val));
                 $nodo->setAttribute($key,$val);
                 if (!isset($quitar[$key])) 
                   if (substr($key,0,3) != "xml" &&
                       substr($key,0,4) != "xsi:")
                    $cadena_original .= $val . "|";
            }
         }
     }

    
    # 14.2 Funciónes que da formato al "Importe total" como lo requiere el SAT para ser integrado al código QR.
     
    function ProcesImpTot($ImpTot){
        $ArrayImpTot = explode(".", $ImpTot);
        $NumEnt = $ArrayImpTot[0];
        $NumDec = ProcesDecFac($ArrayImpTot[1]);
        return $NumEnt.".".$NumDec;
    }
    
    function ProcesDecFac($Num){
        $FolDec = "";
        if ($Num < 10){$FolDec = "00000".$Num;}
        if ($Num > 9 and $Num < 100){$FolDec = $Num."0000";}
        if ($Num > 99 and $Num < 1000){$FolDec = $Num."000";}
        if ($Num > 999 and $Num < 10000){$FolDec = $Num."00";}
        if ($Num > 9999 and $Num < 100000){$FolDec = $Num."0";}
        return $FolDec;
    }        
    
