From dbe1bf1cf2a13352aeea9182a0d51f9fed8d2274 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 1 Feb 2021 22:53:08 +0100 Subject: [PATCH 1/3] adding fakturoid basic functions --- rowers/fakturoid.py | 116 +++++++++++++++++++++++++++++++++++++ rowsandall_app/settings.py | 15 +++++ 2 files changed, 131 insertions(+) create mode 100644 rowers/fakturoid.py diff --git a/rowers/fakturoid.py b/rowers/fakturoid.py new file mode 100644 index 00000000..cf4f8489 --- /dev/null +++ b/rowers/fakturoid.py @@ -0,0 +1,116 @@ +import requests +import json +from requests.auth import HTTPBasicAuth + +from rowsandall_app.settings import ( + FAKTUROID_EMAIL, FAKTUROID_API_KEY, + FAKTUROID_SLUG +) + +slug = FAKTUROID_SLUG + +invoices_url = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/invoices.json'.format(slug=slug) +contacts_url = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/subjects.json'.format(slug=slug) +contacts_search_url = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/subjects/search.json'.format(slug=slug) + +auth = HTTPBasicAuth(FAKTUROID_EMAIL, FAKTUROID_API_KEY) +headers = { + 'Content-Type': 'application/json', + 'User-Agent': 'rowsandall (admin@rowsandall.com)' +} + +def get_contacts(rower): + res = requests.get(contacts_url, auth=auth, headers=headers) + url = contacts_search_url+'?query='+rower.user.email + + res = requests.get(url, auth=auth, headers=headers) + + if res.status_code != 200: + return None + + if len(res.json())==1: + r = res.json()[0] + return r['id'] + + return None + + +def create_contact(rower): + post_data = { + "name": str(rower), + "street": rower.street_address, + "street2": None, + "city": rower.city, + "zip": rower.postal_code, + "country": rower.country.code, + "vat_no": "", + "bank_account": "", + "iban": "", + "variable_symbol": rower.id, + "full_name": rower.user.first_name+" "+rower.user.last_name, + "email": rower.user.email, + "email_copy": "", + "phone": "", + "web": "" + } + + res = requests.post(contacts_url, data=json.dumps(post_data), auth=auth,headers=headers) + + return res + + + +def create_invoice(rower,amount,braintreeid,send=False): + + r_id = get_contacts(rower) + + if not r_id: + return 0 + + post_data = { + 'subject_id': r_id, + 'custom_id': braintreeid, + 'language':'en', + 'payment_method': 'card', + 'currency': 'EUR', + 'paid_amount': amount, + 'status': 'paid', + 'lines': [{ + 'name': 'Rowsandall Subscription', + 'quantity': '1', + 'unit_price': amount, + 'vat_rate': 0, + } + ] + } + + res = requests.post(invoices_url, data=json.dumps(post_data), auth=auth,headers=headers) + + if res.status_code not in [200,201]: + return 0 + + url = res.json()['url'] + id = res.json()['id'] + + urlpay = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/invoices/{id}/fire.json?event=pay'.format( + id=id,slug=slug + ) + urlsend = 'https://app.fakturoid.cz/api/v2/accounts/{slug}/invoices/{id}/fire.json?event=deliver'.format( + id=id,slug=slug + ) + + + res = requests.post(urlpay,auth=auth,headers=headers) + + if res.status_code not in [200,201]: + return 0 + + if send: + res = requests.post(urlsend,auth=auth,headers=headers) + + return id + + #curl -u vas@email.cz:API_TOKEN -H 'User-Agent: YourApp (yourname@example.com)' \ + # -H 'Content-Type: application/json' \ + # -X POST -d '{ "subject_id": "28" }' \ + # https://app.fakturoid.cz/api/v2/accounts/{slug}/invoices.json diff --git a/rowsandall_app/settings.py b/rowsandall_app/settings.py index e57f83ec..7b5bd794 100644 --- a/rowsandall_app/settings.py +++ b/rowsandall_app/settings.py @@ -537,6 +537,21 @@ try: except KeyError: PAYMENT_PROCESSING_ON = False +try: + FAKTUROID_API_KEY = CFG['fakturoid_api_key'] +except KeyError: + FAKTUROID_API_KEY = '' + +try: + FAKTUROID_EMAIL = CFG['fakturoid_email'] +except KeyError: + FAKTUROID_EMAIL = '' + +try: + FAKTUROID_SLUG = CFG['fakturoid_slug'] +except KeyError: + FAKTUROID_SLUG = '' + # ID obfuscation try: OPAQUE_SECRET_KEY = CFG['opaque_secret_key'] From aa5b2c82f0b7df18ec217d8b6aac32797b1a0184 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 1 Feb 2021 22:58:45 +0100 Subject: [PATCH 2/3] some comments --- rowers/fakturoid.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rowers/fakturoid.py b/rowers/fakturoid.py index cf4f8489..bcb50190 100644 --- a/rowers/fakturoid.py +++ b/rowers/fakturoid.py @@ -34,7 +34,7 @@ def get_contacts(rower): return None - +# this should be triggered on braintree payment def create_contact(rower): post_data = { "name": str(rower), @@ -58,8 +58,7 @@ def create_contact(rower): return res - - +# this should be triggered by a Braintree webhook def create_invoice(rower,amount,braintreeid,send=False): r_id = get_contacts(rower) From c41b7611eeeb110a0e276e6f9e8465c0164c3616 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 2 Feb 2021 20:28:36 +0100 Subject: [PATCH 3/3] first version dummy webhook for braintree --- rowers/braintreestuff.py | 11 +++++++++++ rowers/urls.py | 1 + rowers/views/paymentviews.py | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/rowers/braintreestuff.py b/rowers/braintreestuff.py index 4d54595c..3a1aec64 100644 --- a/rowers/braintreestuff.py +++ b/rowers/braintreestuff.py @@ -52,6 +52,17 @@ else: from rowers.models import Rower,PaidPlan, CoachingGroup from rowers.utils import ProcessorCustomerError +def webhook(request): + webhook_notification = gateway.webhook_notification.parse( + str(request.POST['bt_signature']), + request.POST['bt_payload']) + + # Example values for webhook notification properties + print(webhook_notification.kind) # "subscription_went_past_due" + print(webhook_notification.timestamp) # "Sun Jan 1 00:00:00 UTC 2012" + + return Response(status=200) + def create_customer(rower,force=False): if not rower.customer_id or force: result = gateway.customer.create( diff --git a/rowers/urls.py b/rowers/urls.py index 87a5cfb8..de7a0fb2 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -921,6 +921,7 @@ urlpatterns = [ re_path(r'^history/$',views.history_view,name="history_view"), re_path(r'^history/user/(?P\d+)/data/$',views.history_view_data,name="history_view_data"), re_path(r'^history/data/$',views.history_view_data,name="history_view_data"), + re_path(r'^braintree/$',views.braintree_webhook_view,name="braintree_webhook_view"), ] if settings.DEBUG: diff --git a/rowers/views/paymentviews.py b/rowers/views/paymentviews.py index 344f84ac..61daa11c 100644 --- a/rowers/views/paymentviews.py +++ b/rowers/views/paymentviews.py @@ -5,6 +5,10 @@ from __future__ import unicode_literals from rowers.views.statements import * +def braintree_webhook_view(request): + braintreestuff.webhook(request) + return JsonResponse(status=200) + def paidplans_view(request): if not request.user.is_anonymous: r = request.user.rower