Stripe integration with Django - python

I am following a tutorial on stripe.com, to accept a charge it says to use the following form and capture the Token it returns in a view
Stripe "Checkout" form:
<form action="{% url 'payment' %}" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="pk_test_z1bxF7Bk4Rk9PZuBFHMrYZnj"
data-amount="999"
data-name="Demo Site"
data-description="Example charge"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-locale="auto">
</script>
</form>
The next step says to simply copy and paste this and your test account should be able to accept charges
Stripe view:
def payment(request):
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BJUliYkgS5VZEKFM1UQAz9cF"
# Token is created using Checkout or Elements!
# Get the payment token ID submitted by the form:
token = request.POST['stripeToken']
charge = stripe.Charge.create(
amount=999,
currency='usd',
description='Example charge',
source=token,
)
but stripeToken is mentioned nowhere in the form and the code returns an error because of this, can someone explain where this is coming from? (Note, the example was in Flask so I changed token = request.form['stripeToken'] # Using Flask to token = request.POST['stripeToken'] #using Django
Both of these can be found at https://stripe.com/docs/quickstart (Step 1 shows 'Checkout', step 2 shows the python code) Thanks in advance for any help.

try
token = request.GET.get['stripeToken']

Related

In Flask, how do I prevent a route from being accessed unless another route has been visited first?

PROBLEM STATEMENT
I'm working on a Flask web app that displays a list of items in a table. The user can select a row and hit a Delete button to delete the item. However, before the item is deleted from the database, the user is first routed to a confirmation screen where some item details are displayed as well as a Confirm button. The url for the confirmation page follows this pattern: dashboard/confirm-delete/<id> and the url for the actual delete page follows this pattern: dashboard/delete/<id>. See admin/views.py below for more details.
While the system works, the problem I have is that a user can simply skip the confirmation page by typing dashboard/delete/<id>, where <id> is substituted by an actual item id, into the address bar.
QUESTIONS
Is there a way to prevent users from accessing dashboard/delete/<id> unless they first go to dashboard/confirm-delete/<id> (the confirmation screen)? Alternatively, is my approach wrong and is there a better one available?
CURRENT CODE:
Function in my dashboard.html page called when a row is selected and the delete button is pressed:
$('#remove').click(function () {
var id = getId();
window.location.href="/dashboard/confirm-delete" + $.trim(id);
});
Confirm button in confirm-delete.html (the delete confirmation page):
<a class="btn btn-default" href="{{ url_for('admin.delete_item', id=item.id) }}" role="button">Confirm Delete</a>
My admins/views.py:
#admin_blueprint.route('dashboard/confirm-delete/<id>')
#login_required
#groups_required(['admin'})
def confirm_delete_item(id)
item = Item.query.get_or_404(id)
return render_template('admin/confirm-delete.html', item=item, title="Delete Item")
#admin_blueprint.route('dashboard/delete/<id>', methods=['GET', 'POST'])
#login_required
#groups_required(['admin'})
def delete_item(id)
item = Item.query.get_or_404(id)
db.session.delete(item)
db.commit()
return redirect(url_for('home.homepage'))
SOLUTION
Based on the answer marked as accepted I solved the problem as follows:
First, I created a new form to handle the Submit button in the confirm-delete.html page:
admin/forms.py:
from flask_wtf import FlaskForm
from wtforms import SubmitField
class DeleteForm(FlaskForm):
submit = SubmitField('Confirm')
I substituted the Confirm Button code with the following to confirm-delete.html:
<form method="post">
{{ form.csrf_token }}
{{ form.submit }}
</form>
Finally, I merged both of the functions in app/views.py as follows:
#admin_blueprint.route('dashboard/confirm-delete/<id>', methods=['GET', 'POST'])
#login_required
#groups_required(['admin'})
def confirm_delete_item(id)
form = DeleteForm()
item = Item.query.get_or_404(id)
if form.validate_on_submit():
if form.submit.data:
db.session.delete(item)
db.commit()
return redirect(url_for('home.homepage'))
return render_template('admin/confirm-delete.html', item=item, form=form, title="Delete Item")
This way, a user can't bypass the delete confirmation screen by typing a specific link in the address bar, plus it simplifies the code.
As already mentioned in comments, one way of solving your problem is checking for a certain cookie as the user sends a request. But personally I would not recommend this method, because such cookies can very likely be compromised unless you come up with some sort of hashing algorithm to hash the cookie values and check them in some way.
To my mind, the most easy, secure and natural way of doing it is protecting /delete route with CSRF-token. You can implement it with Flask_WTF extension.
In a word, you have to create something like DeleteForm, then you put {{form.csrf_token}} in your confirm-delete.htmland validate it in delete_view() with form.validate_on_submit()
Check out their docs:
http://flask-wtf.readthedocs.io/en/stable/form.html
http://flask-wtf.readthedocs.io/en/stable/csrf.html
I would make the delete page POST-only. The browser may skip a GET request or try it many times, you have no control over it. A crawler could follow an anonymous delete link and delete all your wiki articles. A browser prefetcher could prefetch a logout link.
REST purists would insist you use GET, POST, DELETE and PUT methods for their intended purposes.
https://softwareengineering.stackexchange.com/questions/188860/why-shouldnt-a-get-request-change-data-on-the-server
So,
In HTML
<form action='/dashboard/delete/{{id}}' method='post'>
In Flask
#app.route('/dashboard/delete/<int:id>', methods=['POST'])
def delete(id):
I think there's a mistake in parenthesis.
#groups_required(['admin'})
Shouldn't it be ??
#groups_required(['admin'])

Create a Flask Search Bar that Inserts URI Variable with url_for()

I have a Flask site that has a 'search bar' where you type in the location ID of a particular location and then click Submit to be taken to the page for that location, if it exists. Here's the current form action:
<form id="locationinfo" action="{{ url_for('location') }}">
When you click Submit you are taken to /location?info=SITEID and that works just fine. What I want to do is change this behavior slightly so that when a user clicks Submit they are taken to /location/SITEID/ instead. I have the decorator set up in my main Flask routes file, but I'm struggling to put the pieces together to get this simple form together.
#app.route("/location/<locationid>/")
def locations(locationid):
...
return locationid
Any direction would be greatly appreciated!
[Edit with current full form code]
#app.route("/location")
def location():
location_id = request.args.get("info")
<form id="info" action="{{ url_for('location') }}">
<input type="text" name="info" id="locationfield">
<button type="submit">Go!</button>
</form>
You can't change how HTML forms submit their fields, they will always be in the query string or body (POST). One option is to use JavaScript to override the submit event to perform your own submit and re-render with the results.
A simpler solution is to redirect to the nice url after submit. This keeps the "search" action separate from the "show" action (even if they are handled by the same view).
#app.route('/location/')
#app.route('/location/<int:id>/')
def location(id=None):
# redirect to the second form if the id wasn't in the path
# raises 400 error if id wasn't in the query
if id is None:
return redirect(url_for('location', id=request.args['info']))
# id is in the path, continue
...
You can expand this later if you want to search by something besides id. Perform the search then redirect to the found id.

getting an error didn't return an HttpResponse object. What is going on?

For the first time i am trying to create a small django application. It is just a basic page with two forms, a search form, which returns values from a database (working well), and a insert form (the problem). In the insert form there are three text fields asking for values for a new row in the database. The app is called "searcher". Here is my code:
views.py
from django.http import HttpResponse
from django.template import Context
from django.template.loader import get_template
from searcher.models import Publisher
from django.db.models import Q
def search(request):
if "search" in request.GET:
value = request.GET['search']
results = Publisher.objects.filter(Q(city__contains=value) | Q(name__contains=value))
else:
value = "No term"
results = []
template = get_template("base.html")
html = template.render(Context({"value":value, "results":results}))
return HttpResponse(html)
def insert(request):
if "name" in request.POST:
for key in request.POST:
counter = 0
if key != '':
counter = counter + 1
if counter == len(request.POST):
row = Publisher(name=request.POST['name'], city=request.POST['city'], website=request.POST['website'])
row.save()
base.html
<html>
<body>
<form method = "GET" action = "">
<input type = "text" name = "search"><input type = "submit">
</form><br>
you searched for:{{value}}<br>
your results:
{% for result in results %}
<br>{{result}}<br>
{% endfor %}
<br><br>
<form method = "POST" action = "/test/insert/">
<input type = "text" name = "name" value = "name"><br>
<input type = "text" name = "city" value = "city"><br>
<input type = "text" name = "website" value = "website"><br>
<input type = "submit">
</form>
</body>
</html>
urls.py
from django.conf.urls import patterns, include, url
from searcher import views
urlpatterns = patterns('',
url(r'^test/$', views.search),
url(r'^test/insert/$', views.insert),
)
the error is saying "ValueError at /test/insert The view searcher.views.insert didn't return an HttpResponse object." as a result of me submitting the insert form.
I understand what the error is saying: sorry but all views in views.py MUST return something to show.
Firstly why is this?
And secondly what is the correct way to accomplish the insert form ? Currently i have set the form action = "/test/insert/" so that in urls.py it can recognise the insert form was submitted and call the function insert, how should the function be called instead?
You seem to be asking two questions
"Why must all views return something to show?"
You're misinterpreting the error message. The error you saw didn't
say "all views MUST return something to show" - it only said
"searcher.views.insert didn't return an expected HttpResponse".
Since you're dealing with a HTTP request (a form POST), you're
expected to send back a HTTP Response. All HTTP requests expect a
response - this is a matter of a standard agreement on how to
communicate - the details are part of the http standard
definition, and somewhat explained as part of this answer.
"What is the correct way to accomplish the insert form?"
You're implementing it correctly - just send back a HTTP Response
for the POST. The code for it is almost identical to what you're
sending back in response to the Search request (maybe you'll want to
add a message in saying that the insert completed successfully,
where in the Search request you might have returned the item that
was being searched for?).
If you'd like to avoid having to refresh the form altogether, you might want to use an ajax call, instead of a http call, again, as described here.
All views must return an HttpResponse object because that's what the server returns to the browser that initially makes the request. Pretty much by definition, a view is a callable that accepts a request object and returns a response object.
It's fine to have code in views.py that doesn't return a response, such as helper functions for a view, but it's not fine to use such code as a view.
The usual pattern when working with POST requests is to return a redirect to a success page, on success, and to redisplay the form with the appropriate error messages on failure. See for instance the "Using a form in a view" docs.

flask-wtf form validation not working for my new app

I've used flask before and I've had working form validation, but for some reason it's not working for my new app. Here is the basic code of the form.
from flask.ext.wtf import Form, TextField, TextAreaField, SubmitField, validators,ValidationError
class subReddit(Form):
subreddit = TextField('subreddit', [validators.Required('enter valid subreddit')])
next = SubmitField('next')
change = SubmitField('change')
user = TextField('user', [validators.Required('enter valid user')])
fetch = SubmitField('fetch comments')
I have subreddit as the validation field, so if it's empty, I want it to throw an error and reload the page.
The HTML:
<form class='sub' action="{{ url_for('sr') }}" method='post'>
{{ form.hidden_tag() }}
<p>
if you want to enter more than one subreddit, use the + symbol, like this:
funny+pics+cringepics
<p>
<br/>
{% for error in form.subreddit.errors %}
<p>{{error}}</p>
{% endfor %}
{{form.subreddit.label}}
{{form.subreddit}}
{{form.change}}
</form>
I have CSRF_ENABLED=True in my routes.py as well. What am I missing? When I leave the subredditfield empty and click change, it just reloads the page, no errors. This is an issue because whatever is in the field will get recorded in my database, and it can't be empty.
EDIT
#app.route('/index',methods=['GET','POST'])
#app.route('/',methods=['GET','POST'])
def index():
form = subReddit()
rand = random.randint(0,99)
sr = g.db.execute('select sr from subreddit')
srr = sr.fetchone()[0]
r = requests.get('http://www.reddit.com/r/{subreddit}.json?limit=100'.format(subreddit=srr))
j = json.loads(r.content)
pic = j['data']['children'][rand]['data']['url']
title = None
if form.validate_on_submit():
g.db.execute("UPDATE subreddit SET sr=(?)", [form.subreddit.data])
print 'validate '
if j['data']['children'][rand]['data']['url']:
print 'pic real'
sr = g.db.execute('select sr from subreddit')
srr = sr.fetchone()[0]
r = requests.get('http://www.reddit.com/r/{subreddit}.json?limit=100'.format(subreddit=srr))
pic = j['data']['children'][rand]['data']['url']
title = str(j['data']['children'][rand]['data']['title']).decode('utf-8')
return render_template('index.html',form=form,srr=srr,pic=pic,title=title)
else:
print 'not valid pic'
return render_template('index.html',form=form,srr=srr,pic=pic)
else:
print 'not valid submit'
return render_template('index.html',form=form,srr=srr,pic=pic)
return render_template('index.html',form=form,srr=srr,pic=pic)
You have a number of problems.
The most important is that validation occurs in the POST request view function. In your example this is function sr. That function should create the form object and validate it before adding stuff to the database.
Another problem in your code (assuming the above problem is fixed) is that after validate fails you redirect. The correct thing to do is to render the template right there without redirecting, because the error messages that resulted from validation are loaded in that form instance. If you redirect you lose the validation results.
Also, use validate_on_submit instead of validate as that saves you from having to check that request.method == 'POST'.
Example:
#app.route('/sr', methods=['POST'])
def sr():
form = subReddit()
if not form.validate_on_submit():
return render_template('index.html',form=form)
g.db.execute("UPDATE subreddit SET sr=(?)", [form.subreddit.data])
return redirect(url_for('index'))
Additional suggestions:
it is common practice to start your class names with an upper case character. SubReddit is better than subReddit.
it is also common to have the GET and POST request handlers for a form based page in the same view function, because that keep the URLs clean when validation fails without having to jump through hoops to get redirects working. Instead of having the sr function separately you can just combine it with index() and have the action in the form go to url_for('index').
Flask-WTF adds a new method onto the form called validate_on_submit(). This is like the WTForms validate() method, but hooks into the Flask framework to access the post data. The example given on the Flask site is:
form = MyForm()
if form.validate_on_submit():
flash("Success")
return redirect(url_for("index"))
return render_template("index.html", form=form)
Because you're just using validate(), the form is trying to validate without any data (which, of course, will fail). Then you're redirecting. Try to use validate_on_submit() as shown above.

How to make my welcome text appear?

After login, I want the text "Welcome, Niklas" to display but after logging in I have to reload the page and I didn't understand how to make the page display the text from the server variable current_user. If I login and press reload then the correct welcome message appears. Can you help me achieve what I want? Why is there no simple working example for FB python + javascript? Can I implement facebook connect without javascript? If so, do I have to use and set the cookie myself? Thank you
{% load i18n %}
<!DOCTYPE html>
<html xmlns:fb="https://www.facebook.com/2008/fbml">
<head>
<title>
Test Facebook SDK
</title>
</head>
<body>
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
FB.init({
appId : '164355773607006', // App ID
channelURL : '//WWW.KOOLBUSINESS.COM/static/channel.html', // Channel File
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
oauth : true, // enable OAuth 2.0
xfbml : true // parse XFBML
});
};
// Load the SDK Asynchronously
(function(d){
var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
d.getElementsByTagName('head')[0].appendChild(js);
}(document));
</script>
<fb:login-button autologoutlink="true"></fb:login-button>
{% if current_user %}
<div id="user-ident">
<span>{% trans "Welcome," %} <b>{{ current_user.name|escape }}</b></span>
</div>
{% endif %}
</body>
</html>
Here's how I get the variable current_user
#property
def current_user(self):
if not hasattr(self, "_current_user"):
self._current_user = None
cookie = facebook.get_user_from_cookie(
self.request.cookies, facebookconf.FACEBOOK_APP_ID, facebookconf.FACEBOOK_APP_SECRET)
logging.debug("logging cookie"+str(cookie))
if cookie:
# Store a local instance of the user data so we don't need
# a round-trip to Facebook on every request
user = FBUser.get_by_key_name(cookie["uid"])
logging.debug("user "+str(user))
logging.debug("username "+str(user.name))
if not user:
graph = facebook.GraphAPI(cookie["access_token"])
profile = graph.get_object("me")
user = FBUser(key_name=str(profile["id"]),
id=str(profile["id"]),
name=profile["name"],
profile_url=profile["link"],
access_token=cookie["access_token"])
user.put()
elif user.access_token != cookie["access_token"]:
user.access_token = cookie["access_token"]
user.put()
self._current_user = user
return self._current_user
When using Oauth 2.0
FB.Init(...oauth=true)
the login button is setting a cookie into your domain named
fbsr_(appid)=....
which is encoded using the app secret key, and which do not contain the user token anymore.
If you really want to avoid the use of java script client side (which is the most simple way) you can leverage the presence of this cookie to know if the user is connected to facebook and then perform any authorization check or dynamically display a welcome message.
I do not use python, so i do not have working example, but this give you a way to search.
Hope this help.

Categories

Resources