Saltar a contenido

WebpayProvider

WebpayProvider

Bases: BasicProvider

WebpayProvider es una clase que proporciona integración con Transbank para procesar pagos. Inicializa una instancia de WebpayProvider con el key y el secreto de Transbank.

Parameters:

Name Type Description Default
api_key_id str

ApiKey entregada por Transbank.

required
api_key_secret str

ApiSecret entregada por Transbank.

required
api_endpoint str

Ambiente Transbank, puede ser "produccion" o "integracion" (Valor por defecto: produccion)

'produccion'
**kwargs int

Argumentos adicionales.

{}
Source code in django_payments_chile/WebpayProvider.py
class WebpayProvider(BasicProvider):
    """
    WebpayProvider es una clase que proporciona integración con Transbank para procesar pagos.
    Inicializa una instancia de WebpayProvider con el key y el secreto de Transbank.

    Args:
        api_key_id (str): ApiKey entregada por Transbank.
        api_key_secret (str): ApiSecret entregada por Transbank.
        api_endpoint (str): Ambiente Transbank, puede ser "produccion" o "integracion" (Valor por defecto: produccion)
        **kwargs: Argumentos adicionales.
    """

    form_class = BasePaymentForm
    api_endpoint: str
    api_key_id: str = None
    api_key_secret: str = None

    def __init__(
        self,
        api_key_id: str,
        api_key_secret: str,
        api_endpoint: str = "produccion",
        **kwargs: int,
    ):
        super().__init__(**kwargs)
        self.api_endpoint = api_endpoint
        self.api_key_id = api_key_id
        self.api_key_secret = api_key_secret
        if self.api_endpoint == "produccion":
            self.api_endpoint = "https://webpay3g.transbank.cl/"
        elif self.api_endpoint == "integracion":
            self.api_endpoint = "https://webpay3gint.transbank.cl/"

    def get_form(self, payment, data: Optional[dict] = None) -> Any:
        """
        Genera el formulario de pago para redirigir a la página de pago.

        Args:
            payment ("Payment"): Objeto de pago Django Payments.
            data (dict | None): Datos del formulario (opcional).

        Returns:
            Any: Formulario de pago redirigido a la página de pago.

        Raises:
            RedirectNeeded: Redirige a la página de pago.

        """
        if not payment.transaction_id:
            datos_para_tbk = {
                "buy_order": str(payment.token),
                "session": str(payment.token),
                "return_url": payment.get_process_url(),
                "amount": int(payment.total),
            }

            try:
                pago_req = requests.post(
                    f"{self.api_endpoint} /rswebpaytransaction/api/webpay/v1.2/transactions",
                    data=datos_para_tbk,
                    timeout=5,
                )
                pago_req.raise_for_status()

            except Exception as pe:
                payment.change_status(PaymentStatus.ERROR, str(pe))
                raise PaymentError(pe)
            else:
                pago = pago_req.json()
                payment.transaction_id = pago["token"]
                payment.attrs.request_tbk = datos_para_tbk
                payment.attrs.respuesta_tbk = pago
                payment.save()
                payment.change_status(PaymentStatus.PREAUTH)

            raise RedirectNeeded(f"{pago['url']}?token_ws={pago['token']}")

    def genera_headers(self):
        return {
            "Content-Type": "application/json",
            "Tbk-Api-Key-Id": self.api_key_id,
            "Tbk-Api-Key-Secret": self.api_key_secret,
        }

    def process_data(self, payment, request) -> JsonResponse:
        """
        Procesa la captura del pago
        Usuario deberia volver acá y luego a la pagina de muestra de informacion.

        Args:
            payment ("Payment"): Objeto de pago Django Payments.
            request ("HttpRequest"): Objeto de solicitud HTTP de Django.

        Returns:
            JsonResponse: Respuesta JSON que indica el procesamiento de los datos del pago.

        """

        if payment.status in [PaymentStatus.WAITING, PaymentStatus.PREAUTH]:
            self.commit(self.get_token_from_request(None, payment), payment)

    def get_token_from_request(self, payment, request) -> str:
        """Return payment token from provider request."""

        try:
            return request.POST["token_ws"] or request.GET["token_ws"]
        except Exception as e:
            raise PaymentError(
                code=400,
                message="token_ws is not present",
            ) from e

    def actualiza_estado(self, payment) -> dict:
        """Actualiza el estado del pago con Flow

        Args:
            payment ("Payment): Objeto de pago Django Payments.

        Returns:
            dict: Diccionario con valores del objeto `PaymentStatus`.
        """

        try:
            status_req = requests.put(
                f"{self.api_endpoint}/rswebpaytransaction/api/webpay/v1.2/transactions/{payment.token}",
                timeout=5,
                headers=self.genera_headers(),
            )
            status_req.raise_for_status()
        except Exception as e:
            raise e
        else:
            status = status_req.json()
            payment.attrs.status_response = status
            payment.save()

            if status["response_code"] == 0:
                payment.change_status(PaymentStatus.CONFIRMED)
                return PaymentStatus.CONFIRMED
            else:
                payment.change_status(PaymentStatus.REJECTED)
                return PaymentStatus.REJECTED

    def commit(self, token, payment):
        """Se debe llamar al procesar el retorno"""
        try:
            commit_req = requests.put(
                f"{self.api_endpoint}/rswebpaytransaction/api/webpay/v1.2/transactions/{token}",
                timeout=5,
                headers=self.genera_headers(),
            )
            commit_req.raise_for_status()
        except Exception as e:
            raise e
        else:
            commit = commit_req.json()
            commit["vci_str"] = self.agrega_info_error("vci", commit["vci"])
            commit["payment_type_code_str"] = self.agrega_info_error("pago", commit["payment_type_code"])
            payment.attrs.commit_response = commit
            payment.save()
            if commit["status"] == "AUTHORIZED" and commit["response_code"] == 0:
                raise RedirectNeeded("success")
            else:
                raise RedirectNeeded("error")

    def refund(self, payment, amount: Optional[int] = None) -> int:
        """
        Realiza un reembolso del pago.
        El seguimiendo se debe hacer directamente en Flow

        Args:
            payment ("Payment"): Objeto de pago Django Payments.
            amount (int | None): Monto a reembolsar (opcional).

        Returns:
            int: Monto de reembolso solicitado.

        Raises:
            PaymentError: Error al crear el reembolso.

        """
        if payment.status != PaymentStatus.CONFIRMED:
            raise PaymentError("El pago debe estar confirmado para reversarse.")

        refund_data = {"amount": amount or payment.total}
        try:
            refund_req = requests.put(
                f"{self.api_endpoint}/rswebpaytransaction/api/webpay/v1.2/transactions/{payment.token}/refunds",
                timeout=5,
                headers=self.genera_headers(),
                data=refund_data,
            )
            refund_req.raise_for_status()
        except Exception as e:
            raise e
        else:
            refund = refund_req.json()
            refund["response_code_str"] = self.agrega_info_error("refund", refund["response_code"])
            payment.attrs.refund_response = refund
            payment.save()

            if refund["type"] == "REVERSED":
                payment.change_status(PaymentStatus.REFUNDED)
                return payment.total
            elif refund["type"] == "NULLIFIED" and refund["response_code"] == 0:
                payment.change_status(PaymentStatus.REFUNDED)
                return refund["nullified_amount"]

    def agrega_info_error(self, tipo, codigo):
        if tipo == "vci":
            return vci_status.get(codigo, None)
        elif tipo == "pago":
            return tipo_de_pagos.get(codigo, None)
        elif tipo == "rechazo_l1":
            return codigos_rechazo_nivel_1.get(codigo, None)
        elif tipo == "refund":
            return codigo_rechazo_refund.get(codigo, None)
        else:
            return None

actualiza_estado(payment)

Actualiza el estado del pago con Flow

Parameters:

Name Type Description Default
payment "Payment

Objeto de pago Django Payments.

required

Returns:

Name Type Description
dict dict

Diccionario con valores del objeto PaymentStatus.

Source code in django_payments_chile/WebpayProvider.py
def actualiza_estado(self, payment) -> dict:
    """Actualiza el estado del pago con Flow

    Args:
        payment ("Payment): Objeto de pago Django Payments.

    Returns:
        dict: Diccionario con valores del objeto `PaymentStatus`.
    """

    try:
        status_req = requests.put(
            f"{self.api_endpoint}/rswebpaytransaction/api/webpay/v1.2/transactions/{payment.token}",
            timeout=5,
            headers=self.genera_headers(),
        )
        status_req.raise_for_status()
    except Exception as e:
        raise e
    else:
        status = status_req.json()
        payment.attrs.status_response = status
        payment.save()

        if status["response_code"] == 0:
            payment.change_status(PaymentStatus.CONFIRMED)
            return PaymentStatus.CONFIRMED
        else:
            payment.change_status(PaymentStatus.REJECTED)
            return PaymentStatus.REJECTED

commit(token, payment)

Se debe llamar al procesar el retorno

Source code in django_payments_chile/WebpayProvider.py
def commit(self, token, payment):
    """Se debe llamar al procesar el retorno"""
    try:
        commit_req = requests.put(
            f"{self.api_endpoint}/rswebpaytransaction/api/webpay/v1.2/transactions/{token}",
            timeout=5,
            headers=self.genera_headers(),
        )
        commit_req.raise_for_status()
    except Exception as e:
        raise e
    else:
        commit = commit_req.json()
        commit["vci_str"] = self.agrega_info_error("vci", commit["vci"])
        commit["payment_type_code_str"] = self.agrega_info_error("pago", commit["payment_type_code"])
        payment.attrs.commit_response = commit
        payment.save()
        if commit["status"] == "AUTHORIZED" and commit["response_code"] == 0:
            raise RedirectNeeded("success")
        else:
            raise RedirectNeeded("error")

get_form(payment, data=None)

Genera el formulario de pago para redirigir a la página de pago.

Parameters:

Name Type Description Default
payment Payment

Objeto de pago Django Payments.

required
data dict | None

Datos del formulario (opcional).

None

Returns:

Name Type Description
Any Any

Formulario de pago redirigido a la página de pago.

Raises:

Type Description
RedirectNeeded

Redirige a la página de pago.

Source code in django_payments_chile/WebpayProvider.py
def get_form(self, payment, data: Optional[dict] = None) -> Any:
    """
    Genera el formulario de pago para redirigir a la página de pago.

    Args:
        payment ("Payment"): Objeto de pago Django Payments.
        data (dict | None): Datos del formulario (opcional).

    Returns:
        Any: Formulario de pago redirigido a la página de pago.

    Raises:
        RedirectNeeded: Redirige a la página de pago.

    """
    if not payment.transaction_id:
        datos_para_tbk = {
            "buy_order": str(payment.token),
            "session": str(payment.token),
            "return_url": payment.get_process_url(),
            "amount": int(payment.total),
        }

        try:
            pago_req = requests.post(
                f"{self.api_endpoint} /rswebpaytransaction/api/webpay/v1.2/transactions",
                data=datos_para_tbk,
                timeout=5,
            )
            pago_req.raise_for_status()

        except Exception as pe:
            payment.change_status(PaymentStatus.ERROR, str(pe))
            raise PaymentError(pe)
        else:
            pago = pago_req.json()
            payment.transaction_id = pago["token"]
            payment.attrs.request_tbk = datos_para_tbk
            payment.attrs.respuesta_tbk = pago
            payment.save()
            payment.change_status(PaymentStatus.PREAUTH)

        raise RedirectNeeded(f"{pago['url']}?token_ws={pago['token']}")

get_token_from_request(payment, request)

Return payment token from provider request.

Source code in django_payments_chile/WebpayProvider.py
def get_token_from_request(self, payment, request) -> str:
    """Return payment token from provider request."""

    try:
        return request.POST["token_ws"] or request.GET["token_ws"]
    except Exception as e:
        raise PaymentError(
            code=400,
            message="token_ws is not present",
        ) from e

process_data(payment, request)

Procesa la captura del pago Usuario deberia volver acá y luego a la pagina de muestra de informacion.

Parameters:

Name Type Description Default
payment Payment

Objeto de pago Django Payments.

required
request HttpRequest

Objeto de solicitud HTTP de Django.

required

Returns:

Name Type Description
JsonResponse JsonResponse

Respuesta JSON que indica el procesamiento de los datos del pago.

Source code in django_payments_chile/WebpayProvider.py
def process_data(self, payment, request) -> JsonResponse:
    """
    Procesa la captura del pago
    Usuario deberia volver acá y luego a la pagina de muestra de informacion.

    Args:
        payment ("Payment"): Objeto de pago Django Payments.
        request ("HttpRequest"): Objeto de solicitud HTTP de Django.

    Returns:
        JsonResponse: Respuesta JSON que indica el procesamiento de los datos del pago.

    """

    if payment.status in [PaymentStatus.WAITING, PaymentStatus.PREAUTH]:
        self.commit(self.get_token_from_request(None, payment), payment)

refund(payment, amount=None)

Realiza un reembolso del pago. El seguimiendo se debe hacer directamente en Flow

Parameters:

Name Type Description Default
payment Payment

Objeto de pago Django Payments.

required
amount int | None

Monto a reembolsar (opcional).

None

Returns:

Name Type Description
int int

Monto de reembolso solicitado.

Raises:

Type Description
PaymentError

Error al crear el reembolso.

Source code in django_payments_chile/WebpayProvider.py
def refund(self, payment, amount: Optional[int] = None) -> int:
    """
    Realiza un reembolso del pago.
    El seguimiendo se debe hacer directamente en Flow

    Args:
        payment ("Payment"): Objeto de pago Django Payments.
        amount (int | None): Monto a reembolsar (opcional).

    Returns:
        int: Monto de reembolso solicitado.

    Raises:
        PaymentError: Error al crear el reembolso.

    """
    if payment.status != PaymentStatus.CONFIRMED:
        raise PaymentError("El pago debe estar confirmado para reversarse.")

    refund_data = {"amount": amount or payment.total}
    try:
        refund_req = requests.put(
            f"{self.api_endpoint}/rswebpaytransaction/api/webpay/v1.2/transactions/{payment.token}/refunds",
            timeout=5,
            headers=self.genera_headers(),
            data=refund_data,
        )
        refund_req.raise_for_status()
    except Exception as e:
        raise e
    else:
        refund = refund_req.json()
        refund["response_code_str"] = self.agrega_info_error("refund", refund["response_code"])
        payment.attrs.refund_response = refund
        payment.save()

        if refund["type"] == "REVERSED":
            payment.change_status(PaymentStatus.REFUNDED)
            return payment.total
        elif refund["type"] == "NULLIFIED" and refund["response_code"] == 0:
            payment.change_status(PaymentStatus.REFUNDED)
            return refund["nullified_amount"]