def test_update_incomplete_payments_no_govuk_payment_found(self):
        """
        Test that if GOV.UK Pay returns 404 for one payment, the command marks the related
        MTP payment as failed.
        """
        with responses.RequestsMock() as rsps:
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/'),
                json={
                    'count': 1,
                    'results': [PAYMENT_DATA],
                },
                status=200,
            )
            rsps.add(
                rsps.GET,
                govuk_url(f'/payments/{PAYMENT_DATA["processor_id"]}/'),
                status=404,
            )
            rsps.add(
                rsps.PATCH,
                api_url(f'/payments/{PAYMENT_DATA["uuid"]}/'),
                json={
                    **PAYMENT_DATA,
                    'status': 'failed',
                },
                status=200,
            )

            call_command('update_incomplete_payments', verbosity=0)

            self.assertEqual(rsps.calls[3].request.body.decode(), '{"status": "failed"}')
Example #2
0
    def _test_received_at_date_matches_captured_date(
        self,
        capture_submit_time,
        captured_date,
        received_at,
        mock_send_email,
    ):
        with responses.RequestsMock() as rsps:
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/'),
                json={
                    'count': 1,
                    'results': [PAYMENT_DATA],
                },
                status=200,
            )
            rsps.add(rsps.GET,
                     govuk_url(f'/payments/{PAYMENT_DATA["processor_id"]}/'),
                     json={
                         'reference': PAYMENT_DATA['uuid'],
                         'state': {
                             'status': 'success'
                         },
                         'settlement_summary': {
                             'capture_submit_time': capture_submit_time,
                             'captured_date': captured_date
                         },
                         'email': '*****@*****.**',
                     },
                     status=200)
            rsps.add(
                rsps.PATCH,
                api_url(f'/payments/{PAYMENT_DATA["uuid"]}/'),
                json={
                    **PAYMENT_DATA,
                    'email': '*****@*****.**',
                },
                status=200,
            )

            call_command('update_incomplete_payments', verbosity=0)

            self.assertEqual(
                json.loads(
                    rsps.calls[-1].request.body.decode())['received_at'],
                received_at)
            self.assertEqual(len(mock_send_email.call_args_list), 1)
    def test_confirmation_redirects_for_completed_payments(self):
        self.choose_debit_card_payment_method()
        self.fill_in_prisoner_details()
        self.fill_in_amount()

        with responses.RequestsMock() as rsps:
            ref = 'wargle-blargle'
            processor_id = '3'
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/%s/' % ref),
                json={
                    'processor_id': processor_id,
                    'recipient_name': 'John',
                    'amount': 1700,
                    'status': 'taken',
                    'created': datetime.datetime.now().isoformat() + 'Z',
                },
                status=200,
            )
            with self.patch_prisoner_details_check(), self.silence_logger():
                response = self.client.get(
                    self.url, {'payment_ref': ref}, follow=False
                )
            self.assertRedirects(response, '/')
    def _test_update_incomplete_payments_doesnt_update_before_capture(self, settlement_summary):
        with responses.RequestsMock() as rsps:
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/'),
                json={
                    'count': 1,
                    'results': [PAYMENT_DATA],
                },
                status=200,
            )
            rsps.add(
                rsps.GET,
                govuk_url(f'/payments/{PAYMENT_DATA["processor_id"]}/'),
                json={
                    'reference': PAYMENT_DATA['uuid'],
                    'state': {'status': 'success'},
                    'settlement_summary': settlement_summary,
                    'email': '*****@*****.**',
                },
                status=200,
            )

            call_command('update_incomplete_payments', verbosity=0)

            self.assertEqual(len(mail.outbox), 0)
    def get_performance_data(self, date_from: datetime.date,
                             date_to: datetime.date):
        api_client = get_api_session()

        date_range = {'week__gte': date_from, 'week__lt': date_to}
        data = api_client.get(api_url('performance/data'), params=date_range)
        return data.json()
 def test_prison_list(self):
     with responses.RequestsMock() as rsps, \
             self.settings(CACHES={'default': {'BACKEND': 'django.core.cache.backends.dummy.DummyCache'}}):
         mock_auth(rsps)
         rsps.add(
             rsps.GET,
             api_url('/prisons/'),
             json={
                 'count':
                 2,
                 'results': [
                     {
                         'nomis_id': 'BBB',
                         'short_name': 'Prison 1',
                         'name': 'YOI Prison 1',
                     },
                     {
                         'nomis_id': 'AAA',
                         'short_name': 'Prison 2',
                         'name': 'HMP Prison 2',
                     },
                 ],
             },
         )
         response = self.client.get(reverse('help_area:prison_list'))
         self.assertIn('exclude_empty_prisons=True',
                       rsps.calls[-1].request.url)
     self.assertContains(response, 'Prison 1')
     response = response.content.decode(response.charset)
     self.assertIn('Prison 2', response)
     self.assertLess(response.index('Prison 1'), response.index('Prison 2'))
    def test_failure_page(self):
        processor_id = '3'
        with responses.RequestsMock() as rsps:
            rsps.add(rsps.GET,
                     api_url('/payments/'),
                     json={
                         'uuid': 'wargle-blargle',
                         'processor_id': processor_id,
                         'recipient_name': 'James Bond',
                         'amount': 2000,
                         'status': 'pending',
                         'created': datetime.datetime.now().isoformat() + 'Z',
                         'prisoner_number': 'A5544CD',
                         'prisoner_dob': '1992-12-05',
                     })
            rsps.add(rsps.GET,
                     govuk_url('/payments/%s' % processor_id),
                     json={'state': {
                         'status': 'failed'
                     }})

            self.driver.get(
                self.live_server_url +
                '/en-gb/debit-card/confirmation/?payment_ref=REF12345')
            self.assertInSource(
                'We’re sorry, your payment could not be processed on this occasion'
            )
            self.assertInSource(
                'Your reference number is <strong>REF12345</strong>')
    def test_debit_card_payment(self):
        self.choose_debit_card_payment_method()
        self.fill_in_prisoner_details()
        self.fill_in_amount()

        with responses.RequestsMock() as rsps:
            ref = 'wargle-blargle'
            processor_id = '3'
            mock_auth(rsps)
            rsps.add(
                rsps.POST,
                api_url('/payments/'),
                json={'uuid': ref},
                status=201,
            )
            rsps.add(
                rsps.POST,
                govuk_url('/payments/'),
                json={
                    'payment_id': processor_id,
                    '_links': {
                        'next_url': {
                            'method': 'GET',
                            'href': govuk_url(self.payment_process_path),
                        }
                    }
                },
                status=201
            )
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % ref),
                status=200,
            )
            with self.patch_prisoner_details_check():
                response = self.client.get(self.url, follow=False)

            # check amount and service charge submitted to api
            self.assertEqual(json.loads(rsps.calls[1].request.body)['amount'], 1700)
            self.assertEqual(json.loads(rsps.calls[1].request.body)['service_charge'], 61)
            # check total charge submitted to govuk
            self.assertEqual(json.loads(rsps.calls[2].request.body)['amount'], 1761)

            self.assertRedirects(
                response, govuk_url(self.payment_process_path),
                fetch_redirect_response=False
            )
Example #9
0
 def test_healthcheck_with_service_down_returns_message_to_users(self):
     with responses.RequestsMock() as rsps:
         rsps.add(rsps.GET, api_url('/service-availability/'), json={
             'gov_uk_pay': {'status': False, 'message_to_users': 'Scheduled downtime'}
         })
         available, message_to_users = check_payment_service_available()
     self.assertFalse(available)
     self.assertEqual(message_to_users, 'Scheduled downtime')
Example #10
0
    def _test_captured_payment_doesnt_get_updated_before_capture(
            self, settlement_summary, mock_send_email):
        govuk_payment_data = {
            'payment_id': PAYMENT_DATA['processor_id'],
            'reference': PAYMENT_DATA['uuid'],
            'state': {
                'status': 'capturable'
            },
            'email': '*****@*****.**',
        }
        with responses.RequestsMock() as rsps:
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/'),
                json={
                    'count':
                    1,
                    'results': [
                        {
                            **PAYMENT_DATA,
                            'security_check': {
                                'status': 'accepted',
                                'user_actioned': True,
                            },
                        },
                    ],
                },
                status=200,
            )
            rsps.add(
                rsps.GET,
                govuk_url(f'/payments/{PAYMENT_DATA["processor_id"]}/'),
                json=govuk_payment_data,
                status=200,
            )
            rsps.add(
                rsps.POST,
                govuk_url(
                    f'/payments/{PAYMENT_DATA["processor_id"]}/capture/'),
                status=204,
            )
            rsps.add(
                rsps.GET,
                govuk_url(f'/payments/{PAYMENT_DATA["processor_id"]}/'),
                json={
                    **govuk_payment_data,
                    'state': {
                        'status': 'success'
                    },
                    'settlement_summary': settlement_summary,
                },
                status=200,
            )

            call_command('update_incomplete_payments', verbosity=0)

        mock_send_email.assert_not_called()
    def test_confirmation_handles_rejected_card(self):
        self.choose_debit_card_payment_method()
        self.fill_in_prisoner_details()
        self.fill_in_amount()

        with responses.RequestsMock() as rsps:
            ref = 'wargle-blargle'
            processor_id = '3'
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/%s/' % ref),
                json={
                    'processor_id': processor_id,
                    'recipient_name': 'John',
                    'amount': 1700,
                    'status': 'pending',
                    'created': datetime.datetime.now().isoformat() + 'Z',
                },
                status=200,
            )
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % processor_id),
                json={
                    'state': {'status': 'failed'},
                    'email': '*****@*****.**',
                },
                status=200
            )
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % ref),
                status=200,
            )
            with self.patch_prisoner_details_check(), self.silence_logger():
                response = self.client.get(
                    self.url, {'payment_ref': ref}, follow=True
                )
            self.assertOnPage(response, 'check_details')

            # check session is kept
            for key in self.complete_session_keys:
                self.assertIn(key, self.client.session)
 def assertFormValid(self, form):  # noqa: N802
     with mock.patch.object(self.form_class, 'get_api_session') as mock_session, responses.RequestsMock() as rsps:
         mock_session.side_effect = lambda reconnect: get_api_session()
         mock_auth(rsps)
         rsps.add(
             rsps.GET,
             api_url(f'/prisoner_account_balances/{form.prisoner_number}'),
             json={
                 'combined_account_balance': 3000
             },
             status=200,
         )
         self.assertTrue(form.is_valid(), msg='\n\n%s' % form.errors.as_text())
    def test_debit_card_payment_handles_api_errors(self):
        self.choose_debit_card_payment_method()
        self.fill_in_prisoner_details()
        self.fill_in_amount()

        with responses.RequestsMock() as rsps:
            mock_auth(rsps)
            rsps.add(
                rsps.POST,
                api_url('/payments/'),
                status=500,
            )
            with self.patch_prisoner_details_check(), self.silence_logger():
                response = self.client.get(self.url, follow=False)
            self.assertContains(response, 'We’re sorry, your payment could not be processed on this occasion')
Example #14
0
    def test_dont_send_email(self, mock_send_email):
        """
        Test that the method only sends any email if the govuk payment status is 'capturable'
        and the MTP payment didn't have the email field set
        """
        client = PaymentClient()

        payment = {
            'uuid': 'some-id',
        }

        statuses = [
            status for status in GovUkPaymentStatus
            if status != GovUkPaymentStatus.capturable
        ]

        with responses.RequestsMock() as rsps, silence_logger():
            mock_auth(rsps)

            # API call related to updating the email address on the payment record
            rsps.add(
                rsps.PATCH,
                api_url(f'/payments/{payment["uuid"]}/'),
                json={
                    'email': '*****@*****.**',
                },
                status=200,
            )

            for status in statuses:
                govuk_payment = {
                    'payment_id': 'payment-id',
                    'state': {
                        'status': status.name,

                        # for status == 'errors'
                        'code': 'code',
                        'message': 'message',
                    },
                    'email': '*****@*****.**',
                }
                actual_status = client.complete_payment_if_necessary(
                    payment, govuk_payment)

                self.assertEqual(actual_status, status)
                mock_send_email.assert_not_called()
 def assertFormValid(self, form):  # noqa: N802
     with responses.RequestsMock() as rsps, \
             mock.patch('send_money.forms.PrisonerDetailsForm.get_api_session') as mocked_api_session:
         mocked_api_session.side_effect = get_api_session
         mock_auth(rsps)
         rsps.add(
             rsps.GET,
             api_url('/prisoner_validity/'),
             json={
                 'count': 1,
                 'results': [{
                     'prisoner_number': 'A1234AB',
                     'prisoner_dob': '1980-10-05',
                 }],
             },
             status=200,
         )
         self.assertTrue(form.is_valid(), msg='\n\n%s' % form.errors.as_text())
Example #16
0
    def test_update_incomplete_payments_doesnt_sent_email_if_no_captured_date(
            self, mock_send_email):
        """
        Test that if the MTP payment is in 'pending' and the GOV.UK payment is in 'success'
        but no captured_date is found in the response, the MTP payment is not marked
        as successful yet and no email is sent.

        This is because the actual capture in Worldpay happens at a later time and can fail.
        The only way to find out if a payment really went through is by checking the captured date,
        when it becomes available.
        """
        with responses.RequestsMock() as rsps:
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/'),
                json={
                    'count': 1,
                    'results': [PAYMENT_DATA],
                },
                status=200,
            )
            rsps.add(
                rsps.GET,
                govuk_url(f'/payments/{PAYMENT_DATA["processor_id"]}/'),
                json={
                    'reference': PAYMENT_DATA['uuid'],
                    'state': {
                        'status': 'success'
                    },
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # no call to mark the payment as successful

            call_command('update_incomplete_payments', verbosity=0)

            mock_send_email.assert_not_called()
    def test_confirmation_handles_govuk_errors(self):
        self.choose_debit_card_payment_method()
        self.fill_in_prisoner_details()
        self.fill_in_amount()

        with responses.RequestsMock() as rsps:
            ref = 'wargle-blargle'
            processor_id = '3'
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/%s/' % ref),
                json={
                    'processor_id': processor_id,
                    'recipient_name': 'John',
                    'amount': 1700,
                    'status': 'pending',
                    'created': datetime.datetime.now().isoformat() + 'Z',
                },
                status=200,
            )
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % processor_id),
                status=500
            )
            with self.patch_prisoner_details_check(), self.silence_logger():
                response = self.client.get(
                    self.url, {'payment_ref': ref}, follow=False
                )
            self.assertContains(response, 'your payment could not be processed')
            self.assertContains(response, ref[:8].upper())

            # check session is cleared
            for key in self.complete_session_keys:
                self.assertNotIn(key, self.client.session)
    def test_skip_payments(self):
        """
        Test that checks for some payments get skipped and some get included

        - wargle-aaaa is a recent payment and the check is in pending so should be skipped
        - wargle-bbbb is an old payment so should be checked even if the check is in pending
            as it could have been timed out
        - wargle-cccc is a recent payment and the payment object doesn't have check info so should be included
            just in case
        - wargle-dddd is a recent payment and the check is in accepted so should be included
        - wargle-eeee is a recent payment and the check is in rejected so should be included
        """
        payments = [
            {
                'uuid': 'wargle-aaaa',
                'processor_id': 1,
                'recipient_name': 'John',
                'amount': 1700,
                'status': 'pending',
                'created': datetime.now().isoformat() + 'Z',
                'modified': datetime.now().isoformat() + 'Z',
                'prisoner_number': 'A1409AE',
                'prisoner_dob': '1989-01-21',
                'security_check': {
                    'status': 'pending',
                    'user_actioned': False,
                },
            },
            {
                'uuid': 'wargle-bbbb',
                'processor_id': 2,
                'recipient_name': 'Tom',
                'amount': 2000,
                'status': 'pending',
                'created': (
                    datetime.now() - ALWAYS_CHECK_IF_OLDER_THAN - timedelta(hours=1)
                ).isoformat() + 'Z',
                'modified': datetime.now().isoformat() + 'Z',
                'prisoner_number': 'A1234GJ',
                'prisoner_dob': '1954-04-17',
                'security_check': {
                    'status': 'pending',
                    'user_actioned': False,
                },
            },
            {
                'uuid': 'wargle-cccc',
                'processor_id': 3,
                'recipient_name': 'Harry',
                'amount': 500,
                'status': 'pending',
                'created': datetime.now().isoformat() + 'Z',
                'modified': datetime.now().isoformat() + 'Z',
                'prisoner_number': 'A5544CD',
                'prisoner_dob': '1992-12-05',
                'security_check': None,
            },
            {
                'uuid': 'wargle-dddd',
                'processor_id': 4,
                'recipient_name': 'Lisa',
                'amount': 600,
                'status': 'pending',
                'created': datetime.now().isoformat() + 'Z',
                'modified': datetime.now().isoformat() + 'Z',
                'prisoner_number': 'A4444DB',
                'prisoner_dob': '1992-12-05',
                'security_check': {
                    'status': 'accepted',
                    'user_actioned': False,
                },
            },
            {
                'uuid': 'wargle-eeee',
                'processor_id': 5,
                'recipient_name': 'Tom',
                'amount': 700,
                'status': 'pending',
                'created': datetime.now().isoformat() + 'Z',
                'modified': datetime.now().isoformat() + 'Z',
                'prisoner_number': 'A4444DE',
                'prisoner_dob': '1992-12-05',
                'security_check': {
                    'status': 'rejected',
                    'user_actioned': True,
                },
            },
        ]
        with responses.RequestsMock() as rsps:
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/'),
                json={
                    'count': len(payments),
                    'results': payments,
                },
                status=200,
            )
            # get govuk payment
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 2),
                json={
                    'reference': 'wargle-bbbb',
                    'state': {'status': 'capturable'},
                },
                status=200,
            )
            # get govuk payment
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 3),
                json={
                    'reference': 'wargle-cccc',
                    'state': {'status': 'submitted'},
                    'settlement_summary': {
                        'capture_submit_time': '2016-10-27T15:11:05Z',
                        'captured_date': None
                    },
                },
                status=200,
            )
            # get govuk payment
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 4),
                json={
                    'reference': 'wargle-dddd',
                    'state': {'status': 'success'},
                    'settlement_summary': {
                        'capture_submit_time': '2016-10-27T15:11:05Z',
                        'captured_date': '2016-10-27'
                    },
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # save email
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-dddd'),
                json={
                    **payments[3],
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # update status
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-dddd'),
                json={
                    **payments[3],
                    'email': '*****@*****.**',
                    'status': 'taken',
                },
                status=200,
            )
            # get govuk payment
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 5),
                json={
                    'reference': 'wargle-eeee',
                    'state': {'status': 'cancelled'},
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # update status
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-eeee'),
                json={
                    **payments[4],
                    'status': 'rejected',
                },
                status=200,
            )

            call_command('update_incomplete_payments', verbosity=0)

            # wargle-aaaa, wargle-bbbb and wargle-cccc already checked implicitly by RequestsMock

            # check wargle-dddd
            self.assertEqual(
                json.loads(rsps.calls[5].request.body.decode()),
                {'email': '*****@*****.**'},
            )
            self.assertDictEqual(
                json.loads(rsps.calls[6].request.body.decode()),
                {
                    'status': 'taken',
                    'received_at': '2016-10-27T15:11:05+00:00',
                },
            )
            self.assertEqual(
                mail.outbox[0].subject,
                'Send money to someone in prison: your payment was successful',
            )
            self.assertTrue('Lisa' in mail.outbox[0].body)
            self.assertTrue('£6' in mail.outbox[0].body)

            # check wargle-eeee
            self.assertEqual(
                json.loads(rsps.calls[8].request.body.decode()),
                {
                    'email': '*****@*****.**',
                    'status': 'rejected',
                },
            )
            self.assertEqual(
                mail.outbox[1].subject,
                'Send money to someone in prison: your payment has NOT been sent to the prisoner',
            )
            self.assertTrue('Tom' in mail.outbox[1].body)
            self.assertTrue('£7' in mail.outbox[1].body)

            # double-check that no more emails were sent
            self.assertEqual(len(mail.outbox), 2)
    def test_update_incomplete_payments(self):
        """
        Test that incomplete payments get updated appropriately.

        - wargle-1111 relates to a GOV.UK payment in 'success' status so
            * should become 'taken'
            * a confirmation email should be sent
        - wargle-2222 relates to a GOV.UK payment in 'submitted' status so should be ignored
        - wargle-3333 relates to a GOV.UK payment in 'failed' status without being in capturable status in the past
            so should become 'failed'
        - wargle-4444 relates to a GOV.UK payment in 'cancelled' status so:
            * should become 'rejected'
            * a notification email should be sent to the sender
        - wargle-5555 relates to a GOV.UK payment in 'failed' status which was in a capturable status in the past so:
            * should become 'expired'
            * a notification email should be sent to the sender
        - wargle-6666 relates to a GOV.UK payment in 'success' status after its credit was accepted by FIU so:
            * should become 'taken'
            * a different confirmation email should be sent
        - wargle-7777 relates to a GOV.UK payment in 'failed' status which was in a capturable status in the past and
            was rejected by FIU so:
            * should become 'expired'
            * a rejection email (not a timeout email) should be sent to the sender
        """
        payments = [
            {
                'uuid': 'wargle-1111',
                'processor_id': 1,
                'recipient_name': 'John',
                'amount': 1700,
                'status': 'pending',
                'created': datetime.now().isoformat() + 'Z',
                'modified': datetime.now().isoformat() + 'Z',
                'prisoner_number': 'A1409AE',
                'prisoner_dob': '1989-01-21',
                'security_check': {
                    'status': 'accepted',
                    'user_actioned': False,
                },
            },
            {
                'uuid': 'wargle-2222',
                'processor_id': 2,
                'recipient_name': 'Tom',
                'amount': 2000,
                'status': 'pending',
                'created': datetime.now().isoformat() + 'Z',
                'modified': datetime.now().isoformat() + 'Z',
                'prisoner_number': 'A1234GJ',
                'prisoner_dob': '1954-04-17',
                'security_check': {
                    'status': 'accepted',
                    'user_actioned': False,
                },
            },
            {
                'uuid': 'wargle-3333',
                'processor_id': 3,
                'recipient_name': 'Harry',
                'amount': 500,
                'status': 'pending',
                'created': datetime.now().isoformat() + 'Z',
                'modified': datetime.now().isoformat() + 'Z',
                'prisoner_number': 'A5544CD',
                'prisoner_dob': '1992-12-05',
                'security_check': {
                    'status': 'accepted',
                    'user_actioned': False,
                },
            },
            {
                'uuid': 'wargle-4444',
                'processor_id': 4,
                'recipient_name': 'Lisa',
                'amount': 600,
                'status': 'pending',
                'created': datetime.now().isoformat() + 'Z',
                'modified': datetime.now().isoformat() + 'Z',
                'prisoner_number': 'A4444DB',
                'prisoner_dob': '1992-12-05',
                'security_check': {
                    'status': 'accepted',
                    'user_actioned': False,
                },
            },
            {
                'uuid': 'wargle-5555',
                'processor_id': 5,
                'recipient_name': 'Tom',
                'amount': 700,
                'status': 'pending',
                'created': datetime.now().isoformat() + 'Z',
                'modified': datetime.now().isoformat() + 'Z',
                'prisoner_number': 'A4444DE',
                'prisoner_dob': '1992-12-05',
                'security_check': {
                    'status': 'accepted',
                    'user_actioned': False,
                },
            },
            {
                'uuid': 'wargle-6666',
                'processor_id': 6,
                'recipient_name': 'Tim',
                'amount': 800,
                'status': 'pending',
                'created': datetime.now().isoformat() + 'Z',
                'modified': datetime.now().isoformat() + 'Z',
                'prisoner_number': 'A1409AW',
                'prisoner_dob': '1989-01-21',
                'security_check': {
                    'status': 'accepted',
                    'user_actioned': True,
                },
            },
            {
                'uuid': 'wargle-7777',
                'processor_id': 7,
                'recipient_name': 'Jim',
                'amount': 900,
                'status': 'pending',
                'created': datetime.now().isoformat() + 'Z',
                'modified': datetime.now().isoformat() + 'Z',
                'prisoner_number': 'A4444DQ',
                'prisoner_dob': '1992-12-05',
                'security_check': {
                    'status': 'rejected',
                    'user_actioned': True,
                },
            },
        ]
        with responses.RequestsMock() as rsps:
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/'),
                json={
                    'count': len(payments),
                    'results': payments,
                },
                status=200,
            )
            # get govuk payment
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 1),
                json={
                    'reference': 'wargle-1111',
                    'state': {'status': 'success'},
                    'settlement_summary': {
                        'capture_submit_time': '2016-10-27T15:11:05Z',
                        'captured_date': '2016-10-27'
                    },
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # save email
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-1111'),
                json={
                    **payments[0],
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # update status
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-1111'),
                json={
                    **payments[0],
                    'email': '*****@*****.**',
                    'status': 'taken',
                },
                status=200,
            )
            # get govuk payment
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 2),
                json={
                    'reference': 'wargle-2222',
                    'state': {'status': 'submitted'},
                    'settlement_summary': {
                        'capture_submit_time': '2016-10-27T15:11:05Z',
                        'captured_date': None
                    },
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # get govuk payment
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 3),
                json={
                    'reference': 'wargle-3333',
                    'state': {'status': 'failed'},
                    'settlement_summary': {
                        'capture_submit_time': None,
                        'captured_date': None
                    },
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # update status
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-3333'),
                json={
                    **payments[2],
                    'status': 'failed',
                },
                status=200,
            )
            # get govuk payment
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 4),
                json={
                    'reference': 'wargle-4444',
                    'state': {'status': 'cancelled'},
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # update status
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-4444'),
                json={
                    **payments[3],
                    'status': 'rejected',
                },
                status=200,
            )
            # get govuk payment
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 5),
                json={
                    'payment_id': 'wargle-5555',
                    'reference': 'wargle-5555',
                    'state': {
                        'status': 'failed',
                        'code': 'P0020',
                    },
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # get govuk payment events
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/events/' % 'wargle-5555'),
                status=200,
                json={
                    'events': [
                        {
                            'payment_id': 'wargle-5555',
                            'state': {
                                'status': 'capturable',
                                'finished': False,
                            },
                        },
                    ],
                    'payment_id': 'wargle-5555',
                },
            )
            # update status
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-5555'),
                json={
                    **payments[4],
                    'status': 'expired',
                },
                status=200,
            )
            # get govuk payment
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 6),
                json={
                    'reference': 'wargle-6666',
                    'state': {'status': 'success'},
                    'settlement_summary': {
                        'capture_submit_time': '2016-10-27T15:11:05Z',
                        'captured_date': '2016-10-27'
                    },
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # save email
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-6666'),
                json={
                    **payments[5],
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # update status
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-6666'),
                json={
                    **payments[5],
                    'status': 'taken',
                },
                status=200,
            )
            # get govuk payment
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 7),
                json={
                    'payment_id': 'wargle-7777',
                    'reference': 'wargle-7777',
                    'state': {
                        'status': 'failed',
                        'code': 'P0020',
                    },
                    'email': '*****@*****.**',
                },
                status=200,
            )
            # get govuk payment events
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/events/' % 'wargle-7777'),
                status=200,
                json={
                    'events': [
                        {
                            'payment_id': 'wargle-7777',
                            'state': {
                                'status': 'capturable',
                                'finished': False,
                            },
                        },
                    ],
                    'payment_id': 'wargle-7777',
                },
            )
            # update status
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-7777'),
                json={
                    **payments[0],
                    'status': 'expired',
                },
                status=200,
            )

            call_command('update_incomplete_payments', verbosity=0)

            # check wargle-1111
            self.assertEqual(
                json.loads(rsps.calls[3].request.body.decode()),
                {'email': '*****@*****.**'},
            )
            self.assertDictEqual(
                json.loads(rsps.calls[4].request.body.decode()),
                {
                    'status': 'taken',
                    'received_at': '2016-10-27T15:11:05+00:00',
                },
            )
            self.assertEqual(
                mail.outbox[0].subject,
                'Send money to someone in prison: your payment was successful',
            )
            self.assertTrue('John' in mail.outbox[0].body)
            self.assertTrue('£17' in mail.outbox[0].body)

            # check wargle-3333
            self.assertEqual(
                json.loads(rsps.calls[7].request.body.decode()),
                {
                    'email': '*****@*****.**',
                    'status': 'failed',
                },
            )

            # check wargle-4444
            self.assertEqual(
                json.loads(rsps.calls[9].request.body.decode()),
                {
                    'email': '*****@*****.**',
                    'status': 'rejected',
                },
            )
            self.assertEqual(
                mail.outbox[1].subject,
                'Send money to someone in prison: your payment has NOT been sent to the prisoner',
            )
            self.assertTrue('Lisa' in mail.outbox[1].body)
            self.assertTrue('£6' in mail.outbox[1].body)

            # check wargle-5555
            self.assertEqual(
                json.loads(rsps.calls[12].request.body.decode()),
                {
                    'email': '*****@*****.**',
                    'status': 'expired',
                },
            )
            self.assertEqual(
                mail.outbox[2].subject,
                'Send money to someone in prison: payment session expired',
            )
            self.assertTrue('Tom' in mail.outbox[2].body)
            self.assertTrue('£7' in mail.outbox[2].body)

            # check wargle-6666
            self.assertEqual(
                json.loads(rsps.calls[14].request.body.decode()),
                {'email': '*****@*****.**'},
            )
            self.assertDictEqual(
                json.loads(rsps.calls[15].request.body.decode()),
                {
                    'status': 'taken',
                    'received_at': '2016-10-27T15:11:05+00:00',
                },
            )
            self.assertEqual(
                mail.outbox[3].subject,
                'Send money to someone in prison: your payment has now gone through',
            )
            self.assertTrue('Tim' in mail.outbox[3].body)
            self.assertTrue('£8' in mail.outbox[3].body)

            # check wargle-7777
            self.assertEqual(
                json.loads(rsps.calls[18].request.body.decode()),
                {
                    'email': '*****@*****.**',
                    'status': 'expired',
                },
            )
            self.assertEqual(
                mail.outbox[4].subject,
                'Send money to someone in prison: your payment has NOT been sent to the prisoner',
            )
            self.assertTrue('Jim' in mail.outbox[4].body)
            self.assertTrue('£9' in mail.outbox[4].body)

            # double-check that no more emails were sent
            self.assertEqual(len(mail.outbox), 5)
Example #20
0
 def test_healthcheck_with_service_down_returns_false(self):
     with responses.RequestsMock() as rsps:
         rsps.add(rsps.GET, api_url('/service-availability/'), json={'gov_uk_pay': {'status': False}})
         available, message_to_users = check_payment_service_available()
     self.assertFalse(available)
     self.assertIsNone(message_to_users)
Example #21
0
 def test_healthcheck_with_service_unspecified_returns_true(self):
     with responses.RequestsMock() as rsps:
         rsps.add(rsps.GET, api_url('/service-availability/'), json={'another_service': {'status': False}})
         available, message_to_users = check_payment_service_available()
     self.assertTrue(available)
     self.assertIsNone(message_to_users)
Example #22
0
 def test_failed_healthcheck_returns_true(self):
     with responses.RequestsMock() as rsps:
         rsps.add(rsps.GET, api_url('/service-availability/'), body=b'Server error', status=500)
         available, message_to_users = check_payment_service_available()
     self.assertTrue(available)
     self.assertIsNone(message_to_users)
Example #23
0
 def test_healthcheck_timeout_returns_true(self):
     with responses.RequestsMock() as rsps:
         rsps.add(rsps.GET, api_url('/service-availability/'), body=Timeout())
         available, message_to_users = check_payment_service_available()
     self.assertTrue(available)
     self.assertIsNone(message_to_users)
    def test_confirmation(self):
        self.choose_debit_card_payment_method()
        self.fill_in_prisoner_details()
        self.fill_in_amount()

        with responses.RequestsMock() as rsps:
            ref = 'wargle-blargle'
            processor_id = '3'
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/%s/' % ref),
                json={
                    'processor_id': processor_id,
                    'recipient_name': 'John',
                    'amount': 1700,
                    'status': 'pending',
                    'created': datetime.datetime.now().isoformat() + 'Z',
                },
                status=200,
            )
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % processor_id),
                json={
                    'state': {'status': 'success'},
                    'email': '*****@*****.**',
                    '_links': {
                        'events': {
                            'method': 'GET',
                            'href': govuk_url('/payments/%s/events' % 1),
                        }
                    }
                },
                status=200
            )
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/events' % 1),
                json={
                    'events': [
                        {
                            'state': {'status': 'success'},
                            'updated': '2016-10-27T15:11:05.768Z'
                        }
                    ]
                },
                status=200
            )
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % ref),
                status=200,
            )
            with self.patch_prisoner_details_check():
                response = self.client.get(
                    self.url, {'payment_ref': ref}, follow=False
                )
            self.assertContains(response, 'success')

            # check session is cleared
            for key in self.complete_session_keys:
                self.assertNotIn(key, self.client.session)

            self.assertEqual('Send money to a prisoner: your payment was successful', mail.outbox[0].subject)
            self.assertTrue('WARGLE-B' in mail.outbox[0].body)
            self.assertTrue('£17' in mail.outbox[0].body)

            with self.patch_prisoner_details_check():
                response = self.client.get(
                    self.url, {'payment_ref': ref}, follow=True
                )
            self.assertOnPage(response, 'choose_method')
    def test_update_incomplete_payments_extracts_card_details(self):
        """
        Test that card details are extracted from the GOV.UK payment and saved on the MTP payment.
        """
        payment_extra_details = {
            'cardholder_name': 'Jack Halls',
            'card_brand': 'Visa',
            'worldpay_id': '11112222-1111-2222-3333-111122223333',
            'card_number_first_digits': '100002',
            'card_number_last_digits': '1111',
            'card_expiry_date': '11/18',
            'billing_address': {
                'line1': '102 Petty France',
                'line2': '',
                'city': 'London',
                'postcode': 'SW1H9AJ',
                'country': 'GB',
            },
        }
        with responses.RequestsMock() as rsps:
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/'),
                json={
                    'count': 1,
                    'results': [PAYMENT_DATA],
                },
                status=200,
            )
            rsps.add(
                rsps.GET,
                govuk_url(f'/payments/{PAYMENT_DATA["processor_id"]}/'),
                json={
                    'reference': PAYMENT_DATA['uuid'],
                    'state': {'status': 'success'},
                    'settlement_summary': {
                        'capture_submit_time': '2016-10-27T15:11:05Z',
                        'captured_date': '2016-10-27',
                    },
                    'card_details': {
                        'card_brand': 'Visa',
                        'last_digits_card_number': '1111',
                        'first_digits_card_number': '100002',
                        'cardholder_name': 'Jack Halls',
                        'expiry_date': '11/18',
                        'billing_address': {
                            'line1': '102 Petty France',
                            'line2': '',
                            'postcode': 'SW1H9AJ',
                            'city': 'London',
                            'country': 'GB',
                        },
                    },
                    'provider_id': '11112222-1111-2222-3333-111122223333',
                    'email': '*****@*****.**',
                },
                status=200,
            )
            rsps.add(
                rsps.PATCH,
                api_url(f'/payments/{PAYMENT_DATA["uuid"]}/'),
                json={
                    **PAYMENT_DATA,
                    **payment_extra_details,
                },
                status=200,
            )
            rsps.add(
                rsps.PATCH,
                api_url(f'/payments/{PAYMENT_DATA["uuid"]}/'),
                json={
                    **PAYMENT_DATA,
                    **payment_extra_details,
                },
                status=200,
            )

            call_command('update_incomplete_payments', verbosity=0)

            self.assertDictEqual(
                json.loads(rsps.calls[-2].request.body.decode()),
                payment_extra_details,
            )
            self.assertDictEqual(
                json.loads(rsps.calls[-1].request.body.decode()),
                {
                    'received_at': '2016-10-27T15:11:05+00:00',
                    'status': 'taken',
                },
            )
    def test_success_page(self):
        ref = 'f469ec29-fb86-40db-a9e0-6faa409533be'
        processor_id = '3'
        with responses.RequestsMock(
        ) as rsps, self.patch_view_chain_form_checking():
            mock_auth(rsps)
            rsps.add(rsps.GET,
                     api_url('/payments/%s/' % ref),
                     json={
                         'uuid': ref,
                         'processor_id': processor_id,
                         'recipient_name': 'James Bond',
                         'amount': 2000,
                         'status': 'pending',
                         'created': datetime.datetime.now().isoformat() + 'Z',
                         'prisoner_number': 'A5544CD',
                         'prisoner_dob': '1992-12-05',
                     })
            rsps.add(rsps.GET,
                     govuk_url('/payments/%s' % processor_id),
                     json={
                         'reference': ref,
                         'state': {
                             'status': 'success',
                             'finished': True
                         },
                         'amount': 2000,
                         'payment_id': processor_id,
                         'email': '*****@*****.**',
                         '_links': {
                             'events': {
                                 'href':
                                 govuk_url('/payments/%s/events' %
                                           processor_id),
                                 'method':
                                 'GET'
                             }
                         }
                     })
            rsps.add(rsps.PATCH,
                     api_url('/payments/%s/' % ref),
                     json={
                         'uuid': ref,
                         'processor_id': processor_id,
                         'recipient_name': 'James Bond',
                         'amount': 2000,
                         'status': 'pending',
                         'created': datetime.datetime.now().isoformat() + 'Z',
                         'prisoner_number': 'A5544CD',
                         'prisoner_dob': '1992-12-05',
                         'email': '*****@*****.**',
                     })

            self.driver.get(self.live_server_url +
                            '/en-gb/debit-card/confirmation/?payment_ref=' +
                            ref)
        self.assertInSource('Payment successful')
        self.assertInSource('<strong>F469EC29</strong>')
        self.assertInSource('James Bond')
        self.assertInSource('£20')
        self.assertInSource('Print this page')
Example #27
0
    def test_capturable_payment_that_should_be_cancelled(
            self, mock_send_email):
        """
        Test that if the govuk payment is in 'capturable' state and the payment should be cancelled:

        - the MTP payment record is patched with the card details attributes if necessary
        - the method cancels the payment
        - no email is sent
        - the method returns GovUkPaymentStatus.cancelled
        """
        client = PaymentClient()

        payment = {
            'uuid': 'some-id',
            'recipient_name': 'Alice Re',
            'prisoner_number': 'AAB0A00',
            'prisoner_name': 'John Doe',
            'amount': 1700,
            'security_check': {
                'status': 'rejected',
                'user_actioned': True,
            },
        }
        payment_extra_details = {
            'email': '*****@*****.**',
            'worldpay_id': '123456789',
            'cardholder_name': 'John Doe',
            'card_number_first_digits': '1234',
            'card_number_last_digits': '987',
            'card_expiry_date': '01/20',
            'card_brand': 'visa',
            'billing_address': {
                'line1': '102 Petty France',
                'line2': '',
                'postcode': 'SW1H9AJ',
                'city': 'London',
                'country': 'GB',
            },
        }
        govuk_payment = {
            'payment_id': 'payment-id',
            'state': {
                'status': GovUkPaymentStatus.capturable.name,
            },
            'email': '*****@*****.**',
            'provider_id': '123456789',
            'card_details': {
                'cardholder_name': 'John Doe',
                'first_digits_card_number': '1234',
                'last_digits_card_number': '987',
                'expiry_date': '01/20',
                'card_brand': 'visa',
                'billing_address': {
                    'line1': '102 Petty France',
                    'line2': '',
                    'postcode': 'SW1H9AJ',
                    'city': 'London',
                    'country': 'GB',
                },
            },
        }

        with responses.RequestsMock() as rsps:
            mock_auth(rsps)

            # API call related to updating the email address and card details
            rsps.add(
                rsps.PATCH,
                api_url(f'/payments/{payment["uuid"]}/'),
                json={
                    **payment,
                    **payment_extra_details,
                },
                status=200,
            )

            rsps.add(
                rsps.POST,
                govuk_url(f'/payments/{govuk_payment["payment_id"]}/cancel/'),
                status=204,
            )

            status = client.complete_payment_if_necessary(
                payment, govuk_payment)

            payment_patch_body = json.loads(
                rsps.calls[-2].request.body.decode())
            self.assertDictEqual(
                payment_patch_body,
                payment_extra_details,
            )
        self.assertEqual(status, GovUkPaymentStatus.cancelled)
        mock_send_email.assert_not_called()
Example #28
0
    def test_success_status(self, mock_send_email):
        """
        Test that if the govuk payment is in 'success' state and the MTP payment record
        doesn't have all the card details and email field filled in:

        - the MTP payment record is patched with the extra payment details
        - the method returns GovUkPaymentStatus.success
        - no email is sent
        """
        client = PaymentClient()

        payment = {
            'uuid': 'some-id',
        }
        payment_extra_details = {
            'email': '*****@*****.**',
            'cardholder_name': 'John Doe',
            'card_number_first_digits': '1234',
            'card_number_last_digits': '987',
            'card_expiry_date': '01/20',
            'card_brand': 'visa',
            'billing_address': {
                'line1': '102 Petty France',
                'line2': '',
                'postcode': 'SW1H9AJ',
                'city': 'London',
                'country': 'GB',
            },
        }
        govuk_payment = {
            'payment_id': 'payment-id',
            'state': {
                'status': GovUkPaymentStatus.success.name,
            },
            'email': '*****@*****.**',
            'card_details': {
                'cardholder_name': 'John Doe',
                'first_digits_card_number': '1234',
                'last_digits_card_number': '987',
                'expiry_date': '01/20',
                'card_brand': 'visa',
                'billing_address': {
                    'line1': '102 Petty France',
                    'line2': '',
                    'postcode': 'SW1H9AJ',
                    'city': 'London',
                    'country': 'GB',
                },
            },
        }
        with responses.RequestsMock() as rsps:
            mock_auth(rsps)

            # API call related to updating the email address and other details on the payment record
            rsps.add(
                rsps.PATCH,
                api_url(f'/payments/{payment["uuid"]}/'),
                json={
                    **payment,
                    **payment_extra_details,
                },
                status=200,
            )

            status = client.complete_payment_if_necessary(
                payment, govuk_payment)

            self.assertDictEqual(
                json.loads(rsps.calls[-1].request.body.decode()),
                payment_extra_details,
            )

        self.assertEqual(status, GovUkPaymentStatus.success)
        mock_send_email.assert_not_called()
    def test_update_incomplete_payments(self):
        with responses.RequestsMock() as rsps:
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/'),
                json={
                    'count': 3,
                    'results': [
                        {
                            'uuid': 'wargle-1111',
                            'processor_id': 1,
                            'recipient_name': 'John',
                            'amount': 1700,
                            'status': 'pending',
                            'created': datetime.now().isoformat() + 'Z'
                        },
                        {
                            'uuid': 'wargle-2222',
                            'processor_id': 2,
                            'recipient_name': 'Tom',
                            'amount': 2000,
                            'status': 'pending',
                            'created': datetime.now().isoformat() + 'Z'
                        },
                        {
                            'uuid': 'wargle-3333',
                            'processor_id': 3,
                            'recipient_name': 'Harry',
                            'amount': 500,
                            'status': 'pending',
                            'created': datetime.now().isoformat() + 'Z'
                        },
                    ]
                },
                status=200,
            )
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 1),
                json={
                    'state': {'status': 'success'},
                    'email': '*****@*****.**',
                    '_links': {
                        'events': {
                            'method': 'GET',
                            'href': govuk_url('/payments/%s/events' % 1),
                        }
                    }
                },
                status=200
            )
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/events' % 1),
                json={
                    'events': [
                        {
                            'state': {'status': 'success'},
                            'updated': '2016-10-27T15:11:05.768Z'
                        }
                    ]
                },
                status=200
            )
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-1111'),
                status=200,
            )
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 2),
                json={
                    'state': {'status': 'submitted'},
                    'email': '*****@*****.**',
                },
                status=200
            )
            rsps.add(
                rsps.GET,
                govuk_url('/payments/%s/' % 3),
                json={
                    'state': {'status': 'failed'},
                    'email': '*****@*****.**',
                },
                status=200
            )
            rsps.add(
                rsps.PATCH,
                api_url('/payments/%s/' % 'wargle-3333'),
                status=200,
            )

            call_command('update_incomplete_payments')
    def test_captured_payment_with_captured_date_gets_updated(self):
        """
        Test that when a MTP pending payment is captured, if the captured date
        is immediately available, the payment is marked as 'taken' and a confirmation
        email is sent.
        """
        govuk_payment_data = {
            'payment_id': PAYMENT_DATA['processor_id'],
            'reference': PAYMENT_DATA['uuid'],
            'state': {'status': 'capturable'},
            'email': '*****@*****.**',
        }
        with responses.RequestsMock() as rsps:
            mock_auth(rsps)
            rsps.add(
                rsps.GET,
                api_url('/payments/'),
                json={
                    'count': 1,
                    'results': [
                        {
                            **PAYMENT_DATA,
                            'security_check': {
                                'status': 'accepted',
                                'user_actioned': True,
                            },
                        },
                    ],
                },
                status=200,
            )
            # get govuk payment
            rsps.add(
                rsps.GET,
                govuk_url(f'/payments/{PAYMENT_DATA["processor_id"]}/'),
                json=govuk_payment_data,
                status=200,
            )
            # capture payment
            rsps.add(
                rsps.POST,
                govuk_url(f'/payments/{PAYMENT_DATA["processor_id"]}/capture/'),
                status=204,
            )
            # get govuk payment to see if we have the captured date
            rsps.add(
                rsps.GET,
                govuk_url(f'/payments/{PAYMENT_DATA["processor_id"]}/'),
                json={
                    **govuk_payment_data,
                    'state': {'status': 'success'},
                    'settlement_summary': {
                        'capture_submit_time': '2016-10-27T15:11:05Z',
                        'captured_date': '2016-10-27',
                    },
                },
                status=200,
            )
            # update status
            rsps.add(
                rsps.PATCH,
                api_url(f'/payments/{PAYMENT_DATA["uuid"]}/'),
                json={
                    **PAYMENT_DATA,
                    'status': 'taken',
                    'email': '*****@*****.**',
                },
                status=200,
            )

            call_command('update_incomplete_payments', verbosity=0)

            self.assertEqual(len(mail.outbox), 1)

            self.assertEqual(
                json.loads(rsps.calls[-1].request.body.decode()),
                {
                    'status': 'taken',
                    'received_at': '2016-10-27T15:11:05+00:00',
                },
            )
Example #31
0
    def test_capturable_payment_that_shouldnt_be_captured_yet(
            self, mock_send_email):
        """
        Test that if the govuk payment is in 'capturable' state, the MTP payment record
        doesn't have the email field filled in and the payment should not be captured yet:

        - the MTP payment record is patched with the card details attributes
        - the method returns GovUkPaymentStatus.capturable
        - an email is sent to the sender
        """
        client = PaymentClient()

        payment = {
            'uuid': 'b74a0eb6-0437-4b22-bce8-e6f11bd43802',
            'recipient_name': 'Alice Re',
            'prisoner_name': 'John Doe',
            'prisoner_number': 'AAB0A00',
            'amount': 1700,
            'security_check': {
                'status': 'pending',
                'user_actioned': False,
            },
        }
        payment_extra_details = {
            'email': '*****@*****.**',
            'worldpay_id': '123456789',
            'cardholder_name': 'John Doe',
            'card_number_first_digits': '1234',
            'card_number_last_digits': '987',
            'card_expiry_date': '01/20',
            'card_brand': 'visa',
            'billing_address': {
                'line1': '102 Petty France',
                'line2': '',
                'postcode': 'SW1H9AJ',
                'city': 'London',
                'country': 'GB',
            },
        }
        govuk_payment = {
            'payment_id': 'payment-id',
            'state': {
                'status': GovUkPaymentStatus.capturable.name,
            },
            'email': '*****@*****.**',
            'provider_id': '123456789',
            'card_details': {
                'cardholder_name': 'John Doe',
                'first_digits_card_number': '1234',
                'last_digits_card_number': '987',
                'expiry_date': '01/20',
                'card_brand': 'visa',
                'billing_address': {
                    'line1': '102 Petty France',
                    'line2': '',
                    'postcode': 'SW1H9AJ',
                    'city': 'London',
                    'country': 'GB',
                },
            },
        }

        with responses.RequestsMock() as rsps:
            mock_auth(rsps)

            # API call related to updating the email address and card details
            rsps.add(
                rsps.PATCH,
                api_url(f'/payments/{payment["uuid"]}/'),
                json={
                    **payment,
                    **payment_extra_details,
                },
                status=200,
            )

            status = client.complete_payment_if_necessary(
                payment, govuk_payment)

            payment_patch_body = json.loads(
                rsps.calls[-1].request.body.decode())
            self.assertDictEqual(
                payment_patch_body,
                payment_extra_details,
            )
        self.assertEqual(status, GovUkPaymentStatus.capturable)
        self.assertEqual(len(mock_send_email.call_args_list), 1)
        send_email_kwargs = mock_send_email.call_args_list[0].kwargs
        self.assertEqual(send_email_kwargs['template_name'],
                         'send-money-debit-card-payment-on-hold')
        self.assertEqual(send_email_kwargs['to'], '*****@*****.**')