Source code for salesman.checkout.payment

from typing import List, Optional

from django.core.exceptions import ValidationError
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _

from salesman.basket.models import Basket
from salesman.conf import app_settings
from salesman.orders.models import Order, OrderPayment


[docs]class PaymentError(Exception): """ Payment error for raising payment related exceptions. """
[docs]class PaymentMethod(object): """ Base payment method, all payment methods defined in ``SALESMAN_PAYMENT_METHODS`` must extend this class. """ identifier = None label = None
[docs] def get_urls(self) -> list: """ Hook for adding extra url patterns for payment method. Urls will be included as child patterns under the defined identifier namespace => ``/payment/{identifier}/{urls}``. """ return []
[docs] def validate_basket(self, basket: Basket, request: HttpRequest) -> None: """ Method used to validate that payment method is valid for the given basket. Args: basket (Basket): Basket instance request (HttpRequest): Django request Raises: ValidationError: If payment is not valid for basket """ if not basket.count: raise ValidationError(_("Your basket is empty."))
[docs] def validate_order(self, order: Order, request: HttpRequest) -> None: """ Method used to validate that payment method is valid for the given order. Args: order (Order): Order instance request (HttpRequest): Django request Raises: ValidationError: If payment is not valid for order """ if order.is_paid: raise ValidationError(_("This order has already been paid for.")) if order.status not in order.statuses.get_payable(): msg = _("Payment for order with status '{status}' is not allowed.") raise ValidationError(msg.format(status=order.status_display))
[docs] def basket_payment(self, basket: Basket, request: HttpRequest) -> str: """ This method gets called when new checkout is submitted and is responsible for creating a new order from given basket. Should return the redirect url to either the next payment step or the order success page. Raise ``PaymentError`` in case an issue appears. Args: basket (Basket): Basket instance request (HttpRequest): Django request Raises: PaymentError: If error with payment occurs Returns: str: Redirect url to the next step """ raise NotImplementedError("Method `basket_payment()` is not implemented.")
[docs] def order_payment(self, order: Order, request: HttpRequest) -> str: """ This method gets called when payment for an existing order is requested. Should return the redirect url to either the next payment step or the order success page. Raise ``PaymentError`` in case an issue appears. Args: order (Order): Order instance request (HttpRequest): Django request Raises: PaymentError: If error with payment occurs Returns: str: Redirect url to the next step """ raise NotImplementedError("Method `order_payment()` is not implemented.")
[docs] def refund_payment(self, payment: OrderPayment) -> bool: """ This method gets called when orders payment refund is requested. Should return True if refund was completed. Args: payment (OrderPayment): Order payment instance Returns: bool: True if refund was completed """ return False
[docs]class PaymentMethodsPool(object): """ Pool for storing payment method instances. """ _payments = None
[docs] def get_payments(self, kind: Optional[str] = None) -> List[PaymentMethod]: """ Returns payment method instances. Args: kind (Optional[str], optional): Either basket or order. Defaults to None. Returns: List[PaymentMethod]: Payment method instances """ if not self._payments: self._payments = [P() for P in app_settings.SALESMAN_PAYMENT_METHODS] if kind in ['basket', 'order']: method = f'{kind}_payment' return [p for p in self._payments if method in p.__class__.__dict__] return self._payments
[docs] def get_choices(self, kind: Optional[str] = None) -> list: """ Return payments formated as choices list of tuples. Args: kind (Optional[str], optional): Either basket or order. Defaults to None. Returns: list: List of choices """ return [(p.identifier, p.label) for p in self.get_payments(kind=kind)]
[docs] def get_payment(self, identifier: str, kind: Optional[str] = None) -> PaymentMethod: """ Returns payment with given identifier. Args: identifier (str): Payment identifier kind (Optional[str], optional): Either basket or order. Defaults to None. Returns: PaymentMethod: Payment method instance """ for payment in self.get_payments(kind=kind): if payment.identifier == identifier: return payment
payment_methods_pool = PaymentMethodsPool()