sicoob-sdk 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sicoob_sdk-0.1.0/PKG-INFO +94 -0
- sicoob_sdk-0.1.0/README.md +78 -0
- sicoob_sdk-0.1.0/pyproject.toml +19 -0
- sicoob_sdk-0.1.0/setup.cfg +4 -0
- sicoob_sdk-0.1.0/setup.py +23 -0
- sicoob_sdk-0.1.0/sicoob/__init__.py +52 -0
- sicoob_sdk-0.1.0/sicoob/api_client.py +38 -0
- sicoob_sdk-0.1.0/sicoob/auth/__init__.py +5 -0
- sicoob_sdk-0.1.0/sicoob/auth/oauth.py +68 -0
- sicoob_sdk-0.1.0/sicoob/boleto.py +890 -0
- sicoob_sdk-0.1.0/sicoob/client.py +74 -0
- sicoob_sdk-0.1.0/sicoob/cobranca.py +5 -0
- sicoob_sdk-0.1.0/sicoob/constants.py +4 -0
- sicoob_sdk-0.1.0/sicoob/conta_corrente.py +165 -0
- sicoob_sdk-0.1.0/sicoob/exceptions.py +178 -0
- sicoob_sdk-0.1.0/sicoob/pix.py +334 -0
- sicoob_sdk-0.1.0/sicoob_sdk.egg-info/PKG-INFO +94 -0
- sicoob_sdk-0.1.0/sicoob_sdk.egg-info/SOURCES.txt +25 -0
- sicoob_sdk-0.1.0/sicoob_sdk.egg-info/dependency_links.txt +1 -0
- sicoob_sdk-0.1.0/sicoob_sdk.egg-info/requires.txt +4 -0
- sicoob_sdk-0.1.0/sicoob_sdk.egg-info/top_level.txt +2 -0
- sicoob_sdk-0.1.0/tests/__init__.py +1 -0
- sicoob_sdk-0.1.0/tests/conftest.py +22 -0
- sicoob_sdk-0.1.0/tests/test_boleto.py +1925 -0
- sicoob_sdk-0.1.0/tests/test_client.py +30 -0
- sicoob_sdk-0.1.0/tests/test_conta_corrente.py +141 -0
- sicoob_sdk-0.1.0/tests/test_pix.py +266 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sicoob-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python SDK for Sicoob API
|
|
5
|
+
Author: Fábio Thomaz
|
|
6
|
+
Author-email: fabio@ladder.dev.br
|
|
7
|
+
Requires-Python: >=3.7
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: requests>=2.25.1
|
|
10
|
+
Requires-Dist: python-dotenv>=0.15.0
|
|
11
|
+
Requires-Dist: pytest>=7.0.0
|
|
12
|
+
Requires-Dist: pytest-mock>=3.6.1
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: requires-python
|
|
16
|
+
|
|
17
|
+
# Sicoob SDK Python
|
|
18
|
+
|
|
19
|
+
SDK Python para integração com a API do Banco Sicoob
|
|
20
|
+
|
|
21
|
+
## Instalação
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install -r requirements.txt
|
|
25
|
+
# ou
|
|
26
|
+
pip install -e .
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Configuração
|
|
30
|
+
|
|
31
|
+
Crie um arquivo `.env` na raiz do projeto com as seguintes variáveis:
|
|
32
|
+
|
|
33
|
+
```ini
|
|
34
|
+
SICOOB_CLIENT_ID=seu_client_id
|
|
35
|
+
SICOOB_CLIENT_SECRET=seu_client_secret
|
|
36
|
+
SICOOB_CERTIFICADO=caminho/para/certificado.pem
|
|
37
|
+
SICOOB_CHAVE_PRIVADA=caminho/para/chave_privada.key
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Uso Básico
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from sicoob import Sicoob
|
|
44
|
+
|
|
45
|
+
# Inicializa o cliente
|
|
46
|
+
cliente = Sicoob(
|
|
47
|
+
client_id="seu_client_id",
|
|
48
|
+
client_secret="seu_client_secret",
|
|
49
|
+
certificado="caminho/para/certificado.pem",
|
|
50
|
+
chave_privada="caminho/para/chave_privada.key"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Exemplo: consulta de extratos
|
|
54
|
+
extrato = cliente.consulta_extrato(conta="12345", data_inicio="2023-01-01", data_fim="2023-01-31")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## API de Boletos
|
|
58
|
+
|
|
59
|
+
A classe `BoletoAPI` permite emitir e consultar boletos bancários:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from sicoob.boleto import BoletoAPI
|
|
63
|
+
|
|
64
|
+
# Obtém instância do BoletoAPI
|
|
65
|
+
boleto_api = cliente.boleto()
|
|
66
|
+
|
|
67
|
+
# Emitir boleto
|
|
68
|
+
dados_boleto = {
|
|
69
|
+
"numeroContrato": 123456,
|
|
70
|
+
"modalidade": 1,
|
|
71
|
+
"valor": 100.50,
|
|
72
|
+
"beneficiario": {
|
|
73
|
+
"nome": "Nome Beneficiário",
|
|
74
|
+
"documento": "12345678901"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
boleto = boleto_api.emitir_boleto(dados_boleto)
|
|
78
|
+
|
|
79
|
+
# Consultar boleto
|
|
80
|
+
nosso_numero = boleto["nossoNumero"]
|
|
81
|
+
boleto_consultado = boleto_api.consultar_boleto(nosso_numero)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Tratamento de Erros
|
|
85
|
+
|
|
86
|
+
A API trata os seguintes casos de erro:
|
|
87
|
+
- **404 Not Found**: Retorna `None` quando o boleto não existe
|
|
88
|
+
- **Erros HTTP (400, 500, etc)**: Levanta exceção com código e mensagem
|
|
89
|
+
- **Erros de conexão**: Levanta exceção com detalhes do erro
|
|
90
|
+
|
|
91
|
+
## Links Úteis
|
|
92
|
+
|
|
93
|
+
- [Documentação API Sicoob](https://developers.sicoob.com.br)
|
|
94
|
+
- [Portal de Desenvolvedores](https://developers.sicoob.com.br/portal)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Sicoob SDK Python
|
|
2
|
+
|
|
3
|
+
SDK Python para integração com a API do Banco Sicoob
|
|
4
|
+
|
|
5
|
+
## Instalação
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install -r requirements.txt
|
|
9
|
+
# ou
|
|
10
|
+
pip install -e .
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Configuração
|
|
14
|
+
|
|
15
|
+
Crie um arquivo `.env` na raiz do projeto com as seguintes variáveis:
|
|
16
|
+
|
|
17
|
+
```ini
|
|
18
|
+
SICOOB_CLIENT_ID=seu_client_id
|
|
19
|
+
SICOOB_CLIENT_SECRET=seu_client_secret
|
|
20
|
+
SICOOB_CERTIFICADO=caminho/para/certificado.pem
|
|
21
|
+
SICOOB_CHAVE_PRIVADA=caminho/para/chave_privada.key
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Uso Básico
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from sicoob import Sicoob
|
|
28
|
+
|
|
29
|
+
# Inicializa o cliente
|
|
30
|
+
cliente = Sicoob(
|
|
31
|
+
client_id="seu_client_id",
|
|
32
|
+
client_secret="seu_client_secret",
|
|
33
|
+
certificado="caminho/para/certificado.pem",
|
|
34
|
+
chave_privada="caminho/para/chave_privada.key"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Exemplo: consulta de extratos
|
|
38
|
+
extrato = cliente.consulta_extrato(conta="12345", data_inicio="2023-01-01", data_fim="2023-01-31")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## API de Boletos
|
|
42
|
+
|
|
43
|
+
A classe `BoletoAPI` permite emitir e consultar boletos bancários:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from sicoob.boleto import BoletoAPI
|
|
47
|
+
|
|
48
|
+
# Obtém instância do BoletoAPI
|
|
49
|
+
boleto_api = cliente.boleto()
|
|
50
|
+
|
|
51
|
+
# Emitir boleto
|
|
52
|
+
dados_boleto = {
|
|
53
|
+
"numeroContrato": 123456,
|
|
54
|
+
"modalidade": 1,
|
|
55
|
+
"valor": 100.50,
|
|
56
|
+
"beneficiario": {
|
|
57
|
+
"nome": "Nome Beneficiário",
|
|
58
|
+
"documento": "12345678901"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
boleto = boleto_api.emitir_boleto(dados_boleto)
|
|
62
|
+
|
|
63
|
+
# Consultar boleto
|
|
64
|
+
nosso_numero = boleto["nossoNumero"]
|
|
65
|
+
boleto_consultado = boleto_api.consultar_boleto(nosso_numero)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Tratamento de Erros
|
|
69
|
+
|
|
70
|
+
A API trata os seguintes casos de erro:
|
|
71
|
+
- **404 Not Found**: Retorna `None` quando o boleto não existe
|
|
72
|
+
- **Erros HTTP (400, 500, etc)**: Levanta exceção com código e mensagem
|
|
73
|
+
- **Erros de conexão**: Levanta exceção com detalhes do erro
|
|
74
|
+
|
|
75
|
+
## Links Úteis
|
|
76
|
+
|
|
77
|
+
- [Documentação API Sicoob](https://developers.sicoob.com.br)
|
|
78
|
+
- [Portal de Desenvolvedores](https://developers.sicoob.com.br/portal)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sicoob-sdk"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A Python SDK for Sicoob API"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"requests>=2.25.1",
|
|
9
|
+
"python-dotenv>=0.15.0",
|
|
10
|
+
# Testes
|
|
11
|
+
"pytest>=7.0.0",
|
|
12
|
+
"pytest-mock>=3.6.1",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[dependency-groups]
|
|
16
|
+
dev = [
|
|
17
|
+
"build>=1.2.2.post1",
|
|
18
|
+
"twine>=6.1.0",
|
|
19
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="sicoob-sdk",
|
|
5
|
+
version="0.1.2",
|
|
6
|
+
description="SDK Python para integração com a API do Banco Sicoob",
|
|
7
|
+
long_description=open("README.md", encoding='utf-8').read(),
|
|
8
|
+
long_description_content_type="text/markdown",
|
|
9
|
+
author="Fábio Thomaz",
|
|
10
|
+
author_email="fabio@ladder.dev.br",
|
|
11
|
+
packages=find_packages(),
|
|
12
|
+
install_requires=[
|
|
13
|
+
"requests>=2.25.1",
|
|
14
|
+
"python-dotenv>=0.15.0",
|
|
15
|
+
],
|
|
16
|
+
python_requires=">=3.7",
|
|
17
|
+
classifiers=[
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
],
|
|
23
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Módulo principal do SDK Sicoob"""
|
|
2
|
+
|
|
3
|
+
from .client import Sicoob
|
|
4
|
+
from .conta_corrente import ContaCorrenteAPI
|
|
5
|
+
from .pix import PixAPI
|
|
6
|
+
from .boleto import BoletoAPI
|
|
7
|
+
from .exceptions import (
|
|
8
|
+
SicoobError,
|
|
9
|
+
BoletoError,
|
|
10
|
+
BoletoEmissaoError,
|
|
11
|
+
BoletoConsultaError,
|
|
12
|
+
BoletoNaoEncontradoError,
|
|
13
|
+
ContaCorrenteError,
|
|
14
|
+
PixError,
|
|
15
|
+
AutenticacaoError,
|
|
16
|
+
ExtratoError,
|
|
17
|
+
SaldoError,
|
|
18
|
+
TransferenciaError,
|
|
19
|
+
CobrancaPixError,
|
|
20
|
+
CobrancaPixNaoEncontradaError,
|
|
21
|
+
CobrancaPixVencimentoError,
|
|
22
|
+
WebhookPixError,
|
|
23
|
+
WebhookPixNaoEncontradoError,
|
|
24
|
+
LoteCobrancaPixError,
|
|
25
|
+
QrCodePixError
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__version__ = "0.1.0"
|
|
29
|
+
__all__ = [
|
|
30
|
+
"Sicoob",
|
|
31
|
+
"ContaCorrenteAPI",
|
|
32
|
+
"PixAPI",
|
|
33
|
+
"BoletoAPI",
|
|
34
|
+
"SicoobError",
|
|
35
|
+
"BoletoError",
|
|
36
|
+
"BoletoEmissaoError",
|
|
37
|
+
"BoletoConsultaError",
|
|
38
|
+
"BoletoNaoEncontradoError",
|
|
39
|
+
"ContaCorrenteError",
|
|
40
|
+
"PixError",
|
|
41
|
+
"AutenticacaoError",
|
|
42
|
+
"ExtratoError",
|
|
43
|
+
"SaldoError",
|
|
44
|
+
"TransferenciaError",
|
|
45
|
+
"CobrancaPixError",
|
|
46
|
+
"CobrancaPixNaoEncontradaError",
|
|
47
|
+
"CobrancaPixVencimentoError",
|
|
48
|
+
"WebhookPixError",
|
|
49
|
+
"WebhookPixNaoEncontradoError",
|
|
50
|
+
"LoteCobrancaPixError",
|
|
51
|
+
"QrCodePixError"
|
|
52
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
import requests
|
|
3
|
+
from .auth import OAuth2Client
|
|
4
|
+
from .constants import BASE_URL, SANDBOX_URL
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class APIClientBase:
|
|
8
|
+
"""Classe base para APIs do Sicoob"""
|
|
9
|
+
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
oauth_client: OAuth2Client,
|
|
13
|
+
session: requests.Session,
|
|
14
|
+
sandbox_mode: bool = False,
|
|
15
|
+
):
|
|
16
|
+
"""Inicializa com cliente OAuth e sessão HTTP existente
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
oauth_client: Cliente OAuth2 para autenticação
|
|
20
|
+
session: Sessão HTTP existente
|
|
21
|
+
sandbox_mode: Se True, usa URL de sandbox (default: False)
|
|
22
|
+
"""
|
|
23
|
+
self.sandbox_mode = sandbox_mode
|
|
24
|
+
self.oauth_client = oauth_client
|
|
25
|
+
self.session = session
|
|
26
|
+
|
|
27
|
+
def _get_base_url(self) -> str:
|
|
28
|
+
"""Retorna a URL base conforme modo de operação"""
|
|
29
|
+
return SANDBOX_URL if self.sandbox_mode else BASE_URL
|
|
30
|
+
|
|
31
|
+
def _get_headers(self, scope) -> Dict[str, str]:
|
|
32
|
+
"""Retorna headers padrão com token de acesso"""
|
|
33
|
+
token = self.oauth_client.get_access_token(scope)
|
|
34
|
+
return {
|
|
35
|
+
"Authorization": f"Bearer {token}",
|
|
36
|
+
"Content-Type": "application/json",
|
|
37
|
+
"Accept": "application/json",
|
|
38
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class OAuth2Client:
|
|
8
|
+
"""Cliente OAuth2 para autenticação com a API Sicoob"""
|
|
9
|
+
|
|
10
|
+
def __init__(
|
|
11
|
+
self, client_id=None, certificado=None, chave_privada=None
|
|
12
|
+
):
|
|
13
|
+
"""Inicializa o cliente OAuth2"""
|
|
14
|
+
load_dotenv()
|
|
15
|
+
|
|
16
|
+
self.client_id = client_id or os.getenv("SICOOB_CLIENT_ID")
|
|
17
|
+
self.certificado = certificado or os.getenv("SICOOB_CERTIFICADO")
|
|
18
|
+
self.chave_privada = chave_privada or os.getenv("SICOOB_CHAVE_PRIVADA")
|
|
19
|
+
|
|
20
|
+
self.token_url = "https://auth.sicoob.com.br/auth/realms/cooperado/protocol/openid-connect/token"
|
|
21
|
+
self.token_cache = {} # Cache de tokens por escopo
|
|
22
|
+
self.session = requests.Session()
|
|
23
|
+
self.session.cert = (self.certificado, self.chave_privada)
|
|
24
|
+
|
|
25
|
+
def get_access_token(self, scope=None):
|
|
26
|
+
"""Obtém ou renova o token de acesso para o escopo especificado
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
scope: Escopo(s) necessário(s) para a API. Exemplos:
|
|
30
|
+
- Cobrança por Boleto: "boletos_inclusao boletos_consulta boletos_alteracao webhooks_alteracao webhooks_consulta webhooks_inclusao"
|
|
31
|
+
- Conta Corrente: "cco_consulta cco_transferencias openid"
|
|
32
|
+
- Recebimento no PIX: "cob.write cob.read cobv.write cobv.read lotecobv.write lotecobv.read pix.write pix.read webhook.read webhook.write payloadlocation.write payloadlocation.read"
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
str: Token de acesso válido para o escopo solicitado
|
|
36
|
+
"""
|
|
37
|
+
# Usa escopo padrão se não especificado (para compatibilidade)
|
|
38
|
+
if scope is None:
|
|
39
|
+
scope = "cco_extrato cco_consulta"
|
|
40
|
+
|
|
41
|
+
# Verifica se já existe token válido para este escopo
|
|
42
|
+
if scope in self.token_cache and not self._is_token_expired(scope):
|
|
43
|
+
return self.token_cache[scope]["access_token"]
|
|
44
|
+
|
|
45
|
+
token_data = {
|
|
46
|
+
"grant_type": "client_credentials",
|
|
47
|
+
"client_id": self.client_id,
|
|
48
|
+
"scope": scope,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
response = self.session.post(self.token_url, data=token_data)
|
|
52
|
+
response.raise_for_status()
|
|
53
|
+
|
|
54
|
+
token_info = response.json()
|
|
55
|
+
token_info["expires_at"] = time.time() + token_info["expires_in"]
|
|
56
|
+
|
|
57
|
+
# Armazena o token no cache por escopo
|
|
58
|
+
self.token_cache[scope] = token_info
|
|
59
|
+
|
|
60
|
+
return token_info["access_token"]
|
|
61
|
+
|
|
62
|
+
def _is_token_expired(self, scope):
|
|
63
|
+
"""Verifica se o token para o escopo especificado expirou"""
|
|
64
|
+
if scope not in self.token_cache or "expires_at" not in self.token_cache[scope]:
|
|
65
|
+
return True
|
|
66
|
+
return (
|
|
67
|
+
time.time() >= self.token_cache[scope]["expires_at"] - 60
|
|
68
|
+
) # 60s de margem
|