diff --git a/chezbetty/__init__.py b/chezbetty/__init__.py index 7b30ecf..e89caef 100644 --- a/chezbetty/__init__.py +++ b/chezbetty/__init__.py @@ -87,6 +87,8 @@ def debug(request): config.add_route('item_request', '/item/request') config.add_route('item_request_new', '/item/request/new') + config.add_route('paydebt', '/paydebt/{uniqname}') + config.add_route('paydebt_submit', '/paydebt/{uniqname}/submit') # TERMINAL VIEWS config.add_route('user', '/terminal/profile/{umid}') diff --git a/chezbetty/templates/admin/email_deadbeats.jinja2 b/chezbetty/templates/admin/email_deadbeats.jinja2 index d84e642..837915e 100644 --- a/chezbetty/templates/admin/email_deadbeats.jinja2 +++ b/chezbetty/templates/admin/email_deadbeats.jinja2 @@ -4,8 +4,8 @@ Betty {{ user.balance|format_currency|safe }}. Please pay at your earliest convenience.

-

To pay with a credit card, please visit -your user page. +

+Click here to pay online.

Thank you,

Chez Betty

diff --git a/chezbetty/templates/admin/email_endofsemester.jinja2 b/chezbetty/templates/admin/email_endofsemester.jinja2 index e4ab4c4..ddab150 100644 --- a/chezbetty/templates/admin/email_endofsemester.jinja2 +++ b/chezbetty/templates/admin/email_endofsemester.jinja2 @@ -6,8 +6,8 @@ by graduate students with limited resources. You currently owe Betty {{ user.balance|format_currency|safe }}. We would greatly appreciate if you could settle your debts with Betty.

-

To pay with a credit card, please visit -your user page. +

+Click here to pay online.

Thank you,

Chez Betty

diff --git a/chezbetty/templates/admin/users_email.jinja2 b/chezbetty/templates/admin/users_email.jinja2 index 69c1881..63a7743 100644 --- a/chezbetty/templates/admin/users_email.jinja2 +++ b/chezbetty/templates/admin/users_email.jinja2 @@ -15,7 +15,7 @@ $ in debt to Chez Betty encouraging them to settle up.

-{% set user = {'name': '<< name >>', 'balance': '<< balance >>'} %} +{% set user = {'name': '<< name >>', 'balance': '<< balance >>', 'uniqname': '<< uniqname >> '} %} {% include "email_deadbeats.jinja2" %}
@@ -29,7 +29,7 @@ $ in debt to Chez Betty encouraging them to settle up.

-{% set user = {'name': '<< name >>', 'balance': '<< balance >>'} %} +{% set user = {'name': '<< name >>', 'balance': '<< balance >>', 'uniqname': '<< uniqname >> '} %} {% include "email_endofsemester.jinja2" %}
diff --git a/chezbetty/templates/base.jinja2 b/chezbetty/templates/base.jinja2 index b8c48e9..7c08834 100644 --- a/chezbetty/templates/base.jinja2 +++ b/chezbetty/templates/base.jinja2 @@ -104,9 +104,11 @@ {% endblock %} {% set timeout = timeout|default(60*1000*5) -%} + {% if timeout %} + {% endif %} + + +
+ +{{ _('For more options and transaction history, log in to your user account') }} + +{# +
+ + + + +
+#} + + +{% else %} {# user.balance >= 0 #} + +

+ {{ _('You currently do not owe Chez Betty any money.') }} +

+

+ {{ _('Thank you for paying your debts.') }} +

+
+ + + {{ _('Log into your user account') }} + + + {{ _('Chez Betty Home') }} + + +{% endif %} + +{% endblock %} + diff --git a/chezbetty/utility.py b/chezbetty/utility.py index 6f9ec4e..6cf4f4c 100644 --- a/chezbetty/utility.py +++ b/chezbetty/utility.py @@ -1,7 +1,10 @@ import datetime +from decimal import Decimal import itertools import qrcode import qrcode.image.svg +import stripe +import traceback from pyramid.renderers import render from pyramid.threadlocal import get_current_registry @@ -255,3 +258,67 @@ def timeseries_balance_total_daily(rows): return out +def post_stripe_payment( + datalayer, # Need to pass as argument to avoid circular import, ugh + request, + token, + amount, + total_cents, + account_making_payment, + account_depositing_into, + ): + # See http://stripe.com/docs/tutorials/charges + stripe.api_key = request.registry.settings['stripe.secret_key'] + + charge = (amount + 0.3) / 0.971 + fee = charge - amount + if total_cents != int(round((amount + fee)*100)): + print("Stripe total mismatch. total_cents {} != {}".format( + total_cents, int(round((amount + fee)*100)))) + request.session.flash('Unexpected error processing transaction. Card NOT charged.', 'error') + return False + amount = Decimal(amount) + + if amount <= 0.0: + request.session.flash( + _('Deposit amount must be greater than $0.00. Card NOT charged.'), + 'error' + ) + return False + + try: + charge = stripe.Charge.create( + amount = total_cents, + currency="usd", + source=token, + description=account_making_payment.uniqname+'@umich.edu' + ) + + except stripe.CardError as e: + traceback.print_exc() + request.session.flash('Card error processing transaction. Card NOT charged.', 'error') + return False + except stripe.StripeError as e: + traceback.print_exc() + request.session.flash('Unexpected error processing transaction. Card NOT charged.', 'error') + request.session.flash('Please e-mail chezbetty@umich.edu so we can correct this error', 'error') + return False + + try: + deposit = datalayer.cc_deposit( + account_making_payment, + account_depositing_into, + amount, + charge['id'], + charge['source']['last4']) + + request.session.flash('Deposit added successfully.', 'success') + + except Exception as e: + traceback.print_exc() + request.session.flash('A unknown error has occured.', 'error') + request.session.flash('Your card HAS been charged, but your account HAS NOT been credited.', 'error') + request.session.flash('Please e-mail chezbetty@umich.edu so we can correct this error', 'error') + + return True + diff --git a/chezbetty/views.py b/chezbetty/views.py index 31116b6..7bb9c80 100644 --- a/chezbetty/views.py +++ b/chezbetty/views.py @@ -28,6 +28,7 @@ from .utility import user_password_reset from .utility import send_email +from .utility import post_stripe_payment from pyramid.security import Allow, Everyone, remember, forget @@ -116,3 +117,40 @@ def users(request): .filter(User.balance < -5)\ .order_by(User.balance).all() return {'users': users} + + +@view_config(route_name='paydebt', renderer='templates/paydebt.jinja2') +def paydebt(request): + uniqname = request.matchdict['uniqname'] + user = User.from_uniqname(uniqname, local_only=True) + return { + 'user': user, + 'stripe_pk': request.registry.settings['stripe.publishable_key'], + } + +@view_config(route_name='paydebt_submit', + request_method='POST', + renderer='json', + ) +def paydebt_submit(request): + uniqname = request.matchdict['uniqname'] + user = User.from_uniqname(uniqname, local_only=True) + + print(request.POST) + + token = request.POST['stripeToken'] + amount = float(request.POST['betty_amount']) + total_cents = int(request.POST['betty_total_cents']) + + post_stripe_payment( + datalayer, + request, + token, + amount, + total_cents, + user, + user, + ) + + return {} + diff --git a/chezbetty/views_user.py b/chezbetty/views_user.py index ac16afc..d6e683d 100644 --- a/chezbetty/views_user.py +++ b/chezbetty/views_user.py @@ -36,12 +36,13 @@ from .models.pool import Pool from .models.pool_user import PoolUser +from .utility import post_stripe_payment + from pyramid.security import Allow, Everyone, remember, forget import chezbetty.datalayer as datalayer from .btc import Bitcoin, BTCException -import stripe import uuid import math import pytz @@ -132,9 +133,6 @@ def user_deposit_cc_custom(request): request_method='POST', permission='user') def user_deposit_cc_submit(request): - # See http://stripe.com/docs/tutorials/charges - stripe.api_key = request.registry.settings['stripe.secret_key'] - token = request.POST['stripeToken'] amount = float(request.POST['betty_amount']) total_cents = int(request.POST['betty_total_cents']) @@ -155,61 +153,15 @@ def user_deposit_cc_submit(request): request.session.flash('Unexpected error processing transaction. Card NOT charged.', 'error') return HTTPFound(location=request.route_url('user_index')) - charge = (amount + 0.3) / 0.971 - fee = charge - amount - if total_cents != int(round((amount + fee)*100)): - request.session.flash('Unexpected error processing transaction. Card NOT charged.', 'error') - return HTTPFound(location=request.route_url('user_index')) - amount = Decimal(amount) - - if amount <= 0.0: - request.session.flash( - _('Deposit amount must be greater than $0.00. Card NOT charged.'), - 'error' - ) - return HTTPFound(location=request.route_url('user_index')) - - try: - charge = stripe.Charge.create( - amount = total_cents, - currency="usd", - source=token, - description=request.user.uniqname+'@umich.edu' - ) - - except stripe.CardError as e: - traceback.print_exc() - request.session.flash('Card error processing transaction. Card NOT charged.', 'error') - return HTTPFound(location=request.route_url('user_index')) - except stripe.StripeError as e: - traceback.print_exc() - request.session.flash('Unexpected error processing transaction. Card NOT charged.', 'error') - request.session.flash('Please e-mail chezbetty@umich.edu so we can correct this error', 'error') - return HTTPFound(location=request.route_url('user_index')) - - try: - if to_account == 'user': - deposit = datalayer.cc_deposit( - request.user, - request.user, - amount, - charge['id'], - charge['source']['last4']) - else: - deposit = datalayer.cc_deposit( - request.user, - pool, - amount, - charge['id'], - charge['source']['last4']) - - request.session.flash('Deposit added successfully.', 'success') - - except Exception as e: - traceback.print_exc() - request.session.flash('A unknown error has occured.', 'error') - request.session.flash('Your card HAS been charged, but your account HAS NOT been credited.', 'error') - request.session.flash('Please e-mail chezbetty@umich.edu so we can correct this error', 'error') + post_stripe_payment( + datalayer, + request, + token, + amount, + total_cents, + request.user, + request.user if to_account == 'user' else pool, + ) return HTTPFound(location=request.route_url('user_index'))