Launching a Stripe checkout pop-up after validating a WTForm - python

I have a super simple flask form for a service that I'm trying to integrate with the Stripe API:
from flask_wtf import FlaskForm
import wtforms as wtf
class ServiceForm(FlaskForm):
name = wtf.StringField('The service name')
submit_ = wtf.SubmitField('Submit >')
I'm serving this form along with my stripe key as follows:
#app.route('/index', methods=['GET'])
def index() -> str:
form = ServiceForm()
render_template('index.html', form=form, key='some stripe key',
service='some service', service_price=2500)
and the index.html file looks like this:
<form id="service-form" action="{{ url_for('pay_for_service', service=service) }}" method="POST" novalidate>
{{ form.csrf_token }}
{{ form.name }}
{{ form.submit_(class_="some-button" id="service-form-submit-button") }}
<script
src="https://checkout.stripe.com/checkout.js"
class="stripe-button"
data-key="{{ key }}"
data-amount="{{ service_price }}"
data-locale="auto">
</script>
</form>
The action of this form points to the following route:
#app.route('/pay+for+service/<string:service>', methods=['POST'])
def pay_for_service(service: str) -> str:
form = ServiceForm()
if form.validate_on_submit():
# ... do some validation
# now launch the Stripe dialog box to input credit card details.
which I'll use to validate the form submission before the Stripe payment dialog box is launched.
Basically, I want the stripe-button in the script to be embedded in the form.submit_ button, which has a custom some-button class. Then I want the Stripe payment pop-up to show after I've validated the form. How do I do this please? I think it's pretty easy but been scratching my head for ages!
Thanks for any help, and stay safe :-)

It's most easily done with an ajax call. Set the button action to call "submit_form" which will be:
function submit_form(Elem) {
$.ajax({
type: 'POST',
url: "{{ url_for('pay_for_service', service=service) }}",
data: $("#service-form").serialize(),
success: function(data) {
if (data['status'] == 'success') {
// call stripe
} else {
alert(data['data']['message'])
};
}
});
}
You won't need two view functions, a single one will do:
#app.route('/index', methods=['GET'])
def index() -> str:
form = ServiceForm()
if form.validate_on_submit():
return json.dumps({'status': 'success', 'data': {'message': 'ok'}})
elif request.method == 'POST':
message = str(form.errors)
return json.dumps({'status': 'fail', 'data': {'message': message }})
render_template('index.html', form=form, key='some stripe key',
service='some service', service_price=2500)

This is not possible with the "simple" integration approach. It's possible to run your own validation and trigger the Legacy Checkout modal programmatically using the "custom" integration approach, as seen in this example.
var handler = StripeCheckout.configure({ ... })
// later ...
handler.open({...})
However, note that this is all using an old deprecated version of Checkout. You should strongly consider using the new Checkout. Among other significant advantages, you can to any validation you like server-side before creating the checkout session.

Related

How to access the csrf_token inside django view

I'm trying to access the csrf_token inside the django view function.
I have tried importing the csrf:
from django.template.context_processors import csrf
and using it like this
landingPageHtml = landingPageHtml.replace('csrf_token', csrf(request)['csrf_token'])
But I'm getting an error. second argument must be a str not lazy object.
How can I access the csrf token in a view?
#login_required
def viewLandingPage(request, slug):
lp = getLandingPage(request, slug)
landingPageHtml = getLandingPageFile(request, lp['file'])
landingPageHtml = landingPageHtml.replace(
'{{ lp.seo_title }}', lp['seo_title'])
landingPageHtml = landingPageHtml.replace(
'{{ lp.seo_description }}', lp['seo_description'])
landingPageHtml = landingPageHtml.replace('csrf_token', 'csrf_token')
return HttpResponse(landingPageHtml)
Maybe you are looking for this to get csrf token
django.middleware.csrf.get_token(request)
Reference: How can i get csrftoken in view?

In Flask, how can I redirect to a profile being clicked?

I am very new to Flask. I have a mysql database and a template. Let's say I have 3 images
<div><img src= pathOfImage id="profile1"/></div>
<div><img src= pathOfImage id="profile2"/></div>
<div><img src= pathOfImage id="profile3"/></div>
The id of each image (profile1,profile2,profile3) is the primary key of the some tables in the database. What I want to do is to find the values of the corresponding attributes of that tuple by using the primary key. Then, load that those values to the template from the tuples.
And, I have the following code in Python:
from flask import *
#app.route("/")
def index():
return render_template("index.html")
#app.route('/experts')
def route1():
return render_template("experts.html", data=data)
The snippet of HTML code I gave above is in expert.html. I almost of SQL query that was not listed above, data on the second parameter in render_template in route1() is the SQL tuple, which generate all these images and the ID.
I have tried to put a button next to the images, and give id to the button instead. Then, pass the id to the python script as a variable using Ajax, and get the SQL tuple.
However, this isn't the hard part. The hard part is making the new route and loading the content. I have tried make a new route using "app.route" and pass the data into the second parameter of render_template. But, it didn't redirect to a new profile, and the method was called before I even click on the profile.
previously, I used button to retrieve the id:
<html>
<body>
<button id='1'>Button1</button>
<button id='2'>Button2</button>
<button id='3'>Button3</button>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"</script>
<script>
$(document).ready(function() {
$('button').click(function(event) {
var the_id = event.target.id;
$.ajax({
url: "/get_id",
type: "get",
data: {the_id: the_id},
success: function(response) {
},
error: function(xhr) {
}
});
})});
</script>
and, I used these to generate a new template:
import flask
from flask import *
from flaskext.mysql import MySQL
#app.route("/")
def index():
return render_template("index.html")
#app.route('/experts')
def route1():
return render_template("experts.html", data=data)
#app.route('/get_id')
#app.route('/profile')
def route2():
button_id '"' + flask.request.args.get('the_id') + '"'
//some code here to get the tuples that I need "client_info" and
//"skill_info" variable below
return render_template("profile.html", client_info=client_info,
skill_info=skill_info)
Hope someone would give a fresh start. Thanks in advance!
Instead of passing info by ajax you can pass it through the route url.
experts.html
<div><img src="pathOfImage"></div>
<div><img src="pathOfImage"></div>
<div><img src="pathOfImage"></div>
flask routes
import flask
from flask import *
from flaskext.mysql import MySQL
#app.route("/")
def index():
return render_template("index.html")
#app.route('/experts')
def route1():
return render_template("experts.html", data=data)
#app.route('/profile/<id>')
def route2(id):
# id variable contains user profile id string as per user click
# depending on id set variables client_info and skill_info and render
return render_template("profile.html", client_info=client_info, skill_info=skill_info)
{% for profile in profiles %}
<img src="{{profile.img}}">
{% endfor %}
...I guess? maybe?

Passing dynamically created json url to ajax and display to html page

I am currently working on a system where the login checking of username and password is checked by a python function. If the login details are correct, it will be redirected to a profile page (which i named dashboard). My problem is that my dahsboard/profile route reutrns a json if it is a POST and has also correct login details. I want this json data to be displayed in the html file. I managed to do it but I have used the variables in my jinja template. Although I have accomplished my goal (display the credentials in the html page), I would want it to be handled by ajax. How do I accomplish that?
Below are the codes I have tried so far (passing the data to the jinja variables)
#app.route('/dashboard', methods=['GET', 'POST'])
def dashboard():
if request.method == 'GET':
#get the username passed along with the redirect
data1= getdatafromdb('getdata1',(request.args.get('uname'),))[0][0]
data2= getdatafromdb('getdata2',(code,))[0]
if 'Error' in str(data2):
return jsonify({'status': 'error', 'message': data2[0][0]})
return render_template('dashboard.html', firstname=data2[1],
middleinitial=data2[2],
lastname=data2[3],
contact=data2[4],
code=data2[5],
affiliation=data2[6],
city=data2[7])
elif request.method == 'POST':
return True
return render_template('dashboard.html')
Currently, it appears that you are running your validation process in your /dashboard route, which is not correct if you wish to redirect your user to that very page once their credentials are validated. Instead, you need to create your separate login method with ajax. First, from the / (home) route, render the template that contains the input boxes with ajax:
home.html:
<html>
<body>
<input type='text' name='username' id='username'>
<div class='username_failed'></div>
<input type='password' name='password' id='password'>
<div class='password_failed'></div>
<button type='button' class='login'>Login</button>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('.login').click(function() {
var username = $('#username').val();
var password = $('#password').val();
$.ajax({
url: "/login",
type: "get",
data: {username: username, password:password},
success: function(response) {
if (!response.status){
$('.'+response.issue+'_failed').html('<p>'+response.message+'</p>')
}
else{
window.location.replace('/dashboard'); //redirect to dashboard
}
},
error: function(xhr) {
//Do Something to handle error
}
});
});
});
</script>
</html>
Then, the login route will valid the input dynamically from the ajax in home.html. Previously, you need to create a function to validate the username and password. A possibility is to first check if they are empty, and then query the database:
import typing
def check_if_valid(username:str, password:str) -> typing.Dict[str, typing.Any]:
if not username or not password:
return {'status':False, 'issue':'username' if not username else 'password', 'message':f'{[username, password][username]} cannot be empty'}
_username = check_valid_username_from_db(username)
_password = check_valid_password_from_db(username, password)
if not _username:
return {'status':False, 'issue':'username', 'message':'Invalid username'}
if not _password:
return {'status':False, 'issue':'password', 'message':'Invalid username or password'}
return {'status':True}
#app.route('/login')
def login():
username = flask.requests.args.get('username')
password = flask.requests.args.get('password')
_r = check_if_valid(username, password)
if _r.status:
data2= getdatafromdb('getdata2',(code,))[0]
for i, a in enumerate(['firstname', 'middleinitial', 'lastname', 'contact', 'code', 'affiliation', 'city']):
flask.session[a] = data2[i]
flask.session['user_validated'] = _r.status
return flask.jsonify(_r)
Now, all your user data, if the user was successfully validated, will be stored as part of the session. Now, you can create your dashboard page, first with the html for the dashboard:
dashboard.html:
<html>
<body>
<h1>Welcome, {{firstname}}</h1>
<h4>Your data:</h4>
{%for d in data%}
<span>{{d.title}}: {{d.value}}</span>
{%endfor%}
</body>
</html>
Then, create the dashboard route with user validator:
def isloggedin(f):
def wrapper(*args):
if not flask.session['user_validated']:
return '<p>Not logged in</p>'
return f(*args)
return wrapper
#app.route('/dashboard', methods=['GET'])
#isloggedin
def dashboard():
from collections import namedtuple
headers = ['firstname', 'middleinitial', 'lastname', 'contact', 'code', 'affiliation', 'city']
data = namedtuple('data', ['title', 'value'])
return flask.render_template('dashboard.html', firstname = flask.session['firstname'], data = [data(a, flask.session[a]) for a in headers[1:]])
Lastly, link all together with the home route:
#app.route('/', methods=['GET'])
def home():
return flask.render_template('home.html')

Problems redirecting to dynamic URLs in Flask with 'action'

My app has a text box and a submission button. I am trying to create and redirect to dynamic URLs in my app, that are based off of what is typed in the text box. For example, the user enters in '1234', clicks submit, then is taken to 'website.com/results/1234'. The problem seems to be that the HTML for my button doesn't want to redirect the user to the new, dynamic URL. I am passing this to the HTML with Jinja.
Here is what I have.
The user starts on the home page, that is defined like this
#app.route("/home/", methods=["GET", "POST"])
def start():
return render_template("dashboard.html")
Dashboard.html has a text box and submission button (below). As you can see, the action of this button is to redirect to {{ results_page }}, where "results_page" comes from my Python function load_results (also below) and is passed to the HTML with render_template.
<div>
<form action="{{ results_page }}" class="form-inline" method="post">
<div class="form-group">
<label for="PubmedID">Pubmed ID(s)</label>
<input type="text" class="form-control" id="PubmedID" placeholder="18952863, 18269575" name="pmid" value="{{request.form.pmid}}">
</div>
<button type="submit" id= "myButton" class="btn btn-default" data-toggle="modal" data-target="#myModal">Submit</button>
</form>
</div>
The results page of my app uses the user input to look up some information and display it.
#app.route('/results/<query>', methods=["GET", "POST"])
def load_results(query):
form = pmidForm(secret_key='super secret key')
try:
if request.method == 'POST':
query = form.pmid.data #This is the user input
results_page = "website.com/results/"+query
return(query)
#do lots of stuff using the query
return render_template('results.html', form=form, results_page = results_page)
except Exception as e:
return(str(e))
If I run my app like this, the home page is fine, but when I click "Submit", the app doesn't take in the user input or do anything with it. Simply the home page refreshes.
I am new to web development, but since this code works fine if I hardcode the button to action = "website.com/results" in the HTML, AND do without the <query> in /results/<query> for the results page, I think that only a few adjustments should be needed to make my app dynamically redirect to and load pages correctly. Right now, I'm not sure what I'm missing. Please let me know where I'm going stray.
EDIT -
Now I have implemented a handle_form function that redirects to my dynamic URL. This function properly redirects but then I get a 404 error.
#app.route('/form/', methods=["POST"]) #handles form submission
def handle_form():
form = pmidForm(secret_key='super secret key')
if request.method == "POST":
query = request.form['pmid']
return redirect('/results/'+query)
I have also edited my form in the HTML action to go to /form/
<form action="website.com/form/" class="form-inline" method="post">
<div class="form-group">
<label for="PubmedID">Pubmed ID(s)</label>
<input type="text" class="form-control" id="PubmedID" placeholder="18952863, 18269575" name="pmid" value="{{request.form.pmid}}">
</div>
<button type="submit" id= "myButton" class="btn btn-default" data-toggle="modal" data-target="#myModal">Submit</button>
</form>
With this, my site will properly redirect to /results/<query>/ (e.g. /results/1234) but I get a 404 error. Here is how I have changed my load_results
#app.route('/results/<query>', methods=["GET"])
def load_results(query):
form = pmidForm(secret_key='super secret key')
try:
if request.method == 'GET':
query = form.pmid.data #THIS IS THE USER INPUT FROM THE FORM #referencing 'class pmidForm'
return query
.
.
#do stuff
I think I am very close but need to figure out why I am getting a 404 error. My guess is that I am using "GET" incorrectly. My form in the HTML uses method="post". Since this does not match with "GET", is there no way for my load_results(query) function to retrieve the contents of the form?
EDIT 2 -
Changed handle_form to redirect with url_for:
#app.route('/form/', methods=["POST"]) #handles form submission
def handle_form():
form = pmidForm(secret_key='super secret key')
if request.method == "POST":
query = request.form['pmid']
return redirect(url_for('.load_results', query=query))
And changed load_results to not return "query"
#app.route('/results/<query>', methods=["GET"])
def load_results(query):
form = pmidForm(secret_key='super secret key')
try:
if request.method == 'GET':
query = form.pmid.data # This shouldn't work??
.
.
# do stuff with the variable "query"
With this, it's still returning the 404 Error as before. Should I not be using if request.method == GET ?
Edit 3 -
Even a very simplified load_results will give me the 404 error, so I'm not sure what's up.
#app.route('/results/<query>', methods=["GET", "POST"])
def load_results(query):
q = query
return render_template('test.html', q=q)
EDIT - 3
It seems that the accepted solution IS the correct solution to this problem, however there is an issue with uwsgi in my server that is re-directing it to the wrong location :(
Your punctual problem is that /home route function also needs to put the results_page url on the templating context.
results_page = "website.com/results"
return render_template("dashboard.html", results_page=results_page)
Since the variable is undefined, flask is calling the original endpoint on the form submission.
Your larger problem is that your appraoch isn't going to get you a dynamic results url that looks like /results/1234.
Typical approaches are to redirect on the server side when you handle the post request; or to use JavaScript in the client to get the form data and change the browser location to /results/1234.
A simplified version of how to handle this with a server side redirect might look something like this. One route that handles the form submission and another that displays results. You simply redirect from one to the other to get the nice url.
#app.route('/form', methods=["POST"])
def handle_form():
query = form.pmid.data #This is the user input
return redirect(url_for('.load_results', query=query))
#app.route('/results/<query>') *removed the method spec to handle the redirect?
def load_results(query):
.
.
# do something with query

Django AJAX search function

I'm trying to make a search function in my Django project using AJAX. But the functions in views.py don't seem to work properly. And maybe I have some other mistakes. Could you please tell me what I need to correct?
This is my AJAX file:
$(document).ready( function(){
$('#suggestion').keyup(function(){
var query;
query = $(this).val();
$.get('/friends_plans/suggest_users/', {suggestion: query}, function(data){
$('#user').html(data);
});
});
});
This is part of my template:
<div>
<ul class="nav nav-list">
<li class="nav-header">Find user</li>
<form>
<li><input class="search-query span10" type="text" name="suggestion" value=" " id="suggestion" /></li>
</form>
</ul>
</div>
<div id="user">
</div>
These ara functions from views.py:
def suggest_users(request):
users_list = []
starts_with = ''
if request.method == 'GET':
starts_with = request.GET['suggestion']
users_list = get_users_list(5, starts_with)
return render(request, 'friends_plans/list.html', {'users_list': users_list})
def get_users_list(max_results=0, starts_with=''):
users_list = []
if starts_with:
users_list = Person.objects.filter(username__istartswith=starts_with)
if max_results > 0:
if len(users_list) > 0:
users_list = users_list[:max_results]
return users_list
This is from urls.py:
url(r'^suggest_users/$', views.suggest_users, name='suggest_users')
The istartswith method doesn't work properly with the variable but does with the constant, I can't understand why. And suggest_users function doesn't return users_list to the object with id user ($('#user').html(data)), nothing appears on the page. But maybe there are some other mistakes.
Django's render function renders HTML after parsing it with Jinja. If you want to write a view that acts as an endpoint for an AJAX function, you do not want that view to return render.
Instead you should use return JsonResponse. JsonResponse accepts a dictionary as an argument. And it builds a proper JSON object for you. :) Which will then be picked up by your AJAX's success function.
Here's an example of how to use JsonResponse:
from django.http import JsonResponse
def some_endpoint(request, *args, **kwargs):
data = dict()
data["foo"] = "bar"
data["username"] = User.objects.get(id=request["id"]).username
return JsonResponse(data)
This will cause your view to return a JSON Object, which is what your AJAX function is looking for.
Second suggestion I would make would be to use jQuery's $.ajax() function rather than jQuery's shortcut .get() function. The advantage of this would be learning all the parameters that go along with AJAX calls.
Here's an example of jQuery's $.ajax() function.
$(document).ready( function(){
$('#suggestion').keyup(function(){
var query = $(this).val();
$.ajax(function(){
type: "GET",
url: "/friends_plans/suggest_users/",
data: {suggestion: query},
success: function(data){
console.log("SUCCESS");
console.log(data);
},
failure: function(data){
console.log("FAIL");
console.log(data);
},
});
});
});

Categories

Resources