using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; using StampSOAP; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Text; using System.Web; using System.Xml; namespace MisIngredientesVue.Core.Models { public class EInvoicing { public static string ObtenerNombreFormaPago(string id) { switch (id) { case "01": return "Efectivo"; case "02": return "Cheque"; case "03": return "Transferencia electrónica"; case "04": return "Tarjeta de crédito"; case "05": return "Monedero electrónico"; case "06": return "Dinero electrónico"; case "07": return "Tarjeta digital"; case "08": return "Vales de despensa"; case "09": return "Bienes"; case "10": return "Servicio"; case "11": return "Por cuenta de tercero"; case "12": return "Dación en pago"; case "13": return "Pago por subrogación"; case "14": return "Pago por consignación"; case "15": return "Condonación"; case "16": return "Cancelación"; case "17": return "Compensación"; case "28": return "Tarjeta de débito"; case "29": return "Tarjeta de servicios"; case "99": return "Por definir"; } return id; } public static string ObtenerNombreUsoCFDI(string id) { switch (id) { case "G01": return "Adquisición de mercancías"; case "G02": return "Devoluciones, descuentos o bonificaciones"; case "G03": return "Gastos en general"; case "I01": return "Construcciones"; case "I02": return "Mobilario y equipo de oficina por inversiones"; case "I03": return "Equipo de transporte"; case "I04": return "Equipo de computo y accesorios"; case "I05": return "Dados, troqueles, moldes, matrices y herramental"; case "I06": return "Comunicaciones telefónicas"; case "I07": return "Comunicaciones satelitales"; case "I08": return "Otra maquinaria y equipo"; case "D01": return "Honorarios médicos, dentales y gastos hospitalarios"; case "D02": return "Gastos médicos por incapacidad o discapacidad"; case "D03": return "Gastos funerales"; case "D04": return "Donativos"; case "D05": return "Intereses reales efectivamente pagados por créditos hipotecarios (casa habitación)."; case "D06": return "Aportaciones voluntarias al SAR."; case "D07": return "Primas por seguros de gastos médicos."; case "D08": return "Gastos de transportación escolar obligatoria."; case "D09": return "Depósitos en cuentas para el ahorro, primas que tengan como base planes de pensiones."; case "D10": return "Pagos por servicios educativos(colegiaturas)"; case "P01": return "Por definir"; } return id; } public static string ObtenerNombreMetodoPago(string id) { switch (id) { //case "01": return "Efectivo"; //case "02": return "Cheque"; //case "03": return "Transferencia electrónica"; //case "04": return "Tarjeta de crédito"; //case "05": return "Monedero electrónico"; //case "06": return "Dinero electrónico"; //case "07": return "Tarjeta digital"; //case "08": return "Vales de despensa"; //case "09": return "Bienes"; //case "10": return "Servicio"; //case "11": return "Por cuenta de tercero"; //case "12": return "Dación en pago"; //case "13": return "Pago por subrogación"; //case "14": return "Pago por consignación"; //case "15": return "Condonación"; //case "16": return "Cancelación"; //case "17": return "Compensación"; //case "28": return "Tarjeta de débito"; //case "29": return "Tarjeta de servicio"; //case "98": return "No aplica"; //case "99": return "Otros"; case "PUE": return "Pago en una sola exhibición"; case "PIP": return "Pago inicial y parcialidades"; case "PPD": return "Pago en parcialidades o diferido"; } return id; } public static string ObtenerNombreClaveUnidad(string claveUnidad) { switch (claveUnidad) { case "H87": return "PIEZA"; case "X3H": return "BIDON"; case "KGM": return "KILOGRAMO"; case "ACT": return "ACTIVIDAD"; case "XBH": return "MANOJO"; case "X4H": return "DOMO (CAJA PLASTICO)"; case "XCX": return "LATA CILINDRICA "; case "E54": return "VIAJE"; case "DPC": return "DOCENA"; case "LTR": return "LITRO"; case "E48": return "UNIDAD DE SERVICIO"; case "XDP": return "GARRAFA"; }; return claveUnidad; } //public static string ObtenerClaveUnidad(Unit unit) //{ // switch (unit) // { // case Unit.KG: return "KGM"; // case Unit.L: return "LTR"; // case Unit.M: return "MTR"; // case Unit.Service: return "E48"; // case Unit.Units: return "XUN"; // } // return "H87"; //} public static string ObtenerNombreTipoComprobante(string id) { switch (id) { case "I": return "Ingreso"; case "E": return "Egreso"; case "T": return "Traslado"; case "N": return "Nómina"; case "P": return "Pago"; } return id; } public static string ObtenerNombreImpuesto(string id) { switch (id) { case "001": return "ISR"; case "002": return "IVA"; case "003": return "IEPS"; } return id; } public static string ObtenerNombreRegimen(string id) { switch (id) { case "601": return "General de Ley Personas Morales"; case "603": return "Personas Morales con Fines no Lucrativos"; case "605": return "Sueldos y Salarios e Ingresos Asimilados a Salarios"; case "606": return "Arrendamiento"; case "608": return "Demás ingresos"; case "609": return "Consolidación"; case "610": return "Residentes en el Extranjero sin Establecimiento Permanente en México"; case "611": return "Ingresos por Dividendos(socios y accionistas)"; case "612": return "Personas Físicas con Actividades Empresariales y Profesionales"; case "614": return "Ingresos por intereses"; case "616": return "Sin obligaciones fiscales"; case "620": return "Sociedades Cooperativas de Producción que optan por diferir sus ingresos"; case "621": return "Incorporación Fiscal"; case "622": return "Actividades Agrícolas, Ganaderas, Silvícolas y Pesqueras"; case "623": return "Opcional para Grupos de Sociedades"; case "624": return "Coordinados"; case "628": return "Hidrocarburos"; case "607": return "Régimen de Enajenación o Adquisición de Bienes"; case "629": return "De los Regímenes Fiscales Preferentes y de las Empresas Multinacionales"; case "630": return "Enajenación de acciones en bolsa de valores"; case "615": return "Régimen de los ingresos por obtención de premios"; } return id; } } public class FinkokService { /*Documentación: https://wiki.finkok.com/doku.php usuario: integracion contraseña. 1nT36R4c!0N Portal: https://facturacion.finkok.com/ https://demo-facturacion.finkok.com/ */ private static readonly string DEMO_USERNAME = "raulbojalil@gmail.com"; private static readonly string DEMO_PASSWORD = "mvpNUXmQfK8="; private static readonly string USERNAME = "javo@huerto.com.mx"; private static readonly string PASSWORD = "Misingredientes_13"; private static readonly string SERVICE_URL = "https://facturacion.finkok.com/servicios"; private static readonly string DEMO_SERVICE_URL = "https://demo-facturacion.finkok.com/servicios"; //private static readonly string DEMO_RFC_EMISOR = "LAN7008173R5"; //LAN7008173R7 //private static readonly string DEMO_RFC_RECEPTOR = "LAN7008173R5"; //LAN7008173R7 //(Tipo de usuario O = OnDemand , P = Prepago) public static void registration_add(string rfc, string cer, string key, string passphrase, string userType = "O", bool isDemo = false) { //if (isDemo) rfc = DEMO_RFC_EMISOR; var soapRequest = $@" {(isDemo ? DEMO_USERNAME : USERNAME)} {(isDemo ? DEMO_PASSWORD : PASSWORD)} {rfc} {userType} {cer} {key} {passphrase} "; var response = CallSoapAction("/soap/registration", "\"add\"", soapRequest, isDemo); if (response.Contains("false")) { var status = GetTextInTag(response, "s0:message"); if (string.IsNullOrEmpty(status)) status = response; throw new EInvoicingException(status); } } public static void registration_edit(string rfc, string cer, string key, string passphrase, bool isDemo = false) { //if (isDemo) rfc = DEMO_RFC_EMISOR; var soapRequest = $@" {(isDemo ? DEMO_USERNAME : USERNAME)} {(isDemo ? DEMO_PASSWORD : PASSWORD)} {rfc} {cer} {key} {passphrase} "; var response = CallSoapAction("/soap/registration", "\"edit\"", soapRequest, isDemo); if (response.Contains("false")) { var status = GetTextInTag(response, "s0:message"); if (string.IsNullOrEmpty(status)) status = response; throw new EInvoicingException(status); } } public static int report_total(string rfc, DateTime dateFrom, DateTime dateTo, bool isDemo = false) { //if (isDemo) rfc = DEMO_RFC_EMISOR; var soapRequest = $@" {(isDemo ? DEMO_USERNAME : USERNAME)} {(isDemo ? DEMO_PASSWORD : PASSWORD)} {rfc} {dateFrom.ToString("yyyy-MM-dd")}T00:00:00 {dateTo.ToString("yyyy-MM-dd")}T00:00:01 'I' "; var response = CallSoapAction("/soap/utilities", "\"report_total\"", soapRequest, isDemo); if (response.Contains("Error") || response.Contains("Invalid")) { var status = GetTextInTag(response, "s0:CodEstatus"); if (string.IsNullOrEmpty(status)) status = response; throw new EInvoicingException(status); } return Convert.ToInt32(GetTextInTag(response, "s0:total")); } public static sign_stampResponse sign_stamp(string xml, bool isDemo = false) { //if (isDemo) //{ // xml = xml.Replace("Rfc=\"LUDJ870913F34\"", "Rfc=\"" + DEMO_RFC_EMISOR + "\""); // xml = xml.Replace("RegimenFiscal=\"612\"", "RegimenFiscal=\"601\""); // xml = xml.Replace("Rfc=\"RFC01\"", "Rfc=\"AAA010101AAA\""); //} xml = FixNamespaces(xml); //Console.WriteLine("----------------\r\n\r\n" + xml); byte[] bytes = Encoding.UTF8.GetBytes(xml); var comprobanteXmlBase64 = Convert.ToBase64String(bytes); var soapRequest = $@" {comprobanteXmlBase64} {(isDemo ? DEMO_USERNAME : USERNAME)} {(isDemo ? DEMO_PASSWORD : PASSWORD)} "; //var soapRequest = $@" // {comprobanteXmlBase64} // raulbojalil@gmail.com // mvpNUXmQfK8= //"; var response = CallSoapAction("/soap/stamp", "\"sign_stamp\"", soapRequest, isDemo); //var xmlDocument = new XmlDocument(); //xmlDocument.LoadXml(response); //var mgr = new XmlNamespaceManager(xmlDocument.NameTable); //mgr.AddNamespace("s0", "apps.services.soap.core.views"); //mgr.AddNamespace("tns", "http://facturacion.finkok.com/stamp"); var sresponse = new sign_stampResponse(); sresponse.sign_stampResult = new AcuseRecepcionCFDI() { xml = HttpUtility.HtmlDecode(GetTextInTag(response, "s0:xml")), Incidencias = new Incidencia[1] { new Incidencia() { CodigoError = GetTextInTag(response, "s0:CodigoError"), MensajeIncidencia = GetTextInTag(response, "s0:MensajeIncidencia") } }, CodEstatus = GetTextInTag(response, "s0:CodEstatus"), UUID = GetTextInTag(response, "s0:UUID"), SatSeal = GetTextInTag(response, "s0:SatSeal"), NoCertificadoSAT = GetTextInTag(response, "s0:NoCertificadoSAT"), }; if (!string.IsNullOrEmpty(sresponse.sign_stampResult.xml)) sresponse.sign_stampResult.Incidencias = new Incidencia[0] { }; return sresponse; //response = xmlDocument.LastChild.FirstChild.InnerXml; //var serializer = new XmlSerializer(typeof(AcuseRecepcionCFDI)); //using (var strReader = new StringReader(response)) //{ // using (var reader = new NamespaceIgnorantXmlTextReader(strReader)) // { // return new sign_stampResponse { sign_stampResult = (AcuseRecepcionCFDI)serializer.Deserialize(reader) }; // } //} } private static string FixNamespaces(string xml) { xml = xml.Replace("", ""); xml = xml.Replace("", "") .Replace("", ""); if (!xml.Contains("http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd")) xml = xml.Replace("xmlns:cfdi=\"http://www.sat.gob.mx/cfd/3\"", "xmlns:cfdi=\"http://www.sat.gob.mx/cfd/3\" xmlns:pago10=\"http://www.sat.gob.mx/Pagos\" xsi:schemaLocation=\"http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd http://www.sat.gob.mx/Pagos http://www.sat.gob.mx/sitio_internet/cfd/Pagos/Pagos10.xsd\""); return xml; } public static string sign_cancel(string folioUUID, string rfc, string noCertificado, string motivo, bool isDemo = false) { //if (isDemo) rfc = DEMO_RFC_EMISOR; //TODO: hacer esto string folioSustitucion = ""; var soapRequest = $@" {(isDemo ? DEMO_USERNAME : USERNAME)} {(isDemo ? DEMO_PASSWORD : PASSWORD)} {rfc} {noCertificado} "; Console.WriteLine(soapRequest); var response = CallSoapAction("/soap/cancel", "\"sign_cancel\"", soapRequest, isDemo); Console.WriteLine(response); if (response.Contains("Error") || response.Contains("Invalid")) { var status = GetTextInTag(response, "s0:CodEstatus"); if (string.IsNullOrEmpty(status)) status = response; throw new EInvoicingException(status); } var acuse = GetTextInTag(response, "s0:Acuse"); if (string.IsNullOrEmpty(acuse)) { Console.WriteLine("Sin acuse " + response); throw new EInvoicingException("Sin acuse"); } acuse = WebUtility.HtmlDecode(acuse); return acuse.Contains("" + acuse); } private static string GetTextInTag(string xml, string tag) { try { if (xml.Contains($"<{tag}/>")) return string.Empty; var openingTag = "<" + tag + ">"; var closingTag = ""; var temp = xml.Substring(xml.IndexOf(openingTag) + openingTag.Length); return temp.Substring(0, temp.IndexOf(closingTag)); } catch { return string.Empty; } } private static string CallSoapAction(string url, string soapAction, string xmlRequest, bool isDemo, bool rethrowException = true) { url = (isDemo ? DEMO_SERVICE_URL : SERVICE_URL) + url; if (string.IsNullOrEmpty(xmlRequest.Trim())) return "Empty request body"; var request = (HttpWebRequest)WebRequest.Create(url); var xml = xmlRequest; if (!xmlRequest.Contains(":Envelope>") && !xmlRequest.Contains(":Body>")) { var soapRequest = "{0}"; xml = string.Format(soapRequest, xmlRequest.Replace("\n", "").Replace("\r", "")); } try { var xmlDocument = new XmlDocument(); xmlDocument.LoadXml(xml); } catch (Exception ex) { throw new Exception("XML Parsing error: " + ex.Message); } var data = Encoding.ASCII.GetBytes(xml); request.Headers.Add("SOAPAction", soapAction); request.Method = "POST"; request.ContentType = "text/xml;charset=UTF-8"; request.ContentLength = data.Length; using (var stream = request.GetRequestStream()) { stream.Write(data, 0, data.Length); } var res = string.Empty; try { using (var response = (HttpWebResponse)request.GetResponse()) { using (var responseStream = response.GetResponseStream()) { using (var streamReader = new StreamReader(responseStream)) { res = streamReader.ReadToEnd(); } } } } catch (WebException ex) { using (var responseStream = ex.Response.GetResponseStream()) { res = new StreamReader(responseStream).ReadToEnd(); } throw ex; } return res; } } public class EInvoicingException : Exception { public bool RanOutOfFolios { get; private set; } public EInvoicingException(string message, bool ranOutOfFolios = false) : base(message) { RanOutOfFolios = ranOutOfFolios; } } }