1 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
4 from datetime import date, datetime, timedelta
5 from decimal import Decimal
8 from django.contrib.sites.models import Site
9 from django.urls import reverse
10 from django.utils import timezone
11 from django.conf import settings
12 from .models import BillingPlan, BillingAgreement
14 paypalrestsdk.configure(settings.PAYPAL_CONFIG)
17 class PaypalError(Exception):
21 def absolute_url(url_name, kwargs=None):
22 return "http://%s%s" % (Site.objects.get_current().domain, reverse(url_name, kwargs=kwargs))
25 def create_plan(amount):
26 billing_plan = paypalrestsdk.BillingPlan({
27 "name": "Cykliczna darowizna na Wolne Lektury: %s zł" % amount,
28 "description": "Cykliczna darowizna na wsparcie Wolnych Lektur",
29 "merchant_preferences": {
30 "auto_bill_amount": "yes",
31 "return_url": absolute_url('paypal_return', {'key': '-'}),
32 "cancel_url": absolute_url('paypal_cancel'),
33 # "initial_fail_amount_action": "continue",
34 "max_fail_attempts": "3",
36 "payment_definitions": [
44 "frequency_interval": "1",
45 "name": "Cykliczna darowizna",
52 if not billing_plan.create():
53 raise PaypalError(billing_plan.error)
54 if not billing_plan.activate():
55 raise PaypalError(billing_plan.error)
56 plan, created = BillingPlan.objects.get_or_create(amount=amount, defaults={'plan_id': billing_plan.id})
60 def get_link(links, rel):
66 def create_agreement(amount, key, app=False):
68 plan = BillingPlan.objects.get(amount=amount)
69 except BillingPlan.DoesNotExist:
70 plan_id = create_plan(amount)
72 plan_id = plan.plan_id
73 start = (timezone.now() + timedelta(0, 3600*24)).astimezone(pytz.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
74 billing_agreement = paypalrestsdk.BillingAgreement({
75 "name": "Subskrypcja klubu WL",
76 "description": "Stałe wsparcie Wolnych Lektur kwotą %s złotych" % amount,
82 "payment_method": "paypal"
86 billing_agreement['override_merchant_preferences'] = {
87 'return_url': absolute_url('paypal_app_return', {'key': key}),
90 billing_agreement['override_merchant_preferences'] = {
91 'return_url': absolute_url('paypal_return', {'key': key}),
95 response = billing_agreement.create()
97 return billing_agreement
99 raise PaypalError(billing_agreement.error)
102 def agreement_approval_url(amount, key, app=False):
103 agreement = create_agreement(amount, key, app=app)
104 return get_link(agreement.links, 'approval_url')
107 def get_agreement(agreement_id):
109 return paypalrestsdk.BillingAgreement.find(agreement_id)
110 except paypalrestsdk.ResourceNotFound:
114 def check_agreement(agreement_id):
115 a = get_agreement(agreement_id)
117 return a.state == 'Active'
120 def user_is_subscribed(user):
121 agreements = BillingAgreement.objects.filter(user=user)
122 return any(agreement.check_agreement() for agreement in agreements)
125 def execute_agreement(token):
126 return paypalrestsdk.BillingAgreement.execute(token)
129 def get_donations(agreement_id, year):
130 a = get_agreement(agreement_id)
132 for transaction in a.search_transactions(
133 date(year - 1, 12, 31),
134 date(year + 1, 1, 1))['agreement_transaction_list']:
135 if transaction['status'] != 'Completed':
137 dt = datetime.strptime(
138 transaction['time_stamp'],
139 '%Y-%m-%dT%H:%M:%S%z'
143 assert transaction['amount']['currency'] == 'PLN'
144 transactions.append({
146 'amount': Decimal(transaction['amount']['value'])