def get(self, request, *args, **kwargs): payment_ref = self.request.GET.get('payment_ref') try: # check payment status payment_client = PaymentClient() payment = payment_client.get_payment(payment_ref) if not payment or payment['status'] != 'pending': # bail out if accessed without specifying a payment in pending state return clear_session_view(request) kwargs.update({ 'short_payment_ref': payment_ref[:8].upper(), 'prisoner_name': payment['recipient_name'], 'amount': decimal.Decimal(payment['amount']) / 100, 'email_sent': False, }) # check gov.uk payment status govuk_id = payment['processor_id'] self.success, kwargs = payment_client.check_govuk_payment_status( payment_ref, govuk_id, kwargs ) if not self.success: return redirect(self.build_view_url(DebitCardCheckView.url_name)) except OAuth2Error: logger.exception('Authentication error while processing %s' % payment_ref) except SlumberHttpBaseException as error: error_message = 'Error while processing %s' % payment_ref if hasattr(error, 'content'): error_message += '\nReceived: %s' % error.content logger.exception(error_message) except RequestsTimeout: logger.exception('GOV.UK Pay payment check timed out for %s' % payment_ref) except RequestException as error: error_message = 'GOV.UK Pay payment check failed for %s' % payment_ref if hasattr(error, 'response') and hasattr(error.response, 'content'): error_message += '\nReceived: %s' % error.response.content logger.exception(error_message) except GovUkPaymentStatusException: logger.exception('GOV.UK Pay payment status incomplete for %s' % payment_ref) response = super().get(request, *args, **kwargs) request.session.flush() return response
def get(self, request, *args, **kwargs): payment_ref = self.request.GET.get('payment_ref') if not payment_ref: return clear_session_view(request) kwargs['short_payment_ref'] = payment_ref[:8].upper() try: # check payment status payment_client = PaymentClient() payment = payment_client.get_payment(payment_ref) # only continue if: # - the MTP payment is in pending (it moves to the 'taken' state by the cronjob x mins after # the gov.uk payment succeeds) # OR # - the MTP payment is in the 'taken' state (by the cronjob x mins after the gov.uk payment succeeded) # but only for a limited period of time if not payment or not is_active_payment(payment): return clear_session_view(request) kwargs.update({ 'prisoner_name': payment['recipient_name'], 'prisoner_number': payment['prisoner_number'], 'amount': decimal.Decimal(payment['amount']) / 100, }) if payment['status'] == 'taken': self.status = GovUkPaymentStatus.success else: # check gov.uk payment status govuk_id = payment['processor_id'] govuk_payment = payment_client.get_govuk_payment(govuk_id) self.status = payment_client.complete_payment_if_necessary( payment, govuk_payment) # here status can be either created, started, submitted, capturable, success, failed, cancelled, error # or None error_code = govuk_payment and govuk_payment.get( 'state', {}).get('code') # payment was cancelled programmatically (this would not currently happen) if self.status == GovUkPaymentStatus.cancelled: # error_code is expected to be P0040 error_code == 'P0040' or logger.error( f'Unexpected code for cancelled GOV.UK Pay payment {payment_ref}: {error_code}' ) return render(request, 'send_money/debit-card-cancelled.html') # the user cancelled the payment if self.status == GovUkPaymentStatus.failed and error_code == 'P0030': return render(request, 'send_money/debit-card-cancelled.html') # GOV.UK Pay session expired if self.status == GovUkPaymentStatus.failed and error_code == 'P0020': return render( request, 'send_money/debit-card-session-expired.html') # payment method was rejected by card issuer or processor # e.g. due to insufficient funds or risk management if self.status == GovUkPaymentStatus.failed: # error_code is expected to be P0010 error_code == 'P0010' or logger.error( f'Unexpected code for failed GOV.UK Pay payment {payment_ref}: {error_code}' ) return render(request, 'send_money/debit-card-declined.html') # here status can be either created, started, submitted, capturable, success, error # or None # treat statuses created, started, submitted or None as error as they should have never got here if not self.status or self.status.is_awaiting_user_input(): self.status = GovUkPaymentStatus.error # here status can be either capturable, success, error except OAuth2Error: logger.exception( 'Authentication error while processing %(payment_ref)s', {'payment_ref': payment_ref}, ) self.status = GovUkPaymentStatus.error except RequestException as error: response_content = get_requests_exception_for_logging(error) logger.exception( 'Payment check failed for ref %(payment_ref)s. Received: %(response_content)s', { 'payment_ref': payment_ref, 'response_content': response_content }, ) self.status = GovUkPaymentStatus.error except GovUkPaymentStatusException: logger.exception( 'GOV.UK Pay returned unexpected status for ref %(payment_ref)s', {'payment_ref': payment_ref}, ) self.status = GovUkPaymentStatus.error response = super().get(request, *args, **kwargs) request.session.flush() return response