PPv2: Improve error handling (#2899)

This commit is contained in:
Raphael Michel
2022-11-14 16:55:30 +01:00
committed by GitHub
parent a628f605a6
commit 5b8228bea0
2 changed files with 35 additions and 6 deletions

View File

@@ -43,6 +43,7 @@ from paypalcheckoutsdk.orders import (
OrdersPatchRequest, OrdersPatchRequest,
) )
from paypalcheckoutsdk.payments import CapturesRefundRequest, RefundsGetRequest from paypalcheckoutsdk.payments import CapturesRefundRequest, RefundsGetRequest
from paypalhttp import HttpError
from pretix import settings from pretix import settings
from pretix.base.decimal import round_decimal from pretix.base.decimal import round_decimal
@@ -659,10 +660,34 @@ class PaypalMethod(BasePaymentProvider):
try: try:
capturereq = OrdersCaptureRequest(pp_captured_order.id) capturereq = OrdersCaptureRequest(pp_captured_order.id)
response = self.client.execute(capturereq) response = self.client.execute(capturereq)
except IOError as e: except HttpError as e:
messages.error(request, _('We had trouble communicating with PayPal')) text = _('We were unable to process your payment. See below for details on how to proceed.')
try:
error = json.loads(e.message)
except ValueError:
error = {"message": str(e.message)}
try:
if error["details"][0]["issue"] == "ORDER_ALREADY_CAPTURED":
# ignore, do nothing, write nothing, just redirect user to order page, this is likely
# a race condition
logger.info('PayPal ORDER_ALREADY_CAPTURED, ignoring')
return
elif error["details"][0]["issue"] == "INSTRUMENT_DECLINED":
# Use PayPal's rejection message
text = error["details"][0]["description"]
except (KeyError, IndexError):
pass
payment.fail(info={**pp_captured_order.dict(), "error": error}, log_data=error)
logger.exception('PayPal OrdersCaptureRequest: {}'.format(str(e))) logger.exception('PayPal OrdersCaptureRequest: {}'.format(str(e)))
return raise PaymentException(text)
except IOError as e:
payment.fail(info={**pp_captured_order.dict(), "error": {"message": str(e)}}, log_data={"error": str(e)})
logger.exception('PayPal OrdersCaptureRequest: {}'.format(str(e)))
raise PaymentException(
_('We were unable to process your payment. See below for details on how to proceed.')
)
else: else:
pp_captured_order = response.result pp_captured_order = response.result
@@ -685,7 +710,7 @@ class PaypalMethod(BasePaymentProvider):
if pp_captured_order.status != 'COMPLETED': if pp_captured_order.status != 'COMPLETED':
payment.fail(info=pp_captured_order.dict()) payment.fail(info=pp_captured_order.dict())
logger.error('Invalid state: %s' % str(pp_captured_order)) logger.error('Invalid state: %s' % repr(pp_captured_order.dict()))
raise PaymentException( raise PaymentException(
_('We were unable to process your payment. See below for details on how to proceed.') _('We were unable to process your payment. See below for details on how to proceed.')
) )
@@ -704,7 +729,8 @@ class PaypalMethod(BasePaymentProvider):
except SendMailException: except SendMailException:
messages.warning(request, _('There was an error sending the confirmation mail.')) messages.warning(request, _('There was an error sending the confirmation mail.'))
finally: finally:
del request.session['payment_paypal_oid'] if 'payment_paypal_oid' in request.session:
del request.session['payment_paypal_oid']
def payment_pending_render(self, request, payment) -> str: def payment_pending_render(self, request, payment) -> str:
retry = True retry = True

View File

@@ -179,7 +179,10 @@ class PayView(PaypalOrderView, TemplateView):
return r return r
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.payment.payment_provider.execute_payment(request, self.payment) try:
self.payment.payment_provider.execute_payment(request, self.payment)
except PaymentException as e:
messages.error(request, str(e))
return self._redirect_to_order() return self._redirect_to_order()
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):