newdev-tbank — Cliente Python para Webpay Plus
Librería cliente para el microservicio de pagos Webpay Plus de NewDev.
Instalación
pip install newdev-tbank
Instalación (desarrollo / sin PyPI)
Si querés instalar el cliente directamente desde el código fuente sin publicarlo a PyPI:
# Opción 1: editable (recomendado para desarrollo)
# Cambios en el source se reflejan al instante
pip install -e /ruta/al/newdev_tbank_client/
# Opción 2: copia normal (no editable)
pip install /ruta/al/newdev_tbank_client/
Después de instalarlo, verificá que importa correctamente:
python -c "from newdev_tbank import WebpayClient; print('OK')"
Esto resuelve las dependencias (requests, pydantic) desde PyPI, pero instala el paquete newdev_tbank desde el directorio local. Ideal para integrar con un proyecto Django existente durante desarrollo o debuggeo.
Uso rápido
from newdev_tbank import WebpayClient, TransactionStatus
client = WebpayClient(
api_key="TU_API_KEY",
base_url="https://api.tubanco.io/webpay",
)
# 1. Crear transacción
tx = client.create_transaction(
buy_order="FAC-2024-001",
amount=5000,
return_url="https://miapp.cl/checkout/success",
meta={"user_id": 42},
)
# 2. Redirigir al usuario a Transbank (agregar token_ws al redirect)
redirect_url = f"{tx.url_redirect}?token_ws={tx.token}"
return redirect(redirect_url)
# 3. Transbank redirige al usuario de vuelta a return_url con token_ws
# 4. El frontend llama al backend con el token_ws; el backend confirma:
result = client.commit_transaction(token_ws)
if result.status == TransactionStatus.AUTHORIZED:
mark_invoice_paid(result.authorization_code)
Flujo completo con Django (ejemplo)
El flujo de Webpay Plus REST no incluye webhooks servidor-a-servidor.
Transbank redirige al usuario al return_url del frontend con un parámetro
de querystring (token_ws o TBK_TOKEN). El frontend debe pasar ese
token al backend, que llama al microservicio para confirmar el pago:
# views.py
from django.http import JsonResponse
from newdev_tbank import WebpayClient, TransactionStatus
client = WebpayClient(
api_key=settings.NEWDEV_TBANK_API_KEY,
base_url=settings.PAGOS_MICROSERVICE_URL,
)
def checkout_success(request):
"""Vista que atiende el return_url de Transbank."""
token_ws = request.GET.get("token_ws") or request.GET.get("TBK_TOKEN")
if not token_ws:
return JsonResponse({"error": "missing token"}, status=400)
result = client.commit_transaction(token_ws)
if result.status == TransactionStatus.AUTHORIZED:
mark_invoice_paid(result.buy_order, result.authorization_code)
return JsonResponse({"ok": True, "status": result.status})
return JsonResponse(
{"ok": False, "status": result.status},
status=402,
)
# urls.py
from django.urls import path
from .views import checkout_success
urlpatterns = [
path("checkout/success/", checkout_success, name="checkout_success"),
]
# views.py (creación de transacción)
def start_payment(request):
tx = client.create_transaction(
buy_order="FAC-2024-001",
amount=5000,
return_url="https://miapp.cl/checkout/success",
)
# Redirigir al usuario a Transbank
redirect_url = f"{tx.url_redirect}?token_ws={tx.token}"
return redirect(redirect_url)
API
WebpayClient
| Método | Descripción |
|---|---|
create_transaction(buy_order, amount, return_url, notify_url=None, ...) |
Crea transacción y retorna token + url_redirect |
commit_transaction(token_ws) |
Confirma el pago con el token_ws recibido de Transbank; retorna TransactionResponse con el status final |
get_transaction(buy_order) |
Consulta los detalles de una transacción (BD local) |
get_status(token_ws) |
Consulta el estado actual de una transacción directamente en Transbank (recuperación/polling) |
capture(buy_order, amount, authorization_code=None) |
Captura una transacción diferida; el microservicio usa el código almacenado si se omite |
refund(buy_order, amount, reason=None) |
Reembolsa una transacción AUTHORIZED |
get_health() |
Healthcheck del microservicio |
Constantes
from newdev_tbank import TransactionStatus
TransactionStatus.INITIALIZED # "INITIALIZED"
TransactionStatus.AUTHORIZED # "AUTHORIZED"
TransactionStatus.CAPTURED # "CAPTURED"
TransactionStatus.REVERSED # "REVERSED"
TransactionStatus.FAILED # "FAILED"
TransactionStatus.NULLIFIED # "NULLIFIED"
TransactionStatus.PARTIALLY_NULLIFIED # "PARTIALLY_NULLIFIED"
TransactionStatus.ERROR # "ERROR"
Errores
| Excepción | Cuándo |
|---|---|
NewDevTbankError |
Base para todas las excepciones |
ApiKeyError |
API-key inválida o expirada (401) |
TransactionNotFoundError |
buy_order no existe (404) |
TransbankError |
Transbank rechazó la transacción |
WebpayServiceError |
Error interno del microservicio |
Integración con el cliente Python
Flujo básico (compra aprobada)
from django.conf import settings
from django.http import JsonResponse
from django.shortcuts import redirect
from newdev_tbank import TransactionStatus, WebpayClient
client = WebpayClient(
api_key=settings.NEWDEV_TBANK_API_KEY,
base_url=settings.PAGOS_MICROSERVICE_URL,
)
# 1. Crear transacción
def start_payment(request):
tx = client.create_transaction(
buy_order="FAC-2024-001",
amount=5000,
return_url="https://miapp.cl/checkout/success",
)
redirect_url = f"{tx.url_redirect}?token_ws={tx.token}"
return redirect(redirect_url)
# 2. Transbank redirige al usuario a return_url con token_ws
# 3. Confirmar el pago
def checkout_success(request):
token_ws = request.GET.get("token_ws") or request.GET.get("TBK_TOKEN")
if not token_ws:
return JsonResponse({"error": "missing token"}, status=400)
result = client.commit_transaction(token_ws)
if result.status == TransactionStatus.AUTHORIZED:
mark_invoice_paid(result.buy_order, result.authorization_code)
return JsonResponse({"ok": True, "status": result.status})
# FAILED, REVERSED o ERROR
return JsonResponse(
{"ok": False, "status": result.status}, status=402
)
# urls.py
urlpatterns = [
path("checkout/start/", start_payment, name="start_payment"),
path("checkout/success/", checkout_success, name="checkout_success"),
]
Flujo de captura diferida
Para commerce codes configurados para captura diferida, el monto autorizado se captura de forma explícita. El amount debe ser exactamente igual al monto original autorizado.
from newdev_tbank import TransactionStatus, WebpayClient
client = WebpayClient(
api_key=settings.NEWDEV_TBANK_API_KEY,
base_url=settings.PAGOS_MICROSERVICE_URL,
)
def capture_payment(request):
token_ws = request.GET.get("token_ws")
if not token_ws:
return JsonResponse({"error": "missing token"}, status=400)
# 1. Confirmar el pago (obtiene authorization_code)
result = client.commit_transaction(token_ws)
if result.status != TransactionStatus.AUTHORIZED:
return JsonResponse({"ok": False, "status": result.status}, status=402)
# 2. Capturar el monto exacto autorizado
captured = client.capture(
buy_order=result.buy_order,
amount=result.amount, # debe ser == al monto original autorizado
)
return JsonResponse({
"ok": True,
"captured_amount": captured.captured_amount,
"authorization_date": captured.authorization_date,
})
Flujo de polling / recuperación ante error
Si el servicio estaba caído cuando el usuario volvió de Transbank, se puede consultar el estado directamente y luego confirmar:
from newdev_tbank import TransactionStatus, WebpayClient
client = WebpayClient(
api_key=settings.NEWDEV_TBANK_API_KEY,
base_url=settings.PAGOS_MICROSERVICE_URL,
)
def recover_transaction(token_ws):
# 1. Consultar estado directamente en Transbank
status = client.get_status(token_ws)
if status.status == TransactionStatus.AUTHORIZED:
if status.captured_amount is not None:
print(f"Ya capturado: {status.captured_amount}")
elif status.authorization_code:
# Capturar si la transacción es diferida
client.capture(
buy_order=status.buy_order,
amount=status.amount,
authorization_code=status.authorization_code,
)
# 2. Normalizar el estado local con commit
result = client.commit_transaction(token_ws)
return result
Manejo de errores
from newdev_tbank.exceptions import (
ApiKeyError,
TransactionNotFoundError,
TransbankError,
WebpayServiceError,
)
try:
result = client.commit_transaction(token_ws)
except ApiKeyError:
# API key inválida
...
except TransactionNotFoundError:
# La transacción no existe
...
except TransbankError as exc:
# Transbank rechazó la operación
print(f"Transbank error: {exc} (code: {exc.response_code})")
except WebpayServiceError as exc:
# Error interno del microservicio
print(f"Service error: {exc}")
Testing utilities
El paquete incluye newdev_tbank.testing con utilidades para testear sin
tocar la red ni mockgear con MagicMock genérico. Las responses siguen
validando tipos gracias a los modelos Pydantic reales.
FakeWebpayClient
Doble in-memory de WebpayClient con la misma interfaz (create_transaction,
commit_transaction, get_transaction, get_status, capture, refund,
get_health). Ideal para testear código que consume el cliente.
from newdev_tbank import TransactionStatus
from newdev_tbank.exceptions import TransactionNotFoundError
from newdev_tbank.testing import FakeWebpayClient
fake = FakeWebpayClient()
# Simula la creación de una transacción
tx = fake.create_transaction(
buy_order="FAC-001",
amount=5000,
return_url="https://miapp.cl/ok",
)
assert tx.token == "token_FAC-001"
# Simula el commit (confirmación del pago)
fake.set_status("FAC-001", "AUTHORIZED", authorization_code="123456")
result = fake.commit_transaction("token_FAC-001")
assert result.status == TransactionStatus.AUTHORIZED
assert result.authorization_code == "123456"
# Consultar el estado directamente en Transbank (status polling)
status = fake.get_status("token_FAC-001")
assert status.status == TransactionStatus.AUTHORIZED
# Capturar una transacción diferida
captured = fake.capture("FAC-001", amount=3000, authorization_code="123456")
assert captured.captured_amount == 3000
# Las excepciones son idénticas al cliente real
import pytest
with pytest.raises(TransactionNotFoundError):
fake.get_transaction("NOPE")
fake.reset() # limpia el estado entre tests
Factories de respuestas
Helpers para construir rápidamente respuestas Pydantic reales:
from newdev_tbank.testing import (
make_capture_response,
make_create_transaction_response,
make_refund_response,
make_status_response,
make_transaction_response,
make_transaction_status_response,
)
make_create_transaction_response("FAC-001", url_redirect="https://x.cl/pay")
make_transaction_status_response("FAC-001", status="AUTHORIZED", amount=5000)
make_status_response("FAC-001", status="AUTHORIZED", authorization_code="123456")
make_capture_response("FAC-001", captured_amount=5000, authorization_code="123456")
make_refund_response("FAC-001", amount=2500)
make_transaction_response("FAC-001", status="AUTHORIZED")
Notas
- El flujo no incluye webhooks: el cliente no expone
verify_webhooknicompute_webhook_signaturedesde la versión que adopta el flujo sin webhook de Transbank. notify_urles opcional; no se despacha ninguna notificación.
Desarrollo
# Instalar dependencias de desarrollo
pip install -e ".[dev]"
# Tests
pytest tests/ -v --cov=newdev_tbank
# Lint
ruff check src/
Publicar a PyPI
python -m build
twine check dist/*
twine upload dist/*
Licencia
MIT
Release files for newdev-tbank 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| newdev_tbank-0.3.0.tar.gz | 12.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| newdev_tbank-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.5 kB
Release files / newdev_tbank-0.3.0.tar.gz
| Download URL | newdev_tbank-0.3.0.tar.gz |
|---|---|
| Size | 12.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0b1b61e41f6835ec574e43028a0cc232624834bbc7a0a619de3eb1000981b37b
|
|
BLAKE2b-256 checksum How to use checksums |
1073f53879a520cc6ccedb6ca65cef71494b4e47a221463fa554dd36695e2db0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / newdev_tbank-0.3.0-py3-none-any.whl
| Download URL | newdev_tbank-0.3.0-py3-none-any.whl |
|---|---|
| Size | 12.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9432602752329a841ea7c1835d9ebcddbbd838db9b6fb4e1fbd817ad04416dc7
|
|
BLAKE2b-256 checksum How to use checksums |
859ab3c1bd44b25ad884e918b6dc57f8900c461fd2fc9ccaae7293f33d560a96
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|