Source code for debits.debits_base.processors

import debits.debits_base
from debits.debits_base.models import SubscriptionItem
import abc
import datetime
from django.http import HttpResponse
try:
    from html import escape  # python 3.x
except ImportError:
    from cgi import escape  # python 2.x
import debits.debits_base


[docs]def hidden_field(f, v): """Internal.""" return "<input type='hidden' name='%s' value='%s'/>" % (escape(f), escape(v))
[docs]class BasePaymentProcessor(abc.ABC): """Executing a transaction for a particular payment processor (by a derived class). We receive a derivative of :class:`~debits.debits_base.models.BaseTransaction` object and a hash (see for example PayPay documentation) from user. Then the hash is amended (for example added the price from the transaction object) and passed to the payment processor. """
[docs] @abc.abstractmethod def amend_hash_new_purchase(self, transaction, hash): """Internal.""" pass
[docs] def amend_hash_change_subscription(self, transaction, hash): """Internal.""" raise NotImplementedError()
[docs] def change_subscription(self, transaction, hash): """Start the process of changing a subscription with given hash and transaction.""" hash = self.amend_hash_change_subscription(transaction, hash) return self.redirect_to_processor(hash)
[docs] def make_purchase(self, hash, transaction): """Start the process of purchase with given hash and transaction.""" hash = self.amend_hash_new_purchase(transaction, hash) return self.redirect_to_processor(hash)
[docs] def make_purchase_from_form(self, hash, transaction): """Start the process of purchase with hash received from a HTML form and transaction.""" hash = dict(hash) if 'csrfmiddlewaretoken' in hash: del hash['csrfmiddlewaretoken'] # immediately before redirect to the processor return self.make_purchase(hash, transaction)
[docs] def change_subscription_from_form(self, hash): """Start the process of changing a subscription with hash received from a HTML form and transaction.""" hash = dict(hash) transaction = debits.debits_base.models.Item.objects.get(hash['arcamens_purchaseid']) del hash['arcamens_purchaseid'] hash = self.amend_hash_change_subscription(transaction, hash) return self.change_subscription(transaction, hash)
[docs] def redirect_to_processor(self, hash): """Internal.""" return HttpResponse(BasePaymentProcessor.html(hash))
# Internal # Use this instead of a redirect because we prefer POST over GET
[docs] @staticmethod def html(hash): """Internal.""" action = escape(hash['arcamens_action']) del hash['arcamens_action'] # The following code works even if this Redirect is loaded inside a HTML tag return "<html><head><meta charset='utf-8'' /></head>\n" +\ "<body>\n<p>Redirecting...</p>\n" + \ "<form method='post' action='"+action+"'>\n" + \ '\n'.join([hidden_field(i[0], str(i[1])) for i in hash.items()]) + \ "\n</form>\n" + \ "<script>document.forms[0].submit();</script>\n" + \ "</body></html>"
[docs] def ready_for_subscription(self, transaction): """Check if ready for subscription. If we are in manual recurring mode, we can be not ready for subscription, because some payment processors (PayPal) don't allow to delay the first payment of a subscription for more than :meth:`self.subscription_allowed_date` days.""" return datetime.date.today() >= self.subscription_allowed_date(transaction)
[docs] @abc.abstractmethod def subscription_allowed_date(self, transaction): """See :meth:`ready_for_subscription`.""" pass
[docs] def product_name(self, purchase): """Internal.""" return purchase.item.product.name
PAYMENT_PROCESSOR_AVANGATE = 1 PAYMENT_PROCESSOR_PAYPAL = 2 PAYMENT_PROCESSOR_BRAINTREE = 3 PAYMENT_PROCESSOR_DALPAY = 4 PAYMENT_PROCESSOR_RECURLY = 5
[docs]class PaymentCallback(object): """Mixin this class to make callbacks on a payment. In current implementation, :meth:`on_subscription_created` may be called when it was already started and :meth:`on_subscription_canceled` may be called when it is already stopped. (In other words, they can be called multiple times in a row.) """
[docs] def on_payment(self, payment): """Called on any payment (subscription or regular).""" pass
# def on_upgrade_subscription(self, transaction, old_subscription): # pass
[docs] def on_subscription_created(self, POST, subscription): """Called when a subscription is created.""" pass
[docs] def on_subscription_canceled(self, POST, subscription): """Called when a subscription is canceled.""" pass