Doc[$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;
}