Metadata-Version: 2.4
Name: pywompi
Version: 0.1.0
Summary: Unofficial python cleint for Wompi to create transactions.
Project-URL: Homepage, https://github.com/roquestudios/pywompi
Project-URL: Repository, https://github.com/roquestudios/pywompi
Project-URL: Issues, https://github.com/roquestudios/pywompi/issues
Project-URL: Changelog, https://github.com/roquestudios/pywompi/blob/main/CHANGELOG.md
Author-email: Kevin Rivera <kevinrev26@gmail.com>
License-Expression: GPL-3.0-or-later
License-File: LICENSE
Keywords: colombia,el salvador,pasarela de pago,payments,wompi
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: httpx<1.0,>=0.27
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest-mock>=3.14; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: twine>=5.1; extra == 'dev'
Description-Content-Type: text/markdown

# pywompi

Cliente Python no oficial para la API de **Wompi El Salvador** (`api.wompi.sv`).

Wompi El Salvador es un producto distinto a Wompi Colombia: se autentica con
OAuth2 Client Credentials en vez de public/private key, y firma sus webhooks
con un esquema HMAC diferente. Este paquete está escrito específicamente
para el producto salvadoreño — si necesitás Wompi Colombia, buscá otro
cliente.

No es un paquete oficial de Wompi ni de Bancolombia. Es mantenido de forma
independiente por [Roque Studios](https://roquestudios.io).

## Instalación

```bash
pip install pywompi
```

Requiere Python 3.9+.

## Quickstart

```python
import os
from pywompi import WompiClient

client = WompiClient(
    client_id=os.environ["WOMPI_CLIENT_ID"],       # App ID del panel de Wompi
    client_secret=os.environ["WOMPI_CLIENT_SECRET"], # API Secret del panel de Wompi
)

enlace = client.create_payment_link({
    "identificadorEnlaceComercio": "PEDIDO-123",
    "nombreProducto": "Consulta CloudClinic",
    "monto": 25.00,
})

print(enlace["urlEnlace"])       # URL para redirigir al cliente
print(enlace["urlQrCodeEnlace"]) # QR del enlace de pago

client.close()
```

O como context manager, para que el cliente HTTP interno se cierre solo:

```python
with WompiClient(client_id=..., client_secret=...) as client:
    transaccion = client.get_transaction("SV-1720800000-abc123")
    print(transaccion["estado"])
```

El access token de OAuth2 se pide y se cachea en memoria automáticamente;
no hace falta manejarlo a mano ni refrescarlo vos mismo.

### Endpoints sin wrappear todavía

`client.py` solo wrappea los endpoints que están confirmados contra la
[documentación oficial](https://docs.wompi.sv) (Enlace de Pago, Transacción
de Compra). Para cualquier otro endpoint del Swagger de Wompi (tokenización,
cargos recurrentes, datos de negocio, etc.), usá el método genérico ya
autenticado:

```python
resultado = client.request("GET", "DatosNegocio")
resultado = client.request("POST", "EnlacePagoRecurrente", json={...})
```

## Validar webhooks

Wompi firma cada webhook con el header `wompi_hash`: el HMAC-SHA256 del
**body crudo** de la petición, usando tu API Secret como llave. **Nunca
proceses un webhook sin validar esta firma primero** — cualquiera puede
mandarte un POST diciendo que una transacción fue aprobada si no la validás.

```python
from pywompi import parse_event, WompiWebhookError

def wompi_webhook_view(request):
    try:
        event = parse_event(
            raw_body=request.body,  # bytes CRUDOS, no el dict ya parseado
            received_hash=request.headers.get("wompi_hash", ""),
            api_secret=os.environ["WOMPI_CLIENT_SECRET"],
        )
    except WompiWebhookError:
        return HttpResponse(status=400)

    if event["data"]["transaccion"]["estado"] == "APROBADA":
        ...
```

⚠️ **El punto que más rompe integraciones:** el HMAC se calcula sobre el
body tal cual llegó por la red, byte por byte. Si tu framework ya parseó
el JSON a un dict y vos hacés `json.dumps()` para "reconstruirlo", el hash
casi seguro **no va a matchear** — el orden de las llaves o los espacios
pueden cambiar en la re-serialización. Siempre usá el body crudo:

| Framework | Cómo obtener el body crudo |
|---|---|
| Django | `request.body` (bytes, antes de acceder a `request.POST`) |
| FastAPI | `await request.body()` (no uses un parámetro `Body()` tipado) |
| Flask | `request.get_data()` |

Ver `examples/` para integraciones completas de Django y FastAPI.

## Ambientes (sandbox / producción)

A diferencia de Wompi Colombia, la documentación pública de Wompi El
Salvador no expone subdominios separados de sandbox/producción — el
ambiente se determina por el App ID / API Secret que uses (cada
aplicativo se marca como productivo o no desde el panel). Si tu cuenta sí
tiene URLs distintas, pasalas explícitamente:

```python
client = WompiClient(
    client_id=...,
    client_secret=...,
    token_url="https://id.wompi.sv/connect/token",
    api_base_url="https://api.wompi.sv",
)
```

## Manejo de errores

```python
from pywompi.exceptions import (
    WompiError,           # excepción base
    WompiAuthError,       # falló la autenticación OAuth2
    WompiConnectionError, # error de red / timeout
    WompiAPIError,        # Wompi respondió con status >= 400
    WompiWebhookError,    # firma de webhook inválida
)

try:
    client.create_payment_link({...})
except WompiAPIError as e:
    print(e.status_code, e.payload)  # inspeccionar la respuesta de error
except WompiConnectionError:
    # reintentar, encolar para retry, etc.
    ...
```

## Desarrollo

```bash
git clone https://github.com/roquestudios/pywompi
cd pywompi
pip install -e ".[dev]"

ruff check .
ruff format .
mypy src/pywompi
pytest
```

## Licencia

GPL — ver [LICENSE](LICENSE).
