Informatii generale utilizare API

Atenționare

Regulile despre mediile dispobibile si anume staging si app au fost descrise la Vezi Autorizare - Sectiunea Medii

  • Utilizarea API -ului se face cu ajutorul urmatoarelor operatiilor sau numite si verbe:

    • GET - este folosit pentru a aduce informatii despre un anumit model de date

    • POST - este folosit pentru a adauga informatii in API

    • PUT - este folosit pentru a modifica informatii intr-un anumit model de date

    • PATCH - este folosit pentru a modifica doar anumite informatii dintr-un model de date

    • DELETE - este folosit pentru a sterge din API

Parametri path/request

  • Anumite operatii pot avea sau nu parametrii. Acestia pot fi de mai multe tipuri si se disting prin pozitia in cadrul URL -ului sau a felului cum arata.

    • PathVariable
      • sunt parametrii care fac parte din endpoint si sunt de tip „valoare”

      • Exemplu: https://staging.keez.ro/api/v1.0/public-api/{clientEid}/items

      • Se poate observa pozitia parametrului {clientEid} in interiorului endpoint -ului

    • RequestParam
    • RequestBody
      data = {
          "clientEid": Test123
      }
      requests.post(url=URL, headers={'Content-Type': 'application/json', 'Authorization': token}, json=data)
      

      Atenționare

      Parametrii de tip RequestBody pot fi serviti doar la operatiile de POST/PUT/PATCH/DELETE. Verbul GET nu suporta parametrii RequestBody

Coduri de eroare posibile

Cod

Denumire

Descriere

Optiuni

200

OK

Totul a mers perfect

Folositi datele returnate daca acestea exista

400

Bad Request

Format de date/apel incorect

Modelul de date din parametrii sau din corpul apelului sunt gresit formatate

403

Forbidden

Nu ai permisiune pentru a accesa acea functionalitate

Ai uitat sa apelezi preluarea de token / Nu ai folosit token-ul in apelul catre API /
Chiar nu ai drept si trebuie sa contactezi Keez.ro

401

Unauthorized

Nu ai permisiune pentru a accesa acea functionalitate

Ai uitat sa apelezi preluarea de token / Nu ai folosit token-ul in apelul catre API

404

Not Found

Pagina apelata catre API nu exista

Verifica URL -ul din apel

500

Internal Server Error

Probleme la server

Contacteaza Keez.ro

502

Bad Gateway

Aplicatia nu functioneaza, probleme retea

Asteptati cateva minute si apoi reincercati

504

Gateway Timeout

Timpul de asteptare a fost depasit

Asteptati cateva minute si apoi reincercati

503

Service Unavailable

Aplicatia nu functioneaza

Asteptati cateva minute si apoi reincercati

Exemplu de eroare

{
  "Code": "401",
  "Message": "Nu aveti drepturi suficiente pentru accesarea acestei functionalitati.",
  "Id": "f1c6b834d26141fbad0282f165a015ae",
  "statusCode": "UNAUTHORIZED"
}

Notă

Pentru mai multe informatii despre modelul de eroare Vezi Model de Eroare

Identificatorul Keez

Orice model de date contine o proprietate prin care se poate identifica in mod unic acea resursa (factura, articol, categorie, partener). In majoritatea cazurilor aceasta este numita externalId, iar in manual aceasta se regaseste sub denumirea de Identificator Keez

Reguli generale

  • Orice operatie de tip GET va returna un rezultat de tip model de date sau un simplu identificator externalId. Vezi Modele de Date disponibile

  • Orice operatie de DELETE nu va return nimic la finalul operatiei

  • Orice operatie de PATCH nu va return nimic la finalul operatiei

  • Orice operatie de PUT nu va return nimic la finalul operatiei

  • Orice operatie de PATCH nu va return nimic la finalul operatiei

Atenționare

Indiferent daca se creaza un articol sau o factura, identificatorul Keez al acestuia externalId trebuie furnizat pentru a avea acces ulterior la el.

Notă

Exemplu 1: Daca se creaza un articol cu scopul de a fi folosit pe o factura, identificatorul Keez externalId trebuie furnizat in corpul apelului, | deoarece operatia de PUT/POST pentru crearea articolului nu returneaza identificatorul Keez externalId

Notă

Exemplu 2: Daca se creaza o factura cu scopul de a fi trimisa pe email, identificatorul Keez externalId trebuie furnizat in corpul apelului,
deoarece operatia de PUT/POST pentru crearea facturii nu returneaza identificatorul Keez externalId

Wrapper Python peste API

  • Keez.ro ofera o clasa wrapper peste acest API, scris in Python

import time
from urllib.parse import urlencode

import python_jwt as jwt
import requests


class KeezPublicApi:
    def __init__(self, application_id=None, secret=None, token_url=None, api_url=None):
        self.expires = time.gmtime(0)

        if application_id is None:
            raise Exception("application_id not provided!")
        if secret is None:
            raise Exception("secret not provided!")
        if token_url is None:
            raise Exception("token_url not provided!")
        if api_url is None:
            raise Exception("api_url not provided!")

        self.baseAPIUrl = api_url
        self.token_url = token_url
        self.application_id = application_id
        self.secret = secret

        self.token = None
        self.token_type = None
        self.access_token = None

    def refresh_auth_token(self):
        if self.token_expired():
            self.generate_token()

    def token_expired(self):
        return self.expires < time.gmtime()

    def generate_token(self):
        req = requests.post(self.token_url, data={
            'client_id': f'app{self.application_id}',
            'client_secret': self.secret,
            'grant_type': 'client_credentials',
            "scope": "public-api",
        }, headers={
            'Content-Type': 'application/x-www-form-urlencoded'
        })
        req.raise_for_status()
        json = req.json()

        self.token = f'{json["token_type"]} {json["access_token"]}'
        self.token_type = json['token_type']
        self.access_token = json['access_token']
        (_, claims) = jwt.process_jwt(self.access_token)
        self.expires = time.gmtime(claims['exp'])

    def api_call(self, verb, url, data=None):
        self.refresh_auth_token()

        _req = None

        if verb == 'GET':
            _req = requests.get(url=self.baseAPIUrl + url, headers={'Content-Type': 'application/json',
                                                                    'Authorization': self.token}, json=data)
        elif verb == 'POST':
            _req = requests.post(url=self.baseAPIUrl + url, headers={'Content-Type': 'application/json',
                                                                     'Authorization': self.token}, json=data)
        elif verb == 'PUT':
            _req = requests.put(url=self.baseAPIUrl + url, headers={'Content-Type': 'application/json',
                                                                    'Authorization': self.token}, json=data)
        elif verb == 'PATCH':
            _req = requests.patch(url=self.baseAPIUrl + url, headers={'Content-Type': 'application/json',
                                                                      'Authorization': self.token}, json=data)
        elif verb == 'DELETE':
            _req = requests.delete(url=self.baseAPIUrl + url, headers={'Content-Type': 'application/json',
                                                                       'Authorization': self.token}, json=data)

        if _req is None:
            raise Exception(f'Internal error: invalid verb {verb}')

        _req.raise_for_status()
        return _req.json()

    """ Public call methods (ITEMS)"""

    def items(self, client_eid, filter=None, order=None, count=None, offset=None):
        url = f'/{client_eid}/items'

        params = {}

        if filter is not None:
            params.update({'filter': f' AND '.join(f'{x}:{filter[x]}' for x in filter)})
        if order is not None:
            params.update({'order': f' AND '.join(f'{x} {order[x]}' for x in order)})
        if count is not None:
            params.update({'count': str(count)})
        if offset is not None:
            params.update({'offset': str(offset)})

        qstr = urlencode(params)
        url = url + '?' + qstr

        return self.api_call('GET', url)

    def item(self, client_eid, item_external_id):
        return self.api_call('GET', f'/{client_eid}/items/{item_external_id}')

    def create_item(self, client_eid, data):
        return self.api_call('POST', f'/{client_eid}/items', data=data)

    def replace_item(self, client_eid, item_external_id, data):
        return self.api_call('PUT', f'/{client_eid}/items/{item_external_id}', data=data)

    def update_item(self, client_eid, item_external_id, data):
        return self.api_call('PATCH', f'/{client_eid}/items/{item_external_id}', data=data)

    """ Public call methods (INVOICES)"""

    def invoices(self, client_eid, filter=None, order=None, count=None, offset=None):
        url = f'/{client_eid}/invoices'

        params = {}

        if filter is not None:
            params.update({'filter': f' AND '.join(f'{x}:{filter[x]}' for x in filter)})
        if order is not None:
            params.update({'order': f' AND '.join(f'{x} {order[x]}' for x in order)})
        if count is not None:
            params.update({'count': str(count)})
        if offset is not None:
            params.update({'offset': str(offset)})

        qstr = urlencode(params)
        url = url + '?' + qstr

        return self.api_call('GET', url)

    def invoice(self, client_eid, invoice_external_id):
        return self.api_call('GET', f'/{client_eid}/invoices/{invoice_external_id}')

    def create_invoice(self, client_eid, data):
        return self.api_call('POST', f'/{client_eid}/invoices', data=data)

    def replace_invoice(self, client_eid, invoice_external_id, data):
        return self.api_call('PUT', f'/{client_eid}/invoices/{invoice_external_id}', data=data)

    def delete_invoice(self, client_eid, invoice_eid):
        return self.api_call('DELETE', f'/{client_eid}/invoices', data={
            'externalId': invoice_eid
        })

    def validate_invoice(self, client_eid, invoice_eid):
        return self.api_call('POST', f'/{client_eid}/invoices/valid', data={
            'externalId': invoice_eid
        })

    def submit_e_factura(self, client_eid, invoice_eid):
        return self.api_call('POST', f'/{client_eid}/invoices/efactura/submitted', data={
            'externalId': invoice_eid
        })

    def cancel_invoice(self, client_eid, invoice_eid):
        return self.api_call('POST', f'/{client_eid}/invoices/canceled', data={
            'externalId': invoice_eid
        })

    def deliver_invoice_by_email(self, invoice_eid, to=None, cc=None, bcc=None):
        return self.api_call('POST', f'/invoices/delivery', data={
            "invoiceExternalId": invoice_eid,
            "info": [
                {
                    "deliveryMethod": "Email",
                    "representationType": "Attachment",
                    "recipients": {
                        "to": to,
                        "cc": cc,
                        "bcc": bcc
                    }
                }
            ]
        })

    def invoice_pdf(self, client_eid, invoice_external_id):
        self.refresh_auth_token()

        _req = requests.get(url=self.baseAPIUrl + f'/{client_eid}/invoices/{invoice_external_id}/pdf',
                            headers={'Authorization': self.token})
        if _req is None:
            raise Exception(f'Internal error: invalid download!')

        disp = _req.headers['content-disposition']
        name = None
        if 'filename' in disp:
            name = disp[disp.index('=') + 1:].strip()
            if '"' in name:
                name = name.replace('"', '').strip()

        return name, _req.content

    def measure_unit(self):
        return self.api_call('GET', f'/items/measure-unit')

    def invoice_payment_type(self):
        return self.api_call('GET', f'/invoices/payment-type')

Mod de utilizare wrapper

token_url = 'https://staging.keez.ro/idp/connect/token'
api_url = f'https://staging.keez.ro/api/v1.0/public-api'

client_eid = '{RECEIVED_CLIENT_EID}'
application_id = '{RECEIVED_APPLICATION_ID}'
secret = '{RECEIVED_SECRET}'

# Se face "legatura" la wrapper pe baza proprietatiilor necesare (application_id, secret, token_url, api_url)
wrapper_service = KeezPublicApi(application_id=application_id, secret=secret, token_url=token_url, api_url=api_url)

# Se apeleaza metodele din interiorul wrapper-ului pe baza client_eid si a altor parametrii, in functie de metoda
result = wrapper_service.items(client_eid=client_eid)

# Se printeaza rezultatul
print(result)