folyo 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.
folyo-0.1.0/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ venv/
3
+ dist/
4
+ build/
5
+ *.egg-info/
6
+ __pycache__/
7
+ *.py[cod]
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .coverage
12
+ htmlcov/
folyo-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Folyo Technologies SpA
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
folyo-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,184 @@
1
+ Metadata-Version: 2.4
2
+ Name: folyo
3
+ Version: 0.1.0
4
+ Summary: SDK oficial de Python para la API de Folyo (facturacion electronica chilena, DTE + SII).
5
+ Project-URL: Homepage, https://folyo.cl
6
+ Project-URL: Documentation, https://api.folyo.cl/docs
7
+ Project-URL: Repository, https://github.com/SYCTecnologiaCo/Folyo
8
+ Project-URL: Issues, https://github.com/SYCTecnologiaCo/Folyo/issues
9
+ Author-email: Folyo <soporte@folyo.cl>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: boleta,chile,dte,factura-electronica,facturacion,folyo,sii
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Office/Business :: Financial :: Accounting
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.9
28
+ Requires-Dist: httpx>=0.27
29
+ Provides-Extra: dev
30
+ Requires-Dist: build>=1.2; extra == 'dev'
31
+ Requires-Dist: mypy>=1.11; extra == 'dev'
32
+ Requires-Dist: pip-audit>=2.7; extra == 'dev'
33
+ Requires-Dist: pytest>=8.0; extra == 'dev'
34
+ Requires-Dist: respx>=0.21; extra == 'dev'
35
+ Requires-Dist: ruff>=0.6; extra == 'dev'
36
+ Requires-Dist: twine>=5.1; extra == 'dev'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # Folyo — SDK oficial de Python
40
+
41
+ SDK de Python para la API de [Folyo](https://folyo.cl): facturacion electronica
42
+ chilena (DTE + SII) standalone. Del codigo al SII, sin escalas.
43
+
44
+ - Cliente tipado sobre `httpx` (unica dependencia de runtime).
45
+ - Errores tipados con mapeo de los codigos del servidor.
46
+ - Soporte del patron de emision asincrona (encolar + polling con backoff).
47
+ - Idempotencia, override de ambiente del SII y reintentos seguros ante 429/503.
48
+ - Redaccion de secretos (la API key/JWT nunca aparecen en `repr` ni en errores).
49
+
50
+ ## Instalacion
51
+
52
+ ```bash
53
+ pip install folyo
54
+ ```
55
+
56
+ Requiere Python 3.9 o superior.
57
+
58
+ ## Autenticacion
59
+
60
+ El cliente exige exactamente uno de los dos esquemas:
61
+
62
+ - **API key** (recomendada para integraciones server-side, no expira). Se envia
63
+ como header `X-API-Key`. Fija el tenant y la empresa.
64
+ - **JWT Bearer** (sesion de usuario). Se envia como `Authorization: Bearer ...`.
65
+
66
+ ```python
67
+ from folyo import Folyo
68
+
69
+ # Con API key
70
+ folyo = Folyo(api_key="<tu-api-key>")
71
+
72
+ # O con un token JWT
73
+ folyo = Folyo(token="eyJhbGciOi...")
74
+ ```
75
+
76
+ Tambien funciona como context manager (cierra el cliente HTTP al salir):
77
+
78
+ ```python
79
+ with Folyo(api_key="<tu-api-key>") as folyo:
80
+ ...
81
+ ```
82
+
83
+ ## Quickstart: emitir una Factura Electronica (DTE 33)
84
+
85
+ La emision es **asincrona**: `emitir()` encola y devuelve un `job_id`; el helper
86
+ `emitir_y_esperar()` hace el polling por ti hasta que el job termina.
87
+
88
+ ```python
89
+ import uuid
90
+ from folyo import Folyo, DTERequest, Receptor, DetalleLinea
91
+
92
+ with Folyo(api_key="<tu-api-key>") as folyo:
93
+ req = DTERequest(
94
+ tipo_dte=33, # Factura Electronica
95
+ receptor=Receptor(
96
+ rut="12.345.678-9",
97
+ razon_social="Cliente SpA",
98
+ giro="Comercio",
99
+ ),
100
+ detalle=[
101
+ DetalleLinea(nombre="Servicio de consultoria", monto=100000),
102
+ ],
103
+ )
104
+
105
+ # Encolar y esperar el resultado (con Idempotency-Key para reintentar seguro)
106
+ job = folyo.dte.emitir_y_esperar(req, idempotency_key=str(uuid.uuid4()))
107
+
108
+ if job.estado == "completed" and job.result is not None:
109
+ print("Folio:", job.result.folio)
110
+ print("Track ID:", job.result.track_id)
111
+ print("Total:", job.result.monto_total)
112
+
113
+ # Descargar el PDF
114
+ pdf = folyo.dte.descargar_pdf(33, job.result.folio)
115
+ with open(f"factura-{job.result.folio}.pdf", "wb") as f:
116
+ f.write(pdf)
117
+ else:
118
+ print("La emision fallo:", job.estado)
119
+ ```
120
+
121
+ Si prefieres resolver el resultado por webhook o SSE, usa `emitir()` directo:
122
+
123
+ ```python
124
+ encolada = folyo.dte.emitir(req)
125
+ print(encolada.job_id) # resuelve luego via webhook dte.emitido o polling
126
+ ```
127
+
128
+ ## Manejo de errores
129
+
130
+ ```python
131
+ from folyo import (
132
+ Folyo,
133
+ FolyoAuthError,
134
+ FolyoQuotaError,
135
+ FolyoRateLimitError,
136
+ FolyoValidationError,
137
+ FolyoSiiUnavailableError,
138
+ )
139
+
140
+ try:
141
+ folyo.dte.emitir(req)
142
+ except FolyoRateLimitError as e:
143
+ print("Reintentar en", e.retry_after, "segundos")
144
+ except FolyoQuotaError as e:
145
+ print("Limite de plan o pago requerido:", e.code)
146
+ except FolyoAuthError:
147
+ print("Credenciales invalidas o sin permisos")
148
+ except FolyoValidationError as e:
149
+ print("Datos invalidos:", e.message)
150
+ except FolyoSiiUnavailableError:
151
+ print("El SII no esta disponible, reintenta mas tarde")
152
+ ```
153
+
154
+ El SDK reintenta automaticamente (con backoff exponencial y respetando
155
+ `Retry-After`) ante `429` y `503` en operaciones seguras: peticiones `GET` y
156
+ emisiones con `Idempotency-Key`.
157
+
158
+ ## Recursos disponibles
159
+
160
+ | Recurso | Metodos principales |
161
+ |---|---|
162
+ | `folyo.dte` | `emitir`, `get_emision`, `emitir_y_esperar`, `listar_documentos`, `descargar_xml`, `descargar_pdf`, `regenerar_pdf`, `consultar_estado`, `consultar_envio`, `emitidos`, `recibidos`, `contribuyente`, `situacion_tributaria`, `enviar_rcof`, `resumen_rcof` |
163
+ | `folyo.folios` | `info`, `solicitar` |
164
+ | `folyo.rcv` | `periodos`, `periodo`, `sync`, `resumen_iva` |
165
+ | `folyo.clientes` | `listar`, `upsert`, `importar`, `buscar`, `actualizar`, `eliminar` |
166
+ | `folyo.empresa` | `listar`, `seleccionar` |
167
+ | `folyo.acuse` | `registrar`, `pendientes`, `estado` |
168
+ | `folyo.webhooks` | `listar`, `crear`, `actualizar`, `eliminar` |
169
+ | `folyo.api_keys` | `listar`, `crear`, `eliminar` |
170
+
171
+ Algunos endpoints (clientes, RCV, listado de documentos) requieren "panel
172
+ operativo" y responden `403` en planes solo-API.
173
+
174
+ ## Seguridad
175
+
176
+ - La API key y el JWT nunca se incluyen en `repr(cliente)` ni en los errores.
177
+ - Los errores exponen solo el mensaje sanitizado del servidor, el codigo
178
+ estable, el status HTTP y el `request_id`: nunca el cuerpo de la respuesta
179
+ (que puede traer datos sensibles como el XML firmado o secrets de webhook).
180
+ - El SDK no escribe logs por defecto.
181
+
182
+ ## Licencia
183
+
184
+ MIT. Ver [LICENSE](./LICENSE).
folyo-0.1.0/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # Folyo — SDK oficial de Python
2
+
3
+ SDK de Python para la API de [Folyo](https://folyo.cl): facturacion electronica
4
+ chilena (DTE + SII) standalone. Del codigo al SII, sin escalas.
5
+
6
+ - Cliente tipado sobre `httpx` (unica dependencia de runtime).
7
+ - Errores tipados con mapeo de los codigos del servidor.
8
+ - Soporte del patron de emision asincrona (encolar + polling con backoff).
9
+ - Idempotencia, override de ambiente del SII y reintentos seguros ante 429/503.
10
+ - Redaccion de secretos (la API key/JWT nunca aparecen en `repr` ni en errores).
11
+
12
+ ## Instalacion
13
+
14
+ ```bash
15
+ pip install folyo
16
+ ```
17
+
18
+ Requiere Python 3.9 o superior.
19
+
20
+ ## Autenticacion
21
+
22
+ El cliente exige exactamente uno de los dos esquemas:
23
+
24
+ - **API key** (recomendada para integraciones server-side, no expira). Se envia
25
+ como header `X-API-Key`. Fija el tenant y la empresa.
26
+ - **JWT Bearer** (sesion de usuario). Se envia como `Authorization: Bearer ...`.
27
+
28
+ ```python
29
+ from folyo import Folyo
30
+
31
+ # Con API key
32
+ folyo = Folyo(api_key="<tu-api-key>")
33
+
34
+ # O con un token JWT
35
+ folyo = Folyo(token="eyJhbGciOi...")
36
+ ```
37
+
38
+ Tambien funciona como context manager (cierra el cliente HTTP al salir):
39
+
40
+ ```python
41
+ with Folyo(api_key="<tu-api-key>") as folyo:
42
+ ...
43
+ ```
44
+
45
+ ## Quickstart: emitir una Factura Electronica (DTE 33)
46
+
47
+ La emision es **asincrona**: `emitir()` encola y devuelve un `job_id`; el helper
48
+ `emitir_y_esperar()` hace el polling por ti hasta que el job termina.
49
+
50
+ ```python
51
+ import uuid
52
+ from folyo import Folyo, DTERequest, Receptor, DetalleLinea
53
+
54
+ with Folyo(api_key="<tu-api-key>") as folyo:
55
+ req = DTERequest(
56
+ tipo_dte=33, # Factura Electronica
57
+ receptor=Receptor(
58
+ rut="12.345.678-9",
59
+ razon_social="Cliente SpA",
60
+ giro="Comercio",
61
+ ),
62
+ detalle=[
63
+ DetalleLinea(nombre="Servicio de consultoria", monto=100000),
64
+ ],
65
+ )
66
+
67
+ # Encolar y esperar el resultado (con Idempotency-Key para reintentar seguro)
68
+ job = folyo.dte.emitir_y_esperar(req, idempotency_key=str(uuid.uuid4()))
69
+
70
+ if job.estado == "completed" and job.result is not None:
71
+ print("Folio:", job.result.folio)
72
+ print("Track ID:", job.result.track_id)
73
+ print("Total:", job.result.monto_total)
74
+
75
+ # Descargar el PDF
76
+ pdf = folyo.dte.descargar_pdf(33, job.result.folio)
77
+ with open(f"factura-{job.result.folio}.pdf", "wb") as f:
78
+ f.write(pdf)
79
+ else:
80
+ print("La emision fallo:", job.estado)
81
+ ```
82
+
83
+ Si prefieres resolver el resultado por webhook o SSE, usa `emitir()` directo:
84
+
85
+ ```python
86
+ encolada = folyo.dte.emitir(req)
87
+ print(encolada.job_id) # resuelve luego via webhook dte.emitido o polling
88
+ ```
89
+
90
+ ## Manejo de errores
91
+
92
+ ```python
93
+ from folyo import (
94
+ Folyo,
95
+ FolyoAuthError,
96
+ FolyoQuotaError,
97
+ FolyoRateLimitError,
98
+ FolyoValidationError,
99
+ FolyoSiiUnavailableError,
100
+ )
101
+
102
+ try:
103
+ folyo.dte.emitir(req)
104
+ except FolyoRateLimitError as e:
105
+ print("Reintentar en", e.retry_after, "segundos")
106
+ except FolyoQuotaError as e:
107
+ print("Limite de plan o pago requerido:", e.code)
108
+ except FolyoAuthError:
109
+ print("Credenciales invalidas o sin permisos")
110
+ except FolyoValidationError as e:
111
+ print("Datos invalidos:", e.message)
112
+ except FolyoSiiUnavailableError:
113
+ print("El SII no esta disponible, reintenta mas tarde")
114
+ ```
115
+
116
+ El SDK reintenta automaticamente (con backoff exponencial y respetando
117
+ `Retry-After`) ante `429` y `503` en operaciones seguras: peticiones `GET` y
118
+ emisiones con `Idempotency-Key`.
119
+
120
+ ## Recursos disponibles
121
+
122
+ | Recurso | Metodos principales |
123
+ |---|---|
124
+ | `folyo.dte` | `emitir`, `get_emision`, `emitir_y_esperar`, `listar_documentos`, `descargar_xml`, `descargar_pdf`, `regenerar_pdf`, `consultar_estado`, `consultar_envio`, `emitidos`, `recibidos`, `contribuyente`, `situacion_tributaria`, `enviar_rcof`, `resumen_rcof` |
125
+ | `folyo.folios` | `info`, `solicitar` |
126
+ | `folyo.rcv` | `periodos`, `periodo`, `sync`, `resumen_iva` |
127
+ | `folyo.clientes` | `listar`, `upsert`, `importar`, `buscar`, `actualizar`, `eliminar` |
128
+ | `folyo.empresa` | `listar`, `seleccionar` |
129
+ | `folyo.acuse` | `registrar`, `pendientes`, `estado` |
130
+ | `folyo.webhooks` | `listar`, `crear`, `actualizar`, `eliminar` |
131
+ | `folyo.api_keys` | `listar`, `crear`, `eliminar` |
132
+
133
+ Algunos endpoints (clientes, RCV, listado de documentos) requieren "panel
134
+ operativo" y responden `403` en planes solo-API.
135
+
136
+ ## Seguridad
137
+
138
+ - La API key y el JWT nunca se incluyen en `repr(cliente)` ni en los errores.
139
+ - Los errores exponen solo el mensaje sanitizado del servidor, el codigo
140
+ estable, el status HTTP y el `request_id`: nunca el cuerpo de la respuesta
141
+ (que puede traer datos sensibles como el XML firmado o secrets de webhook).
142
+ - El SDK no escribe logs por defecto.
143
+
144
+ ## Licencia
145
+
146
+ MIT. Ver [LICENSE](./LICENSE).
@@ -0,0 +1,94 @@
1
+ """SDK oficial de Python para la API de Folyo.
2
+
3
+ Folyo es facturacion electronica chilena (DTE + SII). Este SDK envuelve la API
4
+ REST de ``https://api.folyo.cl`` con un cliente tipado, manejo de errores y
5
+ soporte para el patron de emision asincrona.
6
+
7
+ Ejemplo minimo::
8
+
9
+ from folyo import Folyo, DTERequest, Receptor, DetalleLinea
10
+
11
+ with Folyo(api_key="<tu-api-key>") as folyo:
12
+ req = DTERequest(
13
+ tipo_dte=33,
14
+ receptor=Receptor(rut="12.345.678-9", razon_social="Cliente SpA"),
15
+ detalle=[DetalleLinea(nombre="Servicio", monto=100000)],
16
+ )
17
+ job = folyo.dte.emitir_y_esperar(req)
18
+ print(job.result.folio)
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from ._redact import redact
24
+ from .client import DEFAULT_BASE_URL, USER_AGENT, Folyo, __version__
25
+ from .errors import (
26
+ FolyoAuthError,
27
+ FolyoConflictError,
28
+ FolyoConnectionError,
29
+ FolyoError,
30
+ FolyoNotFoundError,
31
+ FolyoQuotaError,
32
+ FolyoRateLimitError,
33
+ FolyoSiiUnavailableError,
34
+ FolyoValidationError,
35
+ )
36
+ from .models import (
37
+ AcuseRequest,
38
+ ApiKey,
39
+ ApiKeyCreated,
40
+ AuthTokens,
41
+ Cliente,
42
+ DescuentoGlobal,
43
+ DetalleLinea,
44
+ Documento,
45
+ DTERequest,
46
+ EmisionEncolada,
47
+ EmisionJob,
48
+ EmisionResult,
49
+ Empresa,
50
+ FoliosInfo,
51
+ Receptor,
52
+ Referencia,
53
+ Transporte,
54
+ Webhook,
55
+ WebhookCreated,
56
+ )
57
+
58
+ __all__ = [
59
+ "__version__",
60
+ "DEFAULT_BASE_URL",
61
+ "USER_AGENT",
62
+ "Folyo",
63
+ "redact",
64
+ # Errores
65
+ "FolyoError",
66
+ "FolyoAuthError",
67
+ "FolyoQuotaError",
68
+ "FolyoValidationError",
69
+ "FolyoNotFoundError",
70
+ "FolyoConflictError",
71
+ "FolyoRateLimitError",
72
+ "FolyoSiiUnavailableError",
73
+ "FolyoConnectionError",
74
+ # Modelos
75
+ "DTERequest",
76
+ "Receptor",
77
+ "DetalleLinea",
78
+ "DescuentoGlobal",
79
+ "Referencia",
80
+ "Transporte",
81
+ "EmisionEncolada",
82
+ "EmisionResult",
83
+ "EmisionJob",
84
+ "Documento",
85
+ "FoliosInfo",
86
+ "Cliente",
87
+ "Empresa",
88
+ "Webhook",
89
+ "WebhookCreated",
90
+ "ApiKey",
91
+ "ApiKeyCreated",
92
+ "AcuseRequest",
93
+ "AuthTokens",
94
+ ]
@@ -0,0 +1,26 @@
1
+ """Utilidades de redaccion de secretos.
2
+
3
+ El SDK no debe filtrar credenciales en ``repr``, logs ni telemetria. Esta
4
+ funcion enmascara cualquier valor sensible mostrando solo una pista minima.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Optional
10
+
11
+ __all__ = ["redact"]
12
+
13
+
14
+ def redact(value: Optional[str]) -> str:
15
+ """Devuelve una version enmascarada y segura de un secreto.
16
+
17
+ - ``None`` o cadena vacia -> ``"None"``.
18
+ - Valores cortos -> ``"***"`` (sin pistas, no hay suficiente entropia).
19
+ - Valores largos -> primeros 4 caracteres + ``"***"`` (ej. ``"stri***"``),
20
+ util para distinguir keys sin revelar el secreto.
21
+ """
22
+ if not value:
23
+ return "None"
24
+ if len(value) <= 8:
25
+ return "***"
26
+ return f"{value[:4]}***"