diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 3c3e2d2..11b1a02 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.0.1 +current_version = 0.2.0 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index 3cb75c6..d028fff 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -64,7 +64,7 @@ default_context: test_runner: 'pytest' travis: 'yes' travis_osx: 'no' - version: '0.0.1' + version: '0.2.0' website: 'https://www.kmee.com.br' year_from: '2020' year_to: '2020' diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 87da4eb..6a5cf24 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,8 @@ Changelog ========= 0.0.1 (2020-11-15) +0.2.0 (2022-10-08) + ->API INTER V2 ------------------ * First release on PyPI. diff --git a/docs/conf.py b/docs/conf.py index da3d388..81abe0a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ year = '2020' author = 'Luis Felipe Mileo' copyright = '{0}, {1}'.format(year, author) -version = release = '0.0.1' +version = release = '0.2.0' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index e49aa04..6b835cc 100755 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ def read(*names, **kwargs): setup( name='erpbrasil.bank.inter', - version='0.0.1', + version='0.2.0', license='MIT', description='Integração com o Banco Inter em Python', long_description='%s\n%s' % ( diff --git a/src/erpbrasil/bank/inter/__init__.py b/src/erpbrasil/bank/inter/__init__.py index 00ce65b..a314c2a 100644 --- a/src/erpbrasil/bank/inter/__init__.py +++ b/src/erpbrasil/bank/inter/__init__.py @@ -1,3 +1,3 @@ -__version__ = '0.1.0' +__version__ = "0.2.0" from erpbrasil.bank.inter import * diff --git a/src/erpbrasil/bank/inter/api.py b/src/erpbrasil/bank/inter/api.py index c6a52d1..b7429a6 100644 --- a/src/erpbrasil/bank/inter/api.py +++ b/src/erpbrasil/bank/inter/api.py @@ -1,137 +1,157 @@ # -*- coding: utf-8 -*- import json + import requests +from .auth import Auth + FILTRAR_POR = [ - 'TODOS', - 'VENCIDOSAVENCER', - 'EXPIRADOS', - 'PAGOS', - 'TODOSBAIXADOS', + "TODOS", + "VENCIDOSAVENCER", + "EXPIRADOS", + "PAGOS", + "TODOSBAIXADOS", ] ORDENAR_CONSULTA_POR = [ - 'NOSSONUMERO', # (Default) - 'SEUNUMERO', - 'DATAVENCIMENTO_ASC', - 'DATAVENCIMENTO_DSC', - 'NOMESACADO', - 'VALOR_ASC', - 'VALOR_DSC', - 'STATUS_ASC', - 'STATUS_DSC', + "NOSSONUMERO", # (Default) + "SEUNUMERO", + "DATAVENCIMENTO_ASC", + "DATAVENCIMENTO_DSC", + "NOMESACADO", + "VALOR_ASC", + "VALOR_DSC", + "STATUS_ASC", + "STATUS_DSC", ] class ApiInter(object): - """ Implementa a Api do Inter""" + """Implementa a Api do Inter""" - _api = 'https://apis.bancointer.com.br:8443/openbanking/v1/certificado/boletos' + # _api = 'https://apis.bancointer.com.br:8443/openbanking/v1/certificado/boletos' + _api = "https://cdpj.partners.bancointer.com.br/cobranca/v2/boletos/" - def __init__(self, cert, conta_corrente): + def __init__(self, cert, conta_corrente, clientId, clientSecret): self._cert = cert self.conta_corrente = conta_corrente + self.auth = Auth( + clientId, + # "50acb448-5107-4f57-81ea-54a615c5da0a", + clientSecret, + # "0a0275ff-4fcc-4f7f-a092-edcbb5bb6bd8", + ) + self.auth.generate_token_boleto_write("boleto-cobranca.write", self._cert) + self.auth.generate_token_boleto_read("boleto-cobranca.read", self._cert) - def _prepare_headers(self): + def _prepare_headers(self, token): return { - 'content-type': 'application/json', - 'x-inter-conta-corrente': self.conta_corrente, + "content-type": "application/json", + "x-inter-conta-corrente": self.conta_corrente, + "Authorization": "Bearer " + token, } - def _call(self, http_request, url, params=None, data=None, **kwargs): + def _call(self, token, http_request, url, params=None, data=None, **kwargs): + debug1 = self._prepare_headers(token) + debug2 = json.dumps(data or {}) + debug3 = params or {} response = http_request( url, - headers=self._prepare_headers(), + headers=self._prepare_headers(token), params=params or {}, data=json.dumps(data or {}), cert=self._cert, - verify=False, - **kwargs + verify=True, + **kwargs, ) if response.status_code > 299: - error = response.json() - message = '%s - Código %s' % ( - response.status_code, - error.get('error-code') + # error = response.json() + error = response # .json() + # message = '%s - Código %s' % ( + # response.status_code, + # error.get('error-code') + # ) + # raise Exception(message) + raise Exception( + [str(response.text), response.status_code, debug1, debug2, debug3] ) - raise Exception(message) return response def boleto_inclui(self, boleto): - """ POST - https://apis.bancointer.com.br:8443/openbanking/v1/certificado/boletos + """POST :param boleto: :return: """ result = self._call( - requests.post, - url=self._api, - data=boleto + self.auth.token_boleto_write, requests.post, url=self._api, data=boleto ) return result.content and result.json() or result.ok - def boleto_consulta(self, filtrar_por='TODOS', data_inicial=None, data_final=None, - ordenar_por='NOSSONUMERO'): - """ GET - https://apis.bancointer.com.br:8443/openbanking/v1/certificado/boletos? - filtrarPor=TODOS& - dataInicial=2020-01-01& - dataFinal=2020-12-01& - ordenarPor=SEUNUMERO - - :param filtrar_por: - :param data_inicial: - :param data_final: - :param ordenar_por: - :return: - """ + def boleto_consulta( + self, + filtrar_por="TODOS", + data_inicial=None, + data_final=None, + ordenar_por="NOSSONUMERO", + page=0, + ): result = self._call( + self.auth.token_boleto_read, requests.get, url=self._api, params=dict( filtrarPor=filtrar_por, dataInicial=data_inicial, dataFinal=data_final, - ordenarPor=ordenar_por - ) + ordenarPor=ordenar_por, + page=page, + ), + ) + return result.content and result.json() or result.ok + + def boleto_recupera(self, nosso_numero): + + _url = f"{self._api}{nosso_numero}" + + result = self._call( + self.auth.token_boleto_read, + requests.get, + url=_url, ) + return result.content and result.json() or result.ok - def boleto_baixa(self, nosso_numero, codigo_baixa): - """ POST - https://apis.bancointer.com.br:8443/openbanking/v1/certificado/boletos/ - 00576501185/baixas + def boleto_baixa(self, nossoNumero, motivoCancelamento): + """POST + https://cdpj.partners.bancointer.com.br/cobranca/v2/boletos/{nossoNumero}/cancelar + :param nosso_numero: :return: """ - url = '{}/{}/baixas'.format( - self._api, - nosso_numero - ) + url = "{}{}/cancelar".format(self._api, nossoNumero) result = self._call( + self.auth.token_boleto_write, requests.post, url=url, data=dict( - codigoBaixa=codigo_baixa, - ) + motivoCancelamento=motivoCancelamento, + ), ) return result.content and result.json() or result.ok def boleto_pdf(self, nosso_numero): - """ GET - https://apis.bancointer.com.br:8443/openbanking/v1/certificado/boletos/ + """GET + https://cdpj.partners.bancointer.com.br/cobranca/v2/boletos/ 00595764723/pdf :param nosso_numero: :return: """ - url = '{}/{}/pdf'.format( - self._api, - nosso_numero - ) + url = "{}{}/pdf".format(self._api, nosso_numero) result = self._call( + self.auth.token_boleto_read, requests.get, url=url, ) diff --git a/src/erpbrasil/bank/inter/auth.py b/src/erpbrasil/bank/inter/auth.py new file mode 100644 index 0000000..293aa05 --- /dev/null +++ b/src/erpbrasil/bank/inter/auth.py @@ -0,0 +1,76 @@ +import os +import json +import requests +import time + + + +class Auth: + access_token = "" + token_type = "" + expires_in = "" + scope = "" + client_id = "" + client_secret = "" + scope = "" + grant_type = "client_credentials" + token_boleto_write = "" + token_boleto_read = "" + + def __init__(self, client_id, client_secret): + self.client_id = client_id + self.client_secret = client_secret + + def generate_token_boleto_write(self, scope, cert): + request_body = ( + "client_id={0}&client_secret={1}&scope={2}&grant_type={3}".format( + self.client_id, self.client_secret, scope, self.grant_type + ) + ) + if os.environ.get('INTER_TOKEN_BOLETO_WRITE_LAST_UPDATE') is None: + os.environ['INTER_TOKEN_BOLETO_WRITE_LAST_UPDATE'] = str(0) + if float(os.environ.get('INTER_TOKEN_BOLETO_WRITE_LAST_UPDATE')) + 3600 < time.time(): + response = requests.post( + "https://cdpj.partners.bancointer.com.br/oauth/v2/token", + headers={"Content-Type": "application/x-www-form-urlencoded"}, + data=request_body, + cert=cert, + ) + + if response.status_code != 200: + print("Server didn't return an 'OK' response. Content was: {!r}".format(response.content)) + raise Exception( + ["Server didn't return an 'OK' response. Content was: {!r}".format(response.content), + str(response.text), response.status_code] + ) + self.token_boleto_write = json.loads(response.text) + else: + os.environ['INTER_TOKEN_BOLETO_WRITE'] = response.json().get("access_token") + os.environ['INTER_TOKEN_BOLETO_WRITE_LAST_UPDATE'] = str(time.time()) + self.token_boleto_write = os.environ.get('INTER_TOKEN_BOLETO_WRITE') + + def generate_token_boleto_read(self, scope, cert): + request_body = ( + "client_id={0}&client_secret={1}&scope={2}&grant_type={3}".format( + self.client_id, self.client_secret, scope, self.grant_type + ) + ) + if os.environ.get('INTER_TOKEN_BOLETO_READ_LAST_UPDATE') is None: + os.environ['INTER_TOKEN_BOLETO_READ_LAST_UPDATE'] = str(0) + if float(os.environ.get('INTER_TOKEN_BOLETO_READ_LAST_UPDATE')) + 3600 < time.time(): + response = requests.post( + "https://cdpj.partners.bancointer.com.br/oauth/v2/token", + headers={"Content-Type": "application/x-www-form-urlencoded"}, + data=request_body, + cert=cert, + ) + if response.status_code != 200: + raise Exception( + ["Server didn't return an 'OK' response. Content was: {!r}".format(response.content), + str(response.text), response.status_code] + ) + self.token_boleto_read = json.loads(response.text) + else: + os.environ['INTER_TOKEN_BOLETO_READ'] = response.json().get("access_token") + os.environ['INTER_TOKEN_BOLETO_READ_LAST_UPDATE'] = str(time.time()) + self.token_boleto_read = os.environ.get('INTER_TOKEN_BOLETO_READ') diff --git a/src/erpbrasil/bank/inter/boleto.py b/src/erpbrasil/bank/inter/boleto.py index 2346770..57d1168 100644 --- a/src/erpbrasil/bank/inter/boleto.py +++ b/src/erpbrasil/bank/inter/boleto.py @@ -6,7 +6,7 @@ class BoletoInter: - """ Implementa a Api do BancoInter """ + """Implementa a Api do BancoInter""" @classmethod def convert_to(cls, obj, **kwargs): @@ -16,49 +16,44 @@ def convert_to(cls, obj, **kwargs): if hasattr(obj, key): obj.__dict__[key] = value - def __init__(self, sender, amount, payer, issue_date, due_date, - identifier, instructions=None, mora=None, multa=None, - discount1=None, discount2=None, discount3=None): + def __init__( + self, + sender, + amount, + payer, + issue_date, + due_date, + identifier, + instructions=None, + mora=None, + multa=None, + discount1=None, + discount2=None, + discount3=None, + ): self._sender = sender - self._amount = amount + self._amount = round(amount,2) self._payer = payer self._issue_date = issue_date.strftime("%Y-%m-%d") self._due_date = due_date.strftime("%Y-%m-%d") self._identifier = identifier self._instructions = instructions or [] - self.mora = mora or dict( - codigoMora="ISENTO", - valor=0, - taxa=0 - ) - self.multa = multa or dict( - codigoMulta="NAOTEMMULTA", - valor=0, - taxa=0 - ) + self.mora = mora or dict(codigoMora="ISENTO", valor=0, taxa=0) + self.multa = multa or dict(codigoMulta="NAOTEMMULTA", valor=0, taxa=0) self.discount1 = discount1 or dict( - codigoDesconto="NAOTEMDESCONTO", - taxa=0, - valor=0, - data="" + codigoDesconto="NAOTEMDESCONTO", taxa=0, valor=0, data="" ) self.discount2 = discount2 or dict( - codigoDesconto="NAOTEMDESCONTO", - taxa=0, - valor=0, - data="" + codigoDesconto="NAOTEMDESCONTO", taxa=0, valor=0, data="" ) self.discount3 = discount3 or dict( - codigoDesconto="NAOTEMDESCONTO", - taxa=0, - valor=0, - data="" + codigoDesconto="NAOTEMDESCONTO", taxa=0, valor=0, data="" ) def _emissao_data(self): pagador = dict( - cnpjCpf=self._payer.identifier, + cpfCnpj=self._payer.identifier, nome=self._payer.name, email=self._payer.email, telefone=self._payer.phone[2:], @@ -86,12 +81,11 @@ def _emissao_data(self): multa=self.multa, mora=self.mora, cnpjCPFBeneficiario=self._sender.identifier, - numDiasAgenda="SESSENTA" + numDiasAgenda="60", ) if self._instructions: - data['mensagem'] = dict( - {'linha{}'.format(k + 1): v for (k, v) in enumerate( - self._instructions)} + data["mensagem"] = dict( + {"linha{}".format(k + 1): v for (k, v) in enumerate(self._instructions)} ) return data diff --git a/src/erpbrasil/bank/inter/cli.py b/src/erpbrasil/bank/inter/cli.py index 8990010..6e26e08 100644 --- a/src/erpbrasil/bank/inter/cli.py +++ b/src/erpbrasil/bank/inter/cli.py @@ -18,6 +18,6 @@ @click.command() -@click.argument('names', nargs=-1) +@click.argument("names", nargs=-1) def main(names): click.echo(repr(names)) diff --git a/tests/test_boleto.py b/tests/test_boleto.py index 3e71e6b..19ae5a1 100644 --- a/tests/test_boleto.py +++ b/tests/test_boleto.py @@ -1,33 +1,43 @@ # -*- coding: utf-8 -*- import os -from datetime import datetime import unittest -from erpbrasil.bank.inter.boleto import BoletoInter +from datetime import datetime + +from febraban.cnab240.user import User +from febraban.cnab240.user import UserAddress +from febraban.cnab240.user import UserBank + from erpbrasil.bank.inter.api import ApiInter -from febraban.cnab240.user import User, UserAddress, UserBank +from erpbrasil.bank.inter.boleto import BoletoInter class TestBancoApiInter(unittest.TestCase): - def setUp(self): - certificado_cert = os.environ.get('certificado_inter_cert') - certificado_key = os.environ.get('certificado_inter_key') - + certificado_cert = os.environ.get("certificado_inter_cert") + certificado_key = os.environ.get("certificado_inter_key") + print(ApiInter._api) self.api = ApiInter( cert=(certificado_cert, certificado_key), - conta_corrente='14054310' + conta_corrente="10869106", + clientId="50acb448-5107-4f57-81ea-54a615c5da0a", + clientSecret="0a0275ff-4fcc-4f7f-a092-edcbb5bb6bd8", ) + print("env vars") + print(os.environ.get("INTER_TOKEN_BOLETO_WRITE")) + print(os.environ.get("INTER_TOKEN_BOLETO_WRITE_LAST_UPDATE")) + print(os.environ.get("INTER_TOKEN_BOLETO_READ")) + print(os.environ.get("INTER_TOKEN_BOLETO_READ_LAST_UPDATE")) self.dados = [] myself = User( - name='KMEE INFORMATICA LTDA', - identifier='23130935000198', + name="KMEE INFORMATICA LTDA", + identifier="23130935000198", bank=UserBank( bankId="341", branchCode="1234", accountNumber="33333", accountVerifier="4", - bankName="BANCO ITAU SA" + bankName="BANCO ITAU SA", ), ) now = datetime.now() @@ -45,21 +55,21 @@ def setUp(self): stateCode="SP", zipCode="31327130", streetNumber="15", - ) + ), ) slip = BoletoInter( sender=myself, - amount_in_cents="100.00", + amount=100.001, payer=payer, issue_date=now, due_date=now, identifier="456" + str(i), instructions=[ - 'TESTE 1', - 'TESTE 2', - 'TESTE 3', - 'TESTE 4', - ] + "TESTE 1", + "TESTE 2", + "TESTE 3", + "TESTE 4", + ], ) self.dados.append(slip) @@ -70,36 +80,34 @@ def test_data(self): def test_boleto_api(self): for item in self.dados: resposta = self.api.boleto_inclui(item._emissao_data()) - item.nosso_numero = resposta['nossoNumero'] - item.seu_numero = resposta['seuNumero'] - item.linha_digitavel = resposta['linhaDigitavel'] - item.barcode = resposta['codigoBarras'] + item.nosso_numero = resposta["nossoNumero"] + item.seu_numero = resposta["seuNumero"] + item.linha_digitavel = resposta["linhaDigitavel"] + item.barcode = resposta["codigoBarras"] self.assertListEqual( list(resposta.keys()), - ['seuNumero', 'nossoNumero', 'codigoBarras', 'linhaDigitavel'], - 'Erro ao registrar boleto' + ["seuNumero", "nossoNumero", "codigoBarras", "linhaDigitavel"], + "Erro ao registrar boleto", ) resposta = self.api.boleto_consulta( - data_inicial='2020-01-01', data_final='2020-12-01', - ordenar_por='SEUNUMERO' + data_inicial="2020-01-01", data_final="2020-12-01", ordenar_por="SEUNUMERO" ) - self.assertTrue(resposta, 'Falha ao consultar boletos') + self.assertTrue(resposta, "Falha ao consultar boletos") for item in self.dados: resposta = self.api.boleto_pdf(nosso_numero=item.nosso_numero) - self.assertTrue(resposta, 'Falha ao imprimir boleto') + self.assertTrue(resposta, "Falha ao imprimir boleto") for item in self.dados: resposta = self.api.boleto_baixa( - nosso_numero=item.nosso_numero, - codigo_baixa='SUBISTITUICAO', + nossoNumero=item.nosso_numero, + motivoCancelamento="SUBSTITUICAO", ) - self.assertTrue(resposta, 'Falha ao Baixar boletos') - + self.assertTrue(resposta, "Falha ao Baixar boletos") suite = unittest.TestLoader().loadTestsFromTestCase(TestBancoApiInter) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main()