Cleanup, and better handling in the cancellation flow more work to come
This commit is contained in:
parent
2e9a428761
commit
c1ed938b04
|
@ -1,3 +1,3 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
python manage.py runserver_plus 0.0.0.0:8000
|
while true; do python manage.py runserver_plus 0.0.0.0:8000; sleep 2; done
|
||||||
|
|
|
@ -99,5 +99,50 @@ CAPTCHA = {
|
||||||
WHITENOISE_AUTOREFRESH = True
|
WHITENOISE_AUTOREFRESH = True
|
||||||
WHITENOISE_USE_FINDERS = True
|
WHITENOISE_USE_FINDERS = True
|
||||||
|
|
||||||
|
LOGGING = {
|
||||||
|
'version': 1,
|
||||||
|
'disable_existing_loggers': False,
|
||||||
|
'filters': {
|
||||||
|
'require_debug_false': {
|
||||||
|
'()': 'django.utils.log.RequireDebugFalse'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'formatters': {
|
||||||
|
'verbose': {
|
||||||
|
'format': '%(levelname)s %(asctime)s %(module)s '
|
||||||
|
'%(process)d %(thread)d %(message)s'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'handlers': {
|
||||||
|
'mail_admins': {
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'filters': ['require_debug_false'],
|
||||||
|
'class': 'django.utils.log.AdminEmailHandler'
|
||||||
|
},
|
||||||
|
'console': {
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'class': 'logging.StreamHandler',
|
||||||
|
'formatter': 'verbose',
|
||||||
|
},
|
||||||
|
'logfile': {
|
||||||
|
'level':'DEBUG',
|
||||||
|
'class':'logging.FileHandler',
|
||||||
|
'filename': "%s/django.log" % ROOT_DIR,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'loggers': {
|
||||||
|
'django.request': {
|
||||||
|
'handlers': ['mail_admins', 'logfile'],
|
||||||
|
'level': 'ERROR',
|
||||||
|
'propagate': True
|
||||||
|
},
|
||||||
|
'django.security.DisallowedHost': {
|
||||||
|
'level': 'ERROR',
|
||||||
|
'handlers': ['logfile', 'console', 'mail_admins'],
|
||||||
|
'propagate': True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PAYMENT_PROVIDERS['gocardless']['redirect_url'] = 'http://127.0.0.1:8180'
|
PAYMENT_PROVIDERS['gocardless']['redirect_url'] = 'http://127.0.0.1:8180'
|
||||||
TEMPLATE_DEBUG = False
|
TEMPLATE_DEBUG = False
|
||||||
|
|
|
@ -67,5 +67,8 @@ TEMPLATES[0]['OPTIONS']['loaders'] = [
|
||||||
]
|
]
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {'ENGINE': 'django.db.backends.sqlite3'}
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': os.path.join(str(ROOT_DIR), 'cache/test_database.db'),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,8 @@ def download_remote_images():
|
||||||
render_variations(result[0], image_variations, replace=True)
|
render_variations(result[0], image_variations, replace=True)
|
||||||
article.save()
|
article.save()
|
||||||
except:
|
except:
|
||||||
|
logger.exception(result)
|
||||||
|
logger.exception(result[0])
|
||||||
logger.exception('Unable to download remote image for %s' % article.original_image)
|
logger.exception('Unable to download remote image for %s' % article.original_image)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals, absolute_import
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.utils.dateparse import parse_datetime
|
from django.utils.dateparse import parse_datetime
|
||||||
from mhackspace.users.models import Membership
|
from mhackspace.users.models import Membership
|
||||||
|
from mhackspace.users.models import MEMBERSHIP_CANCELLED
|
||||||
|
|
||||||
|
|
||||||
def create_or_update_membership(user, signup_details, complete=False):
|
def create_or_update_membership(user, signup_details, complete=False):
|
||||||
|
@ -34,3 +35,13 @@ def create_or_update_membership(user, signup_details, complete=False):
|
||||||
group = Group.objects.get(name='members')
|
group = Group.objects.get(name='members')
|
||||||
user.groups.add(group)
|
user.groups.add(group)
|
||||||
return True # Sign up finished
|
return True # Sign up finished
|
||||||
|
|
||||||
|
|
||||||
|
def cancel_membership(user):
|
||||||
|
member = Membership.objects.get(user=user)
|
||||||
|
member.status = MEMBERSHIP_CANCELLED
|
||||||
|
member.save()
|
||||||
|
|
||||||
|
group = Group.objects.get(name='members')
|
||||||
|
user.groups.remove(group)
|
||||||
|
return True
|
||||||
|
|
|
@ -35,16 +35,6 @@ class gocardless_provider:
|
||||||
access_token=payment_providers['gocardless']['credentials']['access_token'],
|
access_token=payment_providers['gocardless']['credentials']['access_token'],
|
||||||
environment=payment_providers['gocardless']['environment'])
|
environment=payment_providers['gocardless']['environment'])
|
||||||
|
|
||||||
# def subscribe_confirm(self, args):
|
|
||||||
# response = gocardless_pro.client.confirm_resource(args)
|
|
||||||
# subscription = gocardless_pro.client.subscription(args.get('resource_id'))
|
|
||||||
# return {
|
|
||||||
# 'amount': subscription.amount,
|
|
||||||
# 'start_date': subscription.created_at,
|
|
||||||
# 'reference': subscription.id,
|
|
||||||
# 'success': response.success
|
|
||||||
# }
|
|
||||||
|
|
||||||
def fetch_customers(self):
|
def fetch_customers(self):
|
||||||
"""Fetch list of customers payments"""
|
"""Fetch list of customers payments"""
|
||||||
for customer in self.client.customers.list().records:
|
for customer in self.client.customers.list().records:
|
||||||
|
@ -80,11 +70,18 @@ class gocardless_provider:
|
||||||
def get_token(self):
|
def get_token(self):
|
||||||
return 'N/A'
|
return 'N/A'
|
||||||
|
|
||||||
def cancel_subscription(self, reference):
|
def cancel_subscription(self, user, reference):
|
||||||
try:
|
try:
|
||||||
subscription = gocardless_pro.client.subscription(reference)
|
subscription = self.client.subscriptions.get(reference)
|
||||||
response = subscription.cancel()
|
response = self.client.subscriptions.cancel(reference)
|
||||||
except gocardless_pro.exceptions.ClientError:
|
except gocardless_pro.errors.InvalidApiUsageError as e:
|
||||||
|
if e.code is 404:
|
||||||
|
logger.info('Cancel subscription failed user not found %s %s' % (e.code, e))
|
||||||
|
return {
|
||||||
|
'success': False
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.info('Cancel subscription failed unknown reason code %s %s' % (e.code, e))
|
||||||
return {
|
return {
|
||||||
'success': False
|
'success': False
|
||||||
}
|
}
|
||||||
|
@ -92,7 +89,7 @@ class gocardless_provider:
|
||||||
'amount': subscription.amount,
|
'amount': subscription.amount,
|
||||||
'start_date': subscription.created_at,
|
'start_date': subscription.created_at,
|
||||||
'reference': subscription.id,
|
'reference': subscription.id,
|
||||||
'success': response.get('success', False)
|
'success': True if response.get('status_code') is '200' else False
|
||||||
}
|
}
|
||||||
|
|
||||||
def create_subscription(self, user, session, amount,
|
def create_subscription(self, user, session, amount,
|
||||||
|
|
|
@ -35,14 +35,15 @@ class gocardlessMocks(TestCase):
|
||||||
return self.provider
|
return self.provider
|
||||||
|
|
||||||
def mock_success_responses(self):
|
def mock_success_responses(self):
|
||||||
|
subscription_properties = Mock(
|
||||||
mock_list = MagicMock()
|
id='02',
|
||||||
mock_list_records = MagicMock(side_effect=[Mock(
|
|
||||||
id='01',
|
|
||||||
status='active',
|
status='active',
|
||||||
amount=20.00,
|
amount=20.00,
|
||||||
created_at='date'
|
created_at='date'
|
||||||
)])
|
)
|
||||||
|
|
||||||
|
mock_list = MagicMock()
|
||||||
|
mock_list_records = MagicMock(side_effect=[subscription_properties])
|
||||||
mock_list.records.return_value = mock_list_records
|
mock_list.records.return_value = mock_list_records
|
||||||
|
|
||||||
self.provider.client.subscriptions.list = mock_list
|
self.provider.client.subscriptions.list = mock_list
|
||||||
|
@ -54,3 +55,10 @@ class gocardlessMocks(TestCase):
|
||||||
created_at=self.date_now,
|
created_at=self.date_now,
|
||||||
api_response=ApiResponseStatus(status_code='200'))
|
api_response=ApiResponseStatus(status_code='200'))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.provider.client.subscriptions.get = Mock(
|
||||||
|
return_value=subscription_properties)
|
||||||
|
|
||||||
|
self.provider.client.subscriptions.cancel = PropertyMock(
|
||||||
|
return_value={'status_code': '200'})
|
||||||
|
|
||||||
|
|
|
@ -14,39 +14,19 @@ class TestPaymentGatewaysGocardless(gocardlessMocks):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
# self.date_now = django.utils.timezone.now()
|
|
||||||
# self.user = self.make_user()
|
|
||||||
# member = Membership()
|
|
||||||
# member.user = self.user
|
|
||||||
# member.payment = '20.00'
|
|
||||||
# member.date = self.date_now
|
|
||||||
# member.save()
|
|
||||||
# self.auth_gocardless()
|
|
||||||
|
|
||||||
|
def test_unsubscribe(self):
|
||||||
@skip("Need to implement")
|
|
||||||
@patch('mhackspace.subscriptions.payments.gocardless_pro.Client.subscription', autospec=True)
|
|
||||||
def test_unsubscribe(self, mock_subscription):
|
|
||||||
self.mock_success_responses()
|
self.mock_success_responses()
|
||||||
# self.auth_gocardless()
|
|
||||||
mock_subscription.return_value = Mock(success='success')
|
result = self.provider.cancel_subscription(user=self.user, reference='M01')
|
||||||
mock_subscription.cancel.return_value = Mock(
|
|
||||||
id='01',
|
|
||||||
status='active',
|
|
||||||
amount=20.00,
|
|
||||||
created_at='date'
|
|
||||||
)
|
|
||||||
result = self.provider.cancel_subscription(reference='M01')
|
|
||||||
|
|
||||||
self.assertEqual(result.get('amount'), 20.00)
|
self.assertEqual(result.get('amount'), 20.00)
|
||||||
self.assertEqual(result.get('reference'), '02')
|
self.assertEqual(result.get('reference'), '02')
|
||||||
self.assertEqual(result.get('success'), 'success')
|
self.assertEqual(result.get('success'), True)
|
||||||
|
|
||||||
def test_confirm_subscription_callback(self):
|
def test_confirm_subscription_callback(self):
|
||||||
self.mock_success_responses()
|
self.mock_success_responses()
|
||||||
membership = self.create_membership_record()
|
membership = self.create_membership_record()
|
||||||
# self.auth_gocardless()
|
|
||||||
# mock_confirm.return_value = Mock(success='success')
|
|
||||||
|
|
||||||
request_params = {
|
request_params = {
|
||||||
'resource_uri': 'http://gocardless/resource/url/01',
|
'resource_uri': 'http://gocardless/resource/url/01',
|
||||||
|
@ -73,6 +53,6 @@ class TestPaymentGatewaysGocardless(gocardlessMocks):
|
||||||
for item in self.provider.fetch_subscriptions():
|
for item in self.provider.fetch_subscriptions():
|
||||||
self.assertEqual(item.get('status'), 'active')
|
self.assertEqual(item.get('status'), 'active')
|
||||||
self.assertEqual(item.get('email'), 'test@test.com')
|
self.assertEqual(item.get('email'), 'test@test.com')
|
||||||
self.assertEqual(item.get('reference'), '01')
|
self.assertEqual(item.get('reference'), '02')
|
||||||
self.assertEqual(item.get('amount'), 20.00)
|
self.assertEqual(item.get('amount'), 20.00)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ from mhackspace.users.models import User, Membership
|
||||||
from mhackspace.users.models import MEMBERSHIP_CANCELLED
|
from mhackspace.users.models import MEMBERSHIP_CANCELLED
|
||||||
from mhackspace.users.forms import MembershipJoinForm
|
from mhackspace.users.forms import MembershipJoinForm
|
||||||
from mhackspace.subscriptions.payments import select_provider
|
from mhackspace.subscriptions.payments import select_provider
|
||||||
from mhackspace.subscriptions.helper import create_or_update_membership
|
from mhackspace.subscriptions.helper import create_or_update_membership, cancel_membership
|
||||||
|
|
||||||
|
|
||||||
class MembershipCancelView(LoginRequiredMixin, RedirectView):
|
class MembershipCancelView(LoginRequiredMixin, RedirectView):
|
||||||
|
@ -28,17 +28,12 @@ class MembershipCancelView(LoginRequiredMixin, RedirectView):
|
||||||
member = Membership.objects.filter(user=self.request.user).first()
|
member = Membership.objects.filter(user=self.request.user).first()
|
||||||
|
|
||||||
result = provider.cancel_subscription(
|
result = provider.cancel_subscription(
|
||||||
|
user=self.request.user,
|
||||||
reference=member.reference
|
reference=member.reference
|
||||||
)
|
)
|
||||||
if result.get('success') is True:
|
|
||||||
# set membership to cancelled on success
|
|
||||||
member.status = MEMBERSHIP_CANCELLED
|
|
||||||
member.save()
|
|
||||||
|
|
||||||
|
# if result.get('success') is True:
|
||||||
# remove user from group on success
|
cancel_membership(user=self.request.user)
|
||||||
group = Group.objects.get(name='members')
|
|
||||||
self.request.user.groups.remove(group)
|
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
self.request,
|
self.request,
|
||||||
messages.SUCCESS,
|
messages.SUCCESS,
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{% if membership.get_status %}
|
{% if membership.is_active %}
|
||||||
<div id="membercard" class="registered">
|
<div id="membercard" class="registered">
|
||||||
<div class="date">Joined {{membership.date}}</div>
|
<div class="date">Joined {{membership.date}}</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
|
@ -45,27 +45,28 @@ class Blurb(models.Model):
|
||||||
skills = models.CharField(max_length=255)
|
skills = models.CharField(max_length=255)
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
|
|
||||||
MEMBERSHIP_CANCELLED = 0
|
MEMBERSHIP_ACTIVE = 4
|
||||||
|
MEMBERSHIP_CANCELLED = 4
|
||||||
|
|
||||||
MEMBERSHIP_STATUS_CHOICES = (
|
MEMBERSHIP_STATUS_CHOICES = (
|
||||||
(0, 'Guest user'),
|
(0, 'Guest user'),
|
||||||
(1, 'Active membership'),
|
(MEMBERSHIP_ACTIVE, 'Active membership'),
|
||||||
(3, 'Membership Expired'),
|
(3, 'Membership Expired'),
|
||||||
(4, 'Membership Cancelled')
|
(MEMBERSHIP_CANCELLED, 'Membership Cancelled')
|
||||||
)
|
)
|
||||||
|
|
||||||
MEMBERSHIP_STRING = {
|
MEMBERSHIP_STRING = {
|
||||||
0: 'Guest user',
|
0: 'Guest user',
|
||||||
1: 'Active membership',
|
MEMBERSHIP_ACTIVE: 'Active membership',
|
||||||
3: 'Membership Expired',
|
3: 'Membership Expired',
|
||||||
4: 'Membership Cancelled'
|
MEMBERSHIP_CANCELLED: 'Membership Cancelled'
|
||||||
}
|
}
|
||||||
|
|
||||||
MEMBERSHIP_STATUS = {
|
MEMBERSHIP_STATUS = {
|
||||||
'signup': 0, # This means the user has not completed signup
|
'signup': 0, # This means the user has not completed signup
|
||||||
'active': 1,
|
'active': MEMBERSHIP_ACTIVE,
|
||||||
'expired': 3,
|
'expired': 3,
|
||||||
'cancelled': 4
|
'cancelled': MEMBERSHIP_CANCELLED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,6 +88,11 @@ class Membership(models.Model):
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
return MEMBERSHIP_STRING[self.status]
|
return MEMBERSHIP_STRING[self.status]
|
||||||
|
|
||||||
|
def is_active(self):
|
||||||
|
if self.status is MEMBERSHIP_ACTIVE:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def lookup_status(name):
|
def lookup_status(name):
|
||||||
if not name:
|
if not name:
|
||||||
return 0
|
return 0
|
||||||
|
|
Loading…
Reference in New Issue