Integrate Stripe payment flow into django - python

I’m trying to integrate stripe custom payments flow into my Django ecom website as PayPal isn’t as good but the docs (https://stripe.com/docs/payments/quickstart?lang=python) for python are in the flask framework. Does anyone have boilerplate code for handling a simple transaction for this in Django (views, template etc.)

In theory, the only thing that needs to change is the flask part of that code and change it into Django view(s).
The rest, html+js+css, should be able to be copied + pasted (especially cause the html is dynamically created by the Stripe JS)
views.py
from django.shortcuts import render
from django.http import HttpResponse
# The GET checkout form
#
# urlpattern:
# path('checkout', views.checkout, name='checkout'),
def checkout(request):
return render(request, 'checkout.html')
# The POST checkout form
#
# urlpattern:
# path('create-payment-intent', views.create_payment, name='create_payment'),
def create_payment(request):
if request.method == 'POST':
import json
try:
data = json.loads(request.POST)
def calculate_order_amount(items):
# Replace this constant with a calculation of the order's amount
# Calculate the order total on the server to prevent
# people from directly manipulating the amount on the client
return 1400
# this api_key could possibly go into settings.py like:
# STRIPE_API_KEY = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
#
# and fetched out with:
# from django.conf import settings
# stripe.api_key = settings.STRIPE_API_KEY
import stripe
stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
# Create a PaymentIntent with the order amount and currency
intent = stripe.PaymentIntent.create(
amount=calculate_order_amount(data['items']),
currency='usd',
automatic_payment_methods={
'enabled': True,
},
)
return HttpResponse(
json.dumps({'clientSecret': intent['client_secret']}),
content_type='application/json'
)
except Exception as e:
# Just return the 403 and NO error msg
# Not sure how to raise a 403 AND return the error msg
from django.http import HttpResponseForbidden
return HttpResponseForbidden()
# OR you could return just the error msg
# but the js would need to be changed to handle this
return HttpResponse(
json.dumps({'error': str(e)}),
content_type='application/json'
)
# POST only View, Raise Error
from django.http import Http404
raise Http404
Note: You might also have to change the two urls in the .js to match your django URLS. What they have "/create-payment-intent" + "http://localhost:4242/checkout.html" (not sure why that 2nd one is full url, but remember to get the port correct)
This would just be the barebones that your URL you included shows, you still have to figure out how to get the items into checkout.html, dynamically pass them to the page + eventually to the POST and then redo calculate_order_amount(items)

To understand working with Stripepayement I am sharing my GIT URL in which stripe payment is integrated you may refer
https://github.com/KBherwani/BookManagement/

Related

Stripe subscription cancel: KeyError 'HTTP_STRIPE_SIGNATURE'

I'm trying to configure Django Stripe Subscriptions for WebApp.
I want to let Subscribed users cancel the subscription themselves.
The code below is to delete user's information from StripeAPI and Django StripeCustomer model.
Here is view.py
import stripe
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.http.response import JsonResponse, HttpResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import get_user_model
from subscriptions.models import StripeCustomer
#login_required
#csrf_exempt
def cancel_subscription(request):
if request.user.is_authenticated:
endpoint_secret = settings.STRIPE_ENDPOINT_SECRET
payload = request.body
event = None
sig_header = request.META['HTTP_STRIPE_SIGNATURE']
event = stripe.Webhook.construct_event(
payload, sig_header, endpoint_secret
)
session = event['data']['object']
stripe_customer = StripeCustomer.objects.get(user=request.user)
stripe.api_key = settings.STRIPE_SECRET_KEY
sub_id = stripe.Subscription.retrieve(stripe_customer.stripeSubscriptionId)
client_reference_id = session.get('client_reference_id')
user = get_user_model().objects.get(id=client_reference_id)
try:
#delete from stripeapi
stripe.Subscription.delete(sub_id)
#delete from StripeCustomer model
StripeCustomer.objects.delete(
user=user,
stripeCustomerId=stripe_customer_id,
stripeSubscriptionId=stripe_subscription_id,
)
print(user.username + ' unsubscribed.')
except Exception as e:
import traceback
traceback.print_exc()
return JsonResponse({'error': (e.args[0])}, status =403)
return render(request, 'home.html')
When I excecute the code error occuerd at
sig_header = request.META['HTTP_STRIPE_SIGNATURE']
The error message is below
Exception Type: keyError
Exception Value: 'HTTP_STRIPE_SIGNATURE'
I don't understand why the error occurs at request.META['HTTP_STRIPE_SIGNATURE'],because other part of this view can execute this code.
I just mentioned the above settings in this question but still if more code is required then tell me I'll update my question with that information. Thank you
I think you're mixing up a webhook handler and a regular POST request route as part of your application here. You either need one or the other, and I suspect you don't need the webhook stuff at all given what you're trying to to.

return to function from another function

i have a table which include all users and two columns at the end (Edit,Delete) and i just enabled the delete column, the issue is when i click on the delete icon the record will be deleted but the url will stuck on the delete function even if i used return render(request,'getUsersInfo.html') which is get all records function
Model Name: Users
urls:
from django.urls import path
from django.conf.urls import url
from . import views
urlpatterns = [
path('signup.html',views.signup,name=''),
path('getUsersInfo.html',views.getAllUsers,name=''),
url(r'^deleteUser/(?P<fullname>\D+)/$',views.deleteUser, name='deleteUser'),
# this is how to call a function without parameters url(r'^deleteUser/$',views.deleteUser, name='deleteUser'),
in the same view i have 3 functions (singup "add user", getAllUsers "get all the records to the table,deleteUser)
views:
def getAllUsers(request):
print("getAllUsers")
thesearchValue = ''
if 'SearchValue' in request.GET:
thesearchValue = request.GET['SearchValue']
print(request.GET['SearchValue'])
allUsers = User.objects.filter(fullname__icontains=thesearchValue)#all()
# return render(request,'getUsersInfo.html',{'allUsers':allUsers})
return render(request,'getUsersInfo.html',{'allUsers':allUsers})
else:
print("Empty")
allUsers = User.objects.all()
return render(request,'getUsersInfo.html',{'allUsers':allUsers})
def deleteUser(request,fullname):
print('delete the user')
todelete = User.objects.filter(fullname=fullname)
todelete.delete()
return render(request,'getUsersInfo.html')
Notice that i used return render(request,'getUsersInfo.html') which should call getAllUsers(request): but the url stuck on http://127.0.0.1:8000/deleteUser/John/
Rendering the same template as another view does not mean that you will somehow call other views. A template is nothing more than a tool to specify how to convert context data to a string, that is passed as HTTP response. You can use the same template in multiple views, and a view can render multiple templates.
You can make use of redirect(..) [Django-doc] to return a HTTP redirect response (302):
from django.shortcuts import redirect
def deleteUser(request,fullname):
print('delete the user')
todelete = User.objects.filter(fullname=fullname)
todelete.delete()
return redirect(getAllUsers)
Note: A GET request is not supposed to have side-effects, hence removing
objects when a user makes a GET request, is not compliant with the HTTP
standard. Therefore it might be better to remove a User with a POST request.

How can i route my URL to a common method before passing to actual view in Django

Here is my code looks like :-
url.py file :-
from rest_framework import routers
from view_user import user_signup,user_login
router = routers.DefaultRouter()
urlpatterns = [
url(r'^api/v1/user_signup',csrf_exempt(user_signup)),
url(r'^api/v1/user_login',csrf_exempt(user_login))
]
view_user.py file:-
def user_signup(request):
try:
if request.method == 'POST':
json_data = json.loads(request.body)
return JsonResponse(result, safe=False)
except Exception as e:
logger.error("at method user : %s", e)
So, when I call the url:- http://myserver/api/v1/user_signup
it goes to "user_signup" method of view_user.py file.
But what I want is I should be able validate my request before it goes to the user_signup method.
I want this validation for all the requests that comes to my server for all methods (ex:- user_signup,user_login ...) before it goes to respective methods.
Annotate the concerned views with a decorator that contains the logic you want to execute before the views are called.
See Python - Decorators for a head start.
And How to write a custom decorator in django?
If you want to do this on all requests, regardless of the associated view, then you should consider writing a middleware. See how to setup custom middleware in django

When tested http POST with chrome POSTMAN, it doesn't work in django

I use Django 1.9.7 & Python 3.5
I implement creating user mechanism and tried to test with POSTMAN(chrome application), but it doesn't work and it shows something like belows:
Forbidden (CSRF cookie not set.): /timeline/user/create/
This is the code :
urls.py
from django.conf.urls import url
From. import views
app_name = 'timeline'
urlpatterns = [
# ex) /
url(r'^$', views.timeline_view, name='timeline_view'),
# ex) /user/create
url(r'^user/(?P<method>create)/$', views.user_view, name='user_view'),
]
views.py
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, HttpResponse
from timeline.models import *
def timeline_view(request):
return HttpResponse('hello world')
def user_view(request, method):
if method == 'create' and request.method == 'POST':
print("hi")
username = request.POST.get('username')
username = request.POST.get('username')
user = User.objects.create_user(username, password=password)
user.first_name = request.POST.get('name','')
user.save()
profile = UserProfile()
profile.user = user
profile.save()
return HttpResponse('create success')
else:
return HttpResponse('bad request', status=400)
POSTMAN :
I tried Django CSRF Cookie Not Set but I think this post is for past version.
for testing i used the #csrf_exempt decorator.
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def user_view(request, method):
...
now you should be able to call this function without the csrf cookie.
(last time i tried it, i was using django 1.8.7)
source:
https://docs.djangoproject.com/en/1.9/ref/csrf/#edge-cases
You should put CSRFToken in request headers.
After sending request via postman, look at the response Cookies section, take csrftoken value and put in Headers section of request, like this:
key:X-CSRFToken
value: jSdh6c3VAHgLShLEyTjH2N957qCILqmb #your token value
Sometimes Version problem in 'Postman' :
I have face the same problem. While sending the data using the oldest version of postman in POST method.
That time I have received the empty json data in server side.
And I have fix this problem, Once I uninstall the oldest version of postman and installed with latest version.
Use this below statement on top of each and every view function definition (views.py). We don't need to use CRF related statements.
#api_view(["POST", "GET"])
eg:
#api_view(["POST", "GET"])
def GivenInput():
return Response(e.args[0],status.HTTP_400_BAD_REQUEST)
Note*:
But I didn't know that any alternative way to make it global throughout the file.

Django work flow redirect from one view to another and display page with url parameter

I am trying to create a web app that users will select a file, get data processed and then redirected to a confirmation page with a url parameter(?status=1) How can I move from the processing view to the confirmation view and then display the confirmation page?
I have put my code below which is not working in my views and urls py files
# views.py
def marketdata_processing(request):
if request.method == 'POST':
uploadform = forms.MyUploadForm(request.POST, request.FILES)
if uploadform.is_valid():
newdoc = models.UploadDocument(docfile=request.FILES['uploadfile'])
filename = request.FILES['uploadfile'].name
newdoc.save()
selecteddate = newdoc.getselecteddate() # Model method calculated
fileid = newdoc.pk
request.session['fileid'] = fileid
request.session['selecteddate'] = selecteddate
return redirect(reverse('views.confirm_input_details'))
else:
uploadform = forms.MyUploadForm()
# Render list page with the documents and the form
return render_to_response(
'mytemplate.html',
{'uploadform': uploadform},
context_instance=RequestContext(request)
)
def confirm_input_details(request):
fileid = request.session['fileid']
selecteddate = request.session['selecteddate']
msg = 'Proceed to import data for %s? \nFileID Being Imported is %s ' % (
selecteddate, fileid,)
url = reverse('views.confirm_input_details', kwargs={'status': 1})
return HttpResponseRedirect(url)
# urls.py
urlpatterns = [
url(r'', views.marketdata_processing, name='myapp/mytemplate.html'),
url(r'^\?status=(?P<status>[0-9]+)$',
views.confirm_input_details, name='myapp/myconfirmpage.html'),
]
There are a couple of issues I feel exist in your code:
Indentation is a very important thing in python code. Please make sure the indentation of your if-else statements in the 'marketdata_processing' view are in correct order. Beware, in your code:
return render_to_response('mytemplate.html',
{'uploadform': uploadform},
context_instance=RequestContext(request))
will always get executed as it is outside the else statement. You might want to indent it under the else (if it makes sense)- like so:
else:
uploadform = forms.MyUploadForm()
return render_to_response('mytemplate.html',
{'uploadform': uploadform},
context_instance=RequestContext(request))
Instead of 'redirect(reverse())' try 'HttpResponseRedirect()' in the processing view to call the confirm-page view, like so:
from django.http import HttpResponseRedirect
return HttpResponseRedirect('/?status=1') --> relative URL shown
place the relative/absolute url (both would work) above.
Finally, render your confirm-page template in the 'confirm_input_details' view with the context parameters, like so:
def confirm_input_details(request):
fileid = request.session['fileid']
selecteddate = request.session['selecteddate']
msg = 'Proceed to import data for %s? \nFileID Being Imported is %s ' % (selecteddate, fileid)
return render_to_response('confirmpage_template.html'),
{'fileid': fileid,
'selecteddate': selecteddate,
'msg': msg}, context_instance=RequestContext(request))
** P.S: Stick to neat basics and it will work. You happened to call your confirm_input_details view from within the same view itself. As far as I think, that would probably take you into an infinite loop. Simple concept of any view is:
take input:request, parameters
--> process logic with parameters
--> render a specific template or call another url where a template is being rendered.
You can't expect a page to show up without the rendering a template.

Categories