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