From c16c9bb949d9e3669168bb31d57f2b3f3ce9ad36 Mon Sep 17 00:00:00 2001 From: Maria Arias de Reyna Date: Sat, 12 Sep 2020 12:21:08 +0200 Subject: [PATCH] Handle all types of return from MercadoPago Also refactor and comment --- pretix_mercadopago/payment.py | 16 ++---- pretix_mercadopago/urls.py | 4 +- pretix_mercadopago/views.py | 97 +++++++++++++---------------------- 3 files changed, 39 insertions(+), 78 deletions(-) diff --git a/pretix_mercadopago/payment.py b/pretix_mercadopago/payment.py index 70aa51f..405adcb 100644 --- a/pretix_mercadopago/payment.py +++ b/pretix_mercadopago/payment.py @@ -127,7 +127,7 @@ def settings_form_fields(self): def settings_content_render(self, request): settings_content = "" if self.settings.connect_client_id and not self.settings.secret: - # Use MercadoPAgo connect + # Use MercadoPago connect if not self.settings.connect_user_id: settings_content = ( "

{}

" @@ -219,11 +219,11 @@ def execute_payment(self, request: HttpRequest, payment_obj: OrderPayment): "currency_id": payment_obj.order.event.currency } ], - "auto_return": 'approved', # solo para las ordenes aprobadas, all + "auto_return": 'all', # solo para las ordenes aprobadas, all "back_urls": { "failure": build_absolute_uri(request.event, - 'plugins:pretix_mercadopago:abort'), + 'plugins:pretix_mercadopago:return'), "pending": build_absolute_uri(request.event, 'plugins:pretix_mercadopago:return'), @@ -263,16 +263,6 @@ def execute_payment(self, request: HttpRequest, payment_obj: OrderPayment): link = preferenceResult["response"]["init_point"] else: link = preferenceResult["response"]["sandbox_init_point"] -# messages.error(request, _('Debug ' + str(link) )) -# return str(link) -# if link.method == "REDIRECT" and link.rel == "approval_url": -# if request.session.get('iframe_session', False): -# signer = signing.Signer(salt='safe-redirect') -# return ( -# build_absolute_uri(request.event, 'plugins:mercadopago:redirect') + '?url=' + -# urllib.parse.quote(signer.sign(link)) -# ) -# else: return link else: messages.error(request, _('We had trouble communicating with MercadoPago' + str(preferenceResult["response"]))) diff --git a/pretix_mercadopago/urls.py b/pretix_mercadopago/urls.py index a982a7a..7fcb26b 100644 --- a/pretix_mercadopago/urls.py +++ b/pretix_mercadopago/urls.py @@ -4,16 +4,14 @@ from pretix.multidomain import event_url from .views import ( - abort, oauth_disconnect, redirect_view, success, webhook, + oauth_disconnect, redirect_view, success, webhook, ) event_patterns = [ url(r'^mercadopago/', include([ - url(r'^abort/$', abort, name='abort'), url(r'^return/$', success, name='return'), url(r'^redirect/$', redirect_view, name='redirect'), - url(r'w/(?P[a-zA-Z0-9]{16})/abort/', abort, name='abort'), url(r'w/(?P[a-zA-Z0-9]{16})/return/', success, name='return'), event_url(r'^webhook/$', webhook, name='webhook', require_live=False), diff --git a/pretix_mercadopago/views.py b/pretix_mercadopago/views.py index 48fe4f3..7234182 100644 --- a/pretix_mercadopago/views.py +++ b/pretix_mercadopago/views.py @@ -1,4 +1,3 @@ -import json import logging from decimal import Decimal @@ -14,7 +13,6 @@ from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST from django_scopes import scopes_disabled -#from paypalrestsdk.openid_connect import Tokeninfo from pretix.base.models import Event, Order, OrderPayment, OrderRefund, Quota from pretix.base.payment import PaymentException @@ -32,6 +30,7 @@ def admin_view(request, *args, **kwargs): r._csp_ignore = True return r + @xframe_options_exempt def redirect_view(request, *args, **kwargs): signer = signing.Signer(salt='safe-redirect') @@ -46,69 +45,60 @@ def redirect_view(request, *args, **kwargs): r._csp_ignore = True return r +# Return url for MercadoPago when payment is pending or success + def success(request, *args, **kwargs): - pid = request.GET.get('preference_id') orderid = request.GET.get('external_reference') - merchant_order_id = request.GET.get('merchant_order_id') collection_id = request.GET.get('collection_id') status = request.GET.get('collection_status') - urlkwargs = {} + # Ask MercadoPago again about the status + # to avoid pishing! + # (don't trust any call to this url) mp = Mercadopago(request.event).init_api() paymentInfo = mp.get_payment(collection_id) + payment = None if paymentInfo["status"] == 200: if orderid == paymentInfo['response']['external_reference']: payment = OrderPayment.objects.get(pk=orderid) - payment.info = json.dumps(paymentInfo['response'], indent=4) else: - payment = None + messages.error(request, _('Invalid attempt to pay order ' + orderid)) + else: messages.error(request, str(e)) return None - + + # Documentation for payment object: + # https://www.mercadopago.com.ar/developers/es/reference/payments/resource/ if payment: - if status == 'approved' == paymentInfo['response']['status']: + order = payment.order + mpstatus = paymentInfo['response']['status'] + + # Something fishy detected + if status != mpstatus: + messages.error(request, _('Invalid attempt to pay order ' + orderid)) + elif mpstatus == 'approved': payment.order.status = Order.STATUS_PAID - payment.order.save() - payment.state ='confirmed' - payment.save() - if status == 'pending' == paymentInfo['response']['status']: + payment.state = 'confirmed' + elif (mpstatus == 'pending') or (mpstatus == 'authorized') or (mpstatus == 'in_process') or (mpstatus == 'in_mediation'): payment.order.status = Order.STATUS_PENDING - payment.order.save() - payment.state ='pending' - payment.save() - - - """ - return redirect(eventreverse(request.event, 'presale:event.checkout', kwargs=urlkwargs)) + payment.state = 'pending' + elif (mpstatus == 'cancelled'): + payment.order.status = Order.STATUS_CANCELED + payment.state = 'canceled' + elif (mpstatus == 'rejected'): + payment.order.status = Order.STATUS_CANCELED + payment.state = 'failed' + elif (mpstatus == 'refunded') or (mpstatus == 'charged_back'): + payment.order.status = Order.STATUS_CANCELED + payment.state = 'refunded' + + payment.info = paymentInfo['response']['status_detail'] + payment.order.save() + payment.save() - if 'cart_namespace' in kwargs: - urlkwargs['cart_namespace'] = kwargs['cart_namespace'] - - if request.session.get('payment_mercadopago_payment'): - payment = OrderPayment.objects.get(pk=request.session.get('payment_mercadopago_payment')) - else: - payment = None - - if pid == request.session.get('payment_mercadopago_id', None): - if payment: - prov = Mercadopago(request.event) - try: - resp = prov.execute_payment(request, payment) - except PaymentException as e: - messages.error(request, str(e)) - urlkwargs['step'] = 'payment' - return redirect(eventreverse(request.event, 'presale:event.checkout', kwargs=urlkwargs)) - if resp: - return resp - else: - messages.error(request, _('Invalid response from MercadoPago received.')) - logger.error('Session did not contain payment_mercadopago_id') - urlkwargs['step'] = 'payment' - return redirect(eventreverse(request.event, 'presale:event.checkout', kwargs=urlkwargs)) - """ if payment: return redirect(eventreverse(request.event, 'presale:event.order', kwargs={ 'order': payment.order.code, @@ -119,23 +109,6 @@ def success(request, *args, **kwargs): return redirect(eventreverse(request.event, 'presale:event.checkout', kwargs=urlkwargs)) -def abort(request, *args, **kwargs): - messages.error(request, _('It looks like you canceled the MercadoPago payment')) - - if request.session.get('payment_mercadoapgo_payment'): - payment = OrderPayment.objects.get(pk=request.session.get('payment_mercadopago_payment')) - else: - payment = None - - if payment: - return redirect(eventreverse(request.event, 'presale:event.order', kwargs={ - 'order': payment.order.code, - 'secret': payment.order.secret - }) + ('?paid=no' if payment.order.status == Order.STATUS_PAID else '')) - else: - return redirect(eventreverse(request.event, 'presale:event.checkout', kwargs={'step': 'payment'})) - - @csrf_exempt @require_POST @scopes_disabled()