diff --git a/gms/memberdb/templates/thanks.html b/gms/memberdb/templates/thanks.html
index ddaf5a0382173cc8effbc7f99a2dee6d12df1eb0..9f3a24f55a3ceb29dd0b6dc23f8452b88c377f53 100644
--- a/gms/memberdb/templates/thanks.html
+++ b/gms/memberdb/templates/thanks.html
@@ -8,4 +8,11 @@
 <b>Your membership registration has been submitted.</b><br><br>
 Once it has been approved, you will receive an email with instructions to activate your account.
 {% endblock %}
-{% block form %}{% endblock %}
\ No newline at end of file
+
+{% block form %}
+{% if payment_url %}
+<div class="form-row readonly">
+    <a class="button" title="Pay online with card, using Square payments" href="{{ payment_url }}">Pay online now</a>
+</div>
+{% endif %}
+{% endblock %}
diff --git a/gms/memberdb/urls.py b/gms/memberdb/urls.py
index 82bcc9e0f45e81b43044b600408928f4e091106e..63088b9a4247eee4090f66067a1bcee4b159499c 100644
--- a/gms/memberdb/urls.py
+++ b/gms/memberdb/urls.py
@@ -2,24 +2,26 @@ from django.urls import path
 from django.contrib.auth import views as auth_views
 from django.views.generic.base import TemplateView
 
-from . import views
-from .views import MemberHomeView
+from .views import MemberHomeView, MemberTokenView
 from .register import RegisterView, RenewView
 
 app_name = 'memberdb'
 urlpatterns = [
     path('', MemberHomeView.as_view(), name='home'),
     path('', MemberHomeView.as_view(), name='index'),
+
+    # use the django-provided login views with our custom templates
     path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
     path('logout/', auth_views.LogoutView.as_view(template_name='logout.html'), name='logout'),
 
     # override the admin login/logout views
     path('admin/login/', auth_views.LoginView.as_view(template_name='login.html')),
     path('admin/logout/', auth_views.LogoutView.as_view(template_name='logout.html')),
+
+    # for members to "login" before having created a user account
+    path('login/<username>/<member_token>/', MemberTokenView.as_view(), name='login_member'),
     
     path('register/', RegisterView.as_view(), name='register'),
     path('renew/', RenewView.as_view(), name='renew'),
-    path('active/', views.getactive, name='actives'),
     path('thanks/', TemplateView.as_view(template_name='thanks.html'), name='thanks'),
-    #path('<str:username>/', views.info, name='info'),
-]
\ No newline at end of file
+]
diff --git a/gms/memberdb/views.py b/gms/memberdb/views.py
index fca7b770f0013cb63c5ce2945b1300b158ff17e4..c9bac0740d555624b9082fc94f993be2fb18a582 100644
--- a/gms/memberdb/views.py
+++ b/gms/memberdb/views.py
@@ -1,29 +1,63 @@
 from datetime import date
-from django.http import HttpResponse, HttpResponseRedirect
+from django.http import HttpResponse, HttpResponseRedirect, Http404
 from django.shortcuts import render
 from django.urls import reverse
 from django.forms import ModelForm
 from django.contrib import messages
+from django.views.generic.base import View
 from django.views.generic.edit import UpdateView
-from django.contrib.auth.mixins import LoginRequiredMixin
+from django.contrib.auth.mixins import AccessMixin
 
 from .models import Member, IncAssocMember, Membership
 from .forms import MemberHomeForm
 
-def index(request):
-    member_list = Member.objects.all()
-    context = {
-        'member_list': member_list,
-    }
-    return render(request, 'index.html', context)
-
-def getactive(request):
-    actives = Membership.objects.all().select_related('member')
-    actives.filter(date_submitted__range=(date(2018, 1, 1), date(2018, 12, 31)))
-    context = {
-        'member_list': actives,
-    }
-    return render(request, 'index.html', context)
+class MemberMiddleware:
+    """
+    Django middleware to get the member info from a particular session
+    if logged in, get member from username, otherwise store some metadata in the session thingy
+    see https://docs.djangoproject.com/en/2.1/topics/http/sessions/
+    """
+    def __init__(self, get_response):
+        self.get_response = get_response
+        # One-time configuration and initialization.
+
+    def __call__(self, request):
+        # Code to be executed for each request before
+        # the view (and later middleware) are called.
+        request.member = None
+
+        if request.user.is_authenticated:
+            # get the username only when a user is logged in
+            # note that request.user will still exist even when the user isn't logged in
+            request.member = Member.objects.filter(username__exact=self.request.user.username).first()
+
+            if request.member is not None:
+                # clean the member's auth token because they now have a working login
+                request.member.token = None
+                request.member.save()
+
+            # request.session is a dictionary-like object, its content is saved in the database
+            # and only a session ID is stored as a browser cookie (by default, but is configurable)
+            if 'member_id' in request.session:
+                # don't store member ID since we look it up by username
+                del request.session['member_id']
+
+        elif 'member_id' in request.session:
+            request.member = Member.objects.get(id=request.session['member_id'])
+
+        response = self.get_response(request)
+
+        # Code to be executed for each request/response after
+        # the view is called.
+
+        return response
+
+class MemberAccessMixin(AccessMixin):
+    """Verify that the current session has a member object associated with it, or that the user is logged in"""
+    def dispatch(self, request, *args, **kwargs):
+        if (not request.user.is_authenticated) and (request.member is None):
+            return self.handle_no_permission()
+        return super().dispatch(request, *args, **kwargs)
 
 """
 Can update and create models.
@@ -49,7 +83,7 @@ class MyUpdateView(UpdateView):
         kwargs.update({'request': self.request})
         return kwargs
 
-class MemberHomeView(LoginRequiredMixin, MyUpdateView):
+class MemberHomeView(MemberAccessMixin, MyUpdateView):
     model = Member
     template_name = 'home.html'
     form_class = MemberHomeForm
@@ -67,5 +101,20 @@ class MemberHomeView(LoginRequiredMixin, MyUpdateView):
 
     def form_valid(self, form):
         messages.success(self.request, 'Member details updated.')
-        # redirect to ourselves?!! because I can't think of a less hacky way to do this
+        messages.warning(self.request, 'Could not update user display name in AD. Please try again once this feature has been implemented.')
+
+        # redisplay the page
+        return self.get(request, *args, **kwargs)
+
+class MemberTokenView(View):
+    """ allow a user to login using a unique (secure) member token """
+    def get(self, request, **kwargs):
+        if not ('member_token' in kwargs and 'username' in kwargs) or user.is_authenticated:
+            raise Http404()
+
+        # look up the member using exact match for token and username
+        member = Member.objects.get(token=kwargs['member_token'], username=kwargs['username'])
+        if member is None:
+            raise Http404()
+        request.session['member_id'] = member.id
         return HttpResponseRedirect(reverse('memberdb:home'))