Informatii generale utilizare API¶
URL -ul pentru API este: https://{MEDIU}.keez.ro/api/v1.0/public-api
De observat proprietatea {MEDIU} din URL, aceasta se va inlocui cu MEDIU pentru care se face apelul:
Pentru Staging: https://staging.keez.ro/api/v1.0/public-api
Pentru Productie: https://app.keez.ro/api/v1.0/public-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
sunt parametrii care si acestia apar in URL dar nu fac parte din endpoint si sunt de tip „cheie-valoare”
Exemplu: https://staging.keez.ro/api/v1.0/public-api/items?clientEid=Test123
Se poate observa pozitia parametrului {clientEid} dupa ?
- RequestBody
sunt parametrii care nu apar in URL deloc, ci sunt serviti ca si corp al apelului
Se poate observa ca nu exista nici un parametru in url, dar apelul arata de forma
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
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ă
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)