9d9f45ab981e4a2e39e9627c69be9ea06b6eb6bb
[wolnelektury.git] / src / paypal / rest.py
1 # -*- coding: utf-8 -*-
2 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
3 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
4 #
5 from datetime import timedelta
6
7 import paypalrestsdk
8 import pytz
9 from django.contrib.sites.models import Site
10 from django.core.urlresolvers import reverse
11 from django.utils import timezone
12 from paypalrestsdk import BillingPlan, BillingAgreement, ResourceNotFound
13 from django.conf import settings
14 from .models import BillingPlan as BillingPlanModel, BillingAgreement as BillingAgreementModel
15
16 paypalrestsdk.configure(settings.PAYPAL_CONFIG)
17
18
19 class PaypalError(Exception):
20     pass
21
22
23 def absolute_url(url_name):
24     return "http://%s%s" % (Site.objects.get_current().domain, reverse(url_name))
25
26
27 def create_plan(amount):
28     billing_plan = BillingPlan({
29         "name": "Cykliczna darowizna na Wolne Lektury: %s zł" % amount,
30         "description": "Cykliczna darowizna na wsparcie Wolnych Lektur",
31         "merchant_preferences": {
32             "auto_bill_amount": "yes",
33             "return_url": absolute_url('paypal_return'),
34             "cancel_url": absolute_url('paypal_cancel'),
35             # "initial_fail_amount_action": "continue",
36             "max_fail_attempts": "3",
37         },
38         "payment_definitions": [
39             {
40                 "amount": {
41                     "currency": "PLN",
42                     "value": str(amount),
43                 },
44                 "cycles": "0",
45                 "frequency": "MONTH",
46                 "frequency_interval": "1",
47                 "name": "Cykliczna darowizna",
48                 "type": "REGULAR",
49             }
50         ],
51         "type": "INFINITE",
52     })
53
54     if not billing_plan.create():
55         raise PaypalError(billing_plan.error)
56     if not billing_plan.activate():
57         raise PaypalError(billing_plan.error)
58     plan, created = BillingPlanModel.objects.get_or_create(amount=amount, defaults={'plan_id': billing_plan.id})
59     return plan.plan_id
60
61
62 def get_link(links, rel):
63     for link in links:
64         if link.rel == rel:
65             return link.href
66
67
68 def create_agreement(amount, app=False):
69     try:
70         plan = BillingPlanModel.objects.get(amount=amount)
71     except BillingPlanModel.DoesNotExist:
72         plan_id = create_plan(amount)
73     else:
74         plan_id = plan.plan_id
75     start = (timezone.now() + timedelta(0, 3600*24)).astimezone(pytz.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
76     billing_agreement = BillingAgreement({
77         "name": u"Subskrypcja klubu WL",
78         "description": u"Stałe wsparcie Wolnych Lektur kwotą %s złotych" % amount,
79         "start_date": start,
80         "plan": {
81             "id": plan_id,
82         },
83         "payer": {
84             "payment_method": "paypal"
85         },
86     })
87     if app:
88         billing_agreement['override_merchant_preferences'] = {
89             'return_url': absolute_url('paypal_app_return'),
90         }
91
92     response = billing_agreement.create()
93     if response:
94         return billing_agreement
95     else:
96         raise PaypalError(billing_agreement.error)
97
98
99 def agreement_approval_url(amount, app=False):
100     agreement = create_agreement(amount, app=app)
101     return get_link(agreement.links, 'approval_url')
102
103
104 def get_agreement(agreement_id):
105     try:
106         return BillingAgreement.find(agreement_id)
107     except ResourceNotFound:
108         return None
109
110
111 def check_agreement(agreement_id):
112     a = get_agreement(agreement_id)
113     if a:
114         return a.state == 'Active'
115
116
117 def user_is_subscribed(user):
118     try:
119         agreement = BillingAgreementModel.objects.get(user=user)
120     except BillingAgreementModel.DoesNotExist:
121         return False
122     return agreement.check_agreement()
123
124
125 def execute_agreement(token):
126     return BillingAgreement.execute(token)