ilot/completions> finished with 200 status after 192.9799139997922ms 2025-07-23 17:03:41.466 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-23 17:03:56.314 [info] [fetchCompletions] Request f3a7bf6e-5e71-461e-960b-a64dc8c3b22d at finished with 200 status after 364.4961660001427ms 2025-07-23 17:03:56.623 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-23 17:03:56.940 [info] [fetchCompletions] Request e6d601b5-76fb-456f-8cf9-ad0b448c4e4d at finished with 200 status after 190.64131199987605ms 2025-07-23 17:03:57.323 [info] [fetchCompletions] Request 50c018ce-40e4-4cce-8edd-0b3b53afed7b at finished with 200 status after 204.18503500008956ms 2025-07-23 17:03:57.524 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-23 17:03:57.747 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-23 17:03:58.308 [info] [fetchCompletions] Request 8d30141e-2534-43a4-b338-91ff4a15336a at finished with 200 status after 342.441637000069ms 2025-07-23 17:03:58.311 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-23 17:03:58.353 [info] [fetchCompletions] Request 871cb203-65f4-4639-bd0b-77a3091a1542 at finished with 200 status after 255.91746299993247ms 2025-07-23 17:03:58.357 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-23 17:04:09.929 [info] [fetchCompletions] Request 77ab579f-28d6-4ce7-8406-146ebd92cf12 at finished with 200 status after 185.52279200032353ms 2025-07-23 17:04:10.193 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-23 17:04:10.527 [info] [fetchCompletions] Request d6cc3f1b-2c50-4e0e-8d87-631efd0eed63 at finished with 200 status after 190.76089599961415ms 2025-07-23 17:04:10.530 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-23 17:04:10.981 [info] [fetchCompletions] Request 3e8b8a20-d750-473c-8be7-1ddc0d25f386 at finished with 200 status after 186.93295700009912ms 2025-07-23 17:04:11.129 [info] [streamChoices] solution 1 returned. finish reason: [stop] 2025-07-23 17:04:11.165 [info] [streamChoices] solution 2 returned. finish reason: [stop] 2025-07-23 17:05:19.081 [info] [fetchCompletions] Request 2145673a-64d6-46b3-9443-da35919a3ed8 at finished with 200 status after 430.7155890003778ms 2025-07-23 17:05:19.376 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-23 17:05:20.069 [info] [fetchCompletions] Request ba50e49f-bc18-49b1-9058-d1aea09cc0f3 at finished with 200 status after 358.39596200035885ms 2025-07-23 17:05:20.161 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-23 17:05:20.184 [info] [fetchCompletions] Request abc37046-107e-4225-8600-7d067c111ebd at finished with 200 status after 174.87374000018463ms 2025-07-23 17:05:20.262 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-07-23 17:05:20.413 [info] [fetchCompletions] Request 2ca584b9-742d-46d4-bbd1-1d177830558d at finished with 200 status after 147.59932800009847ms 2025-07-23 17:05:20.463 [info] [streamChoices] solution 0 returne "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; } ### 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($bDatRel <> "") { $bdDatRel = base64_decode($bDatRel); } if ($tipoTest == 1){ #== Desplegado de los parametros en formato JSON echo $bdDatPAC; echo '
'; echo '
'; echo $bdDatGen; echo '
'; echo '
'; echo $bdDatEmi; echo '
'; echo '
'; echo $bdDatRec; echo '
'; echo '
'; echo $bdDatArt; echo '
'; echo '
'; echo $bdDatDoc; echo '
'; echo '
'; } #== 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); } #== 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"; #---------------------------------------------------------------- #== Llamado para generar el token para donatello #---------------------------------------------------------------- $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => 'https://donatello.aptuslegal.app/oauth/token/'.$appOwner, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_HTTPHEADER => array( 'Authorization: Token e68d5a079f937ea29ad2ec5a5b105b75491a0e0c' ), )); $response = curl_exec($curl); curl_close($curl); // Decodificamos la respuesta JSON $data = json_decode($response, true); // Accedemos al valor de 'access_token de donatello' $access_token = $data['access_token']; #== Conexión a Zoho Creator para el detalle del CFDI $request_url = 'https://creator.zoho.com/api/v2/'.$appOwner.'/'.$applnkname.'/report/cfdi_cp_query/'.$bRecId; $tipoCambioPDF = $abdDatGen["TipoCambioP"]; $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'); // if($tipoTest > 0){ # 1.2 Muestra la zona horaria predeterminada del servidor (opcional a mostrar) echo '
'; echo 'ZONA HORARIA PREDETERMINADA'; echo '
'; echo '
'; echo date_default_timezone_get(); echo '

'; } ### 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 '
'; echo 'DATOS DEL USUARIO QUE ESTÁ TIMBRANDO'; echo '
'; echo '
'; echo 'USUARIO: '.$username."
"; echo 'PASSWORD: '.$password."
"; echo 'URL: '.$urlPAC."
"; echo '

'; } ### 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"]; //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"]; if ($bFolioN <> ""){ $fact_folio = $bFolioN; } $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"]; $paymentID = $abdDatGen["paymentID"]; $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"],6,'.',''); $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"]; if ($tipoTest > 0){ ### 6. MUESTRA LA ZONA HORARIA PREDETERMINADA DEL SERVIDOR (OPCIONAL A MOSTRAR) ###### echo '
'; echo 'FECHA Y HORA DE SOLICITUD DE TIMBRADO'; echo '
'; echo '
'; echo $fecha_fact; // 6.1 Se muestra solo para consultar y confirmar que sea la correcta. echo '

'; } ### 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 = $TotalRetencionesISR = $TotalRetencionesIEPS = 0.00; $TotalTrasladosBaseIVA16 = $TotalTrasladosImpuestoIVA16 = 0.00; $TotalTrasladosBaseIVA8 = $TotalTrasladosImpuestoIVA8 = 0.00; $TotalTrasladosBaseIVA0 = $TotalTrasladosImpuestoIVA0 = 0.00; $TotalTrasladosBaseIVAExento = $MontoTotalPagos = 0.00; foreach ($abdDatDoc as $doc) { $MonedaDR = $doc["MonedaDR"]; $TipoCambioDR = $doc["TipoCambioDR"]; // Cálculo del importe de pago con precisión de 6 decimales if ($MonedaP == "MXN") { $importePago = round($MonedaDR != $MonedaP ? $doc["ImpPagado"] * $TipoCambioDR : $doc["ImpPagado"], 6); //echo $importePago; //exit; } else { $importePago = round($MonedaDR != $MonedaP ? $doc["ImpPagado"] * $TipoCambioP : $doc["ImpPagado"], 6); } $MontoTotalPagos = $MontoTotalPagos + $importePago; // Verificación de impuestos if ($doc["ObjetoImpDR"] == "02") { // Retenciones if ($doc["RDR_ImpuestoDR"] == "001") $TotalRetencionesISR += $doc["RDR_ImporteDR"]; if ($doc["RDR_ImpuestoDR"] == "002") $TotalRetencionesIVA += $doc["RDR_ImporteDR"]; // Traslados if ($doc["TDR_ImpuestoDR"] == "002") { switch ($doc["TDR_TasaOCuotaDR"]) { case 0.16: $TotalTrasladosBaseIVA16 += $doc["TDR_BaseDR"]; $TotalTrasladosImpuestoIVA16 += $doc["TDR_ImporteDR"]; break; case 0.08: $TotalTrasladosBaseIVA8 += $doc["TDR_BaseDR"]; $TotalTrasladosImpuestoIVA8 += $doc["TDR_ImporteDR"]; break; case 0.00: $TotalTrasladosBaseIVA0 += $doc["TDR_BaseDR"]; $TotalTrasladosImpuestoIVA0 += $doc["TDR_ImporteDR"]; break; } } } } $MontoTotalPagos = $abdDatGen["Monto"]; // Ajustes según la moneda de pago y el tipo de cambio if ($MonedaP != "MXN" && $TipoCambioP > 1.000000) { if ($MonedaDR != "MXN") { $factor = $TipoCambioP; } else { $factor = 1 / $TipoCambioP; } //$tipoCambioPDF = $TipoCambioP; // Aplicamos redondeo con factor de conversión $TotalRetencionesISR = round($TotalRetencionesISR * $factor, 2); $TotalRetencionesIVA = round($TotalRetencionesIVA * $factor, 2); $TotalTrasladosBaseIVA16 = round($TotalTrasladosBaseIVA16 * $factor, 2); $TotalTrasladosImpuestoIVA16 = round($TotalTrasladosImpuestoIVA16 * $factor, 2); $TotalTrasladosBaseIVA8 = round($TotalTrasladosBaseIVA8 * $factor, 2); $TotalTrasladosImpuestoIVA8 = round($TotalTrasladosImpuestoIVA8 * $factor, 2); $TotalTrasladosBaseIVA0 = round($TotalTrasladosBaseIVA0 * $factor, 2); $TotalTrasladosImpuestoIVA0 = round($TotalTrasladosImpuestoIVA0 * $factor, 2); $MontoTotalPagos = round($MontoTotalPagos * $factor, 6); } // Ajuste final cuando MonedaP es MXN y MonedaDR no lo es if ($MonedaP == "MXN" && $MonedaDR != "MXN") { $factor = 1 / $TipoCambioDR; $TotalRetencionesISR = round($TotalRetencionesISR * $factor, 2); $TotalRetencionesIVA = round($TotalRetencionesIVA * $factor, 2); $TotalTrasladosBaseIVA16 = round($TotalTrasladosBaseIVA16 * $factor, 2); $TotalTrasladosImpuestoIVA16 = round($TotalTrasladosImpuestoIVA16 * $factor, 2); $TotalTrasladosBaseIVA8 = round($TotalTrasladosBaseIVA8 * $factor, 2); $TotalTrasladosImpuestoIVA8 = round($TotalTrasladosImpuestoIVA8 * $factor, 2); $TotalTrasladosBaseIVA0 = round($TotalTrasladosBaseIVA0 * $factor, 2); $TotalTrasladosImpuestoIVA0 = round($TotalTrasladosImpuestoIVA0 * $factor, 2); } //Corrección específica de decimales en los montos de pago para los complementos pagados en MXN y documentos originales de USD if($MonedaP == "MXN" && $MonedaDR != "MXN"){ // Corrección del calculo de monto de pago $MontoTotalPagos = $Monto; } ### 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 ========================================= if ($appOwner == "arochiylindner.aptus") { cargaAtt($root, array( "Version"=>"4.0", "Serie"=>$fact_serie, "Folio"=>$fact_folio, "Fecha"=>$fecha_fact, "NoCertificado"=>$noCertificado, "SubTotal"=>$subTotal, "Moneda"=>$moneda, "Total"=>$total, "TipoDeComprobante"=>$fact_tipcompr, "Exportacion"=>$fact_exportacion, "LugarExpedicion"=>$LugarExpedicion, "CondicionesDePago"=>$condicionesDePago, ) ); } else { 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, ) ); } // Adicionado por FFR en Abril 08, 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"]))); } } $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 ) ); $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; $icreateElement("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); $lSoloTotal = true; if($TotalRetencionesIVA > 0.00) { $lSoloTotal = false; cargaAtt($total, array( "TotalRetencionesIVA"=>number_format($TotalRetencionesIVA,2,'.',''), "MontoTotalPagos"=>number_format($MontoTotalPagos,2,'.','') ) ); } if($TotalRetencionesISR > 0.00 && $TotalTrasladosBaseIVA16 == 0.00) { $lSoloTotal = false; cargaAtt($total, array( "TotalRetencionesISR"=>number_format($TotalRetencionesISR,2,'.',''), "MontoTotalPagos"=>number_format($MontoTotalPagos,2,'.','') ) ); } if($TotalRetencionesIEPS > 0.00) { $lSoloTotal = false; cargaAtt($total, array( "TotalRetencionesIEPS"=>number_format($TotalRetencionesIEPS,2,'.',''), "MontoTotalPagos"=>number_format($MontoTotalPagos,2,'.','') ) ); } if($TotalTrasladosBaseIVA16 > 0.00) { $lSoloTotal = false; if($TotalRetencionesISR > 0.00) { cargaAtt($total, array( "TotalRetencionesISR"=>number_format($TotalRetencionesISR,2,'.',''), "TotalTrasladosBaseIVA16"=>number_format($TotalTrasladosBaseIVA16,2,'.',''), "TotalTrasladosImpuestoIVA16"=>number_format($TotalTrasladosImpuestoIVA16,2,'.',''), "MontoTotalPagos"=>number_format($MontoTotalPagos,2,'.','') ) ); } else { cargaAtt($total, array( "TotalTrasladosBaseIVA16"=>number_format($TotalTrasladosBaseIVA16,2,'.',''), "TotalTrasladosImpuestoIVA16"=>number_format($TotalTrasladosImpuestoIVA16,2,'.',''), "MontoTotalPagos"=>number_format($MontoTotalPagos,2,'.','') ) ); } } if($TotalTrasladosBaseIVA8 > 0.00) { $lSoloTotal = false; cargaAtt($total, array( "TotalTrasladosBaseIVA8"=>number_format($TotalTrasladosBaseIVA8,2,'.',''), "TotalTrasladosImpuestoIVA8"=>number_format($TotalTrasladosImpuestoIVA8,2,'.',''), "MontoTotalPagos"=>number_format($MontoTotalPagos,2,'.','') ) ); } if($TotalTrasladosBaseIVA0 > 0.00) { $lSoloTotal = false; cargaAtt($total, array( "TotalTrasladosBaseIVA0"=>number_format($TotalTrasladosBaseIVA0,2,'.',''), "TotalTrasladosImpuestoIVA0"=>number_format($TotalTrasladosImpuestoIVA0,2,'.',''), "MontoTotalPagos"=>number_format($MontoTotalPagos,2,'.','') ) ); } if($TotalTrasladosBaseIVAExento > 0.00) { $lSoloTotal = false; cargaAtt($total, array( "TotalTrasladosBaseIVAExento"=>number_format($TotalTrasladosBaseIVAExento,2,'.',''), "MontoTotalPagos"=>number_format($MontoTotalPagos,2,'.','') ) ); } if($lSoloTotal == true) { $lSoloTotal = false; cargaAtt($total, array("MontoTotalPagos"=>number_format($MontoTotalPagos,2,'.',''))); } $pago = $xml->createElement("pago20:Pago"); $pago = $Pagos->appendChild($pago); if ($MonedaP == "MXN") { cargaAtt($pago, array( "FechaPago"=>$FechaPago, "FormaDePagoP"=>$FormaDePagoP, "MonedaP"=>$MonedaP, "TipoCambioP"=>1, "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" ) ); } else { 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 = $lImpuestosDR = false; for ($i=0; $icreateElement("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"]), //este valor lo cambié porque si los valores de la moneda son iguales la equivalencia debe de ser 1 pero se tiene que verificar // "EquivalenciaDR"=>$abdDatDoc[$i]["TipoCambioDR"], "EquivalenciaDR"=>1, "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" && number_format($abdDatDoc[$i]["RDR_ImporteDR"],2,'.','') > 0) { $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"] == "02" ) { $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") { if ($MonedaP == "MXN" && $MonedaDR != "MXN") { $RDR_ImporteDR = round($RDR_ImporteDR * (1/$TipoCambioDR), 2, PHP_ROUND_HALF_UP); } $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") { if ($MonedaP == "MXN" && $MonedaDR != "MXN") { $TDR_BaseDR = round($TDR_BaseDR * (1/$TipoCambioDR), 2, PHP_ROUND_HALF_UP); $TDR_ImporteDR = round($TDR_ImporteDR * (1/$TipoCambioDR), 2, PHP_ROUND_HALF_UP); } if ($MonedaP != "MXN" && $MonedaDR == "MXN") { $TDR_BaseDR = round($TDR_BaseDR / $TipoCambioDR, 6, PHP_ROUND_HALF_UP); $TDR_ImporteDR = round($TDR_ImporteDR / $TipoCambioDR, 6, PHP_ROUND_HALF_UP); } $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,6,'.',''), "ImpuestoP"=>$TDR_ImpuestoDR, "TipoFactorP"=>$TDR_TipoFactorDR, "TasaOCuotaP"=>number_format($TDR_TasaOCuotaDR,6,'.',''), "ImporteP"=>number_format($TDR_ImporteDR,6,'.','') ) ); } } #== 10.7 Termina de conformarse la "Cadena original" con doble || $cadena_original .= "|"; if ($tipoTest > 0){ #=== Muestra la cadena original (opcional a mostrar) ======================= echo '
'; echo 'CADENA ORIGINAL'; echo '
'; echo '
'; echo $cadena_original; echo '

'; } #=== 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); if ($tipoTest > 0){ #=== Muestra el sello (opcional a mostrar) ================================= echo '
'; echo 'SELLO'; echo '
'; echo '
'; echo $sello; echo '

'; } #== 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 0){ #=== Muestra el certificado del sello digital (opcional a mostrar) ========= echo '
'; echo 'CERTIFICADO DEL SELLO DIGITAL'; echo '
'; echo '
'; echo $certificado; echo '

'; } #== 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 = "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); ### 11. PROCESO DE TIMBRADO ######################################################## if ($tipoTest > 0){ #=== Se muestra el .XML antes de ser timbrado (opcional a mostrar)========== echo '
'; echo 'FACTURA .XML A TIMBRAR'; echo '
'; echo '
'; echo htmlspecialchars($cfdi); echo '

'; } #== 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."PreCFDI-33_".$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); //print_r($sData); $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($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; } if ($tipoTest > 0){ #== 12.5 Se muestra el .XML ya timbrado (CFDI V 3.2), opcional a mostrar ===== echo '
'; echo 'FACTURA .XML (CFDI) YA TIMBRADA'; echo '
'; echo '
'; echo htmlspecialchars($RespServ); echo '

'; } ## 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 = "CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT)."_CP.xml"; $NomArchPDF = "CFDI_".$fact_serie.str_pad($fact_folio, 6, "0", STR_PAD_LEFT)."_CP.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'); $tim_RfcPac = $timFis->getAttribute('RfcProvCertif'); } #== 12.9 Se crea el archivo .PNG con codigo bidimensional ================================= $filename = "archs_graf/Img_".$tim_uuid.".png"; ### 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 .'&tipoCambioPDF='.$tipoCambioPDF; } else { $url = 'https://aptuslegal.app/aptusCFDIRF/'.$FormatoPDF.'.php?NomArchXML='.$NomArchXML.'&NomArchPDF='.$NomArchPDF .'&tipoCambioPDF='.$tipoCambioPDF; } $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); #---------------------------------------------------------------- #== Llamado para generar el token para donatello #---------------------------------------------------------------- $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => 'https://donatello.aptuslegal.app/oauth/token/'.$appOwner, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_HTTPHEADER => array( 'Authorization: Token e68d5a079f937ea29ad2ec5a5b105b75491a0e0c' ), )); $response = curl_exec($curl); curl_close($curl); // Decodificamos la respuesta JSON $data = json_decode($response, true); // Accedemos al valor de 'access_token de donatello' $access_token = $data['access_token']; # 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; //$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_CFDI_PDF/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( '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); # Segundo el XML $file_name_with_full_path = '/var/www/html/aptusCFDIRF/archs_cfdi/'.$NomArchXML; # 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_CFDI_XML/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( '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); if ($paymentID != "") { # 13.4 Envio del Archivos a Invoice de Zoho Books # Primero actualizamos el access_token de Books #== Datos y Variables para OAuth Token $boa_ClientId = $abdDatGen["B_OAuth_client_id"]; $boa_ClientSecret = $abdDatGen["B_OAuth_client_secret"]; $boa_RefreshToken = $abdDatGen["B_OAuth_refresh_token"]; $boa_GrantType = $abdDatGen["B_OAuth_grant_type"]; $boa_RedirectUri = $abdDatGen["B_OAuth_redirect_uri"]; $boa_AuthUrl = "https://accounts.zoho.com/oauth/v2/token"; #---------------------------------------------------------------- # JFA: 2021-06-12 # Obtenemos el access_token #---------------------------------------------------------------- $boa_access_token = oauth($appOwner, 'ZBooks', $boa_RefreshToken, $boa_ClientId, $boa_ClientSecret, $boa_RedirectUri, $boa_GrantType, $boa_AuthUrl); //echo $access_token; # Primero el PDF $file_name_with_full_path = '/var/www/html/aptusCFDIRF/archs_cfdi/'.$NomArchPDF; $request_url = 'https://www.zohoapis.com/books/v3/customerpayments/'.$paymentID.'/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); } $post = array( // 'authtoken' => $authtoken_ZB, 'organization_id' => $organi_id_ZB, '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); #JFA: Cambiamos el método de Autenticación a OAuth curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Zoho-oauthtoken ' . $boa_access_token)); $r = curl_exec($ch); curl_close ($ch); $ra = json_decode($r); $resultPDF = $ra->code.' - '.$ra->message; # Segundo el XML $file_name_with_full_path = '/var/www/html/aptusCFDIRF/archs_cfdi/'.$NomArchXML; $request_url = 'https://www.zohoapis.com/books/v3/customerpayments/'.$paymentID.'/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); } $post = array( // 'authtoken' => $authtoken_ZB, 'organization_id' => $organi_id_ZB, 'can_send_in_mail' => 'true', 'attachment'=> $cFile); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $request_url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Zoho-oauthtoken ' . $boa_access_token)); $r = curl_exec($ch); curl_close ($ch); $ra = json_decode($r); $resultXML = $ra->code.' - '.$ra->message; } #== Se elimina los archivos de trabajo unlink($SendaCFDI."PreCFDI-33_".$NoFac.".xml"); unlink($SendaCFDI."RespServ_".$NoFac.".xml"); unlink($filename); $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, 'tim_RfcPac' => $tim_RfcPac, '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, 'xmlns:pago10'=>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; }