Commit fed18925 authored by frekk's avatar frekk
Browse files

added dispense stuff & more payment-related stuff

parent 7e5cb8b6
......@@ -33,6 +33,7 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'memberdb.views.MemberMiddleware',
]
ROOT_URLCONF = 'gms.urls'
......@@ -139,5 +140,9 @@ LOGGING = {
'level': LOG_LEVEL,
'handlers': ['logfile', 'console'],
},
'squarepay': {
'level': LOG_LEVEL,
'handlers': ['logfile', 'console'],
}
},
}
\ No newline at end of file
}
......@@ -89,4 +89,13 @@ AUTH_LDAP_USER_FLAGS_BY_GROUP = {
# the Square app and location data (set to sandbox unless you want it to charge people)
SQUARE_APP_ID = 'maybe-sandbox-something-something-here'
SQUARE_LOCATION = 'CBASEDE-this-is-probably-somewhere-in-Sydney'
SQUARE_ACCESS_TOKEN = 'keep-this-very-secret'
\ No newline at end of file
SQUARE_ACCESS_TOKEN = 'keep-this-very-secret'
DISPENSE_BIN = '/usr/local/bin/dispense'
# configure the email backend (see https://docs.djangoproject.com/en/2.1/topics/email/)
EMAIL_HOST = "secure.ucc.asn.au"
EMAIL_PORT = 465
EMAIL_USE_SSL = True
EMAIL_HOST_USER = "uccportal"
EMAIL_HOST_PASSWORD = "changeme"
"""
this file contains utilities for wrapping the opendispense2 CLI utility `dispense`
It is essentially a hack to avoid having to write an actual dispense client here.
"""
import subprocess
from subprocess import CalledProcessError, TimeoutExpired
from django.conf import settings
from .payments import log
DISPENSE_BIN = getattr(settings, 'DISPENSE_BIN', None)
if DISPENSE_BIN is None:
log.warning("DISPENSE_BIN is not defined! Lookups for prices will fail!")
def run_dispense(*args):
if DISPENSE_BIN is None:
return None
cmd = [DISPENSE_BIN] + args
log.info("run_dispense: " + cmd)
try:
# get a string containing the output of the program
res = subprocess.check_output(cmd, timeout=4, universal_newlines=True)
except CalledProcessError as e:
log.warning("dispense returned error code %d, output: '%s'" % (e.returncode, e.output))
return None
except TimeoutExpired as e:
log.error(e)
return None
return res
def get_item_price(itemid):
""" gets the price of the given dispense item in cents """
if (itemid is None or itemid == ""):
return None
out = run_dispense('iteminfo', itemid)
if (out is None):
return 123456
s = out.split() # get something like ['pseudo:7', '25.00', 'membership', '(non-student', 'and', 'non-guild)']
if (s[0] != itemid):
log.warning("get_item_price: got result for incorrect item: %s" + s)
return None
else:
# return the price as a number of cents
return int(float(s[0]) * 100)
\ No newline at end of file
"""
This file contains functions for dealing with payments (although that's fairly obvious)
"""
import logging
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils import timezone
import squareconnect
from squareconnect.rest import ApiException
from squareconnect.apis.transactions_api import TransactionsApi
log = logging.getLogger('squarepay')
# load the configuration values
app_id = getattr(settings, 'SQUARE_APP_ID', None)
loc_id = getattr(settings, 'SQUARE_LOCATION', None)
access_key = getattr(settings, 'SQUARE_ACCESS_TOKEN', None)
# make sure the configuration values exist
if (app_id is None) or (loc_id is None) or (access_key is None):
raise ImproperlyConfigured("Please define SQUARE_APP_ID, SQUARE_LOCATION and SQUARE_ACCESS_TOKEN in settings.py")
# instantiate the global squareconnect ApiClient instance (only needs to be done once)
_sqapi_inst = squareconnect.ApiClient()
_sqapi_inst.configuration.access_token = access_key
def get_transactions_api():
return TransactionsApi(_sqapi_inst)
def try_capture_payment(card_payment, nonce):
"""
attempt to charge the customer associated with the given card nonce (created by the PaymentForm in JS)
Note: this can be called multiple times with the same CardPayment instance but the customer will not
be charged multiple times (using the Square idempotency key feature)
Returns either True on success or False on failure.
"""
api_inst = get_transactions_api()
request_body = {
'idempotency_key': card_payment.idempotency_key,
'card_nonce': nonce,
'amount_money': {
'amount': card_payment.amount,
'currency': 'AUD'
}
}
try:
api_response = api_inst.charge(loc_id, request_body)
set_paid(card_payment)
log.info("TransactionApi response without error, charge $%1.2f" % (float(card_payment.amount) / 100.0))
return True
except ApiException as e:
log.error("Exception while calling TransactionApi::charge: %s" % e)
return False
def set_paid(card_payment):
card_payment.is_paid = True
card_payment.date_paid = timezone.now()
card_payment.save()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment