diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..7ad473a --- /dev/null +++ b/.drone.yml @@ -0,0 +1,32 @@ +build: + base: website + image: olymk2/mhackspace + pull: true + commands: + - cd website + - sleep 20 + - cat /usr/lib/python3.5/site-packages/scaffold/__main__.py + - python3 -m "scaffold" + - nose2-3 + + +compose: + database: + image: olymk2/mariadb + pull: true + volume: + - /website/data/migrate:/docker-entrypoint-initdb.d + environment: + MYSQL_DATABASE: maidstone_hackspace + MYSQL_USER: mhackspace + MYSQL_PASSWORD: mhackspace + MYSQL_ROOT_PASSWORD: mhackspace + +notify: + irc: + prefix: build + nick: drone + channel: #maidstone-hackspace + server: + host: chat.freenode.net + port: 6667 diff --git a/Dockerfile b/Dockerfile index a452c81..fce0d4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,11 +9,12 @@ ENV SITE_FOLDER /etc/sites/mysite/ RUN apk add --update --no-cache libssl1.0 libxml2 libxslt ca-certificates RUN apk add --update --no-cache py-psycopg2 py-lxml py-flask py-pillow py-openssl py-cffi -RUN apk add --update --no-cache build-base make bzr python3-dev libffi-dev openssl-dev libxml2-dev libxslt-dev && \ +RUN apk add --update --no-cache build-base make git bzr python3-dev libffi-dev openssl-dev libxml2-dev libxslt-dev && \ pip3 install lxml && \ pip3 install --no-cache-dir lxml dateutils requests requests-oauthlib mailer gocardless paypalrestsdk pytz nose2 oauthlib flask flask-login pymysql misaka && \ - pip3 install --no-cache-dir bzr+lp:scaffold/trunk#egg=scaffold && \ - apk del build-base make bzr python3-dev libffi-dev openssl-dev libxml2-dev libxslt-dev + pip3 install --upgrade git+git://github.com/olymk2/scaffold.git@master && \ + #pip3 install --upgrade --no-cache-dir bzr+lp:scaffold/trunk#egg=scaffold && \ + apk del build-base make git bzr python3-dev libffi-dev openssl-dev libxml2-dev libxslt-dev # RUN pip3 install --no-cache-dir dateutils requests requests-oauthlib gocardless paypalrestsdk pytz nose2 oauthlib flask flask-login pymysql misaka # RUN pip3 install --no-cache-dir bzr+lp:scaffold/trunk#egg=scaffold diff --git a/config/cron/maidstone-hackspace b/config/cron/maidstone-hackspace index 34ba869..03c085b 100644 --- a/config/cron/maidstone-hackspace +++ b/config/cron/maidstone-hackspace @@ -1,3 +1,3 @@ PATH=/usr/local/sbin:/usr/local/bin MAILTO=contact@maidstone-hackspace.org.uk -0 2 * * * python /var/www/maidstone-hackspace.org.uk/site/generate.py +0 2 * * * /var/www/live-maidstone-hackspace.org.uk/site/generate.sh diff --git a/config/letsencrypt/generate.sh b/config/letsencrypt/generate.sh index b373a99..4a15f57 100644 --- a/config/letsencrypt/generate.sh +++ b/config/letsencrypt/generate.sh @@ -1,2 +1,3 @@ +#!/usr/local/env sh ./letsencrypt-auto certonly -d maidstone-hackspace.org.uk -d live.maidstone-hackspace.org.uk ./letsencrypt-auto certonly -d test.maidstone-hackspace.org.uk diff --git a/docker-compose.yml b/docker-compose.yml index b7cde75..0991e7a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,10 +29,10 @@ services: restart: always mariadb: - image: mariadb + image: olymk2/mariadb network_mode: bridge - ports: - - "3300:3306" +# ports: +# - "3300:3306" environment: MYSQL_DATABASE: maidstone_hackspace MYSQL_USER: mhackspace diff --git a/website/config/settings.py b/website/config/settings.py index fae27dc..1388400 100644 --- a/website/config/settings.py +++ b/website/config/settings.py @@ -22,7 +22,7 @@ database = { 'charset': 'utf8', 'use_unicode': True, 'type': 'mysql', - 'host': 'database', + 'host': '127.0.0.1', 'user': 'mhackspace', 'passwd': "mhackspace", 'db': "maidstone_hackspace", diff --git a/website/config/settings_docker.py b/website/config/settings_docker.py index 6a7466d..85718fb 100644 --- a/website/config/settings_docker.py +++ b/website/config/settings_docker.py @@ -5,7 +5,7 @@ database = { 'charset': 'utf8', 'use_unicode': True, 'type': 'mysql', - 'host': 'database', + 'host': '127.0.0.1', 'user': 'mhackspace', 'passwd': "mhackspace", 'db': "maidstone_hackspace", diff --git a/website/libs/brintree.py b/website/libs/brintree.py new file mode 100644 index 0000000..646a9aa --- /dev/null +++ b/website/libs/brintree.py @@ -0,0 +1,310 @@ +from pprint import pprint +from config import settings +from datetime import datetime, timedelta +import pytz +import braintree +import gocardless +import paypalrestsdk as paypal + +from config.settings import app_domain + +PROVIDER_ID = {'gocardless':1, 'paypal': 2, 'braintree': 3} +PROVIDER_NAME = {1: 'gocardless', 2: 'paypal', 3: 'braintree'} + +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': + paypal.configure(**settings.payment_providers[provider]['credentials']) + print(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() + print('payment create') + if payment_response: + print(payment_response) + for link in payment.links: + if link.method == "REDIRECT": + redirect_url = str(link.href) + print(redirect_url) + 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': + #~ I-S39170DK26AF + #~ 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': + print('subscribe_confirm') + 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'): + 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 = {} + confirm_details['successfull'] = False + 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) + + + if payment.execute({"payer_id": args.get('PayerID')}): # return True or False + confirm_details['successfull'] = True + print("Payment[%s] execute successfully" % (args.get('paymentId'))) + else: + print(payment.error) + 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 + confirm_details['successfull'] = True + return confirm_details + return None + +conf = { + 'env': braintree.Environment.Sandbox, + 'merchant_id': 'b3sdmyczd3fz6b3p', + 'public_key': 'rxb7yffm3tk758rqi', + 'private_key': '62f92d2b00a451c40fdf5fbca54f0421' +} + +braintree.Configuration.configure(*conf) +token = braintree.ClientToken.generate() + +result = braintree.Transaction.sale({ + "amount": "10.00", + "payment_method_nonce": token, + "options": { + "submit_for_settlement": True + } +}) diff --git a/website/libs/payment_provider.py b/website/libs/payment_provider.py new file mode 100644 index 0000000..b95a622 --- /dev/null +++ b/website/libs/payment_provider.py @@ -0,0 +1,63 @@ +import braintree +import gocardless +import paypalrestsdk as paypal + + +PROVIDER_ID = {'gocardless':1, 'paypal': 2, 'braintree': 3} +PROVIDER_NAME = {1: 'gocardless', 2: 'paypal', 3: 'braintree'} + +class payment: + def __call__(self, **args): + raise NotImplemented + + def __init__(self, provider='gocardless', style='payment', mode='sandbox'): + raise NotImplemented + + 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): + raise NotImplemented + + def fetch_subscriptions(self): + raise NotImplemented + + def subscribe_confirm(self, args): + raise NotImplemented + + def unsubscribe(self, reference): + raise NotImplemented + + def subscribe(self, amount, name, redirect_success, redirect_failure, interval_unit='month', interval_length='1'): + raise NotImplemented + + def confirm(self, args): + raise NotImplemented + +# conf = { +# 'environment': braintree.Environment.Sandbox, +# 'merchant_id': 'b3sdmyczd3fz6b3p', +# 'public_key': 'rxb7yffm3tk758rqi', +# 'private_key': '62f92d2b00a451c40fdf5fbca54f0421' +# } + +print(braintree.Configuration.configure(braintree.Environment.Sandbox, merchant_id='b3sdmyczd3fz6b3p', public_key='rxb7yffm3tk758rq', private_key='62f92d2b00a451c40fdf5fbca54f0421')) +#braintree.Configuration.configure(**conf) +token = braintree.ClientToken.generate() +print(token) +result = braintree.Transaction.sale({ + "amount": "10.00", + "payment_method_nonce": token, + "options": { + "submit_for_settlement": True + } + }) + +result = braintree.Subscription.create({ + "payment_method_token": token, + "plan_id": "membership", + "price": "20.00" +}) + + +print(result) diff --git a/website/libs/payments.py b/website/libs/payments.py index 554586d..7585288 100644 --- a/website/libs/payments.py +++ b/website/libs/payments.py @@ -10,6 +10,17 @@ from config.settings import app_domain PROVIDER_ID = {'gocardless':1, 'paypal': 2} PROVIDER_NAME = {1: 'gocardless', 2: 'paypal'} + +class provider(object): + # Create based on class name: + def factory(type): + #return eval(type + "()") + if type == "Circle": return Circle() + if type == "Square": return Square() + assert 0, "Bad shape creation: " + type + factory = staticmethod(factory) + + class payment: """ paypal reference = https://github.com/paypal/PayPal-Python-SDK diff --git a/website/setup.py b/website/setup.py deleted file mode 100644 index 4e04aaa..0000000 --- a/website/setup.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python - -from distutils.core import setup - -setup(name='website', - version='1.0', - description='Maidstone hackspace websites', - packages=['website'], - ) diff --git a/website/tests/test_add_badge.py b/website/tests/test_add_badge.py index 6d286b5..7ddef41 100644 --- a/website/tests/test_add_badge.py +++ b/website/tests/test_add_badge.py @@ -31,7 +31,7 @@ class TestBadges(TestBase): self.assertTrue([a for a in fetch_badge({'badge_id': '1', 'user_id': '1' })]) def test_badge_grouping(self): - self.assertEquals(fetch_user_badges_grouped(),{1L: [1L, 2L, 3L]} ) + self.assertEquals(fetch_user_badges_grouped(),{1: [1, 2, 3]} ) def testSelectingBadges(self): # this record should exist @@ -49,7 +49,7 @@ class TestBadges(TestBase): def testAddingBadges(self): create_badge().execute({'name': 'badget'}) - self.assertEquals(fetch_user_badges_grouped(),{1L: [1L, 2L, 3L]} ) + self.assertEquals(fetch_user_badges_grouped(),{1: [1, 2, 3]} ) def testRemoveBadges(self): remove_badge().execute({'id': '1' }) diff --git a/website/tests/test_create_users.py b/website/tests/test_create_users.py index 0e0c7da..255ae96 100644 --- a/website/tests/test_create_users.py +++ b/website/tests/test_create_users.py @@ -8,7 +8,7 @@ from werkzeug.security import generate_password_hash, check_password_hash from config import settings from data import site_user from scaffold.core.data.database import db - +print(settings.database) class TestBasePage(unittest.TestCase): #~ class TestBasePage(TestDataSetup): diff --git a/website/tests/test_oauth_login.py b/website/tests/test_oauth_login.py index eb51b5c..fec5ca4 100644 --- a/website/tests/test_oauth_login.py +++ b/website/tests/test_oauth_login.py @@ -4,9 +4,9 @@ import os, sys import unittest from collections import defaultdict -from config import settings +from website.config import settings from scaffold.core.data.database import db -from data import site_user +from website.data import site_user class TestBasePage(unittest.TestCase):