Skip to main content

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_webhook ni compute_webhook_signature desde la versión que adopta el flujo sin webhook de Transbank.
  • notify_url es 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)

Source distribution for newdev-tbank 0.3.0
File Size Uploaded
newdev_tbank-0.3.0.tar.gz 12.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for newdev-tbank 0.3.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page