';
echo 'PROCESO DE TIMBRADO, RECEPCIÓN DE PAGOS. CFDI VERSIÓN 4.0';
echo '';
echo '';
### 1. CONFIGURACIÓN INICIAL ######################################################
# 1.1 Configuración de zona horaria
date_default_timezone_set('America/Mexico_City');
$Fec1 = date("d/m/Y");
$Fec2 = date("Y/m/d");
$Hora = date("H:i:s");
echo $Fec1." | ".$Hora."
";
# 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. ASIGNACIÓN DE VALORES A VARIABLES ###################################################
$SendaPEMS = "archs_pem/"; // 2.1 Directorio en donde se encuentran los archivos *.cer.pem y *.key.pem (para efectos de demostración se utilizan los que proporciona el SAT para pruebas).
$SendaCFDI = "archs_cfdi/"; // 2.2 Directorio en donde se almacenarán los archivos *.xml (CFDIs).
$SendaGRAFS = "archs_graf/"; // 2.3 Directorio en donde se almacenan los archivos .jpg (logo de la empresa) y .png (códigos bidimensionales).
// 2.5 Datos de acceso del usuario (proporcionados por www.finkok.com) modo de integración (para pruebas) o producción.
$username = "appwebpuntodeventa@gmail.com";
$password = "appPVweb@440";
### MUESTRA LOS DATOS DEL USUARIO QUE ESTÁ TIMBRANDO (OPCIONAL A MOSTRAR) ######
echo '
';
echo 'DATOS DEL USUARIO QUE ESTÁ TIMBRANDO';
echo '
';
### 3. DEFINICIÓN DE VARIABLES INICIALES ##########################################
$noCertificado = "30001000000400002434"; // 3.1 Número de certificado.
$file_cer = "EKU9003173C9.cer.pem"; // 3.2 Nombre del archivo .cer.pem
$file_key = "EKU9003173C9.key.pem"; // 3.3 Nombre del archivo .cer.key
###################################################################################
### 4. DATOS GENERALES DE LA FACTURA ##################################################
$fact_serie = "A"; // 4.1 Número de serie.
$fact_folio = mt_rand(1000, 9999); // 4.2 Número de folio (para efectos de demostración se asigna de manera aleatoria).
$NoFac = $fact_serie.$fact_folio; // 4.3 Serie de la factura concatenado con el número de folio.
$fact_tipcompr = "P"; // 4.4 Tipo de comprobante.
$fact_exportacion = "01"; // 4.5 Atributo requerido para expresar si el comprobante ampara una operación de exportación.
$subTotal = 0; // 4.7 Subtotal, suma de los importes antes de descuentos e impuestos (se calculan mas abajo).
$IVA = 0; // 4.9 IVA, suma de los impuestos (se calculan mas abajo).
$total = 0; // 4.10 Total, Subtotal - Descuentos + Impuestos (se calculan mas abajo).
$fecha_fact = date("Y-m-d")."T".date("H:i:s"); // 4.11 Fecha y hora de facturación.
$NumCtaPago = "6473"; // 4.12 Número de cuenta (sólo últimos 4 dígitos, opcional).
$LugarExpedicion = "45079"; // 4.17 Lugar de expedición (código postal).
$moneda = "XXX"; // 4.18 Moneda
$SumaPagos = 0;
### No. DE CFDI ASIGNADO (CONTROL INTERNO) ######
echo '
';
echo 'No. DE CFDI';
echo '
';
echo '
';
echo $NoFac;
echo '
';
### 5. MUESTRA LA ZONA HORARIA PREDETERMINADA DEL SERVIDOR (OPCIONAL A MOSTRAR) ######
echo '
';
echo 'FECHA Y HORA DE SOLICITUD DE TIMBRADO';
echo '
';
echo '
';
echo $fecha_fact; // 5.1 Se muestra solo para consultar y confirmar que sea la correcta.
echo '
';
### 6. ARRAYS QUE CONTIENEN LOS ARTICULOS QUE FORMAN PARTE DE LA VENTA #####################
$Array_ClaveProdServ = ['84111506']; // 6.1 Clave del SAT correspondiente al artículo o servicio (consultar el catálogo de productos del SAT).
$Array_NoIdentificacion = ['xxxx']; // 6.2 Clave asignada al artículo o servicio, sistema local.
$Array_Cantidad = ['1']; // 6.3 Cantidad.
$Array_ClaveUnidad = ['ACT']; // 6.4 Clave del SAT correspondiente a la unidad de medida (consultar el catálogo de productos del SAT).
$Array_Descripcion = ['Pago']; // 6.6 Descripción del artículo o servicio.
$Array_ValorUnitario = ['0']; // 6.7 Valor unitario del artículo o servicio.
$Array_Importe = ['0']; // 6.8 Importe del artículo o servicio.
$Array_ObjetoImp = ['01'];
### 7. ARRAYS QUE CONTIENEN LAS RECEPCIONES DE PAGOS ###########################
$ArrayDocRel_IdDocumento = ['8B797440-C422-4900-A9BA-DCC74A7BBC02', '320C2B8F-DC38-496D-ACBF-DE5838C457C6']; // 7.1
$ArrayDocRel_Serie = ['A', 'A']; // 7.2
$ArrayDocRel_Folio = ['001', '002']; // 7.3
$ArrayDocRel_MonedaDR = ['MXN', 'MXN']; // 7.4
$ArrayDocRel_EquivalenciaDR = ['1', '1'];
$ArrayDocRel_NumParcialidad = ['1', '4']; // 7.6
$ArrayDocRel_ImpSaldoAnt = ['10000.00', '8000']; // 7.7
$ArrayDocRel_ImpPagado = ['2000.00', '2000']; // 7.8
$ArrayDocRel_ImpSaldoInsoluto = ['8000.00', '6000']; // 7.9
$ArrayDocRel_ObjetoImp = ['01', '01']; // Atributo requerido para expresar si el pago del documento relacionado es objeto o no de impuesto.
// Determinando el importe total de pagos. ==============
for ($i=0; $icreateElement("cfdi:Comprobante");
$root = $xml->appendChild($root);
$cadena_original='||';
$noatt= array();
#== 11.2 Se crea e inserta el primer nodo donde se declaran los namespaces ======
cargaAtt($root, array(
"xmlns:pago20"=>"http://www.sat.gob.mx/Pagos20",
"xmlns:cfdi"=>"http://www.sat.gob.mx/cfd/4",
"xmlns:xs"=>"http://www.w3.org/2001/XMLSchema",
"xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
"xsi:schemaLocation"=>"http://www.sat.gob.mx/Pagos20 http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos20.xsd http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd"
)
);
$mifecha= date('Y-m-d H:i:s');
$NuevaFecha = strtotime ( '+0 hour' , strtotime ($mifecha) ) ;
$NuevaFecha = strtotime ( '-1 minute' , $NuevaFecha ) ;
$NuevaFecha = strtotime ( '+0 second' , $NuevaFecha ) ;
$NuevaHora = date ( 'H:i:s' , $NuevaFecha);
echo $mifecha . " | " . $NuevaFecha;
echo "
';
## Fin de la integración de la Addenda. ####################################
#== 11.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);
// openssl_sign($datos, $firma, $private_key_pem, OPENSSL_ALGO_SHA256); // *****
#== 11.9 Se convierte la cadena digital a Base 64 ==========================
$sello = base64_encode($crypttext); // Firma.
#=== Muestra el sello (opcional a mostrar) =================================
echo '
';
echo 'SELLO';
echo '
';
echo '
';
echo $sello;
echo '
';
#== 11.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';
echo 'CERTIFICADO DEL SELLO DIGITAL';
echo '';
echo '
';
echo $certificado;
echo '
';
#== 11.12 Se continua con la integración de nodos ===========================
$root->setAttribute("Sello",$sello);
$root->setAttribute("Certificado",$certificado); # Certificado.
#== Fin de la integración de nodos =========================================
$NomArchCFDI = $SendaCFDI."PreCFDI-Pagos_".$NoFac.".xml";
#=== 11.12 Se guarda el archivo .XML antes de ser timbrado =======================
$cfdi = $xml->saveXML();
$xml->formatOutput = true;
$xml->save($NomArchCFDI); // Guarda el archivo .XML (sin timbrar) en el directorio predeterminado.
unset($xml);
#=== 11.13 Se dan permisos de escritura al archivo .xml. =========================
chmod($NomArchCFDI, 0777);
##### FIN DE LA CREACIÓN DEL ARCHIVO .XML ANTES DE SER TIMBRADO ####################################################
### 12. PROCESO DE TIMBRADO ########################################################
#=== Se muestra el .XML antes de ser timbrado (opcional a mostrar)==========
echo '
';
echo 'FACTURA .XML A TIMBRAR';
echo '
';
echo '
';
echo htmlspecialchars($cfdi);
echo '
';
#== 12.1 Se crea una variable de tipo DOM y se le carga el CFDI =================================
$xml2 = new DOMDocument();
$xml2->loadXML($cfdi);
#== 12.2 Convirtiendo el contenido del CFDI a BASE 64 ======================
$xml_cfdi_base64 = base64_encode($cfdi);
#== Modo transición ========================================
// $process = curl_init('https://demo-transicion.finkok.com');
#== 12.3 Datos de acceso al servidor de pruebas ============================
$process = curl_init('https://demo-facturacion.finkok.com/servicios/soap/stamp.wsdl');
#== 12.4 Datos de acceso al servidor de producción =========================
# $process = curl_init('https://facturacion.finkok.com/servicios/soap/stamp.wsdl');
#== 12.5 Creando el SOAP de envío ==============================================
$cfdixml = <<$xml_cfdi_base64$username$password
XML;
#== 12.6 Proceso para guardar los datos que se envían al servidor en un archivo .XML ========================
$NomArchSoap = $SendaCFDI."SOAP_Envio_CFDI_".$NoFac.".xml";
#== 12.6.1 Si el archivo ya se encuentra se elimina ===========================
if (file_exists ($NomArchSoap)==true){
unlink($NomArchSoap);
}
#== 12.6.2 Se crea el archivo .XML con el SOAP ================================
// Descomentar si se desea grabar en archivo el SOAP de envío.
// $fp = fopen($NomArchSoap,"a");
// fwrite($fp, $cfdixml);
// fclose($fp);
// chmod($NomArchSoap, 0777);
#=== 12.7 Muestra el contenido del SOAP que se envía al servidor del PAC (REQUEST) =========================
echo '
';
echo 'CONTENIDO DEL SOAP QUE SE ENVIA AL SERVIDOR DEL PAC';
echo '
';
echo '
';
echo htmlspecialchars($cfdixml);
echo '
';
#== 12.8 Se envía el contenido del SOAP al servidor del PAC =====================
curl_setopt($process, CURLOPT_HTTPHEADER, array('Content-Type: text/xml',' charset=utf-8'));
curl_setopt($process, CURLOPT_POSTFIELDS, $cfdixml);
curl_setopt($process, CURLOPT_RETURNTRANSFER, true);
curl_setopt($process, CURLOPT_POST, true);
curl_setopt($process, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($process, CURLOPT_SSL_VERIFYHOST, false);
$RespServ = curl_exec($process);
#== 12.9 Se muestra la respuesta del servidor del PAC (opcional a mostrar) ================
echo '
';
echo 'RESPUESTA DEL SERVIDOR DEL PAC';
echo '
';
echo '
';
echo htmlspecialchars($RespServ);
echo '
';
curl_close($process);
## FIN DEL PROCESO DE TIMBRADO #################################################
## 13. PROCESOS POSTERIORES AL TIMBRADO ########################################
$Total = 0;
$tim_uuid ="";
$Emisor_RFC="";
$Receptor_RFC="";
#== 13.1 Se asigna la respuesta del servidor a una variable de tipo DOM ====
$VarXML = new DOMDocument();
$VarXML->loadXML($RespServ);
#== 13.2 Se graba la respuesta del servidor a un archivo .xml
// $VarXML->save($SendaCFDI."RespServ_Pagos_".$NoFac.".xml");
// chmod($SendaCFDI."RespServ_Pagos_".$NoFac.".xml", 0777);
echo "";
#== 13.3 Se asigna el contenido del tag "xml" a una variable ===============
$RespServ = $VarXML->getElementsByTagName('xml');
#== 13.4 Se obtiene el valor del nodo ======================================
foreach($RespServ as $Nodo){
$valor_del_nodo = $Nodo->nodeValue;
}
#== Si el nodo contiene datos se realizan los siguientes procesos ======
if($valor_del_nodo != ""){
// unlink($SendaCFDI."xlst_".$NoFac.".xml"); <-- Puede ser descomentado para eliminar el archivo .XML sin timbrar.
#== 13.5 Se muestra el .XML ya timbrado (CFDI V 3.2), opcional a mostrar =====
echo '
';
#=== 13.6 Guardando el CFDI en archivo .XML =======================
$NomArchXML = "CFDI40_Pagos_".$NoFac.".xml";
$NomArchPDF = "CFDI40_Pagos_".$NoFac.".pdf";
$xmlt = new DOMDocument();
$xmlt->loadXML($valor_del_nodo);
$xmlt->save($SendaCFDI.$NomArchXML);
chmod($SendaCFDI.$NomArchXML, 0777);
// 13.6.1 Convertir archivo .XML a UTF-8 (OPCIONAL).
// $file_name = $SendaCFDI.$NomArchXML;
// $f = file_get_contents($file_name);
// $f = iconv("WINDOWS-1252", "UTF-8", $f);
// file_put_contents($file_name, "\xEF\xBB\xBF".$f);
#== 13.7 Procesos para extraer datos del Timbre Fiscal del CFDI ====
$docXML = new DOMDocument();
$docXML->load($SendaCFDI."CFDI40_Pagos_".$NoFac.".xml");
$params = $docXML->getElementsByTagName("Comprobante");
foreach ($params as $param) {
$VersionCFDI = $param->getAttribute("Version");
}
$comprobante = $docXML->getElementsByTagName("TimbreFiscalDigital");
#== 13.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');
echo 'Versión de CFDI: '.$VersionCFDI.' ';
echo 'Versión de timbre: '.$version_timbre.' ';
echo 'Sello del SAT: '.$sello_SAT.' ';
echo 'Certificado del SAT: '.$cert_SAT.' ';
echo 'Sello del CFDI: '.$sello_CFD.' ';
echo 'Fecha de timbrado: '.$tim_fecha.' ';
echo 'Folio fiscal: '.$tim_uuid.' ';
}
#== Se muestra el número de factura asignado por el sistema local (no asingado por el SAT).
echo 'No. de factura asignado por el sistema local: '.$NoFac.'
';
echo '';
#== 13.10 Se crea código HTML para mostrar opciones al usuario.
?>
TODO supply a title
getElementsByTagName('CodigoError');
foreach($codigoError as $NodoStatus){
$valorNod = $NodoStatus->nodeValue;
}
echo '
';
echo 'CÓDIGO DE ERROR.';
echo '
';
echo '
';
echo $valorNod;
echo '
';
}
##### FIN DE PROCEDIMIENTOS ####################################################
### 14. FUNCIONES DEL MÓDULO ###################################################
# 14.1 Función que integra los nodos al archivo .XML y forma la "Cadena original".
function cargaAtt(&$nodo, $attr){
global $xml, $cadena_original;
$quitar = array('sello'=>1,'noCertificado'=>1,'certificado'=>1);
foreach ($attr as $key => $val){
$val = preg_replace('/\s\s+/', ' ', $val);
$val = trim($val);
if (strlen($val)>0){
$val = utf8_encode(str_replace("|","/",$val));
$nodo->setAttribute($key,$val);
if (!isset($quitar[$key]))
if (substr($key,0,3) != "xml" &&
substr($key,0,4) != "xsi:")
$cadena_original .= $val . "|";
}
}
}
# 14.2 Función que integra los nodos al archivo .XML sin integrar a la "Cadena original".
function cargaAttSinIntACad(&$nodo, $attr){
global $xml;
$quitar = array('sello'=>1,'noCertificado'=>1,'certificado'=>1);
foreach ($attr as $key => $val){
$val = preg_replace('/\s\s+/', ' ', $val);
$val = trim($val);
if (strlen($val)>0){
$val = str_replace("|","/",$val);
$nodo->setAttribute($key,$val);
if (!isset($quitar[$key]))
if (substr($key,0,3) != "xml" &&
substr($key,0,4) != "xsi:");
}
}
}
# 14.3 Funciónes que da formato al "Importe total" como lo requiere el SAT para ser integrado al código QR.
function ProcesImpTot($ImpTot){
$ImpTot = number_format($ImpTot, 4); // <== Se agregó el 30 de abril de 2017.
$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;
}