more work on payments and login

This commit is contained in:
Oliver Marks 2016-02-27 16:19:03 +00:00
parent d328575caf
commit 530aeb4ccc
12 changed files with 426 additions and 29 deletions

View File

@ -11,13 +11,13 @@ RUN \
apt-get upgrade -y && \
apt-get install -y libssl-dev libffi-dev && \
apt-get install -y software-properties-common python-software-properties && \
apt-get install -y python-pip python-dev python-requests python-lxml python-flask python-flask-login && \
apt-get install -y python-MySQLdb python-psycopg2 python-pip python-dev python-requests python-lxml python-flask python-flask-login && \
apt-get install -y cssmin slimit && \
add-apt-repository -y ppa:oly/ppa && \
apt-get update && \
apt-get install -y python-scaffold
RUN pip install gocardless paypalrestsdk
RUN pip install gocardless paypalrestsdk pytz
#allow access to flask
EXPOSE 5000 5002

View File

@ -9,7 +9,9 @@ To suggest changes to the site hit the fork button on the github page, then make
push your changes to your github account and create a pull request back into the main branch where it can be reviewed and merged
if everything is okay.
The simplest way to setup this site locally to test and make changes is to run.
The simplest way to setup this site is to use docker so please install that from this site https://docs.docker.com/engine/installation/
and make sure the quick start guide works https://docs.docker.com/machine/get-started/ then you can use the commands below to test and make changes.
docker build -t maidstone-hackspace .
docker run -p 5000:5000 maidstone-hackspace

View File

@ -195,6 +195,8 @@ def oauth(provider, state=None):
print '@@@@@@@'
print request.url
print oauth_provider.get('redirect_uri')
print oauth_provider.get('token_uri')
print oauth_provider.get('client_secret')
# code error is todo with authorisation response
oauth_session.fetch_token(
oauth_provider.get('token_uri'),
@ -202,27 +204,50 @@ def oauth(provider, state=None):
authorization_response=request.url,
verify=oauth_verify)
#~ r = oauth_session.get('https://api.github.com/user')
#~ print r.content
# Fetch a protected resource, i.e. user profile
r = oauth_session.get(oauth_provider.get('user_uri'))
print oauth_provider.get('user_uri')
response = oauth_session.get(oauth_provider.get('user_uri'))
oauth_user = response.json()
if provider is 'github':
oauth2_github_handle_user(oauth_user)
if provider is 'facebook':
oauth2_github_handle_user(oauth_user)
if provider is 'google':
oauth2_github_handle_user(oauth_user)
oauth_user = r.json()
print oauth_user
user_details = site_user.get_by_email({
'email': oauth_user.get('email')
email = oauth_user.get('email') or ''
user_details = site_user.fetch_oauth_login({
'username': oauth_user.get('login') or ''
}).get()
if oauth_user.get('login'):
#err what now we should probably error
pass
if not user_details:
flash('Your new profile has been created, and your now logged in')
site_user.create_oauth_login().execute({
'username': oauth_user.get('login') or '',
'provider': 'oauth'})
site_user.create().execute({
'email': oauth_user.get('email'),
'email': oauth_user.get('email') or '',
'password': 'oauth',
'profile_image': oauth_user.get('picture'),
'username': oauth_user.get('email'),
'first_name': oauth_user.get('given_name'),
'last_name': oauth_user.get('family_name')})
user_details = site_user.get_by_email({
'username': oauth_user.get('login'),
'first_name': oauth_user.get('given_name') or '',
'last_name': oauth_user.get('family_name') or ''})
user_details = site_user.get_by_ouath_login({
'email': oauth_user.get('email')
}).get()
@ -231,6 +256,10 @@ def oauth(provider, state=None):
site_user.update_last_login().execute(user_details)
return redirect('/profile')
def oauth2_github_handle_user(user):
print user
@authorize_pages.route("/change-password/<code>", methods=['GET'])
@authorize_pages.route("/change-password", methods=['GET'])
@ -343,7 +372,7 @@ def login_screen():
header('Members Login')
web.page.create('Member Login')
web.page.section(
web.login_box.create().enable_oauth('google').render()
web.login_box.create().enable_oauth('google').enable_oauth('facebook').enable_oauth('github').render()
)
#~ web.template.body.append(web.messages.render())
web.template.body.append(web.page.render())

View File

@ -23,19 +23,15 @@ database = {
'db': "maidstone_hackspace",
'port': 3306}
# secret so not included in default settings
oauth_live = False
oauth_redirect_uri = app_domain + '/oauth'
oauth_conf = {
'google': {},
'twitter': {}
}
google_calendar_id = 'contact@maidstone-hackspace.org.uk'
google_calendar_api_key = 'AIzaSyA98JvRDmplA9lVLZeKwrs1f2k17resLy0'
oauth_conf = {}
payment_providers = {}
google_calendar_id = ''
google_calendar_api_key = ''
if os.path.exists('config/settings_dev.py'):
print 'Using settings for dev enviroment'
@ -52,14 +48,14 @@ if os.path.exists('config/settings_live.py'):
with web.template as setup:
#css
#css for jquery, material sprite sheet and custom css
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/default.css" media="" type="text/css" />')
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/js/jquery-ui/themes/base/jquery-ui.css" media="" type="text/css" />')
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-navigation-white.css" media="" type="text/css" />')
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-action-white.css" media="" type="text/css" />')
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-content-white.css" media="" type="text/css" />')
#javascript
#javascript, using jquery and angular
setup.persistent_header('<script type="text/javascript" src="/static/js/jquery-2.1.4.min.js"></script>')
setup.persistent_header('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script>')
setup.persistent_header('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.js"></script>')

57
site/data/badges.py Normal file
View File

@ -0,0 +1,57 @@
import os
import time
from collections import defaultdict
from scaffold.core.data.select import select_data
from scaffold.core.data.insert import insert_data
from scaffold.core.data.update import update_data
from scaffold.core.data.delete import delete_data
from scaffold.core.data.sql import query_builder
query_builder.query_path = os.path.abspath('./data/sql/')
class create_badge(insert_data):
table = 'badges'
required = {'name'}
columns = {'name'}
class assign_badge(insert_data):
table = 'user_badges'
required = {'user_id', 'badge_id'}
columns = {'user_id', 'badge_id'}
class fetch_badges(select_data):
debug = True
table = 'badges'
required = {}
columns = {'id', 'name'}
class fetch_badge(select_data):
debug = True
table = 'user_badges'
required = {'user_id'}
columns = {'user_id', 'badge_id'}
columns_where = {'user_id', 'badge_id'}
columns_optional_where = {'user_id', 'badge_id'}
columns_optional = {'user_id', 'badge_id'}
class fetch_user_badges(select_data):
debug = True
table = 'user_badges'
columns = {'user_id', 'badge_id'}
#~ columns_where = {'user_id'}
columns_optional_where = {'user_id', 'badge_id'}
#~ columns_optional = {'user_id', 'badge_id'}
def fetch_user_badges_grouped():
badge_lookup = defaultdict(list)
for badge in fetch_user_badges():
badge_lookup[badge.get('user_id')].append(badge.get('badge_id'))
return badge_lookup
class remove_badge(delete_data):
table = 'user_badges'
required = {'id'}
columns = {'id'}

View File

@ -113,6 +113,7 @@ class get_by_email(select_data):
query_file = 'get_users.sql'
columns_where = {'email'}
class get_by_username(select_data):
required = {'email'}
query_file = 'get_user_credentials.sql'
@ -122,3 +123,33 @@ class authorize(select_data):
required = {'id'}
query_file = 'get_user_credentials.sql'
columns_where = {'id'}
class create_oauth_login(insert_data):
required = {'username', 'provider'}
query_file = 'get_user_by_oauth_username.sql'
columns_where = {'username', 'provider'}
def calculated_data(self):
return {'registered': time.strftime('%Y-%m-%d %H:%M:%S')}
def set(self, data):
data['registered'] = time.strftime('%Y-%m-%d %H:%M:%S')
super(create, self).set(data)
class update_oauth_login(update_data):
required = {'username', 'provider'}
query_file = 'get_user_by_oauth_username.sql'
columns_where = {'username', 'provider'}
def calculated_data(self):
return {'registered': time.strftime('%Y-%m-%d %H:%M:%S')}
def set(self, data):
data['registered'] = time.strftime('%Y-%m-%d %H:%M:%S')
super(create, self).set(data)
class fetch_oauth_login(select_data):
required = {'username', 'provider'}
query_file = 'get_user_by_oauth_username.sql'
columns_where = {'username', 'provider'}

View File

@ -0,0 +1,3 @@
select users.id as user_id, user_detail.id as user_detail_id, username, first_name, last_name, status, email, users.profile_image, last_login, description, skills
from users
join user_badges on users.id=user_badges.user_id

View File

@ -0,0 +1,2 @@
select user_id, username, description, skills
from user_detail

View File

@ -0,0 +1 @@
select user_id, last_login from user_oauth

276
site/libs/payments.py Normal file
View File

@ -0,0 +1,276 @@
from pprint import pprint
from config import settings
from datetime import datetime, timedelta
import pytz
import gocardless
import paypalrestsdk as paypal
from config.settings import app_domain
PROVIDER_ID = {'gocardless':1, 'paypal': 2}
PROVIDER_NAME = {1: 'gocardless', 2: 'paypal'}
class payment:
"""
paypal reference = https://github.com/paypal/PayPal-Python-SDK
gocardless reference = https://github.com/paypal/PayPal-Python-SDK
"""
#~ def __call__(self, **args):
#~ return self
def __init__(self, provider='gocardless', style='payment', mode='sandbox'):
self.provider = provider
self.environment = int(mode=='production')
self.provider_id = PROVIDER_ID.get(provider)
if provider == 'paypal':
print settings.payment_providers[provider]['credentials']
paypal.configure(**settings.payment_providers[provider]['credentials'])
return
#~ environment = int('production' = settings.payment_providers[provider]['environment'])
gocardless.environment = settings.payment_providers[provider]['environment']
gocardless.set_details(**settings.payment_providers[provider]['credentials'])
merchant = gocardless.client.merchant()
def lookup_provider_by_id(self, provider_id):
return PROVIDER_NAME.get(provider_id, None)
def make_donation(self, amount, reference, redirect_success, redirect_failure):
if self.provider == 'paypal':
payment = paypal.Payment({
"intent": "sale",
"payer": {"payment_method": "paypal"},
"redirect_urls": {
"return_url": redirect_success,
"cancel_url": redirect_failure},
"transactions": [{
"amount": {
"total": amount,
"currency": "GBP"},
"description": reference}]})
payment_response = payment.create()
if payment_response:
for link in payment.links:
if link.method == "REDIRECT":
redirect_url = str(link.href)
return str(redirect_url)
else:
print("Error while creating payment:")
print(payment.error)
if self.provider == 'gocardless':
return gocardless.client.new_bill_url(
amount,
name=reference,
redirect_uri=redirect_success)
return 'Error something went wrong'
def fetch_subscriptions(self):
if self.provider == 'gocardless':
merchant = gocardless.client.merchant()
for paying_member in merchant.subscriptions():
user=paying_member.user()
yield {
'email': user.email,
'start_date': paying_member.created_at,
'reference': paying_member.id,
'amount': paying_member.amount}
if self.provider == 'paypal':
#~ start_date, end_date = "2014-07-01", "2014-07-20"
billing_agreement = paypal.BillingAgreement.find('')
print billing_agreement
print dir(billing_agreement)
#~ print billing_agreement.search_transactions(start_date, end_date)
#~ transactions = billing_agreement.search_transactions(start_date, end_date)
payment_history = paypal.Payment.all({"count": 2})
# List Payments
print("List Payment:")
print payment_history
for payment in payment_history.payments:
print(" -> Payment[%s]" % (payment.id))
#~ print paypal.BillingAgreement.all()
history = paypal.BillingPlan.all(
{"status": "CREATED", "page_size": 5, "page": 1, "total_required": "yes"})
print(history)
print("List BillingPlan:")
for plan in history.plans:
print dir(plan)
print plan.to_dict()
print(" -> BillingPlan[%s]" % (plan.id))
#~ merchant = gocardless.client.merchant()
#~ for paying_member in merchant.subscriptions():
#~ user=paying_member.user()
#~ yield {
#~ 'email': user.email,
#~ 'start_date': paying_member.created_at,
#~ 'reference': paying_member.id,
#~ 'amount': paying_member.amount}
def subscribe_confirm(self, args):
if self.provider == 'gocardless':
response = gocardless.client.confirm_resource(args)
subscription = gocardless.client.subscription(args.get('resource_id'))
return {
'amount': subscription.amount,
'start_date': subscription.created_at,
'reference': subscription.id
}
if self.provider == 'paypal':
payment_token = args.get('token', '')
billing_agreement_response = paypal.BillingAgreement.execute(payment_token)
amount = 0
print billing_agreement_response
print billing_agreement_response.id
for row in billing_agreement_response.plan.payment_definitions:
amount = row.amount.value
return {
'amount': amount,
'start_date': billing_agreement_response.start_date,
'reference': billing_agreement_response.id
}
return None
def unsubscribe(self, reference):
if self.provider == 'gocardless':
print 'unsubscribe gocardless'
subscription = gocardless.client.subscription(reference)
print subscription.cancel()
if self.provider == 'paypal':
# this may be wrong
# ManageRecurringPaymentsProfileStatus
print reference
billing_plan = paypal.BillingAgreement.find(reference)
print billing_plan
print(billing_plan.error)
#~ billing_plan.replace([{"op": "replace","path": "/","value": {"state":"DELETED"}}])
print(billing_plan.error)
#~ invoice = paypal.Invoice.find(reference)
options = {
"subject": "Cancelling membership",
"note": "Canceling invoice",
"send_to_merchant": True,
"send_to_payer": True
}
if billing_plan.cancel(options): # return True or False
print("Invoice[%s] cancel successfully" % (invoice.id))
else:
print(billing_plan.error)
def subscribe(self, amount, name, redirect_success, redirect_failure, interval_unit='month', interval_length='1'):
print '%s/profile/gocardless' % app_domain
if self.provider == 'gocardless':
return gocardless.client.new_subscription_url(
amount=amount,
interval_length=interval_length,
interval_unit=interval_unit,
name=name,
redirect_uri=redirect_success)
if self.provider == 'paypal':
billing_plan = paypal.BillingPlan({
"name": name,
"description": "Membership subscription",
"merchant_preferences": {
"auto_bill_amount": "yes",
"cancel_url": redirect_failure,
"initial_fail_amount_action": "continue",
"max_fail_attempts": "1",
"return_url": redirect_success,
"setup_fee": {
"currency": "GBP",
"value": amount
}
},
"payment_definitions": [{
"amount": {
"currency": "GBP",
"value": amount
},
"cycles": "0",
"frequency": interval_unit,
"frequency_interval": interval_length,
"name": "Regular 1",
"type": "REGULAR"
}
],
"type": "INFINITE"
})
print 'create bill'
response = billing_plan.create()
billing_plan = paypal.BillingPlan.find(billing_plan.id)
if billing_plan.activate():
start_date = datetime.utcnow() + timedelta(minutes=10)
billing_agreement = paypal.BillingAgreement({
"name": billing_plan.name,
"description": name,
"start_date": start_date.strftime('%Y-%m-%dT%H:%M:%SZ'),
"plan": {"id": str(billing_plan.id)},
"payer": {"payment_method": "paypal"}
})
if billing_agreement.create():
print 'billing agreement id'
print billing_agreement.id
for link in billing_agreement.links:
if link.rel == "approval_url":
approval_url = link.href
return approval_url
else:
print(billing_agreement.error)
print 'failed'
def confirm(self, args):
confirm_details = {}
print '---------------------'
print args
from pprint import pprint
if self.provider == 'paypal':
print args.get('paymentId')
print args.get('PayerID')
payment = paypal.Payment.find(args.get('paymentId'))
pprint(payment)
print pprint(payment)
print payment
confirm_details['name'] = payment['payer']['payer_info'].first_name + ' ' + payment['payer']['payer_info'].last_name
confirm_details['user'] = payment['payer']['payer_info'].email
confirm_details['status'] = payment.state
confirm_details['amount'] = payment['transactions'][0]['amount'].total
confirm_details['created'] = payment.create_time
confirm_details['reference'] = payment.id
pprint(confirm_details)
return confirm_details
if self.provider == 'gocardless':
bill_id = args.get('resource_id')
gocardless.client.confirm_resource(args)
if bill_id:
bill = gocardless.client.bill(bill_id)
confirm_details['name'] = bill.name
confirm_details['user'] = bill.user
confirm_details['status'] = bill.status
confirm_details['amount'] = bill.amount
#~ confirm_details['amount_minus_fees'] = bill.amount_minus_fees
confirm_details['created'] = bill.created_at
confirm_details['reference'] = bill_id
return confirm_details
return None

View File

@ -11,7 +11,7 @@ from data.site_user import get_user_details, update_membership, update_membershi
from data.profile import update_description, create_description, fetch_users
from data import badges
from data import members
from config.settings import gocardless_environment, gocardless_credentials
#~ from config.settings import gocardless_environment, gocardless_credentials
from config.settings import app_domain
from libs.payments import payment

View File

@ -19,7 +19,7 @@ class control(base_widget):
if 'google' in self.oauth_enabled:
htm += '<a title="Login with Google" href="/oauth/google/login"><img src="/static/images/oauth/google.png" /></a><br />'
if 'facebook' in self.oauth_enabled:
htm += '<a title="Login with facebook" href="/oauth/facebook">Facebook</a>.<br />'
htm += '<a title="Login with facebook" href="/oauth/facebook/login">Facebook</a>.<br />'
if 'github' in self.oauth_enabled:
htm += '<a title="Login with twitter" href="/oauth/github/login">GitHub</a><br />'
htm += '</div>'