change subscription mechanism to use new gocardless api
This commit is contained in:
parent
b3d580d49c
commit
54e1bce190
|
@ -0,0 +1,3 @@
|
|||
* cache folder
|
||||
|
||||
Store things like pip cache here so wee dont have to download every time wee build.
|
|
@ -0,0 +1,3 @@
|
|||
* Logs folder
|
||||
|
||||
Store logs generated by containers you want to keep in this folder, usefull if your container fails on startup
|
|
@ -10,15 +10,52 @@ https://docs.djangoproject.com/en/dev/ref/settings/
|
|||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import os
|
||||
import time
|
||||
import environ
|
||||
|
||||
|
||||
# from spirit.settings import *
|
||||
ROOT_DIR = environ.Path(__file__) - 3 # (mhackspace/config/settings/common.py - 3 = mhackspace/)
|
||||
APPS_DIR = ROOT_DIR.path('mhackspace')
|
||||
|
||||
env = environ.Env()
|
||||
env.read_env('%s/.env' % ROOT_DIR)
|
||||
|
||||
ST_TOPIC_PRIVATE_CATEGORY_PK = 1
|
||||
ST_RATELIMIT_ENABLE = True
|
||||
ST_RATELIMIT_CACHE_PREFIX = 'srl'
|
||||
ST_RATELIMIT_CACHE = 'default'
|
||||
ST_RATELIMIT_SKIP_TIMEOUT_CHECK = False
|
||||
ST_NOTIFICATIONS_PER_PAGE = 20
|
||||
ST_COMMENT_MAX_LEN = 3000
|
||||
ST_MENTIONS_PER_COMMENT = 30
|
||||
ST_DOUBLE_POST_THRESHOLD_MINUTES = 30
|
||||
ST_YT_PAGINATOR_PAGE_RANGE = 3
|
||||
ST_SEARCH_QUERY_MIN_LEN = 3
|
||||
ST_USER_LAST_SEEN_THRESHOLD_MINUTES = 1
|
||||
ST_PRIVATE_FORUM = False
|
||||
ST_ALLOWED_UPLOAD_IMAGE_FORMAT = ('jpeg', 'png', 'gif')
|
||||
ST_ALLOWED_URL_PROTOCOLS = {
|
||||
'http', 'https', 'mailto', 'ftp', 'ftps',
|
||||
'git', 'svn', 'magnet', 'irc', 'ircs'}
|
||||
|
||||
ST_UNICODE_SLUGS = True
|
||||
ST_UNIQUE_EMAILS = True
|
||||
ST_CASE_INSENSITIVE_EMAILS = True
|
||||
|
||||
# Tests helpers
|
||||
ST_TESTS_RATELIMIT_NEVER_EXPIRE = False
|
||||
ST_BASE_DIR = os.path.dirname(__file__)
|
||||
|
||||
|
||||
HAYSTACK_CONNECTIONS = {
|
||||
'default': {
|
||||
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
|
||||
'PATH': os.path.join(os.path.dirname(__file__), 'search', 'whoosh_index'),
|
||||
},
|
||||
}
|
||||
|
||||
# APP CONFIGURATION
|
||||
# ------------------------------------------------------------------------------
|
||||
DJANGO_APPS = (
|
||||
|
@ -40,11 +77,41 @@ THIRD_PARTY_APPS = (
|
|||
'allauth.socialaccount', # registration
|
||||
'allauth.socialaccount.providers.google', # registration
|
||||
'allauth.socialaccount.providers.github', # registration
|
||||
'allauth.socialaccount.providers.facebook', # registration
|
||||
# 'allauth.socialaccount.providers.facebook', # registration
|
||||
'whitenoise.runserver_nostatic',
|
||||
'stdimage',
|
||||
'rest_framework',
|
||||
'draceditor',
|
||||
|
||||
'haystack',
|
||||
'djconfig',
|
||||
'spirit.core',
|
||||
'spirit.admin',
|
||||
'spirit.search',
|
||||
|
||||
'spirit.user',
|
||||
'spirit.user.admin',
|
||||
'spirit.user.auth',
|
||||
|
||||
'spirit.category',
|
||||
'spirit.category.admin',
|
||||
|
||||
'spirit.topic',
|
||||
'spirit.topic.admin',
|
||||
'spirit.topic.favorite',
|
||||
'spirit.topic.moderate',
|
||||
'spirit.topic.notification',
|
||||
'spirit.topic.poll', # todo: remove in Spirit v0.6
|
||||
'spirit.topic.private',
|
||||
'spirit.topic.unread',
|
||||
|
||||
'spirit.comment',
|
||||
'spirit.comment.bookmark',
|
||||
'spirit.comment.flag',
|
||||
'spirit.comment.flag.admin',
|
||||
'spirit.comment.history',
|
||||
'spirit.comment.like',
|
||||
'spirit.comment.poll',
|
||||
)
|
||||
|
||||
# Apps specific for this project go here.
|
||||
|
@ -74,6 +141,16 @@ MIDDLEWARE = (
|
|||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
|
||||
#fix for ip logging behind a proxy
|
||||
'x_forwarded_for.middleware.XForwardedForMiddleware',
|
||||
'djconfig.middleware.DjConfigMiddleware',
|
||||
|
||||
'spirit.user.middleware.TimezoneMiddleware',
|
||||
'spirit.user.middleware.LastIPMiddleware',
|
||||
'spirit.user.middleware.LastSeenMiddleware',
|
||||
'spirit.user.middleware.ActiveUserMiddleware',
|
||||
'spirit.core.middleware.PrivateForumMiddleware',
|
||||
)
|
||||
|
||||
# MIGRATIONS CONFIGURATION
|
||||
|
@ -171,6 +248,7 @@ TEMPLATES = [
|
|||
'django.template.context_processors.tz',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
# Your stuff: custom template context processors go here
|
||||
'djconfig.context_processors.config',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
|
@ -47,14 +47,14 @@ CACHES = {
|
|||
'IGNORE_EXCEPTIONS': True, # mimics memcache behavior.
|
||||
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
|
||||
}
|
||||
},
|
||||
'st_rate_limit': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'LOCATION': 'spirit_rl_cache',
|
||||
'TIMEOUT': None
|
||||
}
|
||||
}
|
||||
# CACHES = {
|
||||
# 'default': {
|
||||
# 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
# 'LOCATION': ''
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
# django-debug-toolbar
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -92,3 +92,5 @@ CAPTCHA = {
|
|||
|
||||
WHITENOISE_AUTOREFRESH = True
|
||||
WHITENOISE_USE_FINDERS = True
|
||||
|
||||
PAYMENT_PROVIDERS['gocardless']['redirect_url'] = 'http://127.0.0.1:8180'
|
||||
|
|
|
@ -151,6 +151,11 @@ CACHES = {
|
|||
'IGNORE_EXCEPTIONS': True, # mimics memcache behavior.
|
||||
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
|
||||
}
|
||||
},
|
||||
'st_rate_limit': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'LOCATION': 'spirit_rl_cache',
|
||||
'TIMEOUT': None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -152,6 +152,11 @@ CACHES = {
|
|||
'IGNORE_EXCEPTIONS': True, # mimics memcache behavior.
|
||||
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
|
||||
}
|
||||
},
|
||||
'st_rate_limit': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'LOCATION': 'spirit_rl_cache',
|
||||
'TIMEOUT': None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,11 @@ CACHES = {
|
|||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'LOCATION': ''
|
||||
},
|
||||
'st_rate_limit': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'LOCATION': 'spirit_rl_cache',
|
||||
'TIMEOUT': None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include, url
|
||||
from django.conf.urls.static import static
|
||||
|
@ -21,6 +20,7 @@ from mhackspace.blog.views import PostViewSet, CategoryViewSet, BlogPost, PostLi
|
|||
from mhackspace.blog.sitemaps import PostSitemap, CategorySitemap
|
||||
from mhackspace.feeds.views import FeedViewSet, ArticleViewSet
|
||||
|
||||
# import spirit.urls
|
||||
router = DefaultRouter()
|
||||
router.register(r'posts', PostViewSet)
|
||||
router.register(r'categories', CategoryViewSet)
|
||||
|
@ -39,6 +39,7 @@ urlpatterns = [
|
|||
url(r'^mailing-list/$', TemplateView.as_view(template_name='pages/mailing-list.html'), name='group'),
|
||||
url(r'^contact/$', contact, name='contact'),
|
||||
|
||||
url(r'^discuss/', include('spirit.urls')),
|
||||
url(r'^api/v1/', include(router.urls, namespace='v1')),
|
||||
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
|
||||
url(r'^draceditor/', include('draceditor.urls')),
|
||||
|
@ -79,6 +80,7 @@ urlpatterns = [
|
|||
url(r'^reset/done/$', auth_views.password_reset_complete, name='password_reset_complete'),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
|
||||
if settings.DEBUG:
|
||||
# This allows the error pages to be debugged during development, just visit
|
||||
# these url in browser to see how these error pages look like.
|
||||
|
|
7
dev.yml
7
dev.yml
|
@ -20,9 +20,10 @@ services:
|
|||
command: /start-dev.sh
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
- POSTGRES_USER=mhackspace
|
||||
- USE_DOCKER=yes
|
||||
env_file: .env
|
||||
# environment:
|
||||
# - POSTGRES_USER=mhackspace
|
||||
# - USE_DOCKER=yes
|
||||
volumes:
|
||||
- .:/app
|
||||
ports:
|
||||
|
|
1
live.yml
1
live.yml
|
@ -28,6 +28,7 @@ services:
|
|||
env_file: .env
|
||||
volumes:
|
||||
- .:/app
|
||||
- ./compose/data/logs:/var/log/gunicorn
|
||||
- sockets:/data/sockets
|
||||
|
||||
node:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import random
|
||||
from autofixture import AutoFixture
|
||||
from autofixture.generators import ImageGenerator
|
||||
from django.core.management.base import BaseCommand
|
||||
|
@ -7,10 +8,12 @@ from mhackspace.feeds.models import Article, Feed
|
|||
from mhackspace.users.models import User
|
||||
from mhackspace.blog.models import Category, Post
|
||||
|
||||
|
||||
class ImageFixture(AutoFixture):
|
||||
class Values:
|
||||
scaled_image = ImageGenerator(width=800, height=300, sizes=((1280, 300),))
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Build test data for development environment'
|
||||
|
||||
|
@ -30,10 +33,11 @@ class Command(BaseCommand):
|
|||
call_command('loaddata', 'mhackspace/users/fixtures/groups.json', verbose=0)
|
||||
|
||||
# random data
|
||||
users = AutoFixture(User)
|
||||
users = AutoFixture(User, field_values={
|
||||
'title': random.choicee(('Mr', 'Mrs', 'Emperor', 'Captain'))
|
||||
})
|
||||
users.create(10)
|
||||
|
||||
|
||||
banners = ImageFixture(BannerImage)
|
||||
banners.create(10)
|
||||
self.stdout.write(
|
||||
|
|
|
@ -6,3 +6,11 @@
|
|||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
width:100%;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import Group
|
||||
from mhackspace.users.models import Membership
|
||||
|
||||
|
||||
def create_or_update_membership(user, signup_details, complete=False):
|
||||
try:
|
||||
member = Membership.objects.get(user=user)
|
||||
except Membership.DoesNotExist:
|
||||
member = Membership()
|
||||
member.user = user
|
||||
|
||||
if complete is True:
|
||||
member.status = Membership.lookup_status(name=signup_details.get('status'))
|
||||
member.email = signup_details.get('email')
|
||||
member.reference = signup_details.get('reference')
|
||||
member.payment = signup_details.get('amount')
|
||||
member.date = signup_details.get('start_date')
|
||||
|
||||
member.save()
|
||||
|
||||
if complete is False:
|
||||
return False # sign up not completed
|
||||
|
||||
# add user to group on success
|
||||
group = Group.objects.get(name='members')
|
||||
user.groups.add(group)
|
||||
return True # Sign up finished
|
|
@ -22,20 +22,16 @@ class Command(BaseCommand):
|
|||
|
||||
payment_objects = []
|
||||
for customer in provider.fetch_customers():
|
||||
# self.stdout.write(str(dir(customer)))
|
||||
# self.stdout.write(str(customer))
|
||||
|
||||
user = User.objects.get(email=customer.get('email'))
|
||||
payment_objects.append(Payments(
|
||||
user=None,
|
||||
user_reference=customer.get('user_id'),
|
||||
user=user,
|
||||
user_reference=customer.get('user_reference'),
|
||||
user_email=customer.get('email'),
|
||||
reference=customer.get('payment_id'),
|
||||
amount=customer.get('amount'),
|
||||
type=Payments.lookup_payment_type(customer.get('payment_type')),
|
||||
date=customer.get('payment_date')
|
||||
))
|
||||
# self.stdout.write(str(customer.email))
|
||||
# self.stdout.write(str(dir(customer['email']())))
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
'\t{reference} - {amount} - {type} - {user_email}'.format(**model_to_dict(payment_objects[-1]))))
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.forms.models import model_to_dict
|
|||
from django.core.management.base import BaseCommand
|
||||
from mhackspace.subscriptions.payments import select_provider
|
||||
from mhackspace.users.models import Membership, User
|
||||
|
||||
from mhackspace.subscriptions.helper import create_or_update_membership
|
||||
|
||||
|
||||
def update_subscriptions(provider_name):
|
||||
|
@ -54,28 +54,44 @@ class Command(BaseCommand):
|
|||
group = Group.objects.get(name='members')
|
||||
|
||||
for sub in provider.fetch_subscriptions():
|
||||
sub['amount'] = sub['amount'] * 0.01
|
||||
try:
|
||||
user_model = User.objects.get(email=sub.get('email'))
|
||||
if sub.get('status') == 'active':
|
||||
user_model.groups.add(group)
|
||||
except User.DoesNotExist:
|
||||
user_model = None
|
||||
self.style.NOTICE(
|
||||
'\tMissing User {reference} - {payment} - {status} - {email}'.format(**{
|
||||
'reference': sub.get('reference'),
|
||||
'payment': sub.get('amount'),
|
||||
'status': sub.get('status'),
|
||||
'email': sub.get('email')
|
||||
}))
|
||||
continue
|
||||
|
||||
create_or_update_membership(user=user_model,
|
||||
signup_details=sub,
|
||||
complete=True)
|
||||
subscriptions.append(
|
||||
Membership(
|
||||
user=user_model,
|
||||
email=sub.get('email'),
|
||||
reference=sub.get('reference'),
|
||||
payment=10.00,
|
||||
payment=sub.get('amount'),
|
||||
date=sub.get('start_date'),
|
||||
# date=timezone.now(),
|
||||
status=Membership.lookup_status(name=sub.get('status'))
|
||||
)
|
||||
)
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
'\t{reference} - {payment} - {status} - {email}'.format(**model_to_dict(subscriptions[-1]))))
|
||||
'\t{reference} - {payment} - {status} - {email}'.format(**{
|
||||
'reference': sub.get('reference'),
|
||||
'payment': sub.get('amount'),
|
||||
'status': sub.get('status'),
|
||||
'email': sub.get('email')
|
||||
})))
|
||||
|
||||
Membership.objects.bulk_create(subscriptions)
|
||||
|
|
@ -1,19 +1,14 @@
|
|||
from pprint import pprint
|
||||
import pytz
|
||||
import gocardless
|
||||
import gocardless_pro as gocardless
|
||||
import braintree
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
payment_providers = settings.PAYMENT_PROVIDERS
|
||||
|
||||
# import gocardless_pro
|
||||
logger = logging.getLogger(__name__)
|
||||
# import paypalrestsdk as paypal
|
||||
|
||||
# from website.config import settings
|
||||
# from website.config.import app_domain
|
||||
|
||||
# from website.config.logger import log
|
||||
|
||||
PROVIDER_ID = {'gocardless':1, 'braintree': 2}
|
||||
PROVIDER_NAME = {1: 'gocardless', 2: 'braintree'}
|
||||
|
||||
|
@ -26,19 +21,17 @@ def select_provider(type):
|
|||
assert 0, "No Provider for " + type
|
||||
|
||||
class gocardless_provider:
|
||||
"""
|
||||
gocardless test account details 20-00-00, 55779911
|
||||
"""
|
||||
form_remote = True
|
||||
client = None
|
||||
|
||||
def __init__(self):
|
||||
# gocardless are changing there api, not sure if we can switch yet
|
||||
# self.client = gocardless_pro.Client(
|
||||
# access_token=payment_providers['gocardless']['credentials']['access_token'],
|
||||
# environment=payment_providers['gocardless']['environment'])
|
||||
|
||||
print(payment_providers.keys)
|
||||
gocardless.environment = payment_providers['gocardless']['environment']
|
||||
gocardless.set_details(**payment_providers['gocardless']['credentials'])
|
||||
self.client = gocardless.client.merchant()
|
||||
self.client = gocardless.Client(
|
||||
access_token=payment_providers['gocardless']['credentials']['access_token'],
|
||||
environment=payment_providers['gocardless']['environment'])
|
||||
|
||||
def subscribe_confirm(self, args):
|
||||
response = gocardless.client.confirm_resource(args)
|
||||
|
@ -50,49 +43,33 @@ class gocardless_provider:
|
|||
'success': response.success
|
||||
}
|
||||
|
||||
|
||||
|
||||
def fetch_customers(self):
|
||||
merchant = gocardless.client.merchant()
|
||||
for customer in merchant.bills():
|
||||
user = customer.user()
|
||||
# print(dir(customer))
|
||||
# print(dir(customer.reference_fields))
|
||||
# print(customer.reference_fields)
|
||||
# print(customer.payout_id)
|
||||
# print(customer.reference_fields.payout_id)
|
||||
result = {
|
||||
'user_id': user.id,
|
||||
'email': user.email,
|
||||
'status': customer.status,
|
||||
'payment_id': customer.id,
|
||||
'payment_type': customer.source_type,
|
||||
'payment_date': customer.created_at,
|
||||
'amount': customer.amount
|
||||
"""Fetch list of customers payments"""
|
||||
for customer in self.client.customers.list().records:
|
||||
for payment in self.client.payments.list(params={"customer": customer.id}).records:
|
||||
yield {
|
||||
'user_reference': customer.id,
|
||||
'email': customer.email,
|
||||
'status': payment.status,
|
||||
'payment_id': payment.links.subscription,
|
||||
'payment_type': 'subscription' if payment.links.subscription else 'payment',
|
||||
'payment_date': payment.created_at,
|
||||
'amount': payment.amount
|
||||
}
|
||||
yield result #customer
|
||||
|
||||
|
||||
|
||||
# for customer in self.client.users():
|
||||
# result = {
|
||||
# 'email': customer.email,
|
||||
# 'created_date': customer.created_at,
|
||||
# 'first_name': customer.first_name,
|
||||
# 'last_name': customer.last_name
|
||||
# }
|
||||
# yield customer
|
||||
|
||||
def fetch_subscriptions(self):
|
||||
for paying_member in self.client.subscriptions():
|
||||
user=paying_member.user()
|
||||
# for paying_member in self.client.mandates.list().records:
|
||||
for paying_member in self.client.subscriptions.list().records:
|
||||
mandate=self.client.mandates.get(paying_member.links.mandate)
|
||||
user=self.client.customers.get(mandate.links.customer)
|
||||
|
||||
# gocardless does not have a reference so we use the id instead
|
||||
yield {
|
||||
'status': paying_member.status,
|
||||
'email': user.email,
|
||||
'start_date': paying_member.created_at,
|
||||
'reference': paying_member.id,
|
||||
'reference': mandate.id,
|
||||
'amount': paying_member.amount}
|
||||
|
||||
def get_redirect_url(self):
|
||||
|
@ -116,25 +93,61 @@ class gocardless_provider:
|
|||
'success': response.get('success', False)
|
||||
}
|
||||
|
||||
def create_subscription(self, amount, name, redirect_success, redirect_failure, interval_unit='month', interval_length='1'):
|
||||
return gocardless.client.new_subscription_url(
|
||||
amount=float(amount),
|
||||
interval_length=interval_length,
|
||||
interval_unit=interval_unit,
|
||||
name=name,
|
||||
redirect_uri=redirect_success)
|
||||
|
||||
def confirm_subscription(self, provider_response):
|
||||
response = gocardless.client.confirm_resource(provider_response)
|
||||
subscription = gocardless.client.subscription(provider_response.get('resource_id'))
|
||||
user = subscription.user()
|
||||
return {
|
||||
'amount': subscription.amount,
|
||||
'email': user.email,
|
||||
'start_date': subscription.created_at,
|
||||
'reference': subscription.id,
|
||||
'success': response.get('success')
|
||||
def create_subscription(self, user, session, amount,
|
||||
name, redirect_success, redirect_failure,
|
||||
interval_unit='monthly', interval_length='1'):
|
||||
return self.client.redirect_flows.create(params={
|
||||
"description": name,
|
||||
"session_token": session,
|
||||
"success_redirect_url": redirect_success,
|
||||
"prefilled_customer": {
|
||||
"given_name": user.first_name,
|
||||
"family_name": user.last_name,
|
||||
"email": user.email
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def confirm_subscription(self, membership, session, provider_response,
|
||||
name, interval_unit='monthly', interval_length='1'):
|
||||
r = provider_response.get('redirect_flow_id')
|
||||
|
||||
# response = self.client.redirect_flows.complete(r, params={
|
||||
# "session_token": session
|
||||
# })
|
||||
response = self.client.redirect_flows.get(r)
|
||||
# response = self.client.redirect_flows.get(provider_response.get('redirect_flow_id'))
|
||||
|
||||
# response = gocardless.client.confirm_resource(provider_response)
|
||||
# subscription = gocardless.client.subscription(provider_response.get('resource_id'))
|
||||
user_id = response.links.customer
|
||||
mandate_id = response.links.mandate
|
||||
# user = subscription.user()
|
||||
user = self.client.customers.get(response.links.customer)
|
||||
mandate = self.client.mandates.get(response.links.mandate)
|
||||
logging.debug(user)
|
||||
logging.debug(mandate)
|
||||
|
||||
# for some reason go cardless is in pence, so 20.00 needs to be sent as 2000
|
||||
# what genious decided that was a good idea, now looks like i am charging £2000 :p
|
||||
# the return is the same so you need to convert on send and receive
|
||||
subscription_response = self.client.subscriptions.create(
|
||||
params={
|
||||
'amount': str(membership.payment).replace('.', ''),
|
||||
'currency': 'GBP',
|
||||
'interval_unit': interval_unit,
|
||||
'name': name,
|
||||
# 'metadata': {'reference': },
|
||||
'links': {'mandate': mandate_id}
|
||||
})
|
||||
return {
|
||||
'amount': membership.payment,
|
||||
'email': user.email,
|
||||
'start_date': subscription_response.created_at,
|
||||
'reference': mandate_id,
|
||||
'success': subscription_response.api_response.status_code
|
||||
}
|
||||
|
||||
|
||||
class braintree_provider:
|
||||
form_remote = False
|
||||
|
@ -189,6 +202,7 @@ class braintree_provider:
|
|||
|
||||
class payment:
|
||||
"""
|
||||
https://developer.gocardless.com/api-reference/#redirect-flows-create-a-redirect-flow
|
||||
paypal reference = https://github.com/paypal/PayPal-Python-SDK
|
||||
gocardless reference = https://github.com/paypal/PayPal-Python-SDK
|
||||
"""
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django.shortcuts import redirect
|
||||
from django.views.generic import UpdateView, RedirectView
|
||||
|
@ -13,6 +14,7 @@ from mhackspace.users.models import User, Membership
|
|||
from mhackspace.users.models import MEMBERSHIP_CANCELLED
|
||||
from mhackspace.users.forms import MembershipJoinForm
|
||||
from mhackspace.subscriptions.payments import select_provider
|
||||
from mhackspace.subscriptions.helper import create_or_update_membership
|
||||
|
||||
|
||||
class MembershipCancelView(LoginRequiredMixin, RedirectView):
|
||||
|
@ -54,7 +56,6 @@ class MembershipJoinView(LoginRequiredMixin, UpdateView):
|
|||
return User.objects.get(username=self.request.user.username)
|
||||
|
||||
def form_valid(self, form):
|
||||
app_domain = 'http://test.maidstone-hackspace.org.uk'
|
||||
payment_provider = 'gocardless'
|
||||
provider = select_provider(payment_provider)
|
||||
app_domain = provider.get_redirect_url()
|
||||
|
@ -64,16 +65,31 @@ class MembershipJoinView(LoginRequiredMixin, UpdateView):
|
|||
form_subscription = MembershipJoinForm(data=self.request.POST)
|
||||
form_subscription.is_valid()
|
||||
|
||||
result = {
|
||||
'email': self.request.user.email,
|
||||
'reference': user_code,
|
||||
'amount': form_subscription.cleaned_data.get('amount', 20.00) * 0.01,
|
||||
'start_date': timezone.now()
|
||||
}
|
||||
|
||||
create_or_update_membership(
|
||||
user=self.request.user,
|
||||
signup_details=result,
|
||||
complete=False
|
||||
)
|
||||
|
||||
success_url = '%s/membership/%s/success' % (app_domain, payment_provider)
|
||||
failure_url = '%s/membership/%s/failure' % (app_domain, payment_provider)
|
||||
url = provider.create_subscription(
|
||||
user=self.request.user,
|
||||
session=self.request.session.session_key,
|
||||
amount=form_subscription.cleaned_data.get('amount', 20.00),
|
||||
name="Membership your membership id is MH%s" % user_code,
|
||||
redirect_success=success_url,
|
||||
redirect_failure=failure_url
|
||||
)
|
||||
|
||||
return redirect(url)
|
||||
return redirect(url.redirect_url)
|
||||
|
||||
|
||||
class MembershipJoinSuccessView(LoginRequiredMixin, RedirectView):
|
||||
|
@ -83,8 +99,14 @@ class MembershipJoinSuccessView(LoginRequiredMixin, RedirectView):
|
|||
def get_redirect_url(self, *args, **kwargs):
|
||||
payment_provider = 'gocardless'
|
||||
provider = select_provider(payment_provider)
|
||||
membership = Membership.objects.get(user=self.request.user)
|
||||
|
||||
name="Membership your membership id is MH%s" % membership.reference
|
||||
result = provider.confirm_subscription(
|
||||
provider_response=self.request.GET
|
||||
membership=membership,
|
||||
session=self.request.session.session_key,
|
||||
provider_response=self.request.GET,
|
||||
name=name
|
||||
)
|
||||
|
||||
# if something went wrong return to profile with an error
|
||||
|
@ -96,27 +118,15 @@ class MembershipJoinSuccessView(LoginRequiredMixin, RedirectView):
|
|||
return super(MembershipJoinSuccessView, self).get_redirect_url(*args, **kwargs)
|
||||
|
||||
del(kwargs['provider'])
|
||||
try:
|
||||
member = Membership.objects.get(user=self.request.user)
|
||||
except Membership.DoesNotExist:
|
||||
member = Membership()
|
||||
|
||||
member.user = self.request.user
|
||||
member.email = result.get('email')
|
||||
member.reference = result.get('reference')
|
||||
member.payment = result.get('amount')
|
||||
member.date = result.get('start_date')
|
||||
member.status = Membership.lookup_status(name=result.get('status'))
|
||||
member.save()
|
||||
kwargs['username'] = self.request.user.get_username()
|
||||
|
||||
# add user to group on success
|
||||
group = Group.objects.get(name='members')
|
||||
self.request.user.groups.add(group)
|
||||
if create_or_update_membership(user=self.request.user,
|
||||
signup_details=result,
|
||||
complete=True) is True:
|
||||
messages.add_message(
|
||||
self.request,
|
||||
messages.SUCCESS,
|
||||
'Success your membership should now be active')
|
||||
kwargs['username'] = self.request.user.get_username()
|
||||
return super(MembershipJoinSuccessView, self).get_redirect_url(*args, **kwargs)
|
||||
|
||||
|
||||
|
|
|
@ -27,11 +27,12 @@
|
|||
<meta name="msapplication-config" content="{% static 'browserconfig.xml' %}">
|
||||
<meta name="theme-color" content="#008080">
|
||||
|
||||
<link href="{% sass_src 'sass/project.scss' %}" rel="stylesheet">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
|
||||
{% block css %}
|
||||
<link href="{% sass_src 'sass/project.scss' %}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
||||
{% block head-extra %}{% endblock head-extra %}
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "http://schema.org",
|
||||
|
@ -45,7 +46,7 @@
|
|||
"@type": "PostalAddress",
|
||||
"streetAddress": "Maidstone Hackspace, Maidstone Community Support Centre",
|
||||
"addressLocality": "Maidstone",
|
||||
"addressRegion": "FLKent",
|
||||
"addressRegion": "Kent",
|
||||
"postalCode": "ME14 1HH",
|
||||
"addressCountry": "UK"
|
||||
},
|
||||
|
@ -66,6 +67,7 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<nav class="navbar navbar-toggleable-md navbar-inverse bg-inverse mb-4">
|
||||
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
{% extends "base.html" %}
|
||||
{% load sass_tags %}
|
||||
{% load static i18n %}
|
||||
{% block title %}Members{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<link rel="stylesheet" href="{% static 'spirit/stylesheets/styles.all.min.css' %}">
|
||||
<link href="{% sass_src 'sass/project.scss' %}" rel="stylesheet">
|
||||
{{ super.block }}
|
||||
{% endblock css %}
|
||||
|
||||
{% block head-extra %}
|
||||
|
||||
<script src="{% static "spirit/scripts/all.min.js" %}"></script>
|
||||
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
$.tab();
|
||||
$( 'a.js-post' ).postify( {
|
||||
csrfToken: "{{ csrf_token }}",
|
||||
} );
|
||||
$('.js-messages').messages();
|
||||
{% if user.is_authenticated %}
|
||||
$.notification( {
|
||||
notificationUrl: "{% url "spirit:topic:notification:index-ajax" %}",
|
||||
notificationListUrl: "{% url "spirit:topic:notification:index-unread" %}",
|
||||
mentionTxt: "{% trans "{user} has mention you on {topic}" %}",
|
||||
commentTxt: "{% trans "{user} has commented on {topic}" %}",
|
||||
showAll: "{% trans "Show all" %}",
|
||||
empty: "{% trans "No new notifications, yet" %}",
|
||||
unread: "{% trans "unread" %}",
|
||||
} );
|
||||
{% endif %}
|
||||
});
|
||||
</script>
|
||||
{% endblock head-extra %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h2>Users</h2>
|
||||
|
||||
<div class="list-group">
|
||||
{% for user in user_list %}
|
||||
<a href="{% url 'users:detail' user.username %}" class="list-group-item">
|
||||
<h4 class="list-group-item-heading">{{ user.username }}</h4>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
|
@ -2,6 +2,7 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load socialaccount %}
|
||||
|
||||
{% block title %}User: {{ object.username }}{% endblock %}
|
||||
|
||||
|
@ -15,14 +16,22 @@
|
|||
<p>{{ user.name }}</p>
|
||||
<p>{{ user.email }}</p>
|
||||
<p>Last login {{ user.last_login}}</p>
|
||||
<p>Member since</p>
|
||||
{% if blurb.description %}
|
||||
<p>Description: {{ blurb.description }}</p>
|
||||
<p>Skills: {{ blurb.description }}</p>
|
||||
{% endif %}
|
||||
{% if blurb.skills %}
|
||||
<p>Skills: {{ blurb.skills }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if membership %}
|
||||
<h3>Membership status</h3>
|
||||
<p>Member since {{membership.date}}</p>
|
||||
<p>Membership Status: {{ membership.get_status }}</p>
|
||||
<p>Last Payment: {{membership.date}}</p>
|
||||
<p>Amount: £{{membership.payment}}</p>
|
||||
{% else %}
|
||||
You are not currently a member consider signing up.
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% if membership.get_status %}
|
||||
|
@ -63,11 +72,27 @@
|
|||
<div class="row">
|
||||
|
||||
<div class="col-sm-12">
|
||||
<p>
|
||||
<a class="btn btn-primary" href="{% url 'users:update' %}" role="button">My Info</a>
|
||||
<a class="btn btn-primary" href="{% url 'account_email' %}" role="button">E-Mail</a>
|
||||
</p>
|
||||
<!-- Your Stuff: Custom user template urls -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<p>
|
||||
<a class="btn btn-primary" href="{% provider_login_url "google" process="connect" %}">Link your Google account</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<p>
|
||||
<a class="btn btn-primary" href="{% provider_login_url "github" process="connect" %}">Link your Github account</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Action buttons -->
|
||||
{% endif %}
|
||||
|
|
|
@ -11,7 +11,7 @@ from django.urls import reverse
|
|||
from django.conf.urls import url
|
||||
from .models import User, Membership, MEMBERSHIP_STATUS_CHOICES
|
||||
|
||||
from mhackspace.subscriptions.management.commands.refresh_subscriptions import update_subscriptions
|
||||
from mhackspace.subscriptions.management.commands.update_membership_status import update_subscriptions
|
||||
|
||||
|
||||
class MyUserChangeForm(UserChangeForm):
|
||||
|
@ -41,7 +41,7 @@ class MyUserAdmin(AuthUserAdmin):
|
|||
form = MyUserChangeForm
|
||||
add_form = MyUserCreationForm
|
||||
fieldsets = (
|
||||
('User Profile', {'fields': ('name', 'image')}),
|
||||
('User Profile', {'fields': ('name', '_image')}),
|
||||
) + AuthUserAdmin.fieldsets
|
||||
list_display = ('username', 'name', 'is_superuser')
|
||||
search_fields = ['name']
|
||||
|
|
|
@ -11,17 +11,28 @@ from stdimage.models import StdImageField
|
|||
|
||||
class User(AbstractUser):
|
||||
name = models.CharField(_('Name of User'), blank=True, max_length=255)
|
||||
public = models.BooleanField(default=False, help_text='If the users email is public on post feeds')
|
||||
image = StdImageField(
|
||||
public = models.BooleanField(
|
||||
default=False, help_text='If the users email is public on post feeds')
|
||||
_image = StdImageField(
|
||||
upload_to='avatars/',
|
||||
blank=True,
|
||||
null=True,
|
||||
db_column='image',
|
||||
variations={
|
||||
'profile': {
|
||||
"width": 256,
|
||||
"height": 256,
|
||||
"crop": True}})
|
||||
|
||||
# https://github.com/pennersr/django-allauth/issues/520
|
||||
@property
|
||||
def image(self):
|
||||
return self._image
|
||||
|
||||
@image.setter
|
||||
def image(self, value):
|
||||
self._image = value
|
||||
|
||||
def __str__(self):
|
||||
return self.username
|
||||
|
||||
|
@ -50,6 +61,7 @@ MEMBERSHIP_STRING = {
|
|||
}
|
||||
|
||||
MEMBERSHIP_STATUS = {
|
||||
'signup': 0, # This means the user has not completed signup
|
||||
'active': 1,
|
||||
'cancelled': 2
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ class UserDetailView(LoginRequiredMixin, DetailView):
|
|||
context['membership_form'] = MembershipJoinForm(initial={'amount': 20.00})
|
||||
return context
|
||||
|
||||
|
||||
class UserRedirectView(LoginRequiredMixin, RedirectView):
|
||||
permanent = False
|
||||
|
||||
|
@ -35,7 +36,7 @@ class UserRedirectView(LoginRequiredMixin, RedirectView):
|
|||
|
||||
|
||||
class UserUpdateView(LoginRequiredMixin, UpdateView):
|
||||
fields = ['name', 'image', ]
|
||||
fields = ['name', '_image', ]
|
||||
model = User
|
||||
|
||||
# send the user back to their own page after a successful update
|
||||
|
|
|
@ -52,7 +52,7 @@ lxml==3.7.3
|
|||
# Your custom requirements go here
|
||||
mock==2.0.0
|
||||
|
||||
gocardless
|
||||
gocardless_pro
|
||||
braintree==3.37.2
|
||||
|
||||
django-autofixture==0.12.1
|
||||
|
@ -62,5 +62,10 @@ git+https://github.com/olymk2/scaffold.git
|
|||
djangorestframework==3.6.3
|
||||
django-filter==1.0.2
|
||||
|
||||
#git+https://github.com/olymk2/dracos-markdown-editor.git
|
||||
draceditor==1.1.8
|
||||
# django-spirit
|
||||
django-djconfig
|
||||
django-haystack
|
||||
git+https://github.com/nitely/Spirit.git
|
||||
# git+https://github.com/olymk2/django-xforwardedfor-middleware.git
|
||||
django-xforwardedfor-middleware==2.0
|
||||
|
|
|
@ -11,3 +11,10 @@ factory-boy==2.8.1
|
|||
# pytest
|
||||
pytest-django==3.1.2
|
||||
pytest-sugar==0.8.0
|
||||
|
||||
django-test-plus==1.0.18
|
||||
# improved REPL
|
||||
ipdb==0.10.3
|
||||
|
||||
pytest-django==3.1.2
|
||||
pytest-sugar==0.8.0
|
||||
|
|
Loading…
Reference in New Issue