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 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