Buenas tardes, felices fiestas para todos. Soy de reciente ingreso a este grupo, para solicitar de su valiosa ayuda.
Programé un código muy simple para poder solicitar COVE a Ventanilla Única de Comercio Exterior Mexicana (VUCEM) generando el XML necesario para el consumo del webservice de esta plataforma, utilizando el active X de Chilkat_9_5_0.Http
Actualmente ya hice pruebas satisfactorias utilizando unos certificados, cadena original y cadena firmada de una “factura de prueba” que ya había obtenido COVE previamente (de otra aplicación en Delphi) y sin problema obtengo nuevos COVE de VUCEM al correr mi programita.
La cuestión que me falta es precisamente poder “firmar” una cadena de texto (la famosa “cadena original”, que es un string (o un TXT) con muchos datos delimitados por el carácter | ) y es por eso que publico esta entrada en el foro, pues la intención es hacerlo desde VFP, utilizando las librerías que fuesen necesarias, obteniendo un astringe (o un TXT) ya “firmado”.
¿Tendrá ya alguien del grupo, alguna rutina en VFP, para firmar con CER, KEY y contraseña, la cadena original? De nueva cuenta, me estoy refiriendo al VUCEM, pero pienso que quizás sea lo mismo que aplica para los CFDI aquí en México.
Una disculpa también por unirme al grupo nada más por buscar ayuda para esto, pero les prometo que publicaré mi código completo una vez que ya pueda firmar la cadena original, claro, con su ayuda.
¡De antemano muchas gracias y mis mejores deseos para todos en este 2022!
--
Has recibido este mensaje porque estás suscrito al grupo "vfp-factura-electronica-mexico" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a vfp-factura-electroni...@googlegroups.com.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/vfp-factura-electronica-mexico/dc1a3010-dd90-4453-a68a-de59ca917af8n%40googlegroups.com.
Muchas gracias al usuario del grupo António Tavares Lopes, con un comentario que me hizo en otro foro, pude hacer la prueba con Chilkat para completar este programita de prueba, aquí les comparto el código básico sin validaciones ni nada por el estilo, está muy elemental, pero lo pueden adaptar ustedes a sus necesidades y estilo.
Tienen que
tener una licencia de chilkat activex comprada.
Pero vale la pena pues queda demasiado sencillo todo el proceso para
general el XML y consumir el webservice.
Felices fiestas!!!
LicGlo = CreateObject('Chilkat_9_5_0.Global')
Resultado = LicGlo.UnlockBundle("la llave de activación de Chilkat")
*************************************************************
* Sección para general el XML que se enviará a VUCEM
*************************************************************
m.archivo = &&Para grabar el XML
m.cadena_original = &&La cadena original a firmar, esta la pueden armar a mano que es bastante sencillo siguiendo el orden del XML, pues es texto separado por |
m.elcer = &&La ruta del archivo .CER
m.elkey = &&La ruta del archivo .KEY
m.elpwd = &&La contraseña de la FIEL
m.cadena_original_FIRMADA = ''
m.CERTIFICADO = ''
loPkey = CreateObject('Chilkat_9_5_0.PrivateKey') &&Creamos el objeto private key
Resultado = loPkey.LoadPkcs8EncryptedFile(m.elkey, m.elpwd) &&Se carga el .KEY y la contraseña de la FIEL
lcPkeyXml = loPkey.GetXml() &&Se convierte a XML para aplicar el algoritmo RonaldShamirAdleman (RSA)
loRsa = CreateObject('Chilkat_9_5_0.Rsa') &&Creamos el objeto RSA
Resultado = loRsa.ImportPrivateKey(lcPkeyXml) &&Se importa al RSA la llave privada en XML
loRsa.EncodingMode = "base64" &&Definimos la codificación de salida (de acuerdo a VUCEM)
loRsa.Charset = "utf-8" &&Y el charset
m.cadena_original_FIRMADA = loRsa.SignStringENC(cadena,"sha256") &&Obtenemos la cadea CADENA_FIRMADAda aplicando el HASH sha256 (de acuerdo a VUCEM)
loCert = CreateObject('Chilkat_9_5_0.Cert') &&Creamos el objeto certificado
Resultado = loCert.LoadFromFile(m.elcer) &&Cargamos el .CER
m.CERTIFICADO = loCert.ExportCertPem() &&Obtenemos el texto del certificado
* El texto del certificado viene con los siguientes datos que hay que eliminar
m.CERTIFICADO = strtran(m.CERTIFICADO, '-----BEGIN CERTIFICATE-----', '')
m.CERTIFICADO = strtran(m.CERTIFICADO, '-----END CERTIFICATE-----', '')
m.CERTIFICADO = strtran(m.CERTIFICADO, chr(13), '')
m.CERTIFICADO = strtran(m.CERTIFICADO, chr(10), '')
* Las siguientes variables las use para llenar la información que requiere el XML
* para COVE de VUCEM y así solo referenciarlas al armarlo, ya depende de cada
* programador como implementarlo
m.correo = &&Correo para recibir cove
m.observaciones = &&Por lo general van los datos del transporte
m.tidenimp = &&Indentificador registro emisor "1" para RFC "0" TAX-ID "3" sin TAX-ID
m.nombreimp = &&Nombre emisor
m.rfcimp = &&RFC o TAX-ID emisor
m.calleimp = &&Calle emisor
m.numeximp = &&Numero exterior emisor
m.colimp = &&Colonia emisor
m.munimp = &&Municio emisor
m.edoimp = &&Estado emisor
m.paisimp = &&Pais emisor
m.cpimp = &&CP emisor
m.tidendes = &&Indentificador registro destino "1" para RFC "0" TAX-ID "3" sin TAX-ID
m.nombredes = &&Nombre destino
m.rfcdes = &&RFC o TAX-ID del destino
m.calledes = &&Calle destino
m.numexdes = &&Numero exterior destino
m.coldes = &&Colonia destino
m.mundes = &&Municio destino
m.edodes = &&Estado destino
m.paisdes = &&Pais destino
m.cpdes = &&CP destino
m.fecfactura = &&Fecha de factura
m.fechafvu = substr(dtoc(m.fecfactura), 7, 4) + '-' + substr(dtoc(m.fecfactura), 4, 2) + '-' + substr(dtoc(m.fecfactura), 1, 2) &&Se convierte la fecha al formato de VUCEM
m.factura = &&Numero de factura
m.userwebs = &&Usuario webservices VUCEM
m.pwdwebs = &&Contraseña webservices VUCEM
m.topercove = &&Tipo de operación para COVE "TOCE.EXP" para exportación, "TOCE.IMP" para importación
m.aapatente = &&Patente Agente Aduanal
m.RFCConsulta = &&RFC adicional para consultar COVE en VUCEM
m.soltipofig = &&Tipo figura de quien envía la solicitud de COVE a VUCEM "1" Agente Aduanal "2" Apoderado Aduanal "3" Mandatario "4" Exportador "5" Importador
m.corigen = &&Se cuenta con certificado de origen "0" NO "1" SI
m.subdivision = &&Se subdivide el valor "0" NO "1" SI
loXml = CreateObject('Chilkat_9_5_0.Xml') &&Creamos el objeto XML
* Se definen todos los parametros para el consumo del webservice (probados al 100%)
loXml.Tag = "soapenv:Envelope"
loXml.AddAttribute("xmlns:soapenv","http://schemas.xmlsoap.org/soap/envelope/")
loXml.UpdateAttrAt("soapenv:Header|wsse:Security",1,"xmlns:wsse","http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd")
loXml.UpdateChildContent("soapenv:Header|wsse:Security|wsse:UsernameToken|wsse:Username", m.userwebs)
loXml.UpdateAttrAt("soapenv:Header|wsse:Security|wsse:UsernameToken|wsse:Password",1,"Type","http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText")
loXml.UpdateChildContent("soapenv:Header|wsse:Security|wsse:UsernameToken|wsse:Password", m.pwdwebs)
loXml.UpdateAttrAt("soapenv:Body|oxml:solicitarRecibirCoveServicio",1,"xmlns:oxml","http://www.ventanillaunica.gob.mx/cove/ws/oxml/")
* Se agregan los datos generales para la solicitud
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:tipoOperacion", m.topercove)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:patenteAduanal", m.aapatente)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:fechafvuedicion", m.fechafvu)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:observaciones", m.observaciones)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:tipoFigura", m.soltipofig)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:rfcConsulta", m.RFCConsulta)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:correoElectronico", m.correo)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:numeroFacturaOriginal", m.factura)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:factura|oxml:CERTIFICADOOrigen", m.corigen)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:factura|oxml:subdivision", m.subdivision)
* Se agregan los datos del emisor
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:tipoIdentificador", m.tidenimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:identificacion", m.rfcimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:nombre", m.nombreimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:calle", m.calleimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:numeroExterior", m.numeximp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:colonia", m.colimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:municipio", m.munimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:entidadFederativa", m.edoimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:pais", m.paisimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:codigoPostal", m.cpimp)
* Se agregan los datos del destino
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:tipoIdentificador", m.tidendes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:identificacion", m.rfcdes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:nombre", m.nombredes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:domicilio|oxml:calle", m.calledes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:domicilio|oxml:numeroExterior", m.numexdes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:domicilio|oxml:municipio", m.mundes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:domicilio|oxml:entidadFederativa", m.edodes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:domicilio|oxml:pais", m.paisdes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:domicilio|oxml:codigoPostal", m.cpdes)
* A continuación se obtiene la informacion de las partidas y se comienza con los tags primero sin numero
* y a partir de la segunda partida se va armando un TAG distinto en el XML
select partidas &&La información que obtengan de su base de datos
m.renglonp = 0
go top
if reccount() != 0
scan
m.claveUnidadMedida = alltrim(claveUnidadMedida)
m.tipoMoneda = alltrim(tipoMoneda)
m.cantidad = cantidad
m.valorUnitario = valorUnitario
m.valorTotal = valorTotal
m.valorDolares = valorDolares
m.descripcionGenerica = alltrim(descripcionGenerica)
m.marca = alltrim(marca)
m.modelo = alltrim(modelo)
m.submodelo = alltrim(submodelo)
m.numeroserie = alltrim(numeroserie)
if m.renglonp = 0
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:claveUnidadMedida", m.claveUnidadMedida)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:tipoMoneda", m.tipoMoneda)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:cantidad", m.cantidad)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:valorUnitario", m.valorUnitario)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:valorTotal", m.valorTotal)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:valorDolares", m.valorDolares)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:descripcionGenerica", m.descripcionGenerica)
if len(alltrim(m.marca)) != 0
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:marca", m.marca)
endif
if len(alltrim(m.modelo)) != 0
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:modelo", m.modelo)
endif
if len(alltrim(m.submodelo)) != 0
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:submodelo", m.submodelo)
endif
if len(alltrim(m.numeroserie)) != 0
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:numeroserie", m.numeroserie)
endif
else
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:claveUnidadMedida", m.claveUnidadMedida)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:tipoMoneda", m.tipoMoneda)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:cantidad", m.cantidad)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:valorUnitario", m.valorUnitario)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:valorTotal", m.valorTotal)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:valorDolares", m.valorDolares)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:descripcionGenerica", m.descripcionGenerica)
if len(alltrim(m.marca)) != 0
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:marca", m.marca)
endif
if len(alltrim(m.modelo)) != 0
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:modelo", m.modelo)
endif
if len(alltrim(m.submodelo)) != 0
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:submodelo", m.submodelo)
endif
if len(alltrim(m.numeroserie)) != 0
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:numeroserie", m.numeroserie)
endif
endif
m.renglonp = m.renglonp + 1
endscan
endif
* Los datos para "FIRMAR" la solicitud de servicio de COVE
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:firmaElectronica|oxml:cadenaOriginal", m.cadena_original)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:firmaElectronica|oxml:certificado", m.CERTIFICADO)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:firmaElectronica|oxml:firma", m.cadena_original_FIRMADA)
loXml.SaveXml(m.archivo) && Grabamos el XML
RELEASE loXml && Soltamos el objeto XML
*************************************************************
* Sección para consumir el webservice de VUCEM
*************************************************************
loHttp = CreateObject('Chilkat_9_5_0.Http') &&Creamos un objeto HTTP
loXml = CreateObject('Chilkat_9_5_0.Xml') &&Creamos un objeto XML
Resultado = loXml.LoadXmlFile(m.archivo) &&Cargamos el archivo XML que generamos en la sección anterior
lcStrXml = loXml.GetXml() &&Cargamos al XML a una variable de texto
* Definición de URL para consumir los webservices de VUCEM
loHttp.SetRequestHeader("SOAPAction","https://www.ventanillaunica.gob.mx/ventanilla/RecibirCoveService?wsdl")
loHttp.SetRequestHeader("Content-Type","text/xml; charset=utf-8")
lcEndPoint = "https://www.ventanillaunica.gob.mx/ventanilla/RecibirCoveService?wsdl"
loResp = loHttp.PostXml(lcEndPoint,lcStrXml,"utf-8") &&Se entrega el XML al webservice
m.laresp = loHttp.LastMethodSuccess &&Si el valor es diferente de 1 hubo error y lo podemos obtener con la función = loHttp.LastErrorText
m.StatusSolicitud = loResp.StatusCode &&Datos de respuesta
m.UltimoMensaje = loHttp.LastHeader &&Datos de respuesta
loXmlResp = CreateObject('Chilkat_9_5_0.Xml') &&Creamos otro ojeto XML
loXmlResp.LoadXml(loResp.BodyStr) &&Cargamos el XML de respuesta
m.TXTRESP = loXmlResp.GetXml() &&Convertimos a txte el XML de respuesta
* Soltamos todos los objetos
RELEASE loHttp
RELEASE loXml
RELEASE loXmlResp
RELEASE LicGlo