<?php
    /**
     *  wsInvoiceSFJ.PHP
     * 
     *  Autor: Felipe Faccinetto
     *  Fecha: Septiembre/2016
     * 
     *  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
	 *  pRecordId		Identidicador del registro a actualizar en Zoho Creator
	 *  pFolioNum		Numeor de Folio asignado al CFDI por la serie
     * 
    */
    
    header('Content-Type: text/html; charset=UTF-8');
    include("qrlib/qrlib.php");

    $bDatPAC  = "";
    $bDatGen  = "";
    $bDatEmi  = "";
    $bDatRec  = "";
    $bDatArt  = "";
	$bRecId   = "";
	$bFolioN  = "";
	$tipoTest = 0;

    ## 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['pRecordId']))
    {
        $bRecId = $_REQUEST['pRecordId'];        // Identidicador del registro
    }

    if (isset($_REQUEST['pFolioNum']))
    {
        $bFolioN = $_REQUEST['pFolioNum'];        // Identidicador del registro
    }

    if (isset($_REQUEST['tipoTest']))
    {
        $tipoTest = $_REQUEST['tipoTest'];        // Indicador de si despliega resultados parciales
    }
	
    if($bRecId == "")
    {
		$j_array = array('code' => "300", "message" => "ERROR - [wsInvoiceSF] 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 - [wsInvoiceSF] 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 - [wsInvoiceSF] 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 - [wsInvoiceSF] 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 - [wsInvoiceSF] Parametros incompletos para el Servicio Web, faltan los Datos de los Articulos");
		$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);

	if ($tipoTest == 1){
		echo $bdDatPAC;
		echo '<br>';
		echo '<br>';
		echo $bdDatGen;
		echo '<br>';
		echo '<br>';
		echo $bdDatEmi;
		echo '<br>';
		echo '<br>';
		echo $bdDatRec;
		echo '<br>';
		echo '<br>';
		echo $bdDatArt;
		echo '<br>';
		echo '<br>';
	}
	
    #== 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);

    $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?wsdl";
    }
    else
    {
        ## Timbrado en pruebas
        $urlPAC   = "https://testing.solucionfactible.com/ws/services/Timbrado?wsdl";
    }
    
    $username = $abdDatPAC["username"];
    $password = $abdDatPAC["password"];

    if($tipoTest > 0){
		$username = "testing@solucionfactible.com";
		$password = "timbrado.SF.16672";
        $urlPAC   = "https://testing.solucionfactible.com/ws/services/Timbrado?wsdl";
	}
    
    $valorNod = '';
    $metodoDePago  = "";

	if ($tipoTest > 0){
		### MUESTRA LOS DATOS DEL USUARIO QUE ESTÁ TIMBRANDO (OPCIONAL A MOSTRAR) ######
		echo '<div style="font-size: 10pt; color: #000099; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'DATOS DEL USUARIO QUE ESTÁ TIMBRANDO';
		echo '</div>';
		echo '<div style="font-size: 10pt; color: #000000; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'USUARIO: <span style="color: #088A29; font-size: 11pt;">'.$username."</span><br>";
		echo 'PASSWORD: <span style="color: #088A29; font-size: 11pt;">'.$password."</span><br>";
		echo 'URL: <span style="color: #088A29; font-size: 11pt;">'.$urlPAC."</span><br>";
		echo '</div><br>';  
	}		
    
    ### 3. DEFINICIÓN DE VARIABLES INICIALES ##########################################
    $noCertificado = "00001000000301647911";
    $file_cer      = "00001000000301647911.cer.pem";  
    $file_key      = "00001000000301647911.key.pem";  

    ### 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["fact_serie"];
    $fact_folio        = $abdDatGen["fact_folio"];
	
	if ($bFolioN <> ""){
		$fact_folio = $bFolioN;
	}
	
    $NoFac             = $fact_serie.$fact_folio;
    $fact_tipcompr     = $abdDatGen["fact_tipcompr"];
    $tasa_iva          = $abdDatGen["tasa_iva"];
    $subTotal          = number_format($abdDatGen["subTotal"],2,'.','');
    $descuento         = number_format($abdDatGen["descuento"],2,'.','');
    $IVA               = number_format($abdDatGen["IVA"],2,'.','');
    $total             = number_format($abdDatGen["total"],2,'.','');
    $fecha_fact        = $abdDatGen["fecha_fact"];
    $NumCtaPago        = $abdDatGen["NumCtaPago"];
    $condicionesDePago = $abdDatGen["condicionesDePago"];
    $formaDePago       = utf8_decode($abdDatGen["formaDePago"]);
    $TipoCambio        = $abdDatGen["TipoCambio"];
    $LugarExpedicion   = utf8_decode($abdDatGen["LugarExpedicion"]);
    $moneda            = $abdDatGen["moneda"];
	$invoiceID		   = $abdDatGen["invoiceID"];
	$invoiceNumber	   = $abdDatGen["invoiceNumber"];

    $iva_retenido1     = number_format($abdDatGen["iva_retenido1"],2,'.','');
    $iva_retenido2     = number_format($abdDatGen["iva_retenido2"],2,'.','');
	
    $isr_retenido      = number_format($abdDatGen["isr_retenido"],2,'.','');
	
    $IEPS_Tasa_1       = $abdDatGen["IEPS_Tasa_1"];
    $IEPS_Importe_1    = number_format($abdDatGen["IEPS_Importe_1"],2,'.','');

    $IEPS_Tasa_2       = $abdDatGen["IEPS_Tasa_2"];
    $IEPS_Importe_2    = number_format($abdDatGen["IEPS_Importe_2"],2,'.','');
    
    $metodoDePago      = $abdDatGen["metodoDePago"];

    $fact_impuesto     = $IVA + $IEPS_Importe_1 + $IEPS_Importe_2 - $iva_retenido1 - $iva_retenido2 - $isr_retenido;
    
    $SumaImpTras       = $IVA + $IEPS_Importe_1 + $IEPS_Importe_2; 
    
    $totalImpuestosRetenidos   = number_format($isr_retenido,2,'.','');            
    $totalImpuestosTrasladados = number_format($SumaImpTras,2,'.',''); 
 
    
    ### 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"]);
    
    #== Domicilio fiscal =======================================================
    $emisor_cal       = utf8_decode($abdDatEmi["emisor_cal"]);
    $emisor_ne        = utf8_decode($abdDatEmi["emisor_ne"]);
    $emisor_ni        = utf8_decode($abdDatEmi["emisor_ni"]);
    $emisor_col       = utf8_decode($abdDatEmi["emisor_col"]);
	$emisor_localidad = utf8_decode($abdDatEmi["emisor_localidad"]);
    $emisor_delmpio   = utf8_decode($abdDatEmi["emisor_delmpio"]);
    $emisor_pais      = utf8_decode($abdDatEmi["emisor_pais"]);
    $emisor_edo       = utf8_decode($abdDatEmi["emisor_edo"]);
    $emisor_cp        = utf8_decode($abdDatEmi["emisor_cp"]);
    $emisor_referdom  = utf8_decode($abdDatEmi["emisor_referdom"]);

    ### 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_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"]);
    

    ### 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/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd",
            "xmlns:cfdi"=>"http://www.sat.gob.mx/cfd/3",
            "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance"
        )
    );
    
    #== 10.3 Rutina de integración de nodos =========================================
    cargaAtt($root, array(
             "version"=>"3.2", 
             "fecha"=>date("Y-m-d")."T".date("H:i:s"),
             "tipoDeComprobante"=>$fact_tipcompr,
             "formaDePago"=>$formaDePago,
             "condicionesDePago"=>$condicionesDePago,
             "noCertificado"=> $noCertificado,
             "certificado"=>"@",
             "subTotal"=>$subTotal,
             "descuento"=>$descuento,
             "TipoCambio"=>$TipoCambio,
             "Moneda"=>$moneda,
             "total"=>$total,
             "metodoDePago"=>$metodoDePago,
             "LugarExpedicion"=>$LugarExpedicion,
             "NumCtaPago"=>$NumCtaPago,
          )
       );
               
    $emisor = $xml->createElement("cfdi:Emisor");
    $emisor = $root->appendChild($emisor);
    cargaAtt($emisor, array("rfc"=>$emisor_rfc,
                             "nombre"=>$emisor_rs
                         )
                      );
    
    $domfis = $xml->createElement("cfdi:DomicilioFiscal");
    $domfis = $emisor->appendChild($domfis);
    cargaAtt($domfis, array("calle"=>$emisor_cal,
                    "noExterior"=>$emisor_ne,
                    "noInterior"=>$emisor_ni,
                    "colonia"=>$emisor_col,
                    "localidad"=>$emisor_localidad,
                    "referencia"=>$emisor_referdom,
                    "municipio"=>$emisor_delmpio,
                    "estado"=>$emisor_edo,
                    "pais"=>$emisor_pais,
                    "codigoPostal"=>$emisor_cp
               )
            ); 
    
    $expedido = $xml->createElement("cfdi:ExpedidoEn");
    $expedido = $emisor->appendChild($expedido);
    cargaAtt($expedido, array("calle"=>$emisor_cal,
                    "noExterior"=>$emisor_ne,
                    "noInterior"=>$emisor_ni,
                    "colonia"=>$emisor_col,
                    "localidad"=>$emisor_delmpio,
                    "referencia"=>$emisor_referdom,
                    "municipio"=>$emisor_delmpio,
                    "estado"=>$emisor_edo,
                    "pais"=>$emisor_pais,
                    "codigoPostal"=>$emisor_cp
               )
            ); 
                    
    $regfis = $xml->createElement("cfdi:RegimenFiscal");
    $regfis = $emisor->appendChild($regfis);    
    cargaAtt($regfis, array("Regimen"=>$emisor_regfis)
    );
    
    $receptor = $xml->createElement("cfdi:Receptor");
    $receptor = $root->appendChild($receptor);
    cargaAtt($receptor, array("rfc"=>$receptor_rfc,
                    "nombre"=>$receptor_rs
                )
            );
    
    $domicilio = $xml->createElement("cfdi:Domicilio");
    $domicilio = $receptor->appendChild($domicilio);
    cargaAtt($domicilio, array("calle"=>$receptor_cal,
                    "noExterior"=>$receptor_ne,
                    "noInterior"=>$receptor_ni,
                    "colonia"=>$receptor_col,
                    "localidad"=>$receptos_loc,
                    "referencia"=>"",
                    "municipio"=>$receptor_del,
                    "estado"=>$receptor_edo,
                    "pais"=>$receptor_pai,
                    "codigoPostal"=>$receptor_cp
               )
           );
    
    $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(
                    "cantidad" => $abdDatArt[$i]["cantidad"],
                    "unidad" => utf8_decode($abdDatArt[$i]["unidad"]),
                    //"noIdentificacion" => utf8_decode($abdDatArt[$i]["clave"]),
                    "descripcion" => utf8_decode($abdDatArt[$i]["descripcion"]),
                    "valorUnitario" => $abdDatArt[$i]["precio"],
                    "importe" => $abdDatArt[$i]["importe"]
            )
*/			
        cargaAtt($concepto, array(
                    "cantidad" => $abdDatArt[$i]["cantidad"],
                    "unidad" => utf8_decode($abdDatArt[$i]["unidad"]),
                    //"noIdentificacion" => utf8_decode($abdDatArt[$i]["clave"]),
                    //"descripcion" => utf8_decode($abdDatArt[$i]["descripcion"]),
                    "descripcion" => utf8_decode(str_replace("[\n|\r|\n\r]", ' ', $abdDatArt[$i]["descripcion"])),
                    "valorUnitario" => number_format($abdDatArt[$i]["precio"],4,'.',''),
                    "importe" => number_format($abdDatArt[$i]["importe"],4,'.','')
            )
        );
             
    }

    $impuestos = $xml->createElement("cfdi:Impuestos");
    $impuestos = $root->appendChild($impuestos);
    
    if($iva_retenido1 > 0 || $iva_retenido2 > 0 || $isr_retenido > 0){
        $retenciones = $xml->createElement("cfdi:Retenciones");
        $retenciones = $impuestos->appendChild($retenciones);
    }
    
    if($iva_retenido1 > 0){
          $retencion = $xml->createElement("cfdi:Retencion");
          $retencion = $retenciones->appendChild($retencion);
          cargaAtt($retencion,array("impuesto"=>"IVA","importe"=>$iva_retenido1)); 
    }
      
    if($iva_retenido2 > 0){
          $retencion = $xml->createElement("cfdi:Retencion");
          $retencion = $retenciones->appendChild($retencion);
          cargaAtt($retencion, array("impuesto"=>"IVA",
                        "importe"=>$iva_retenido2
                )
        );
    }  
    
    if($isr_retenido > 0 ){
          $retencion = $xml->createElement("cfdi:Retencion");
          $retencion = $retenciones->appendChild($retencion);
          cargaAtt($retencion, array("impuesto"=>"ISR",
                        "importe"=>$isr_retenido
                )
        ); 
    }      

	if($IVA > 0 || $IEPS_Importe_1 > 0 || $IEPS_Importe_2 > 0){
		$traslados = $xml->createElement("cfdi:Traslados");
		$traslados = $impuestos->appendChild($traslados);
	}
	
    if($IVA > 0){
		$traslado = $xml->createElement("cfdi:Traslado");
		$traslado = $traslados->appendChild($traslado);
		
		cargaAtt($traslado, array("impuesto"=>"IVA",
								  "tasa"=>$tasa_iva,
								  "importe"=> $IVA
				 )
	//	cargaAtt($traslado, array("impuesto"=>"IVA",
	//							  "tasa"=>$tasa_iva,
	//							  "importe"=> $fact_impuesto
	//			 )
		);      
    }
        
    #== 10.6 Nodos que registran el IEPS, en este ejemplo se ingresan dos, ambos con tasas distintas.        
    #        Se tiene que agregar uno por cada tasa distinta.    
        
    if($IEPS_Importe_1 > 0){
        $traslado = $xml->createElement("cfdi:Traslado"); // Nodo opcional.
        $traslado = $traslados->appendChild($traslado);
        cargaAtt($traslado, array("impuesto"=>"IEPS",
                                  "tasa"=>$IEPS_Tasa_1,
                                  "importe"=> $IEPS_Importe_1
                 )
        );      
    }

    if($IEPS_Importe_2 > 0){
        $traslado = $xml->createElement("cfdi:Traslado"); // Nodo opcional.
        $traslado = $traslados->appendChild($traslado);
        cargaAtt($traslado, array("impuesto"=>"IEPS",
                                  "tasa"=>$IEPS_Tasa_2,
                                  "importe"=> $IEPS_Importe_2
                 )
        );      
    }
        
    #===============================================================================================    
    
    // Nodo: "totalImpuestosRetenidos" agregado en Julio del 2016. 
    if($iva_retenido1 > 0 || $iva_retenido2 > 0 || $isr_retenido > 0){
		cargaAtt($impuestos, array("totalImpuestosRetenidos"=>$totalImpuestosRetenidos));
    }
    
	// Nodo: "totalImpuestosTrasladados" agregado en Julio del 2016. 
	if($IVA > 0 || $IEPS_Importe_1 > 0 || $IEPS_Importe_2 > 0){
		cargaAtt($impuestos, array("totalImpuestosTrasladados"=>$totalImpuestosTrasladados));
	}
    
                        
//    $complemento = $xml->createElement("cfdi:Complemento");
//    $complemento = $root->appendChild($complemento);
    
    #== 10.7 Termina de conformarse la "Cadena original" con doble ||
    $cadena_original .= "|";   
	
	if ($tipoTest > 0){
		#=== Muestra la cadena original (opcional a mostrar) =======================
		echo '<div style="font-size: 11pt; color: #000099; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'CADENA ORIGINAL';
		echo '</div>';
		echo '<div style="font-size: 9pt; color: #000000; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo $cadena_original;
		echo '</div><br>';
	}
	
    #=== 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_SHA1);
    openssl_free_key($keyid);

    #== 10.9 Se convierte la cadena digital a Base 64 ===========================
    $sello = base64_encode($crypttext);    
	
	if ($tipoTest > 0){
		#=== Muestra el sello (opcional a mostrar) =================================
		echo '<div style="font-size: 11pt; color: #000099; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'SELLO';
		echo '</div>';
		echo '<div style="font-size: 9pt; color: #000000; ; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo $sello;
		echo '</div><br>';
	}
    
    #== 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.
    //$root->setAttribute("motivoDescuento",$MotivDesc); # Motivo de descuento. 
    $root->setAttribute("folio",$fact_folio);          # Folio de la factura (número entero).
    $root->setAttribute("serie",$fact_serie);          # Serie de la factura (letra o letras).
    
    #== Fin de la integración de nodos =========================================
    
    
    #=== 10.12 Se guarda el archivo .XML antes de ser timbrado =======================
    $cfdi = $xml->saveXML();
    $xml->formatOutput = true;             
    $xml->save($SendaCFDI."PreFact_".$NoFac.".xml"); 
    unset($xml);
    
    #=== 10.13 Se dan permisos de escritura al archivo .xml. =========================
    chmod($SendaCFDI."PreFact_".$NoFac.".xml", 0777); 
    
    ### 11. PROCESO DE TIMBRADO ########################################################

    #=== Se muestra el .XML antes de ser timbrado (opcional a mostrar)==========
	if ($tipoTest > 0){
		echo '<div style="font-size: 11pt; color: #000099; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo 'FACTURA .XML A TIMBRAR';
		echo '</div>';
		echo '<div style="font-size: 9pt; color: #000000; font-family: Verdana, Arial, Helvetica, sans-serif;">';
		echo htmlspecialchars($cfdi);
		echo '</div><br>'; 
	}
    
    #== 11.1 Se crea una variable de tipo DOM y se le carga el CFDI =================================
    $xml2 = new DOMDocument();
    $xml2->loadXML($cfdi); 
    
    #== 11.2 Proceso de validación de la variable de tipo DOM (estructura del CFDI a timbrar) contra el esquema cfdv32.xsd ==========================
   
    $fname = $SendaCFDI."PreFact_".$NoFac.".xml";
    if(!file_exists($fname)){
     die(PHP_EOL . "File not found" . PHP_EOL . PHP_EOL);
    }
     
    $handle = fopen($fname, "r");
    $sData = '';
    #== 11.3 Convirtiendo el contenido del CFDI a BASE 64 ======================
    while(!feof($handle))
        $sData .= fread($handle, filesize($fname));
    fclose($handle);
    $b64 = base64_encode($sData);
    
    $response = '';

    if($abdDatPAC["tipoTim"] == 1){
        ## Timbrado en producción
        $urlLocation   = 'https://solucionfactible.com/ws/services/Timbrado';
      }
      else
      {
        ## Timbrado en pruebas
        $urlLocation   = 'https://testing.solucionfactible.com/ws/services/Timbrado';
      }

    #== 11.5 Se lleva a cabo el timbrado del XML ============================
    try {
            $client = new SoapClient($urlPAC);
            $client->__setLocation($urlLocation);
            $params = array('usuario' => $username, 'password' => $password, 'cfdiBase64'=>$b64, 'zip'=>False);
            $response = $client->__soapCall('timbrarBase64', array('parameters' => $params));
    } catch (SoapFault $fault) { 
			$j_array = array('code' => "400", "message" => $fault->faultcode."-".$fault->faultstring);
			$Resultado = json_encode($j_array);
			echo $Resultado;
			return;
    }    

    $ret = $response->return;

	if ($tipoTest > 0){
		echo '<br>';
		print_r($ret);
		echo '<br>';
		return;
	}
	
	
    if($ret->status != 200) {
		$j_array = array('code' => "500", "message" => "ERROR EN EL PROCESO DE TIMBRADO. ".$ret->status." - ".$ret->mensaje);
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
    }

    #=== 11.7 Muestra los resultados del timbrado del CFDI =========================

    $RespServ = $ret->resultados->cfdiTimbrado;

    if($RespServ == NULL){
	    $j_array = array('code' => "600", "message" => "ERROR EN EL PROCESO DE TIMBRADO. status: ".$ret->status.", mensaje:  ".$ret->mensaje.".  ==> Resultado Timbrado: "."  status: ".$ret->resultados->status.", mensaje: ".$ret->resultados->mensaje);
		$Resultado = json_encode($j_array);
		echo $Resultado;
        return;
/*		
		//echo $cadena_original;
		echo "status: ".$ret->resultados->status.", mensaje: ".$ret->resultados->mensaje;
		echo "<br>";
		echo "<br>";
		print_r($ret);
		echo "<br>";
		echo "<br>";
		die(PHP_EOL . "Error al timbrar el CFDI." . PHP_EOL . PHP_EOL);
*/
    }
	
	
    ## 12. PROCESOS POSTERIORES AL TIMBRADO ########################################
    
    #== 12.1 Se asigna la respuesta del servidor a una variable de tipo DOM ====
    $VarXML = new DOMDocument();
    $VarXML->loadXML($RespServ);

    #== 12.2 Se graba la respuesta del servidor a un archivo .xml
    $VarXML->save($SendaCFDI."RespServ_".$NoFac.".xml");
    chmod($SendaCFDI."RespServ_".$NoFac.".xml", 0777);

    //$NomArchXML = $invoiceNumber."_CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT).".xml";
    //$NomArchPDF = $invoiceNumber."_CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT).".pdf";

    $NomArchXML = "CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT)."_".$invoiceNumber.".xml";
    $NomArchPDF = "CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT)."_".$invoiceNumber.".pdf";
	
    $xmlt = new DOMDocument();
    $xmlt->loadXML($RespServ);
    $xmlt->save($SendaCFDI.$NomArchXML); 
    chmod($SendaCFDI.$NomArchXML, 0777);
    #== 12.7 Procesos para extraer datos del Timbre Fiscal del CFDI =========
    $docXML = new DOMDocument();
    //$docXML->load($SendaCFDI."Fact_CFDI_".$NoFac.".xml");
    $docXML->load($SendaCFDI.$NomArchXML);
    $comprobante = $docXML->getElementsByTagName("TimbreFiscalDigital");

    #== 12.8 Se obtienen contenidos de los atributos y se asignan a variables para ser mostrados =======
    foreach($comprobante as $timFis){
            $version_timbre = $timFis->getAttribute('version');
            $sello_SAT      = $timFis->getAttribute('selloSAT');
            $cert_SAT       = $timFis->getAttribute('noCertificadoSAT'); 
            $sello_CFD      = $timFis->getAttribute('selloCFD'); 
            $tim_fecha      = $timFis->getAttribute('FechaTimbrado'); 
            $tim_uuid       = $timFis->getAttribute('UUID'); 
    }
    
    #== 12.8.1 Se muestra el número de factura asignado por el sistema local (no asingado por el SAT).
    
    $params = $docXML->getElementsByTagName('Emisor');
    foreach ($params as $param) {
        $Emisor_RFC = $param->getAttribute('rfc');
    }  
    
    $params = $docXML->getElementsByTagName('Receptor');
    foreach ($params as $param) {
        $Receptor_RFC = $param->getAttribute('rfc');
    }              
    
    $params = $docXML->getElementsByTagName('Comprobante');
    foreach ($params as $param) {
        $total = $param->getAttribute('total');
    }        
    
    #== 12.9 Se crea el archivo .PNG con codigo bidimensional =================================
    $filename = "archs_graf/Img_".$tim_uuid.".png";
    $CadImpTot = ProcesImpTot($total);
    $Cadena = "?re=".$Emisor_RFC."&rr=".$Receptor_RFC."&tt=".$CadImpTot."&id=".$tim_uuid;
    QRcode::png($Cadena, $filename, 'H', 3, 2);    
    chmod($filename, 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/aptusCFDI/wsInvoicePDF.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.2 Envio de los Archivos a Dropbox
	require_once "dropbox-sdk/Dropbox/autoload.php";
	use \Dropbox as dbx;
	
	$accessToken = "LXhhFlG1vXAAAAAAAAAAKrL_P44fP6tky0PejLwU93gVxPEb8NbqQwxTwtyDbHku";
	$dbxClient = new dbx\Client($accessToken, "AptusCFDI/1.0");

	$f = fopen($SendaCFDI.$NomArchXML, "rb");
	$result = $dbxClient->uploadFile("/CFDI_2017/".$NomArchXML, dbx\WriteMode::add(), $f);
	fclose($f);

	$f = fopen($SendaCFDI.$NomArchPDF, "rb");
	$result = $dbxClient->uploadFile("/CFDI_2017/".$NomArchPDF, dbx\WriteMode::add(), $f);
	fclose($f);
*/	
	
    # 13.4 Envio del Archivos a Invoice de Zoho Books
	# Primero el PDF
    $file_name_with_full_path = '/var/www/html/aptusCFDI/archs_cfdi/'.$NomArchPDF;
    $request_url = 'https://www.zohoapis.com/books/v3/invoices/'.$invoiceID.'/attachment';

    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);
    }

    //'authtoken' => '1161ed5b29d4a0a3b83c12e4ee07b192',
	
    $post = array(
    'authtoken' => '7ff241d65d839996183b1656292623b6',
    'organization_id' => '52408581',
    'can_send_in_mail' => 'true',
    'attachment'=> $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); 

    $r = curl_exec($ch);
    curl_close ($ch);
	$ra = json_decode($r);
	$resultPDF = $ra->code.' - '.$ra->message;
    //print_r($r);

	# Segundo el XML
    $file_name_with_full_path = '/var/www/html/aptusCFDI/archs_cfdi/'.$NomArchXML;
    $request_url = 'https://www.zohoapis.com/books/v3/invoices/'.$invoiceID.'/attachment';

    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);
    }

    //'authtoken' => '1161ed5b29d4a0a3b83c12e4ee07b192',

    $post = array(
    'authtoken' => '7ff241d65d839996183b1656292623b6',
    'organization_id' => '52408581',
    'can_send_in_mail' => 'true',
    'attachment'=> $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); 

    $r = curl_exec($ch);
    curl_close ($ch);
	$ra = json_decode($r);
	$resultXML = $ra->code.' - '.$ra->message;

    # 13.3 Envio del Archivos a Invoice de Zoho Creator
	# Primero el PDF
    $file_name_with_full_path = '/var/www/html/aptusCFDI/archs_cfdi/'.$NomArchPDF;
    $request_url = 'https://creator.zoho.com/api/xml/fileupload/scope=creatorapi';

    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);
    }

    //'authtoken' => '86c6eecafda890a29c377a515639cdf4',
	
    $post = array(
    'authtoken' => '6a5a28ac4e847f8a8e14b43fd5f08ee3',
    'applinkname' => 'cfdi',
    'formname' => 'facturaCFDI',
	'fieldname' => 'File_CFDI_PDF',
    '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); 

    $r = curl_exec($ch);
    curl_close ($ch);
	
	# Segundo el XML
    $file_name_with_full_path = '/var/www/html/aptusCFDI/archs_cfdi/'.$NomArchXML;
    $request_url = 'https://creator.zoho.com/api/xml/fileupload/scope=creatorapi';

    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' => '6a5a28ac4e847f8a8e14b43fd5f08ee3',
    'applinkname' => 'cfdi',
    'formname' => 'facturaCFDI',
	'fieldname' => 'File_CFDI_XML',
    'recordId' => $bRecId,
    'filename' => $NomArchXML,
    '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); 

    $r = curl_exec($ch);
    curl_close ($ch);

	
    #== Se elimina los archivos de trabajo
    unlink($SendaCFDI."PreFact_".$NoFac.".xml");
    unlink($SendaCFDI."RespServ_".$NoFac.".xml"); 
    unlink($filename); 
	
//	$version_timbre = $timFis->getAttribute('version');
//	$sello_SAT      = $timFis->getAttribute('selloSAT');
//	$cert_SAT       = $timFis->getAttribute('noCertificadoSAT'); 
//	$sello_CFD      = $timFis->getAttribute('selloCFD'); 
//	$tim_fecha      = $timFis->getAttribute('FechaTimbrado'); 
//	$tim_uuid       = $timFis->getAttribute('UUID'); 
	
    $j_array = array('code' => "200", "message" => "Proceso de creacion de CFDI fue exitoso", 'version_timbre' => $version_timbre,
						'cert_SAT' => $cert_SAT, 'tim_fecha' => $tim_fecha, 'tim_uuid' => $tim_uuid, 
						'sello_SAT' => $sello_SAT, 'sello_CFD' => $sello_CFD);
    $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);
        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;
    }        
    
