diff --git a/gms/memberdb/approve.py b/gms/memberdb/approve.py
index c80632fb9a460c25989133bfaa8f7d22a0704e63..36bf5171a66f8390213faa565cab00946a14916b 100644
--- a/gms/memberdb/approve.py
+++ b/gms/memberdb/approve.py
@@ -8,25 +8,10 @@ from django.urls import reverse
 from django.utils import timezone
 from django import forms
 
-from memberdb.models import Member, Membership, MEMBERSHIP_TYPES_
+from memberdb.models import Member, Membership, get_membership_type
 from memberdb.forms import MyModelForm
 from memberdb.views import MyUpdateView
 
-def get_membership_type(member):
-    best = None
-    is_fresh = member.memberships.all().count() == 0
-    for t in MEMBERSHIP_TYPES_:
-        if (t['must_be_fresh'] == is_fresh and t['is_student'] == member.is_student and t['is_guild'] == member.is_guild):
-            best = t
-            break
-        elif (t['is_student'] == member.is_student and t['is_guild'] == member.is_guild):
-            best = t
-            break
-    if (best is None):
-        return MEMBERSHIP_TYPES_[1]['dispense']
-    else:
-        return best['dispense']
-
 def make_pending_membership(member):
     # check if this member already has a pending membership
     ms = Membership.objects.filter(member=member, approved__exact=False).first()
diff --git a/gms/memberdb/models.py b/gms/memberdb/models.py
index b6d1e32c4cfcd51a5b4ad3293de607d47f079dbf..7551994bca3f9a686080b4c825cc8c3ea885dbf4 100644
--- a/gms/memberdb/models.py
+++ b/gms/memberdb/models.py
@@ -2,61 +2,96 @@ from django.db import models
 from django.db.models import F
 from django.core.validators import RegexValidator
 
+from squarepay.dispense import get_item_price
+
 """
 dictionary of membership types & descriptions, should be updated if these are changed in dispense.
 """
-MEMBERSHIP_TYPES_ = [
-    {
+MEMBERSHIP_TYPES = {
+    'oday': {
         'dispense':'pseudo:11',
-        'desc':'O\' Day Special',
+        'desc':'O\' Day Special - first time members only',
         'is_guild':True,
         'is_student':True,
         'must_be_fresh':True,
     },
-    {
+    'student_and_guild': {
         'dispense':'pseudo:10',
         'desc':'Student and UWA Guild member',
         'is_guild':True,
         'is_student':True,
         'must_be_fresh':False,
     },
-    {
+    'student_only': {
         'dispense':'pseudo:9',
         'desc':'Student and not UWA Guild member',
         'is_guild':False,
         'is_student':True,
         'must_be_fresh':False,
     },
-    {
+    'guild_only': {
         'dispense':'pseudo:8',
         'desc':'Non-Student and UWA Guild member',
         'is_guild':True,
         'is_student':False,
         'must_be_fresh':False,
     },
-    {
+    'non_student': {
         'dispense':'pseudo:7',
         'desc':'Non-Student and not UWA Guild member',
         'is_guild':False,
         'is_student':False,
         'must_be_fresh':False,
     },
-    {
+    'lifer': {
         'dispense':'',
         'desc':'Life member',
         'is_guild':False,
         'is_student':False,
         'must_be_fresh':False,
     }
-]
+}
+
+def get_membership_choices(is_renew=None, get_prices=True):
+    """
+    turn MEMBERSHIP_TYPES into a list of choices used by Django
+    also dynamically fetch the prices from dispense (if possible)
+    """
+    choices = []
+    for key, val in MEMBERSHIP_TYPES.items():
+        if (val['must_be_fresh'] and is_renew == True):
+            # if you have an account already, you don't qualify for the fresher special
+            continue
+        if (val['dispense'] == '' and is_renew == False):
+            # free memberships can only apply to life members, and they will have an existing membership somewhere
+            # so this option is only displayed on the renewal form
+            continue
+        else:
+            price = get_item_price(val['dispense'])
+            if (get_prices and price is not None):
+                desc = "%s ($%1.2f)" % (val['desc'], price / 100.0)
+                choices += [(key, desc)]
+            else:
+                # don't display the price
+                choices += [(key, val['desc'])]
 
-def get_membership_types():
-    l = []
-    for t in MEMBERSHIP_TYPES_:
-        l += [(t['dispense'], t['desc'])]
-    return l
+    return choices
 
-MEMBERSHIP_TYPES = get_membership_types()
+def get_membership_type(member):
+    best = 'non_student'
+    is_fresh = member.memberships.all().count() == 0
+    for i, t in MEMBERSHIP_TYPES.items():
+        if (t['must_be_fresh'] == is_fresh and t['is_student'] == member.is_student and t['is_guild'] == member.is_guild):
+            best = i
+            break
+        elif (t['is_student'] == member.is_student and t['is_guild'] == member.is_guild):
+            best = i
+            break
+    return best
+
+
+def make_token():
+    return get_random_string(128)
 
 PAYMENT_METHODS = [
     ('dispense', 'Existing dispense credit'),
@@ -84,7 +119,6 @@ class IncAssocMember (models.Model):
     class Meta:
         verbose_name = "Incorporations Act member data"
         verbose_name_plural = verbose_name
-        default_permissions = ['view']
 
 """
 Member table: only latest information, one record per member
@@ -95,10 +129,11 @@ class Member (IncAssocMember):
     display_name    = models.CharField ('Display name', max_length=200)
     username        = models.SlugField ('Username', max_length=32, null=False, blank=False, unique=True, validators=[RegexValidator(regex='^[a-z0-9._-]+$')])
     phone_number    = models.CharField ('Phone number', max_length=20, blank=False, validators=[RegexValidator(regex='^\+?[0-9() -]+$')])
-    is_student      = models.BooleanField ('Student at UWA', default=True, blank=True)
+    is_student      = models.BooleanField ('Student', default=True, blank=True, help_text="Tick this box if you are a current student at a secondary or tertiary institution in WA")
     is_guild        = models.BooleanField ('UWA Guild member', default=True, blank=True)
-    id_number       = models.CharField ('Student number or Drivers License', max_length=50 , blank=False)
+    id_number       = models.CharField ('Student email or Drivers License', max_length=255, blank=False, help_text="Student emails should end with '.edu.au' and drivers licences should be in the format '[WA]DL 1234567'")
     member_updated  = models.DateTimeField ('Internal UCC info last updated', auto_now=True)
+    login_token     = models.CharField ('Temporary access key', max_length=128, null=True, editable=False, default=make_token)
 
     def __str__ (self):
         if (self.display_name != "%s %s" % (self.first_name, self.last_name)):
@@ -115,7 +150,7 @@ Membership table: store information related to individual (successful/accepted)
 """
 class Membership (models.Model):
     member          = models.ForeignKey (Member, on_delete=models.CASCADE, related_name='memberships')
-    membership_type = models.CharField ('Membership type', max_length=10, blank=True, null=False, choices=MEMBERSHIP_TYPES)
+    membership_type = models.CharField ('Membership type', max_length=20, blank=True, null=False, choices=get_membership_choices(get_prices=False))
     payment_method  = models.CharField ('Payment method', max_length=10, blank=True, null=True, choices=PAYMENT_METHODS, default=None)
     approved        = models.BooleanField ('Membership approved', default=False)
     approver        = models.ForeignKey (Member, on_delete=models.SET_NULL, null=True, blank=True, related_name='approved_memberships')
@@ -126,6 +161,9 @@ class Membership (models.Model):
     def __str__ (self):
         return "Member [%s] (%s) renewed membership on %s" % (self.member.username, self.member.display_name, self.date_submitted.strftime("%Y-%m-%d"))
 
+    def get_dispense_item(self):
+        return MEMBERSHIP_TYPES[self.membership_type]['dispense']
+
     class Meta:
         verbose_name = "Membership renewal record"
-        ordering = ['approved', '-date_submitted']
\ No newline at end of file
+        ordering = ['approved', '-date_submitted']
diff --git a/gms/memberdb/register.py b/gms/memberdb/register.py
index 045250dd744dde592e1c4169d6beff234dd63e18..ed604eb70ad41d57c5012c04d862459e86db57f0 100644
--- a/gms/memberdb/register.py
+++ b/gms/memberdb/register.py
@@ -10,7 +10,10 @@ from django.utils.safestring import mark_safe
 from django.contrib import messages
 from django import forms
 
-from .models import Member, Membership
+from squarepay.models import MembershipPayment
+from squarepay.dispense import get_item_price
+
+from .models import Member, Membership, get_membership_choices, MEMBERSHIP_TYPES
 from .forms import MyModelForm
 from .views import MyUpdateView
 from .approve import make_pending_membership
@@ -26,7 +29,7 @@ class RegisterForm(MyModelForm):
         "You agree to abide by the UCC Constitution, rulings of the UCC Committee, UCC and "
         "UWA’s Network Usage Guidelines and that you will be subscribed to the UCC Mailing List. <br>"
         'Policies can be found <a href="https://www.ucc.asn.au/infobase/policies.ucc">here</a>.'))
-    #membership_type = forms.ChoiceField(label='Select your membership type', required=True, choices=MEMBERSHIP_TYPES)
+    membership_type = forms.ChoiceField(label='Select your membership type', required=True, choices=get_membership_choices(is_renew=False))
 
     class Meta:
         model = Member
@@ -56,13 +59,17 @@ class RegisterForm(MyModelForm):
 
         # now create a corresponding Membership (marked as pending / not accepted, mostly default values)
         ms = make_pending_membership(m)
+
+        # make a card payment thing as well
+
         if (commit):
             ms.save();
         return m, ms
 
 class RenewForm(RegisterForm):
     confirm_email = None
-    
+    membership_type = forms.ChoiceField(label='Select your membership type', required=True, choices=get_membership_choices(is_renew=True))
+
     class Meta(RegisterForm.Meta):
         fields = ['first_name', 'last_name', 'phone_number', 'is_student', 'is_guild', 'id_number', 'email_address']
 
@@ -91,7 +98,7 @@ class RegisterView(MyUpdateView):
         # save the member data and get the Member instance
         m, ms = form.save()
         #messages.success(self.request, 'Your registration has been submitted.')
-        return HttpResponseRedirect(reverse("memberdb:thanks"))
+        return
 
 class RenewView(LoginRequiredMixin, MyUpdateView):
     template_name = 'renew.html'
@@ -121,3 +128,19 @@ class RenewView(LoginRequiredMixin, MyUpdateView):
         m, ms = form.save()
         messages.success(self.request, 'Your membership renewal has been submitted.')
         return HttpResponseRedirect(reverse("memberdb:home"))
+
+def create_member_payment(membership, commit=True):
+    """ creates a MembershipPayment object for the given membership """
+    # get the amount from dispense
+    price = get_item_price(membership.membership_type)
+    if (price is None or price == 0):
+        return None
+    desc = MEMBERSHIP_TYPES[membership.membership_type]['desc']
+    payment = MembershipPayment(description=desc, amount=price, membership=membership)
+
+    if (commit):
+        payment.save()
+    return payment
+
+def thanks_page(request, membership):
+    pass