Education.
[wolnelektury.git] / src / club / payment_methods.py
1 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
3 #
4 from hashlib import sha256
5 from django.conf import settings
6 from django.urls import reverse
7 from django.utils.timezone import now
8 from django.utils.translation import get_language
9 from paypal.rest import agreement_approval_url
10 from django.template.loader import render_to_string
11 from .payu import POSS
12
13
14 class PaymentMethod(object):
15     is_onetime = False
16     is_recurring = False
17     expiration_reliable = False
18     cancellable = False
19     updateable = False
20
21     def initiate(self, request, schedule):
22         return reverse('club_dummy_payment', args=[schedule.key])
23
24
25 class PayU(PaymentMethod):
26     is_onetime = True
27     expiration_reliable = True
28     slug = 'payu'
29     name = 'PayU'
30     template_name = 'club/payment/payu.html'
31
32     def __init__(self, pos_id):
33         self.pos_id = pos_id
34
35     def invite_widget(self, schedule):
36         return render_to_string(
37             'club/payment/payu_invite.html',
38             {
39                 'schedule': schedule,
40             }
41         )
42
43     def initiate(self, request, schedule):
44         # Create Order at once.
45         from .models import PayUOrder
46         order = PayUOrder.objects.create(
47             pos_id=self.pos_id,
48             customer_ip=request.META['REMOTE_ADDR'],
49             schedule=schedule,
50         )
51         return order.put()
52
53
54 class PayURe(PaymentMethod):
55     slug = 'payu-re'
56     name = 'PayU recurring'
57     template_name = 'club/payment/payu-re.html'
58     is_recurring = True
59     expiration_reliable = True
60     cancellable = True
61     updateable = True
62
63     def __init__(self, pos_id):
64         self.pos_id = pos_id
65
66     def initiate(self, request, schedule):
67         return reverse('club_payu_rec_payment', args=[schedule.key])
68
69     def invite_widget(self, schedule):
70         from . import forms
71         pos = POSS[self.pos_id]
72         widget_args = {
73             'merchant-pos-id': pos.pos_id,
74             'shop-name': "SHOW NAME",
75             'total-amount': str(int(schedule.amount * 100)),
76             'currency-code': pos.currency_code,
77             'customer-language': get_language(), # filter to pos.languages
78             'customer-email': schedule.email,
79             'store-card': 'true',
80             'recurring-payment': 'true',
81         }
82         widget_sig = sha256(
83             (
84                 "".join(v for (k, v) in sorted(widget_args.items())) +
85                 pos.secondary_key
86             ).encode('utf-8')
87         ).hexdigest()
88         
89         return render_to_string(
90             'payu/rec_widget.html',
91             {
92                 'form': forms.PayUCardTokenForm(),
93                 'pos': POSS[self.pos_id],
94                 'widget_args': widget_args,
95                 'widget_sig': widget_sig,
96             }
97         )
98     
99     def pay(self, request, schedule):
100         # Create order, put it and see what happens next.
101         from .models import PayUOrder
102         if request is not None:
103             ip = request.META['REMOTE_ADDR']
104         else:
105             ip = '127.0.0.1'
106
107         if request is None:
108             if not self.needs_retry(schedule):
109                 return
110             
111         order = PayUOrder.objects.create(
112             pos_id=self.pos_id,
113             customer_ip=ip,
114             schedule=schedule,
115         )
116         return order.put()
117
118     def needs_retry(self, schedule):
119         retry_last = schedule.payuorder_set.exclude(
120             created_at=None).order_by('-created_at').first()
121         if retry_last is None:
122             return True
123
124         n = now().date()
125         days_since_last = (n - retry_last.created_at.date()).days
126
127         retry_start = max(
128             schedule.expires_at.date(),
129             settings.CLUB_RETRIES_START
130         )
131         retry_days = (n - retry_start).days
132         
133         if retry_days > settings.CLUB_RETRY_DAYS_MAX:
134             print('expired')
135             return False
136         if retry_days > settings.CLUB_RETRY_DAYS_DAILY:
137             print('retry less often now')
138             return days_since_last > settings.CLUB_RETRY_LESS
139         return days_since_last > 0
140
141
142 class PayPal(PaymentMethod):
143     slug = 'paypal'
144     name = 'PayPal'
145     template_name = 'club/payment/paypal.html'
146     is_recurring = True
147     is_onetime = False
148
149     def invite_widget(self, schedule):
150         return render_to_string(
151             'club/payment/paypal_invite.html',
152             {
153                 'schedule': schedule,
154             }
155         )
156     
157     def initiate(self, request, schedule):
158         app = request.GET.get('app')
159         return agreement_approval_url(schedule.amount, schedule.key, app=app)
160
161     def pay(self, request, schedule):
162         from datetime import date, timedelta, datetime, timezone
163         tomorrow = datetime(*(date.today() + timedelta(2)).timetuple()[:3], tzinfo=timezone.utc)
164         any_active = False
165         for ba in schedule.billingagreement_set.all():
166             active = ba.check_agreement()
167             ba.active = active
168             ba.save()
169             if active:
170                 any_active = True
171         if any_active:
172             schedule.expires_at = tomorrow
173             schedule.save()
174
175
176 methods = []
177
178 pos = getattr(settings, 'CLUB_PAYU_RECURRING_POS', None)
179 if pos:
180     recurring_payment_method = PayURe(pos)
181     methods.append(recurring_payment_method)
182 else:
183     recurring_payment_method = None
184
185 pos = getattr(settings, 'CLUB_PAYU_POS', None)
186 if pos:
187     single_payment_method = PayU(pos)
188     methods.append(single_payment_method)
189 else:
190     single_payment_method = None
191
192
193
194 methods.append(
195     PayPal()
196 )