Fundraising in PDF.
[wolnelektury.git] / src / club / payment_methods.py
index 363d445..98c138d 100644 (file)
+# This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
+#
+from hashlib import sha256
+from django.conf import settings
 from django.urls import reverse
+from django.utils.timezone import now
+from django.utils.translation import get_language
+from paypal.rest import agreement_approval_url
+from django.template.loader import render_to_string
+from .payu import POSS
 
 
 class PaymentMethod(object):
+    is_onetime = False
     is_recurring = False
+    expiration_reliable = False
+    cancellable = False
+    updateable = False
 
-    @classmethod
-    def get_payment_url(cls, schedule):
-        return reverse('club_dummy_payment', args=[schedule.key])
+    def initiate(self, request, schedule):
+        raise NotImplementedError
 
 
 class PayU(PaymentMethod):
+    is_onetime = True
+    expiration_reliable = True
     slug = 'payu'
     name = 'PayU'
-    template_name = 'club/payment/payu.html'
 
-    @classmethod
-    def get_payment_url(cls, schedule):
-        return reverse('club_dummy_payment', args=[schedule.key])
+    def __init__(self, pos_id):
+        self.pos_id = pos_id
+
+    def invite_widget(self, schedule, request):
+        return render_to_string(
+            'club/payment/payu_invite.html',
+            {
+                'schedule': schedule,
+            },
+            request=request
+        )
+
+    def initiate(self, request, schedule):
+        # Create Order at once.
+        from .models import PayUOrder
+        order = PayUOrder.objects.create(
+            pos_id=self.pos_id,
+            customer_ip=request.META['REMOTE_ADDR'],
+            schedule=schedule,
+        )
+        return order.put()
 
 
 class PayURe(PaymentMethod):
-    slug='payu-re'
-    name = 'PayU Recurring'
-    template_name = 'club/payment/payu-re.html'
+    slug = 'payu-re'
+    name = 'PayU recurring'
     is_recurring = True
+    expiration_reliable = True
+    cancellable = True
+    updateable = True
+
+    def __init__(self, pos_id):
+        self.pos_id = pos_id
+
+    def initiate(self, request, schedule):
+        return reverse('club_payu_rec_payment', args=[schedule.key])
+
+    def invite_widget(self, schedule, request):
+        from . import forms
+        pos = POSS[self.pos_id]
+        widget_args = {
+            'merchant-pos-id': pos.pos_id,
+            'shop-name': "SHOW NAME",
+            'total-amount': str(int(schedule.amount * 100)),
+            'currency-code': pos.currency_code,
+            'customer-language': get_language(), # filter to pos.languages
+            'customer-email': schedule.email,
+            'store-card': 'true',
+            'recurring-payment': 'true',
+        }
+        widget_sig = sha256(
+            (
+                "".join(v for (k, v) in sorted(widget_args.items())) +
+                pos.secondary_key
+            ).encode('utf-8')
+        ).hexdigest()
+        
+        return render_to_string(
+            'payu/rec_widget.html',
+            {
+                'schedule': schedule,
+                'form': forms.PayUCardTokenForm(),
+                'pos': POSS[self.pos_id],
+                'widget_args': widget_args,
+                'widget_sig': widget_sig,
+            },
+            request=request
+        )
+
+    def pay(self, request, schedule):
+        # Create order, put it and see what happens next.
+        from .models import PayUOrder
+        if request is not None:
+            ip = request.META['REMOTE_ADDR']
+        else:
+            ip = '127.0.0.1'
+
+        if request is None:
+            if not self.needs_retry(schedule):
+                return
+            
+        order = PayUOrder.objects.create(
+            pos_id=self.pos_id,
+            customer_ip=ip,
+            schedule=schedule,
+        )
+        return order.put()
 
-    @classmethod
-    def get_payment_url(cls, schedule):
-        return reverse('club_dummy_payment', args=[schedule.key])
+    def needs_retry(self, schedule):
+        retry_last = schedule.payuorder_set.exclude(
+            created_at=None).order_by('-created_at').first()
+        if retry_last is None:
+            return True
 
+        n = now().date()
+        days_since_last = (n - retry_last.created_at.date()).days
 
-class PayPalRe(PaymentMethod):
-    slug='paypal-re'
-    name = 'PayPal Recurring'
-    template_name = 'club/payment/paypal-re.html'
+        retry_start = max(
+            schedule.expires_at.date(),
+            settings.CLUB_RETRIES_START
+        )
+        retry_days = (n - retry_start).days
+        
+        if retry_days > settings.CLUB_RETRY_DAYS_MAX:
+            print('expired')
+            return False
+        if retry_days > settings.CLUB_RETRY_DAYS_DAILY:
+            print('retry less often now')
+            return days_since_last > settings.CLUB_RETRY_LESS
+        return days_since_last > 0
+
+
+class PayPal(PaymentMethod):
+    slug = 'paypal'
+    name = 'PayPal'
     is_recurring = True
+    is_onetime = False
+
+    def invite_widget(self, schedule, request):
+        if settings.PAYPAL_ENABLED:
+            return render_to_string(
+                'club/payment/paypal_invite.html',
+                {
+                    'schedule': schedule,
+                },
+                request=request
+            )
+        else:
+            return ''
+    
+    def initiate(self, request, schedule):
+        app = request.GET.get('app')
+        return agreement_approval_url(schedule.amount, schedule.key, app=app)
+
+    def pay(self, request, schedule):
+        from datetime import date, timedelta, datetime, timezone
+        tomorrow = datetime(*(date.today() + timedelta(2)).timetuple()[:3], tzinfo=timezone.utc)
+        any_active = False
+        for ba in schedule.billingagreement_set.all():
+            active = ba.check_agreement()
+            ba.active = active
+            ba.save()
+            if active:
+                any_active = True
+        if any_active:
+            schedule.expires_at = tomorrow
+            schedule.save()
+
+
+methods = []
+
+pos = getattr(settings, 'CLUB_PAYU_RECURRING_POS', None)
+if pos:
+    recurring_payment_method = PayURe(pos)
+    methods.append(recurring_payment_method)
+else:
+    recurring_payment_method = None
 
-    @classmethod
-    def get_payment_url(cls, schedule):
-        return reverse('club_dummy_payment', args=[schedule.key])
+pos = getattr(settings, 'CLUB_PAYU_POS', None)
+if pos:
+    single_payment_method = PayU(pos)
+    methods.append(single_payment_method)
+else:
+    single_payment_method = None
 
 
-methods = [
-    PayU,
-    PayURe,
-    PayPalRe,
-]
 
-method_by_slug = {
-    m.slug: m
-    for m in methods
-}
+methods.append(
+    PayPal()
+)