Django - AttributeError: 'tuple' object has no attribute 'get' [ DRF, Stripe, Python ] - python

I constantly get an error whenever I make a POST request to /api/tickets/:
Internal Server Error: /payment-stripe/
Traceback (most recent call last):
File "/ENV/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/ENV/lib/python3.8/site-packages/django/utils/deprecation.py", line 96, in __call__
response = self.process_response(request, response)
File "/ENV/lib/python3.8/site-packages/django/middleware/clickjacking.py", line 26, in process_response
if response.get('X-Frame-Options') is not None:
AttributeError: 'tuple' object has no attribute 'get'
[03/Jan/2020 03:17:09] "POST /payment-stripe/ HTTP/1.1" 500 58576
Here is my stripe payment handling view that I think is causing the problem, but I can't find out why:
#require_http_methods(["POST"])
#csrf_exempt
def stripe_payment_handling(request):
"""
The following code is copied from Stripe Docs.
Everything inside the IF statements is how the ticket gets handled.
"""
payload = request.body
# Checks for the signature of stripe.
sig_header = request.headers['Stripe-Signature']
event = None
try:
# Tests if it can create a connection with Stripe.
event = stripe.Webhook.construct_event(
payload, sig_header, endpoint_secret
)
except ValueError as e:
# invalid payload: If it doesn't pass it gives back a 400
return "Invalid payload", 400
except stripe.error.SignatureVerificationError as e:
# invalid signature: If the Stripe Signature is not there it wont accept it.
return "Invalid signature", 400
# Create the a dictionary with the data
event_dict = event.to_dict()
print(event_dict)
if event_dict['type'] == "payment_intent.created":
# If someone redirects to the bank to pay it will create a payment intent
# Now it will use that value to validate the payment
intent = event_dict['data']['object']
ticket = Ticket.objects.get(
stripe_payment_intent=intent["id"]
)
ticket.status = "Created"
ticket.save()
elif event_dict['type'] == "payment_intent.succeeded":
# If the payment Succeeded:
intent = event_dict['data']['object']
# get the ticket with the intent id that was created from payment_intent.created
ticket = Ticket.objects.get(stripe_payment_intent=intent["id"])
# Change status to Completed
ticket.status = "Completed"
ticket.save()
# Reduce the amount of stock for the selected parking location
ticket.stock_type.quantity -= 1
ticket.stock_type.save()
# Send the email to the client
# The .send_ticket_email() function is part of the Ticket Model
ticket.send_ticket_email()
elif event_dict['type'] == "payment_intent.payment_failed":
# If the payment has failed:
intent = event_dict['data']['object']
# Get the error message
error_message = intent['last_payment_error']['message']
print("Failed: ", intent['id'], error_message)
# Notify the customer that payment failed
try:
# Find the ticket based on the intent id
ticket = Ticket.objects.get(stripe_payment_intent=intent["id"])
# Change its status to Failed
ticket.status = "Failed"
ticket.save()
# Send Failed email
# the .send_failed_email() function is part of the Ticket model
ticket.send_failed_email()
except Exception as e:
# if the payment intent wasn't found send an email to contact support:
print("Payment intent not found! => {0}".format(e))
elif event_dict['type'] == "payment_intent.canceled":
print("PAYMENT INTENT CANCELED")
elif event_dict['type'] == "payment_intent.amount_capturable_updated":
print("PAYMENT INTENT UPDATED")
else:
print("INTENT = {NONE}")
return HttpResponse(status=200)
(extra) Here is my api/views.py:
class TicketInitialize(generics.GenericAPIView):
queryset = Ticket.objects.all()
serializer_class = TicketSerializer
permission_classes = (permissions.AllowAny,)
def post(self, request, *args, **kwargs):
ticket_price = int(float(request.data["price"])) * 100
print(ticket_price)
stripe.api_key = 'my_stripe_api_key'
intent = stripe.PaymentIntent.create(
amount=ticket_price,
currency='eur',
payment_method_types=['card', 'ideal']
)
request.data["stripe_payment_intent"] = intent["id"]
stock = Stock.objects.get(id=request.data["stock_type"])
event = Event.objects.get(id=request.data["event"])
new_ticket = Ticket.objects.create(
first_name=request.data["first_name"],
last_name=request.data["last_name"],
quantity=request.data["quantity"],
cell_phone=request.data["cell_phone"],
email=request.data["email"],
price=request.data["price"],
service_fee=request.data["service_fee"],
stock_type=stock,
event=event,
stripe_payment_intent=request.data["stripe_payment_intent"]
)
return Response(intent)
my urls.py:
urlpatterns = [
path('payment-stripe/', views.stripe_payment_handling, name="stripe-endpoint"),
]
I don't understand why it keeps giving me the Attribute Error. It won't allow me to handle any webhooks from stripe or complete orders.
Thank you for taking a look, and I'm looking forward to your suggestions and answers!

Change return "Invalid signature", 400 to
return HttpResponse(status=400, content="Invalid signature")
and return "Invalid payload", 400 to
return HttpResponse(status=400, content="Invalid payload")

Related

return self.browse((self._ids[key],)) IndexError: tuple index out of range : When sending message from Discuss in Odoo V13

Below error occur, whenever sending a message on discuss module, which we configured to send notifications to users firebase_id.
File "/odoo/odoo-server/addons/mail/models/mail_channel.py", line 368, in message_post
message = super(Channel, self.with_context(mail_create_nosubscribe=True)).message_post(message_type=message_type, moderation_status=moderation_status, **kwargs)**
File "/odoo/custom/addons/elite_event_management_api/controllers/message.py", line 34, in message_post
registration_id = channel_partner_id.partner_id.user_ids[0].firebase_id
File "/odoo/odoo-server/odoo/models.py", line 5624, in _getitem_
return self.browse((self._ids[key],))
IndexError: tuple index out of range
HERE IS THE CODE -All users are unable to send messages on discuss Module.
import logging
from odoo import models, api
from odoo.exceptions import AccessDenied, UserError
logger = logging.getLogger(__name_)
class MailThred(models.AbstractModel):
_inherit = "mail.thread"
#api.returns('mail.message', lambda value: value.id)
def message_post(self, *,
body='', subject=None, message_type='notification',
email_from=None, author_id=None, parent_id=False,
subtype_id=False, subtype=None, partner_ids=None, channel_ids=None,
attachments=None, attachment_ids=None,
add_sign=True, record_name=False,
**kwargs):
res = super(MailThred, self).message_post(body=body, subject=subject, message_type=message_type,
email_from=email_from, author_id=author_id, parent_id=parent_id,
subtype_id=subtype_id, subtype=subtype, partner_ids=partner_ids, channel_ids=channel_ids,
attachments=attachments, attachment_ids=attachment_ids,
add_sign=add_sign, record_name=record_name,
**kwargs)
message_subtype_id = self.env['mail.message.subtype'].sudo().search([('name', 'ilike', 'Discussions')])
if res.message_type == 'comment' and res.subtype_id.id == message_subtype_id.id:
for each in res.channel_ids:
for channel_partner_id in each.channel_last_seen_partner_ids:
if channel_partner_id.partner_id.id != res.author_id.id:
from . import FCMManager as fcm
registration_id = channel_partner_id.partner_id.user_ids[0].firebase_id
if registration_id:
try:
tokens = [str(registration_id)]
message_title = "ControlE#ERP - CHAT"
message_body = res.body
fcm.sendPush(message_title, message_body, tokens)
_logger.info('ControlE#ERP Alert- NEW CHAT MESSAGE SENT')
except Exception as e:
_logger.info('not sent')
return res
You have no users in channel_partner_id.partner_id.user_ids, so you are trying to get the 0th element from empty list ,
So check your code and try again.

How to process webhook request coming from a 3rd party application?

I need help to evaluate weather i am doing it right or is there a better way, the scenario is an 3rd party application is sending an webhook request after a successful payment but the problem is that sometimes this application may send the same notification more than once.so it is recommended to ensure that implementation of the webhook is idempotent.so steps that i am implementing for this are
if signature is correct (assume it is corect),Find orders record in the database using orderId in the request params.
Please note: orderId in request params is payment_gateway_order_identifier in orders table.
if txStatus = 'SUCCESS' AND haven't already processed COLLECTION payment for this same order,
Create payments record.
201 response with nothing in the response body.
else
201 response with nothing in the response body.
else
422 response with {message: "Signature is incorrect"} in response body
views.py
#api_view(['POST'])
def cashfree_request(request):
if request.method == 'POST':
data=request.POST.dict()
payment_gateway_order_identifier= data['orderId']
amount = data['orderAmount']
transaction_status = data['txStatus']
signature = data['signature']
if(computedsignature==signature): #assume it to be true
order=Orders.objects.get(
payment_gateway_order_identifier=payment_gateway_order_identifier)
if transaction_status=='SUCCESS':
try:
payment= Payments.objects.get(orders=order)
return Response({"Payment":"Done"},status=status.HTTP_200_OK)
except (Payments.DoesNotExist):
payment = Payments(orders=order,amount=amount,datetime=datetime)
payment.save()
return Response(status=status.HTTP_200_OK)
else:
return Response(status=status.HTTP_422_UNPROCESSABLE_ENTITY)
models.py
class Orders(models.Model):
id= models.AutoField(primary_key=True)
amount = models.DecimalField(max_digits=19, decimal_places=4)
payment_gateway_order_identifier = models.UUIDField(
primary_key=False,default=uuid.uuid4,editable=False,unique=True)
sussessfull
class Payments(models.Model):
id = models.AutoField(primary_key=True)
orders = models.ForeignKey(Orders, on_delete=models.CASCADE)
amount = models.DecimalField(max_digits=19, decimal_places=4, verbose_name='Price in INR')
datetime = models.DateTimeField(auto_now=False,auto_now_add=False)
This rather belongs to Codereview site. Anyway - you are doing up to 3 consecutive SQL queries, so there's a chance for a race condition. A simple way how to prevent that: use some KV storage like Redis/Memcache as a lock - save the value you're using as a nonce on the start of the function, and delete it on the end.
#api_view(['POST'])
def cashfree_request(request):
data = request.POST.dict()
payment_gateway_order_identifier = data['orderId']
# `nx` will set & return only if the key does not exist
# set a timeout in case it wont reach `delete()` on the end
if not redis.set("lock_%s" % payment_gateway_order_identifier, "1", nx=True, ex=2):
return Response(status=status.HTTP_409_CONFLICT)
amount = data['orderAmount']
transaction_status = data['txStatus']
signature = data['signature']
if computedsignature == signature:
order = Orders.objects.get(payment_gateway_order_identifier=payment_gateway_order_identifier)
if transaction_status == 'SUCCESS':
try:
Payments.objects.get(orders=order)
res = Response({"Payment": "Done"}, status=status.HTTP_200_OK)
except Payments.DoesNotExist:
payment = Payments(orders=order, amount=amount, datetime=datetime)
payment.save()
res = Response(status=status.HTTP_200_OK)
else:
res = Response(status=status.HTTP_422_UNPROCESSABLE_ENTITY)
# unlock for another request
redis.delete("lock_%s" % payment_gateway_order_identifier)
return res
you dont need if request.method == 'POST': since the code is accessible via POST only anyway, that will make your code less indented.
notice you dont handle the case where transaction_status is not SUCCESS

iot edge direct method handler in python

I have created a module for a Bacnet scan and it will respond with a list of devices and its address as a result. But I'm having trouble implementing a direct method handler in python. When i first tried implementing it myself i got this error. Which could mean I didn't successfully register the direct method callback. I have some references but it was from C# and azure docs is not helping me figure out the right method to register the callback. for IoTHubModuleClient there's a on_method_request_received and a receive_method_request. appreciate any help!
def iothub_client_scan_run():
try:
iot_client = iothub_client_init()
bacnet_scan_listener_thread = threading.Thread(target=device_method_listener, args=(iot_client,))
bacnet_scan_listener_thread.daemon = True
bacnet_scan_listener_thread.start()
while True:
time.sleep(1000)
def device_method_listener(iot_client):
while True:
# Receive the direct method request
method_request = iot_client.receive_method_request()
print (
"\nMethod callback called with:\nmethodName = {method_name}\npayload = {payload}".format(
method_name=method_request.name,
payload=method_request.payload
)
)
if method_request.name == "runBacnetScan":
response = bacnet_scan_device(method_request)
else:
response_payload = {"Response": "Direct method {} not defined".format(method_request.name)}
response_status = 404
# Send a method response indicating the method request was resolved
print('Sending method response')
iot_client.send_method_response(response)
print('Message sent!')
Edit:
Here is my route config
I was able to resolve my issue or at least find the root cause and it was my network configuration under the createOptions. It seems like there's an issue when I'm trying to do NetworkMode: host and connects to the IotModuleClient.connect_from_edge_environment via connect with connection string. I'm still trying to tweak the connection configuration but at least i know its not on the code.
async def method_request_handler(module_client):
while True:
method_request = await module_client.receive_method_request()
print (
"\nMethod callback called with:\nmethodName = {method_name}\npayload = {payload}".format(
method_name=method_request.name,
payload=method_request.payload
)
)
if method_request.name == "method1":
payload = {"result": True, "data": "some data"} # set response payload
status = 200 # set return status code
print("executed method1")
elif method_request.name == "method2":
payload = {"result": True, "data": 1234} # set response payload
status = 200 # set return status code
print("executed method2")
else:
payload = {"result": False, "data": "unknown method"} # set response payload
status = 400 # set return status code
print("executed unknown method: " + method_request.name)
# Send the response
method_response = MethodResponse.create_from_method_request(method_request, status, payload)
await module_client.send_method_response(method_response)
print('Message sent!')
def stdin_listener():
while True:
try:
selection = input("Press Q to quit\n")
if selection == "Q" or selection == "q":
print("Quitting...")
break
except:
time.sleep(10)
# Schedule task for C2D Listener
listeners = asyncio.gather(input1_listener(module_client), twin_patch_listener(module_client), method_request_handler(module_client))

Unit Testing Bootstrap Modal Redirect

I've been unable resolve this issue on IRC, hoping I could find some guidance here. I have the following test:
def test_validation_errors_return_hops_list_page(self):
response = self.client.post(
'/beerdb/add/hops',
data={
'name': '',
'min_alpha_acid': '',
'max_alpha_acid': '',
'country': '',
'comments': ''
}, follow=True
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'homebrewdatabase/addhops.html')
name_validation_error = escape("A hop name is required")
min_alpha_acid_error = escape("You must enter a min alpha acid")
max_alpha_acid_error = escape("You must enter a max alpha acid")
country_error = escape("You must enter a country")
comments_error = escape("You must enter a comment")
self.assertContains(response, name_validation_error)
self.assertContains(response, min_alpha_acid_error)
self.assertContains(response, max_alpha_acid_error)
self.assertContains(response,country_error)
self.assertContains(response, comments_error)
It's failing on self.assertContains(response, name_validation_error). Here's the trace back:
Failure
Traceback (most recent call last):
File "/Users/USER/workspace/PycharmProjects/hashtagbrews/homebrewdatabase/tests/test_views.py", line 189, in test_validation_errors_return_hops_list_page
self.assertContains(response, name_validation_error)
File "/Users/USER/workspace/Envs/hashtagbrews/lib/python3.4/site-packages/django/test/testcases.py", line 398, in assertContains
msg_prefix + "Couldn't find %s in response" % text_repr)
AssertionError: False is not true : Couldn't find 'A hop name is required' in response
My view in views.py renders the hops.html template with errors when the form is invalid:
def addhops(request):
add_form = HopForm(request.POST or None)
if request.method == 'POST':
if add_form.is_valid():
Hop.objects.create(name=request.POST['name'],
min_alpha_acid=request.POST['min_alpha_acid'],
max_alpha_acid=request.POST['max_alpha_acid'],
country=request.POST['country'],
comments=request.POST['comments']
)
return redirect('hops_list')
else:
hops_list = Hop.objects.all()
return render(request, 'homebrewdatabase/hops.html', {'hops': hops_list, 'form': add_form})
return render(request, 'homebrewdatabase/addhops.html', {'form': add_form})
When I manually click through the site, I get exactly what I'm looking for: a redirect from the modal to the main hops page list with a Bootstrap alert box at the top containing an unordered list of errors from add_hops.errors. I've printed out the response after the post request (self.client.post('url', data={invalid data})) and it only contains the modal form. What would be the proper method to finish this test? Or do I need to rewrite my form validation?
The issue here, as identified in the comments, is that the Django test client runs a GET request on the addhops view method after executing the post request. According to the view logic, if the method isn't POST, then it returns the bootstrap modal, which does not contain the form errors by design. So the test will fail when using the test client. However, the test can be altered to, instead, use the HttpRequest object to send invalid data in a POST request and then assert that the content contains the form errors. So I rewrote the test to use the following, which passes--
def test_validation_errors_return_hops_list_page(self):
request = HttpRequest()
request.method = 'POST'
request.POST['name'] = ''
request.POST['min_alpha_acid'] = ''
request.POST['max_alpha_acid'] = ''
request.POST['country'] = 'USA'
request.POST['comments'] = ''
response = addhops(request)
self.assertEqual(response.status_code, 200)
name_validation_error = escape("A hop name is required")
min_alpha_acid_error = escape("You must enter a min alpha acid")
max_alpha_acid_error = escape("You must enter a max alpha acid")
comments_error = escape("You must enter a comment")
self.assertContains(response, name_validation_error)
self.assertContains(response, min_alpha_acid_error)
self.assertContains(response, max_alpha_acid_error)
self.assertContains(response, comments_error)
I cannot assert which template was used as assertTemplateUsed is a method that belongs to the test client, but my functional tests with selenium should be enough to check that the required elements are in the rendered view. This may have to be changed in the future, but for now, it's enough to work with.

django testing response.context TypeError: 'NoneType' object is not subscriptable

I want to test this view:
def register(request):
"""
handle user registration
code variable is for testing purposes
"""
if request.method== 'GET':
form = RegisterForm(auto_id=False)
code = 1
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
elif request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
password = form.cleaned_data['password']
password_confirmation = form.cleaned_data['password_confirmation']
if password == password_confirmation:
#if True:
login = form.cleaned_data['login']
email = form.cleaned_data['email']
newsletter = form.cleaned_data['newsletter']
key = register_user(login,email,password,newsletter)
if key:
#send email
send_mail("Dziękujemy za rejestrację"," Klucz aktywacyjny to " + key,settings.EMAIL_HOST_USER,[email])
request.session['email'] = email
return redirect(register_success)
else:
code = 4
error = "Login /email are taken"
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
else:
code = 3
error = "invalid password"
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
else:
code = 2
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
And here is my part of my test:
def test_valid_credentials(self):
#now try to register valid user
data = {'login':'test','password':'zaq12wsx','password_confirmation':'zaq12wsx','terms':True,'newsletter':True,'email':'test#test.com'}
response = self.c.post(reverse('register'),data)
#our user should be registred
self.assertEquals(302, response.status_code,'We dont have benn redirected')
self.assertEqual(len(mail.outbox), 1,'No activation email was sent')
#clen email box
mail.outbox = []
#now try to add anotheer user with the same data
response = self.c.post(reverse('register'),data)
#template should be rendered with error message about used login and email
self.assertEqual(response.context['code'],4)
And here is the error that I got:
,
in test_valid_credentials
self.assertEqual(response.context['code'],4)
TypeError: 'NoneType' object is not subscriptable
I tried it with get method and it works perfectly. Just with post it don't want to work.What am I doing wrong?Best regards
What is the response status? Redirects doesn't have context. Anyway, printing the response should help.
My guess is
key = register_user(login,email,password,newsletter)
throws an exception on duplicate register attempts and thus the handler does not generate a response.

Categories