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