Allow paying course fees with Protocoin

master
Tanner Collin 11 months ago
parent cfbbe2095d
commit c8378374b0
  1. 2
      apiserver/apiserver/api/utils_paypal.py
  2. 52
      apiserver/apiserver/api/views.py
  3. 13
      webclient/src/Classes.js
  4. 10
      webclient/src/Paymaster.js

@ -250,7 +250,7 @@ def check_training(data, training_id, amount):
if training.attendance_status == 'Waiting for payment':
training.attendance_status = 'Confirmed'
training.paid_date = datetime.date.today()
training.paid_date = utils.today_alberta_tz()
training.save()
logger.info('IPN - Amount valid for training cost, id: ' + str(training.id))

@ -1157,6 +1157,8 @@ class ProtocoinViewSet(Base):
source_user = self.request.user
source_member = source_user.member
training = None
try:
balance = float(request.data['balance'])
except KeyError:
@ -1175,9 +1177,33 @@ class ProtocoinViewSet(Base):
category = str(request.data['category'])
except KeyError:
raise exceptions.ValidationError(dict(category='This field is required.'))
if category not in ['Consumables', 'Donation']:
if category not in ['Consumables', 'Donation', 'OnAcct']:
raise exceptions.ValidationError(dict(category='Invalid category.'))
if category == 'OnAcct':
try:
training_id = int(request.data['training'])
except KeyError:
raise exceptions.ValidationError(dict(training='This field is required.'))
except ValueError:
raise exceptions.ValidationError(dict(training='Invalid number.'))
training = get_object_or_404(models.Training, id=training_id)
if not training.session:
raise exceptions.ValidationError(dict(training='Invalid session.'))
if training.session.is_cancelled:
raise exceptions.ValidationError(dict(training='Class is cancelled.'))
if training.paid_date:
raise exceptions.ValidationError(dict(training='Already paid.'))
if training.session.cost != amount:
msg = 'Protocoin training payment amount mismatch:\n' + str(request.data.dict())
utils.alert_tanner(msg)
raise exceptions.ValidationError(dict(training='Class cost doesn\'t match amount.'))
memo = str(request.data.get('memo', ''))
# also prevents negative spending
@ -1193,11 +1219,19 @@ class ProtocoinViewSet(Base):
if source_user_balance < amount:
raise exceptions.ValidationError(dict(amount='Insufficient funds.'))
tx_memo = 'Protocoin - Transaction spent ₱{} on {}{}'.format(
amount,
category,
', memo: ' + memo if memo else ''
)
if training:
tx_memo = 'Protocoin - Transaction spent ₱{} on {}, session: {}, training: {}'.format(
amount,
training.session.course.name,
str(training.session.id),
str(training.id),
)
else:
tx_memo = 'Protocoin - Transaction spent ₱{} on {}{}'.format(
amount,
category,
', memo: ' + memo if memo else ''
)
tx = models.Transaction.objects.create(
user=source_user,
@ -1211,6 +1245,12 @@ class ProtocoinViewSet(Base):
)
utils.log_transaction(tx)
if training:
if training.attendance_status == 'Waiting for payment':
training.attendance_status = 'Confirmed'
training.paid_date = utils.today_alberta_tz()
training.save()
return Response(200)
except OperationalError:
self.spend_request(request)

@ -7,6 +7,7 @@ import { apiUrl, isAdmin, getInstructor, BasicTable, requester, useIsMobile } fr
import { NotFound } from './Misc.js';
import { InstructorClassDetail, InstructorClassAttendance } from './InstructorClasses.js';
import { PayPalPayNow } from './PayPal.js';
import { PayWithProtocoin } from './Paymaster.js';
import { tags } from './Courses.js';
function ClassTable(props) {
@ -693,6 +694,18 @@ export function ClassDetail(props) {
name={clazz.course_data.name}
custom={JSON.stringify({ training: userTraining.id })}
/>
<p/>
<PayWithProtocoin
token={token} user={user} refreshUser={refreshUser}
amount={clazz.cost}
onSuccess={() => {
refreshUser();
refreshClass();
}}
custom={{ category: 'OnAcct', training: userTraining.id }}
/>
</div>
}
</div>

@ -7,7 +7,7 @@ import { MembersDropdown } from './Members.js';
import { requester } from './utils.js';
export function PayWithProtocoin(props) {
const { token, user, refreshUser, amount, setAmount, custom } = props;
const { token, user, refreshUser, amount, onSuccess, custom } = props;
const member = user.member;
const [error, setError] = useState({});
const [loading, setLoading] = useState(false);
@ -23,7 +23,9 @@ export function PayWithProtocoin(props) {
.then(res => {
setLoading(false);
setSuccess(true);
setAmount('');
if (onSuccess) {
onSuccess();
}
setError({});
refreshUser();
})
@ -232,7 +234,7 @@ export function Paymaster(props) {
<PayWithProtocoin
token={token} user={user} refreshUser={refreshUser}
amount={consumables}
setAmount={setConsumables}
onSuccess={() => setConsumables('')}
custom={{ category: 'Consumables', memo: consumablesMemo }}
/>
</Grid.Column>
@ -274,7 +276,7 @@ export function Paymaster(props) {
<PayWithProtocoin
token={token} user={user} refreshUser={refreshUser}
amount={donate}
setAmount={setDonate}
onSuccess={() => setDonate('')}
custom={{ category: 'Donation', memo: memo }}
/>
</Grid.Column>

Loading…
Cancel
Save