I can't get metadata from stripe session in django - python-3.x

I'm doing a course in udemy and there's a stripe payment lesson in django. And the problem is that today's stripe docs are different from docs from the lesson and I can't do a one thing: get an order_id from the order to make my function work correct (function change order status, save order in 'basket_history' dict and delete an actual basket). There are code from the video and my one:
views.py
Similar code:
class OrderCreateView(TitleMixin, CreateView):
title = 'Store - Оформление заказа'
template_name = 'orders/order-create.html'
form_class = OrderForm
success_url = reverse_lazy('orders:order_create')
def post(self, request, *args, **kwargs):
super().post(request, *args, **kwargs)
baskets = Basket.objects.filter(user=self.request.user)
checkout_session = stripe.checkout.Session.create(
line_items = baskets.stripe_products(),
metadata = {'order_id': self.object.id},
mode='payment',
success_url='{}{}'.format(settings.DOMAIN_NAME, reverse('orders:order_success')),
cancel_url='{}{}'.format(settings.DOMAIN_NAME, reverse('orders:order_canceled')),
)
return HttpResponseRedirect(checkout_session.url, status = HTTPStatus.SEE_OTHER)
def form_valid(self, form):
form.instance.initiator = self.request.user
return super(OrderCreateView, self).form_valid(form)
#csrf_exempt
def my_webhook_view(request):
payload = request.body
sig_header = request.META['HTTP_STRIPE_SIGNATURE']
event = None
try:
event = stripe.Webhook.construct_event(
payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
)
except ValueError as e:
# Invalid payload
return HttpResponse(status=400)
except stripe.error.SignatureVerificationError as e:
# Invalid signature
return HttpResponse(status=400)
Then the difference
My code:
# Handle the checkout.session.completed event
if event['type'] == 'checkout.session.completed':
# Retrieve the session. If you require line items in the response, you may include them by expanding line_items.
session = stripe.checkout.Session.retrieve(
event['data']['object']['id'],
expand=['line_items'],
)
line_items = session.line_items
# Fulfill the purchase...
fulfill_order(line_items)
# Passed signature verification
return HttpResponse(status=200)
def fulfill_order(line_items):
order_id = int(line_items.metadata.order_id)
order = Order.objects.get(id = order_id)
order.update_after_payment()
Lesson's code:
# Handle the checkout.session.completed event
if event['type'] == 'checkout.session.completed':
# Retrieve the session. If you require line items in the response, you may include them by expanding line_items.
session = event['data']['object']
# Fulfill the purchase...
fulfill_order(session)
# Passed signature verification
return HttpResponse(status=200)
def fulfill_order(session):
order_id = int(session.metadata.order_id)
order = Order.objects.get(id = order_id)
order.update_after_payment()
So, fulfill_order doesn't work.
Help me pls and sorry for my english)
P.S. I've tried the old docs code, but id doesn't work

Related

Best practice to call function inside route methode

I'm new to flask and in order to refactor an existing route method on a Flask API, i'm looking for the best practice to reduce it and call method inside the route method.
Acutally the route is designed like that :
#qman.route('/add_report/', methods=['POST'])
def create_report():
"""
Check if data send throught http POST request, is correct based on the report
schema and not already recorded in the table report of the DB.
:param: data from POST request
:return: Ok, valide and imported -> 201, Correct but AlreadyKnown -> 208,
InvalideScheme -> 422
"""
jsonData = request.get_json()
reportSchema = ReportSchema()
try:
data = reportSchema.load(jsonData)
except ValidationError as validation_err:
return(validation_err.messages), 422
nameReportCheck = data["report_name"]
report = Report.query.filter_by(report_name=nameReportCheck).first()
if report is None:
# Create new report
report = Report(
report_name=nameReportCheck,
hostname=data["hostname"],
status=data["status"],
date=data["date"],
nb_analysis_erreur=data["nb_analysis_erreur"]
)
db.session.add(report)
db.session.commit()
NewResult = reportSchema.dump(Report.query.get(report.reportID))
return{"message" : "Created new report" , "report" : NewResult}, 201
else :
reportAlreadyKnown = reportSchema.dump(Report.query.get(report.reportID))
return{"message" : "This report is already in the DB", "report" : reportAlreadyKnown}, 208
In the facts i would like to call a function named valid_schema(_schema, _jsondata) to check if the data send throught POST request match with my schema of model Report().
This function return a Response() object with serialized data and a 200 code if it's serialization is possible or an error that i cath inside try/except with 400 error code.
def valid_schema(_schema, _jsondata):
schema = _schema()
try:
data = schema.load(_jsondata)
except ValidationError as validation_err:
response = Response(validation_err.messages, 422)
return response
response = Response(data, 200, mimetype="application/json")
return response
Then the route method call an other function named create_report(report_data) if valid_schema(_schema, _jsondata) return report_data and 200 code in response object.
With his args, this method check if the records is not already in the DB and if is not, he create a Report() object from report_data arg and insert this one as a new record into the DB.
In fact I guess I can easily call this method inside the route function but it seem weird and there is probably an other way that I can't find, maybe decorator ?
One possibility for refactoring is the use of webargs, Flask-Marshmallow and marshmallow-sqlalchemy.
With Flask-Marshmallow you can check the input by specifying fields and validators. Webargs offers you the option of validating the defined scheme in a decorator and passing it on to the route as an argument. Using marshmallow-sqlalchemy in combination, this is immediately converted into a database model.
The following example is based on your information and gives you a brief overview of the usage. By defining your own error handler, the error messages can also be sent as JSON. Use in blueprints, views or the like is possible.
from flask import Flask
from flask import jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from marshmallow.validate import Length, OneOf
from webargs.flaskparser import use_args
app = Flask(__name__)
db = SQLAlchemy(app)
ma = Marshmallow(app)
class Report(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=True)
hostname = db.Column(db.String)
status = db.Column(db.String)
date = db.Column(db.DateTime)
nb_analysis_error = db.Column(db.String)
class ReportSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Report
load_instance = True
sqla_session = db.session
name = ma.Str(required=True, validate=Length(min=3))
hostname = ma.Str(required=True)
date = ma.DateTime(required=True)
status = ma.Str(required=True, validate=OneOf(['online', 'offline']))
nb_analysis_error = ma.Str(missing='Unknown Error')
#app.route('/add_report', methods=['POST'])
#use_args(ReportSchema(), location='json')
def add_report(report):
report_schema = ReportSchema()
_report = Report.query.filter_by(name=report.name).first()
if _report:
report_data = report_schema.dump(_report)
return jsonify(message='Already Reported', report=report_data), 208
else:
db.session.add(report)
db.session.commit()
report_data = report_schema.dump(report)
return jsonify(message='Created', report=report_data), 201
with app.app_context():
db.drop_all()
db.create_all()

How to filter Django serializer data?

I'm trying to filter data based on userName in JWT. This is how I've been trying to do it:
views.py:
class TestView(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request):
token = request.META.get('HTTP_AUTHORIZATION', " ").split(' ')[1]
data = {'token': token}
try:
valid_data = VerifyJSONWebTokenSerializer().validate(data)
user = valid_data['user']
request.user = user
person = Person.objects.filter(userName=request.user)
except ValidationError as v:
print("validation error", v)
return Response(person[0])
This works as I can get the Person data with print("Person: ", person[0]). The return Response(person[0]) however returns an error: TypeError: Object of type Person is not JSON serializable. I guess I could use a serializer class to return a valid JSON, am I right? I have this in my serializers.py:
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = '__all__'
I just don't know how to use this with my view. If I use serializer instead of person = Person.objects.filter(userName=request.user), how is the filtering supposed to be done?
Please correct me if I'm not on right track at all.
You can add serializer to view, provide query result to serializer and get serialized data:
class TestView(APIView):
permission_classes = (IsAuthenticated,)
serializer_class = TestSerializer # add serializer
def get(self, request):
token = request.META.get('HTTP_AUTHORIZATION', " ").split(' ')[1]
data = {'token': token}
try:
valid_data = VerifyJSONWebTokenSerializer().validate(data)
user = valid_data['user']
request.user = user
person = Person.objects.filter(userName=request.user).last()
data = self.serializer_class(instance=person).data # serialize query result
except ValidationError as v:
print("validation error", v)
raise v
return Response(data) # return serialized response
Here you can find some examples of using serializer with class based views.
Also note that your Person model might have security-sensitive fields like password, etc, so it's better to specify exact fields you need in serializer rather then use fields = '__all__'.
Are you using Django-Rest-Framework (based on your use of ModelSerializer)?
In pure Django, from the documentation on serializers, you can do something like:
from django.core import serializers
json_response = serializers.serialize("json", person[0])
return Response(json_response)
If you are using Django-Rest-Framework:
return Response(TestSerializer(person[0]).data)

ParseError On Django Restframework

I have created a Custom user Model for my project, using Django 3.2.
To POST/PUT the details in the user I am using restframework.
But while giving the data for "PUT" operation to change some change I am getting the error as JSON parse error - Expecting value: line 1 column 1 (char 0)
def update_masteruser(request,pk):
try:
user = MasterUser.objects.get(pk=pk)
except MasterUser.DoesNotExist:
return JsonResponse({'message': 'The User does not exist'}, status=status.HTTP_404_NOT_FOUND)
if request.method == "PUT":
user_data = JSONParser().parse(request) #problem seems to be on this line
serializer = RegisterUserSerializer(user, data=user_data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
alternate method, this throws 'WSGIRequest' object has no attribute 'data':
def update_masteruser(request,pk):
try:
user = MasterUser.objects.get(pk=pk)
except MasterUser.DoesNotExist:
return JsonResponse({'message': 'The User does not exist'}, status=status.HTTP_404_NOT_FOUND)
if request.method == "PUT":
serializer = MasterUserSerializer(user, data=request.data)
data = {}
if serializer.is_valid():
serializer.save()
data["success"] = "updated"
return Response(data=data)
return Response(serializer.data)
serializers.py
class MasterUserSerializer(serializers.ModelSerializer):
class Meta:
model = MasterUser
fields = "__all__"
I searched the error cause, but couldnt find the reason on it. Why cant it parse the data from request?
Refer to This Documentation given below!!
https://www.django-rest-framework.org/api-guide/parsers/#how-the-parser-is-determined
I tried a alternate method. Instead of using .save , I use .update() and it worked. Something like below
serializer.update(user,serializer.validated_data)
Thanks #bdbd for you response and time! Appreciated

how to terminate rest request gracefully

I want to terminate rest request coming to server without further processing if input params are missing.
Currently this is the implementation, which I think is not very good for verify_required_params().
I want to terminate this request without returning any value from verify_required_params() in case of missing params. else flow should continue.
Running this on flask server and open to include any new package for best/ optimized approach.
Can please someone suggest an optimize way for this?
#app.route('/is_registered', methods=['POST'])
def is_registered():
_json = request.get_json()
keys = _json.keys()
customer = Customer()
if verify_required_params(['mobile'], keys) is True:
_mobile = _json['mobile']
validated = validate_mobile(_mobile)
registered = customer.find(_mobile)
if not validated:
response = get_response('MOBILE_NUMBER_NOT_VALID')
return jsonify(response)
if not registered:
response = get_response('MOBILE_NUMBER_NOT_REGISTERED')
return jsonify(response)
response = get_response('MOBILE_NUMBER_REGISTERED')
return jsonify(response)
else:
return verify_required_params(['mobile'], keys)
def verify_required_params(required, received):
required = set(required)
received = set(received)
missing = list(sorted(required - received))
data = {"missing_key(s)": missing}
# response = app.response_class(
# response=json.dumps(data),
# status=200,
# mimetype='application/json'
# )
if missing:
return jsonify(data)
return True
🎶 You say it works in a RESTful way, then your errors come back as 200 OK 🎶
In REST, your URL should encode all the information about your entity. In your case, you are identifying a client by their phone number, and you are getting rather than updating information about them, so your endpoint should look like GET /client/<phonenumber>/registered. That way, a request can't not provide this information without going to a different endpoint.
In short, your code will be replaced with:
#app.route('/client/<mobile>/registered', methods=['GET'])
def is_registered(mobile):
if not mobile.is_decimal():
return jsonify({'error': 'mobile is not number'}), 400 # Bad Request
customer = Customer()
registered = bool(customer.find(mobile))
# does it make sense to have a customer who is not registered yet?
# if not, use:
if not registered:
return jsonify({'error': 'client not found'}), 404 # Not Found
validated = validate_mobile(mobile)
return jsonify( {'validated': validated, 'registered': registered} )
In addition, it's better to have the validation function be a decorator. That way it gets called before the actual business logic of the function. For your example of checking whether request.get_json() contains the proper fields, this is how it would look like:
import functools
def requires_fields(fields):
required_fields = set(fields)
def wrapper(func):
#functools.wraps(decorated)
def decorated(*args, **kwargs):
current_fields = set(request.get_json().keys())
missing_fields = required_fields
if missing_fields:
return jsonify({'error': 'missing fields', 'fields': list(missing_fields)}), 400 # Bad Request
resp = func(*args, **kwargs)
return resp
return wrapper
# usage:
#app.route('/comment', methods=['POST'])
#requires_fields(['author', 'post_id', 'body'])
def create_comment():
data = request.get_json()
id = FoobarDB.insert('comment', author=data['author'], post_id=data['post_id'], body=data['body'])
return jsonify({'new_id': id}), 201 # Created
If you must leave it the way it is now, then in order to not return data from the validation function, you must raise an HTTPException. The default function to do it is flask.abort(code).

Django rest framework. If user exist return True, False if not

Need to preform simple check: If user exist return True, False if not.I am trying to solve thise problem using custom json answers, not sure it is the right way to deal with it. Please help me .
My serializer:
User = get_user_model()
Class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (User.USERNAME_FIELD, )
My views:
#api_view(['GET'])
def UserViewSet(self, request):
if request.method == 'GET':
try:
user = User.objects.get(username=User.USERNAME_FIELD)
if User.DoesNotExist:
json = {}
json['message']= 'False'
return Response(json)
else:
json = {}
json['message']= 'True'
return Response(json)
Instead of using UserViewSet and UserSerializer, you can just use APIView for that.
class UserExistsView(APIView):
def get(self, request, *args, **kwargs):
# use this if username is in url kwargs
username = self.kwargs.get('username')
# use this if username is being sent as a query parameter
username = self.request.query_params.get('username')
try:
user = User.objects.get(username=username) # retrieve the user using username
except User.DoesNotExist:
return Response(data={'message':False}) # return false as user does not exist
else:
return Response(data={'message':True}) # Otherwise, return True

Resources