11:47:12.398 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-06-20 11:49:05.222 [info] [fetchCompletions] Request 3a7fff38-e72d-4d0e-9ddd-54a777917168 at finished with 200 status after 258.36577900126576ms 2025-06-20 11:49:05.224 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-06-20 11:49:05.530 [info] [fetchCompletions] Request 4f9e9b34-f35d-46bb-8cbb-e8cdb1b83722 at finished with 200 status after 282.95667799934745ms 2025-06-20 11:49:05.531 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-06-20 11:49:05.848 [info] [fetchCompletions] Request 7cfd5d6e-5e96-4192-be2c-9927753681e6 at finished with 200 status after 263.27958200126886ms 2025-06-20 11:49:05.851 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-06-20 11:49:06.112 [info] [fetchCompletions] Request 8ca4d0ae-2cde-427a-ba37-f7445049e8f0 at finished with 200 status after 239.83375699818134ms 2025-06-20 11:49:06.114 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-06-20 11:51:21.330 [info] [fetchCompletions] Request f00de02b-15e9-4e14-96f5-96ded78de8f2 at finished with 200 status after 502.73624899983406ms 2025-06-20 11:51:21.332 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-06-20 11:52:35.719 [info] [fetchCompletions] Request 7748c2bf-849f-47d4-b601-c85a09619ce9 at finished with 200 status after 560.0330380015075ms 2025-06-20 11:52:36.050 [info] [fetchCompletions] Request 277f937a-9d85-4510-ac84-5115db20702f at finished with 200 status after 325.737254999578ms 2025-06-20 11:52:36.052 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-06-20 11:52:37.474 [info] [fetchCompletions] Request 872ec1ba-d632-46f8-95cc-3c8a9dde3010 at finished with 200 status after 596.9206549972296ms 2025-06-20 11:52:37.476 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-06-20 11:52:38.053 [info] [fetchCompletions] Request 5b0cdaba-3c55-457d-ade9-f6205dbb44a4 at finished with 200 status after 555.4373739995062ms 2025-06-20 11:52:38.056 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-06-20 11:52:38.670 [info] [fetchCompletions] Request c6a5abd5-bcde-4b47-8872-427e3e6d28a2 at finished with 200 status after 296.6515999995172ms 2025-06-20 11:52:38.672 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-06-20 11:52:39.027 [info] [fetchCompletions] Request cee1a26e-a0c4-434d-b0d4-18b2f47d0e6b at finished with 200 status after 332.29028299823403ms 2025-06-20 11:52:39.029 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-06-20 11:52:39.737 [info] [fetchCompletions] Request 189c82a9-3f02-41c8-9687-37ccae00f141 at finished with 200 status after 261.6780340000987ms 2025-06-20 11:52:39.738 [info] [streamChoices] solution 0 returned. finish reason: [stop] 2025-06-20 11:52:40.301 [info] [fetchCompletions] Request 66ac6f6e-90f3-410a-bb8c-8608d6ad09c0 at finished with 200 status after 543.5836790017784ms 2025-0 "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($donatello == "") { $j_array = array('code' => "300", "message" => "ERROR - [wsInvoiceSF] Parametros incompletos para el Servicio Web, faltan los Datos Donatello"); $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); } #---------------------------------------------------------------- #== Llamado para generar el token para donatello #---------------------------------------------------------------- $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => 'https://donatello.aptuslegal.app/oauth/token/'.$donatello, 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; $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"]; $tipoCambioPDF = $abdDatGen["TipoCambioP"]; $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)); } //Validacion de la existencia de los archivos .XML y .PDF para evitar duplicar timbrado $serie = $fact_serie; $folio = str_pad($fact_folio, 6, "0", STR_PAD_LEFT); $NomArchXML = "CFDI_{$serie}{$folio}_CP.xml"; $NomArchPDF = "CFDI_{$serie}{$folio}_CP.pdf"; // Ruta a la carpeta de archivos $basePath = __DIR__ . '/archs_cfdi/'; // Validación de existencia if (file_exists($basePath . $NomArchXML) && file_exists($basePath . $NomArchPDF)) { // Ya existe el XML: no timbres de nuevo $j_array = array('code' => "600", "message" => "ERROR EL COMPLEMENTO DE PAGO YA ESTÁ TIMBRADO"); $Resultado = json_encode($j_array); echo $Resultado; return; } // 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 != $MonedaDR){ if ($MonedaP == "MXN") { $importePago = round($doc["ImpPagado"] * $TipoCambioDR, 6); } else { $importePago = round($doc["ImpPagado"] * (1/ $TipoCambioDR), 6); } }else{ $importePago = round($doc["ImpPagado"], 6); } $MontoTotalPagos += round($importePago, 6); // 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; } } } } // Si la moneda del pago es distinta a MXN if ($MonedaP != "MXN" && $TipoCambioP > 1.0) { if ($MonedaDR == "MXN") { // Convertir MXN → USD (dividir) $factor = 1 / $TipoCambioP; } else { // Monedas foráneas distintas → usar el mismo TipoCambioP $factor = $TipoCambioP; } // Aplicar 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); } // Caso principal: pago en MXN, documento relacionado en USD u otra moneda if ($MonedaP == "MXN" && $MonedaDR != "MXN") { // Convertir de USD a MXN → multiplicar por el TipoCambioP $factor = $TipoCambioP; $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); // Nota: el MontoTotalPagos ya lo calculaste antes, no necesitas volver a convertirlo aquí } //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); cargaAtt($pago, array( "FechaPago"=>$FechaPago, "FormaDePagoP"=>$FormaDePagoP, "MonedaP"=>$MonedaP, "TipoCambioP"=>$TipoCambioP, "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); $EquivalenciaDR = 1.000000; if($MonedaP != $MonedaDR){ if ($MonedaP == "MXN" && $MonedaDR != "MXN") { $EquivalenciaDR = number_format((1/$abdDatDoc[$i]["TipoCambioDR"]),6,'.',''); } // Si pagas en USD y el documento es en MXN, convierte MXN a USD if ($MonedaP != "MXN" && $MonedaDR == "MXN") { $EquivalenciaDR = number_format($abdDatDoc[$i]["TipoCambioDR"],6,'.',''); } }else{ $EquivalenciaDR = number_format(1,6,'.',''); } $EquivalenciaDR = 1.000000; 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"=>$EquivalenciaDR, "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") { 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") { // Si pagas en MXN y el documento es en USD, convierte USD a MXN if($MonedaP != $MonedaDR){ if ($MonedaP == "MXN" && $MonedaDR != "MXN") { $baseP = number_format($TDR_BaseDR * $TipoCambioP, 2, '.', ''); $importeP = number_format($TDR_ImporteDR * $TipoCambioP, 2, '.', ''); } // Si pagas en USD y el documento es en MXN, convierte MXN a USD if ($MonedaP != "MXN" && $MonedaDR == "MXN") { $baseP = number_format($TDR_BaseDR * (1/$TipoCambioP), 2, '.', ''); $importeP = number_format($TDR_ImporteDR * (1/$TipoCambioP), 2, '.', ''); } }else{ $baseP = number_format($TDR_BaseDR, 2, '.', ''); $importeP = number_format($TDR_ImporteDR, 2, '.', ''); } // Ahora genera el nodo TrasladoP con valores en moneda del pago $trasladosP = $impuestosP->appendChild($xml->createElement("pago20:TrasladosP")); $trasladoP = $trasladosP->appendChild($xml->createElement("pago20:TrasladoP")); cargaAtt($trasladoP, array( "BaseP" => $baseP, "ImpuestoP" => $TDR_ImpuestoDR, "TipoFactorP" => $TDR_TipoFactorDR, "TasaOCuotaP" => number_format($TDR_TasaOCuotaDR, 6, '.', ''), "ImporteP" => $importeP )); } } #== 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); # 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; }