(Resend with urls which I previously omitted)
In a 'Substance' app I have a Stripe payment facility working from
within the Admin. The problem is it spoils admin_urls so the user cannot
get back to the Substance Admin. When the Stripe work is done, how do I
re-initialise the admin urls?
I want to display a success page with a link back to the substance page.
I can provide exactly the correct url but all it shows is an admin page
with no content. The correct url is /admin/substance/substance/<pk>/ and
that works only if the Stripe mechanism has not been called since the
dev server reloaded itself.
I probably have an incorrect diagnosis here. The problem is most likely
my misunderstanding of how to use the Admin properly. Any help will be
very much appreciated.
Thanks
Mike
The process uses ModelAdmin.change_view()[1] to call my
billing_payment_view()[2] which uses PaymentForm[3] and payment.html[4]
template to pass the necessary hidden vars.
When the view executes (depending on whether payment is required) it
pops up a (Stripe js) credit card collection form with a [Pay] button
and that submits the detail to the Stripe API which in turn generates a
token if successful or an error message and re-executes the
billing_payment_view with request.POST including that detail plus all
the hidden fields in the form. The view goes on to process the detail,
generate a receipt, send an email to the payer and launch the
billing_success_view()[5] and success.html[6] template.
Oh .. and the urls[7]
[1] SubstanceAdmin.change_view() def change_view(self, request,
object_id, form_url='', extra_context=None): """ self = SubstanceAdmin
request = wsgi request object object_id = substance form_url = no idea!
extra_context = dict of apps, models, admin_urls and permissions sm2mi
stands for substance m2m ingredient. Ingredients are substances in a
many-to-many relationship with another substance (ie a mixture). The
through table is called Substance_Ingredients """ ingredients =
Substance_Ingredients.objects.filter(substance_id=object_id)
subscription = None for sm2mi in ingredients: payable, fee_type =
sm2mi.fee_payable() # eg., True, PAID_DATA if payable: subscription =
billing_subscribe(sm2mi, fee_type) if subscription: # we need to collect
money for the owner self.change_form_template = 'payment.html' context =
billing_collect_context( sm2mi, subscription, ) # get everything into
the payment_view context if not extra_context: extra_context = dict()
extra_context.update(self.admin_site.each_context(request))
extra_context.update(context) self.admin_site.admin_view( # now call the
Stripe mechanism billing_payment_view( request, sm2mi, subscription,
context=extra_context, ) ) # only one sm2mi at a time break return
super(SubstanceAdmin, self).change_view( request, object_id, form_url,
extra_context ) [2] billing_payment_view() @ensure_csrf_cookie def
payment_view(request, sm2mi=None, subscription=None, context=None):
display_message = '' display_insert = '' email_message = ''
subscription_message = '' subscription_insert = '' charge = None context
= context or {} templt = 'payment.html' form = PaymentForm() if
request.method == "POST": resp = dict(request.POST) # data has been
submitted to Stripe via Stripe js # stripe submits the js form which
comes back without identifying args form = PaymentForm(request.POST) if
form.is_valid(): go_back = '/admin/substance/substance/' try: token =
form.cleaned_data['stripeToken'] if token: email =
form.cleaned_data['stripeEmail'] sm2mi_id =
form.cleaned_data['sm2mi_id'] if sm2mi_id and sm2mi is None: sm2mi =
Substance_Ingredients.objects.get(pk=int(sm2mi_id)) subscription_id =
form.cleaned_data['subscription_id'] if subscription_id and subscription
is None: subscription =
Subscription.objects.get(pk=int(subscription_id)) if sm2mi and
subscription: ingredient = sm2mi.ingredient cents =
subscription.get_cents() fee_total = subscription.calc_fee(total=True) #
gst in charge = stripe.Charge.create( amount=int(float(fee_total) *
cents), currency=subscription.fee_currency,
description='{0}'.format(subscription.ingredient.name), source=token, )
if charge: go_back = '/admin/substance/substance/%s/' %
sm2mi.substance.id subscription.token = token # see above try: with
transaction.atomic(): subscription.save() except Exception: raise #
transaction is done now make a receipt amount_paid = charge['amount'] if
cents > 1: # cents may be zero eg', JPY amount_paid =
round(charge['amount'] / cents, 2) receipt = Receipt.objects.create(
licensee=sm2mi.substance.division.company, ingredient=ingredient,
currency=subscription.fee_currency, amount_paid=amount_paid,
charge=charge, ) receipt.save() # defaults need all methods to execute #
make a received message for display and emailing para = '<p>' endpara =
'</p>' strong = '<strong>' endstrong = '</strong>' display_message =
'<p>Thank you for your {0} {1}{2} '\ 'payment to {3} for
<strong>{4}</strong></p>'.format( subscription.fee_currency,
subscription.fee_symbol, amount_paid, settings.BRANDING,
ingredient.name.capitalize(), ) display_insert = '<p>A receipt will be
emailed to you '\ 'shortly</p>' subscription_message = '<p>You are now
'\ 'subscribed to {0} until 30 September. You may include '\ 'it in as
many mixtures as desired.</p>'.format( ingredient.name.capitalize(), )
subscription_insert = '<p>If you have difficulty '\ 'accessing <a
href="{0}">{1}</a> or you have any '\ 'questions please get in touch via
{2}. Easy questions '\ 'might have an answer on the <a
href="https://{3}/user-'\ 'docs/sharing/">sharing page</a>.</p>'.format(
ingredient.get_absolute_url(), ingredient.name.capitalize(),
settings.MANAGERS[0][1], settings.WEBSITE, ) email_message =
'{0}{1}{2}'.format( display_message, '\n', subscription_message,
).replace( para, '' ).replace( endpara, '\n' ).replace( strong, '*'
).replace( endstrong, '*' ) display_message = '{0}{1}{2}{3}'.format(
display_message, display_insert, subscription_message,
subscription_insert, ) from_address = get_admin_email(full=True)
to_address = [charge['source']['name']] subject = 'Subscription receipt
for {0}'.format( ingredient.name.capitalize() ) try: send_mail( subject,
email_message, from_address, to_address, fail_silently=False, ) except
Exception as err: print('\n315 billing.views %s' % err) pass
context['sm2mi'] = sm2mi context['receipt'] = receipt
context['subscription'] = subscription context['message'] =
mark_safe(display_message) #context['link'] =
sm2mi.substance.get_substance_url() context['link'] = go_back return
success_view(request, context) else: raise Exception('Gateway error')
except Exception as err: err = '%s' % err if 'cannot use a Stripe token'
in err: backto = '' if sm2mi: backto = '<p><a href="{0}">Back to
{1}</a>'\ '</p>'.format( go_back, sm2mi.substance.name.capitalize() )
display_message = '<p>Cannot process the same payment '\
'twice.</p><p>If an emailed receipt is not received at the '\ 'address
just specified in the payment popup please get in '\ 'touch with
<em>{0}</em> to ascertain whether payment was '\ 'successful. Please
copy and paste the token below as a '\
'reference.</p><p>{1}</p><p>{2}<p>'.format( get_manager_email(), token
or 'No token at this point (375)', backto ) else: display_message =
'{0}. Please report this error message to '\ '{1}'.format(err,
get_admin_email()) context['message'] = mark_safe(display_message) #
report failure to the card payer form.add_error(None, '%s' % err) return
success_view(request, context) context['form'] = form return render(
request=request, template_name=templt, context=context ) [3] PaymentForm
class PaymentForm(forms.Form): sm2mi_id =
forms.CharField(widget=forms.HiddenInput, required=False) ingredient_id
= forms.CharField(widget=forms.HiddenInput, required=False)
subscription_id = forms.CharField(widget=forms.HiddenInput,
required=False) stripeToken = forms.CharField(widget=forms.HiddenInput,
required=False) stripeEmail = forms.CharField(widget=forms.HiddenInput,
required=False) stripeBillingName =
forms.CharField(widget=forms.HiddenInput, required=False)
stripeBillingAddressLine1 = forms.CharField(widget=forms.HiddenInput,
required=False) stripeBillingAddressZip =
forms.CharField(widget=forms.HiddenInput, required=False)
stripeBillingAddressState = forms.CharField(widget=forms.HiddenInput,
required=False) stripeBillingAddressCity =
forms.CharField(widget=forms.HiddenInput, required=False)
stripeBillingAddressCountry = forms.CharField(widget=forms.HiddenInput,
required=False) [4] payment.html {% extends "admin/base_site.html" %} {%
load admin_urls %} {% load i18n %} {% load common_tags %} {% block title
%}{{ original }}{% endblock %} {% block content %} {% if subscription %}
<div class="payment"> <p>{{ prepay_message }}</p> <p>Please proceed to
payment.</p> <blockquote><strong> We do not store your payment detail
here. Our PCI DSS compliant gateway service provider interfaces your
card directly with your bank. Links to their terms and privacy policy
will be visible when you click the <em>Pay with Card</em> button. See
also our own privacy policy (link above). </strong> (PCI DSS =
Payment Card Industry Data Security Standard) </blockquote>
<p> </p> <form action="payment" method="POST"> {% csrf_token %} {%
if form.errors or form.non_field_errors %} {{ form.errors }} {{
form.non_field_errors }} {% endif %} {% for field in form %} {% if
field.name == "sm2mi_id" %} <input type="hidden" name="sm2mi_id"
id="id_sm2mi_id" value="{{ sm2mi.id }}" /> {% elif field.name ==
"ingredient_id" %} <input type="hidden" name="ingredient_id"
id="id_ingredient_id" value="{{ ingredient.id }}" /> {% elif field.name
== "subscription_id" %} <input type="hidden" name="subscription_id"
id="id_subscription_id" value="{{ subscription.id }}" /> {% else %} {{
field }} {% endif %} {% endfor %} <script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ data_key }}" data-amount="{{ data_amount }}" data-name="{{
data_name }}" data-description="{{ data_description }}" data-image="{{
data_image }}" data-locale="{{ data_locale }}" data-currency="{{
data_currency | lower}}" data-zip-code="true"
data-allow-remember-me="true" data-panel-label="Pay"> </script> </form>
</div> {% endif %} {{ block.super }} {% endblock %} [5]
billing_success_view def success_view(request, context=None): """
launched after a Stripe token is received by PaymentForm context is
passed in by payment_view and has context['message'] """ if context is
None: context = dict() context['message'] = 'Sorry - payment details
cannot be reproduced' if 'Thank you for your' in context['message']:
pass return render( request=request, template_name='success.html',
context=context, ) [6] success.html {% extends "admin/base_site.html" %}
{% load admin_urls %} {% load i18n %} {% load common_tags %} {% block
title %}{{ original }}{% endblock %} {% block content %} <div
id="container"> <div id="content" class="colM"> {% if message %}{{
message }}{% endif %} {% if link %} <p>Please do not refresh this page
but rather click the 'Home' link above or <a href="{{ link }}">go back
to {{ sm2mi.substance.name }}</a></p> {% endif %} </div> </div> {{
block.super }} {% endblock %} [7] urls.py for the project from
__future__ import unicode_literals, absolute_import, division from
django.conf import settings from django.conf.urls import include from
django.conf.urls import url as re_path #from django.urls import include,
path, re_path from django.contrib import admin import
django.contrib.auth.views as auth_views import django.views as
django_views admin.autodiscover() adminurls = admin.site.urls
urlpatterns = [ re_path(r'^substance/', include('substance.urls')),
re_path(r'', include('common.urls')), re_path(r'payment$',
billing_views.payment_view, name='payment_view' ), re_path(r'success$',
billing_views.success_view, name='success_view' ),
re_path(r'^admin/password_reset/$', auth_views.password_reset,
name='django.contrib.auth.views.admin_password_reset'),
re_path(r'^admin/password_reset/done/$', auth_views.password_reset_done,
name='django.contrib.auth.views.password_reset_done'),
re_path(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$',
auth_views.password_reset_confirm,
name='django.contrib.auth.views.password_reset_confirm'),
re_path(r'^reset/done/$', auth_views.password_reset_complete,
name='django.contrib.auth.views.password_reset_complete'), # Uncomment
the admin/doc line below to enable admin documentation:
re_path(r'^admin/doc/', include('django.contrib.admindocs.urls')),
re_path(r'^admin/', admin.site.urls), ] if not settings.APACHE:
urlpatterns.append( re_path(r'^media\/(?P<path>.*)$',
django_views.static.serve, {'document_root': settings.MEDIA_ROOT}), )
urlpatterns.append( re_path(r'^static\/(?P<path>.*)$',
django_views.static.serve, {'document_root': settings.STATIC_ROOT}), )
Python 3.6, Django 1.11.20, dev server on Windows 10.
Haven't been *able* to get it working yet on Ubuntu 16.04 with Python
2.7 and haven't tried it yet on Python 2.7 on the dev server.
Getting Python 3.x running with Apache2 on Ubuntu is non-trivial due to
co-existence with Trac (2.7 only) which also requires my SVN to cohabit
on the same machine.
--
You received this message because you are subscribed to the Google
Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to django-users+unsubscr...@googlegroups.com
<mailto:django-users+unsubscr...@googlegroups.com>.
To post to this group, send email to django-users@googlegroups.com
<mailto:django-users@googlegroups.com>.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-users/f4d7fd65-ea8c-1a7f-4aea-4a34e58ee27c%40dewhirst.com.au
<https://groups.google.com/d/msgid/django-users/f4d7fd65-ea8c-1a7f-4aea-4a34e58ee27c%40dewhirst.com.au?utm_medium=email&utm_source=footer>.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "Django
users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to django-users+unsubscr...@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-users/0a6134aa-1ec6-b648-8e76-866e08817251%40dewhirst.com.au.
For more options, visit https://groups.google.com/d/optout.