La mayor parte del esfuerzo de desarrollo consiste en conectarse a este webservice y obtener los tickets de autorización. Al mismo tiempo. es la parte más interesante.
- Buscamos en la base de datos un ticket para el CUIT solicitado y para el servicio WSFE. Si el mismo existe y faltan más de 10 minutos para su vencimiento, lo devolvemos. En caso contrario, seguimos.
- Creamos el XML del ticket:
<?xml version="1.0" encoding="UTF-8"?> <loginTicketRequest version="1.0"> <header> <uniqueId>(Numero entero aleatorio)</uniqueId> <generationTime>yyyy-mm-ddThh:mm:ss-03:00</generationTime> <expirationTime>yyyy-mm-ddThh:mm:ss-03:00</expirationTime> </header> <service>WSFE</service> </loginTicketRequest>'
Nota: Existen dos tags adicionales en la documentación vieja del WS de la AFIP, source y destination, que contenian el campo DN del certificado digital y el de la AFIP respectivamente. Esos campos ya no son necesarios. Esto nos ahorra un paso, ya que obtener el campo DN tiene sus vueltas si nuestro certificado tiene caracteres especiales como letras acentuadas, etc.
Nota WSFEv1: Se debe solicitar el ticket para el WSFE y trabajar con las URLs del WSFEv1. Es decir, la AFIP espera en el campo service el valor "wsfe" y no "wsfev1".
La fecha de generación, es la fecha actual. La fecha de expiración es la actual más 10 minutos.
Por ejemplo:
<?xml version="1.0" encoding="UTF-8" ?> <loginTicketRequest version="1.0"> <header> <uniqueId>43255</uniqueId> <generationTime>2007-07-12T10:07:00-03:00</generationTime> <expirationTime>2007-07-12T10:17:00-03:00</expirationTime> </header> <service>wsfe</service> </loginTicketRequest>
Archivo de Ejemplo: ejemploWSAAEnvioTicket01.xml
- Firmamos el XML anterior utilizando la clave privada y la contraseña para desencriptarla.
- Armamos el SOAP Envelope:
<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.authws.sua.dvadac.desein.afip.gov"> <soapenv:Header/> <soapenv:Body> <ser:loginCms soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <request xsi:type="xsd:string"> (Resultado de la firma - Base 64) </request> </ser:loginCms> </soapenv:Body> </soapenv:Envelope>
Archivo de Ejemplo: ejemploWSAAEnvio1.xml
- A continuación realizamos el request POST. La SOAPAction es igual a la URL para este Web Service. La misma es: https://wsaahomo.afip.gov.ar/ws/services/LoginCms. El valor de la cabecera Host se extrae de alli.
- Recibiremos una respuesta similar a:
<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Body> <loginCmsResponse xmlns="http://services.authws.sua.dvadac.desein.afip.gov"> <loginCmsReturn> <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <loginTicketResponse version="1"> <header> <source>CN=wsaahomo, O=AFIP, C=AR, SERIALNUMBER=CUIT 33693450239</source> <destination>SERIALNUMBER=CUIT 20305949125, CN=miSistema, OU=facturacion, O=unaPrepaga, ST=capital federal, C=ar</destination> <uniqueId>4191986574</uniqueId> <generationTime>2007-07-12T12:00:06.291-03:00</generationTime> <expirationTime>2007-07-13T00:00:06.291-03:00</expirationTime> </header> <credentials> <token>PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/Pgo8c3NvIHZlcnNpb249IjIuMCI+CiAgICA8aWQgdW5pcXVlX2lkPSIxNzMwNDU5MTg2IiBzcmM9IkNOPXdzYWFob21vLCBPPUFGSVAsIEM9QVIsIFNFUklBTE5VTUJFUj1DVUlUIDMzNjkzNDUwMjM5IiBnZW5fdGltZT0iMTE4NDI1MjQwNiIgZXhwX3RpbWU9IjExODQyOTU2MDYiIGRzdD0iQ049d3NmZSwgTz1BRklQLCBDPUFSIi8+CiAgICA8b3BlcmF0aW9uIHZhbHVlPSJncmFudGVkIiB0eXBlPSJsb2dpbiI+CiAgICAgICAgPGxvZ2luIHVpZD0iU0VSSUFMTlVNQkVSPUNVSVQgMjAzMDU5NDkxMjUsIENOPWFmaWxtZWQsIE9VPWZhY3R1cmFjaW9uLCBPPXBsYW4gZGUgc2FsdWQgaG9zcGl0YWwgYnJpdGFuaWNvLCBTVD1jYXBpdGFsIGZlZGVyYWwsIEM9YXIiIHNlcnZpY2U9IndzZmUiIHJlZ21ldGhvZD0iMjIiIGVudGl0eT0iMzM2OTM0NTAyMzkiIGF1dGhtZXRob2Q9ImNtcyI+CiAgICAgICAgICAgIDxyZWxhdGlvbnM+CiAgICAgICAgICAgICAgICA8cmVsYXRpb24gcmVsdHlwZT0iNCIga2V5PSIyMDMwNTk0OTEyNSIvPgogICAgICAgICAgICAgICAgPHJlbGF0aW9uIHJlbHR5cGU9IjQiIGtleT0iNTU1NTU1NTU1NTQiLz4KICAgICAgICAgICAgPC9yZWxhdGlvbnM+CiAgICAgICAgPC9sb2dpbj4KICAgIDwvb3BlcmF0aW9uPgo8L3Nzbz4KCg==</token> <sign>ZCDODkWoBlrJhL5BcWuSegaD/C0RuWIMbWzgugm6EAryJ1Lkt/cUHkbEbg5zHDLH5ZDVzZ0QEuyDl2Hmxf/KqVt4LApLnFyPsy+KxWHWx7asbNEf/trfG8/+7vvuSMLga47DyFtUyCkud+mdqL8Llvkrm875yCIwNO9gON5OeT4=</sign> </credentials> </loginTicketResponse> </loginCmsReturn> </loginCmsResponse> </soapenv:Body> </soapenv:Envelope>
Archivo de Ejemplo: ejemploWSAARespuesta1.xml
Nota: El XML recibido de la AFIP incluye '<' y '>' en vez de '<' y '>'. Esto normalmente se da en el cuerpo principal del XML. Tuve que reemplazar esos strings para que el parser de XML tomará el XML.
Los tags que nos interesan son token y sign, que guardamos en la base junto la fecha de expiración y el tipo de WS ('WSFE').
En nuestro caso, utilizamos campos blob para almacenar el token y la firma porque el token tiene una longitud mayor a 255, pero también podríamos haber utilizado dos campos varchar(4000) (solo para MSSQL).
- De ahora en más cuando se pida un ticket de sesión para este CUIT, devolveremos:
<ar:argAuth> <ar:Token>(token)</ar:Token> <ar:Sign>(Sign)</ar:Sign> <ar:cuit>(CUIT)</ar:cuit> </ar:argAuth>
Sugerencia para la implementación
Es muy útil tener la posibilidad de forzar la renovación de los tickets. Al hacer esto se puede verificar que toda la configuración de los Web Services funciona bien y que las funciones criptográficas devuelven resultados correctos. Es otra herramienta de diagnóstico junto con el web service FEDummy.