API v1

Punto de acceso para el JSON API de Winfra. A continuación puedes empezar a leer al detalle como esta diseñado el API de Winfra.

No tengo tiempo, quiero conectarme ya...

Para acceder al API de winfra necesitas 6 piezas de información. Las tres primeras te proporciona Winfra S.L. Estos datos son comunes para todas las instalaciones de Winfra:

AUTH_SERVER_URL
Url del servicio de autenticación de Winfra
AUTH_USER y AUTH_PASS
Usuario y contraseña que te identifica como tercero para acceder a cualquier api de winfra.
API_NOMBRE
Nombre de api a que vas acceder, como por ejemplo maestros o contabilidad

Siguientes tres piezas te proporciona la empresa con la que vas a conectar. Obviamente cada empresa tendrá su propia configuración.

DOMAIN
Identificador de la empresa a que vas a acceder
DOMAIN_USER y DOMAIN_PASS
Usuario y contraseña para la empresa a que vas a acceder
DOMAIN_SERVER_URL
Url de servicio a que vas a conectar, el url del servidor + el servicio

¿Tienes usuarios y contraseñas? Adelante con el ejemplo o prueba la conexión directamente aquí.

¡Muestrame el código!

Vamos a obtener la lista de los artículos e información del primer artículo. El ejemplo esta en Python, la lógica es igual en otros lenguajes. Para la comunicación utilizo el package requests.

Vamos a obtener los artículos, eso pertenece al api de los maestros API_NOMBRE="maestros".

import requests

# esto el la peticion para el token de autentificacion
peticion_de_token = {
    "domain": DOMAIN,
    "api": "maestros"  # API_NOMBRE
}

# enviamos la peticion al servidor de autenticacion
r = requests.post(AUTH_SERVER_URL + "/api_token/", auth=(AUTH_USER, AUTH_PASS), json=peticion_de_token)

# mostrar el json devuelto
print(r.json())

El resultado de la ejecución

{
    'WINFRA-API-TOKEN': 'eyJ0eXAiOiJKV1QiLCJhbGciOi... (mucho mas largo) ...QEyG4O85g9w0mB_e1bOLHg'
}

El servicio es /api_v1/articulos/

import requests

r = requests.get(
    DOMAIN_SERVER_URL + "/api_v1/articulos/",
    auth=(DOMAIN_USER, DOMAIN_PASS),
    headers={"WINFRA-API-TOKEN": "eyJ0eXAiOiJKV1QiLCJhbGciOi... (mucho mas largo) ...QEyG4O85g9w0mB_e1bOLHg"}
)

print(r.json())
Y la respuesta:
[
    {
        "codigo": "1",
        "hash": 0,
        "id": "8fd593d0-f7d8-44c9-bfb9-026d823b3d95",
        "nombre": "San Miguel 1/3",
        "tipo": "producto"
    },
    ... (muchos mas artículos por aquí) ...
    {
        "codigo": "10",
        "hash": 0,
        "id": "1fcb8b44-8a32-4dfa-8639-39fa5df3a8b0",
        "nombre": "San Miguel 1/5",
        "tipo": "producto"
    }
]

Ahora para obtener un artículo detallado tenemos que incluir su id en la petición.

import requests

r = requests.get(
    DOMAIN_SERVER_URL + "/api_v1/articulos/1d3f74f8-4494-48ca-b294-d3ad6d2cd437",
    auth=(DOMAIN_USER, DOMAIN_PASS),
    headers={"WINFRA-API-TOKEN": "eyJ0eXAiOiJKV1QiLCJhbGciOi... (mucho mas largo) ...QEyG4O85g9w0mB_e1bOLHg"}
)

print(r.json())

Con el resultado como este:

{
    "adjuntos": [],
    "codigo": "1045",
    "dimension1_codigo": "",
    "dimension1_descripcion": "",
    "dimension2_codigo": "",
    "dimension2_descripcion": "",
    "fabricante_codigo": "199",
    "fabricante_descripcion": "COOPERATIVA VINICOLA UNION DEL VALLE",
    "formatos": [
        {
            "codigo": "1045",
            "descripcion": "",
            "equivalencia": 1.0,
            "id": "3b3ab89d-427f-4a79-9863-2060ef5c9d0c",
            "nombre": "VIVAZ TINTO CRIANZA TIERRA LEON",
            "tipo": "Caja"
        }
    ],
    "hash": 0,
    "id": "1d3f74f8-4494-48ca-b294-d3ad6d2cd437",
    "impuestos": [
        {
            "base": 100.0,
            "descripcion": "IVA general",
            "grupo": "general",
            "iva": 21.0,
            "recargo": 5.2
        }
    ],
    "marca": "",
    "nombre": "VIVAZ TINTO CRIANZA TIERRA LEON",
    "organizacion_venta": "Bebidas",
    "subtipo": "",
    "subtipo_descripcion": "",
    "tipo": "producto",
    "tipo_descripcion": "Producto"
}

Código en c# para obtener la lista de los artículos. En el ejemplo utiliza la clase WebClient que no esta recomendada en la nuevas versiones de .net pero es compatible desde versión 2.0.

Las funciones auxiliares para aceptar los certificados y para procesar posibles errores.


static bool CertValidation(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    // acepta todos los certificados para utilizar el api con el certificado firmado por winfra
    return true;
}

static void HandleWebError(WebException ex)
{
    // controlo el estado de resultado y saco en error devuelto por el servidor
    if (ex.Response != null)
    {
        int status = (int)((HttpWebResponse)ex.Response).StatusCode;
        if (status == 401 || status == 422 || status == 555)
        {
            using (var sr = new StreamReader(ex.Response.GetResponseStream()))
            {
                string errjson = sr.ReadToEnd();
                throw new ApplicationException(errjson);
            }
        }
    }
    throw ex;
}

Para empezar configuramos el cliente y registramos los usuarios y contraseñas con sus urls.


ServicePointManager.Expect100Continue = false;
ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CertValidation);

// tengo dos credenciales diferentes para el los dos servidores
var creds = new CredentialCache();
creds.Add(new Uri(AUTH_SERVER_URL + "api_token/"), "Basic", new NetworkCredential(AUTH_USER, AUTH_PASS));
creds.Add(new Uri(DOMAIN_SERVER_URL + "api_v1/"), "Basic", new NetworkCredential(DOMAIN_USER, DOMAIN_PASS));

Obtenemos el token


string token = "";
using (var wc = new WebClient())
{
    wc.Credentials = creds;

    // obtener el token
    try
    {
        // como serializar y deserializar el json en c# depende de versiones de framework o librerias externas
        // crearé y leeré el JSON como strings.
        string peticion = string.Format("{{\"domain\": \"{0}\", \"api\": \"{1}\"}}", DOMAIN, API_NOMBRE);
        byte[] data = wc.UploadData(AUTH_SERVER_URL + "api_token/", Encoding.UTF8.GetBytes(peticion));
        string json = Encoding.UTF8.GetString(data);
        string[] partes = json.Split('"');
        token = partes[3];
    }
    catch (WebException ex)
    {
        HandleWebError(ex);
    }
}

Obtenemos los artículos proporcionando el token


using (var wc = new WebClient())
{
    wc.Credentials = creds;

    // obtener los articulos
    try
    {
        // anado el token para acceder a api
        wc.Headers.Add("WINFRA-API-TOKEN", token);
        string json = wc.DownloadString(DOMAIN_SERVER_URL + "api_v1/articulos/");
    }
    catch (WebException ex)
    {
        HandleWebError(ex);
    }
}

La implementación de Javascript que utilizamos en la herramienta de pruebas.

La funcion implementa la llamada a web y devuelve null en caso de error o JSON con el resultado.


async function call_http(method, url, user, pass, token=null, data=null) {
    // implementa la llamada a web y devuelve el json
    // o null si ha ocurido algun error.
    // Los errores van directamente a log.
    var headers = new Headers()
    headers.set('Authorization', 'Basic ' + window.btoa(unescape(encodeURIComponent(user + ":" + pass))));
    if (token !== null) {
        headers.set('WINFRA-API-TOKEN', token);
    }

    var req = {}
    req.method = method;
    req.headers = headers;
    if (data !== null) {
        req.body = JSON.stringify(data)
    }

    var sc;
    json = await fetch(url, req)
        .then(function(response) {
            sc = response.status
            console.log('Status: ' + sc + ' ' + response.statusText)
            if (sc == 200 | sc == 401 | sc == 422 | sc == 555) {
                return response.json()
            }
        })
        .catch(function(error) {
            console.log('ERROR: ' + error.message);
        });

    if (json) {
        if (sc == 200) {
            return json
        } else {
            console.log("ERROR: " + json.error)
            return null
        }
    } else {
        return null
    }
}

La lógica de llamada a los servicios:


gettoken = call_http("POST", AUTH_SERVER_URL + "api_token/", AUTH_USER, AUTH_PASS, null, {"domain" : DOMAIN, "api": API_NOMBRE});
gettoken.then(function(json) {
    if (json !== null) {
        // tenemos un valor de json devuelto
        token = json["WINFRA-API-TOKEN"]
        if (token) {
            // llamada a api indicado, incluyendo el token
            getapi = call_http("GET", DOMAIN_SERVER_URL + "api_v1/articulos/", DOMAIN_USER, DOMAIN_PASS, token, null)
            getapi.then(function(json) {
                if (json !== null) {
                    // tenemos un resultado
                    console.log(json)
                }
            })
        }
    }
})

Detalles de API y la estructura de los datos estan en la correspondiente documentación.