def perform_update(self): payment_client = PaymentClient() payments = payment_client.get_incomplete_payments() for payment in payments: if not self.should_be_checked(payment): continue payment_ref = payment['uuid'] govuk_id = payment['processor_id'] try: govuk_payment = payment_client.get_govuk_payment(govuk_id) previous_govuk_status = GovUkPaymentStatus.get_from_govuk_payment( govuk_payment) govuk_status = payment_client.complete_payment_if_necessary( payment, govuk_payment) # not yet finished and can't do anything so skip if govuk_status and not govuk_status.finished(): continue if previous_govuk_status != govuk_status: # refresh govuk payment to get up-to-date fields (e.g. error codes) govuk_payment = payment_client.get_govuk_payment(govuk_id) # if here, status is either success, failed, cancelled, error # or None (in case of govuk payment not found) payment_client.update_completed_payment(payment, govuk_payment) except OAuth2Error: logger.exception( 'Scheduled job: Authentication error while processing %s' % payment_ref) except RequestException as error: error_message = 'Scheduled job: Payment check failed for ref %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: # expected much of the time pass
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