Buenas tardes, felices fiestas para todos.
Pronto compartiré 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.
Actualmente ya hice pruebas satisfactorias utilizando unos certificados, cadena original y cadena firmada que ya habían obtenido COVE previamente (de otra aplicación en Delphi y con documentos de prueba) y sin problema obtengo nuevos COVE al correr mi programita.
Sin embargo, la parte precisamente que me detiene para publicar el programa completo, es poder firmar una cadena de texto (la famosa cadena original, ojo, en este caso NO es un XML, es un simple texto con muchos datos delimitados por el carácter | ) y es por eso que publico esta entrada en el foro.
¿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.
¡De antemano muchas gracias por su ayuda!
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, eso ya lo pueden adaptar ustedes. 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