def test_with_different_govuk_payment_status_updates_session( self, govuk_status, payment_url, requests_mock, ): """ Test that if the GOV.UK payment status is not the same as the payment gateway session one, the record is updated. """ # choose an initial status != from the govuk one to test the update initial_status = PaymentGatewaySessionStatus.created if initial_status == govuk_status: initial_status = PaymentGatewaySessionStatus.started session = PaymentGatewaySessionFactory(status=initial_status) # mock GOV.UK call used to get the existing session requests_mock.get( govuk_url(f'payments/{session.govuk_payment_id}'), status_code=200, json={ 'state': { 'status': govuk_status }, 'payment_id': session.govuk_payment_id, '_links': { 'next_url': None if not payment_url else { 'href': payment_url }, }, }, ) # make API call url = reverse( 'api-v3:omis-public:payment-gateway-session:detail', kwargs={ 'public_token': session.order.public_token, 'pk': session.id }, ) client = self.create_api_client( scope=Scope.public_omis_front_end, grant_type=Application.GRANT_CLIENT_CREDENTIALS, ) response = client.get(url) assert response.status_code == status.HTTP_200_OK # refresh record session.refresh_from_db() assert session.status == govuk_status # check API response assert response.json() == { 'id': str(session.id), 'created_on': format_date_or_datetime(session.created_on), 'status': govuk_status, 'payment_url': payment_url, }
def test_with_different_govuk_payment_status_updates_session( self, status, requests_mock): """ Test that if the GOV.UK payment status is not the same as the payment gateway session one, the record is updated. """ # choose an initial status != from the govuk one to test the update initial_status = PaymentGatewaySessionStatus.created if initial_status == status: initial_status = PaymentGatewaySessionStatus.started session = PaymentGatewaySessionFactory(status=initial_status) url = govuk_url(f'payments/{session.govuk_payment_id}') requests_mock.get( url, status_code=200, json={ 'state': { 'status': status }, }, ) assert session.refresh_from_govuk_payment() session.refresh_from_db() assert session.status == status assert requests_mock.call_count == 1
def test_ongoing(self): """ Test that given: session 1 - order 1 - status created session 2 - order 1 - status submitted session 3 - order 1 - status failed session 4 - order 2 - status started session 5 - order 2 - status success session 6 - order 2 - status cancelled the method .ongoing() on the queryset only returns the sessions which are considered not finished. """ order1, order2 = OrderWithAcceptedQuoteFactory.create_batch(2) order1_sessions = PaymentGatewaySessionFactory.create_batch( 3, order=order1, status=factory.Iterator([ PaymentGatewaySessionStatus.CREATED, PaymentGatewaySessionStatus.SUBMITTED, PaymentGatewaySessionStatus.FAILED, ]), ) order2_sessions = PaymentGatewaySessionFactory.create_batch( 3, order=order2, status=factory.Iterator([ PaymentGatewaySessionStatus.STARTED, PaymentGatewaySessionStatus.SUCCESS, PaymentGatewaySessionStatus.CANCELLED, ]), ) # test qs without filters qs = PaymentGatewaySession.objects.ongoing() assert set(qs.values_list('id', flat=True)) == { order1_sessions[0].id, order1_sessions[1].id, order2_sessions[0].id, } # test qs with order filter qs = PaymentGatewaySession.objects.filter(order=order1).ongoing() assert set(qs.values_list('id', flat=True)) == { order1_sessions[0].id, order1_sessions[1].id, }
def test_doesnt_call_govuk_pay_if_session_finished(self, session_status, requests_mock): """ Test a successful call to get a payment gateway session when the session is finished. The system does not call GOV.UK Pay as the record is up-to-date. """ session = PaymentGatewaySessionFactory(status=session_status) # make API call url = reverse( 'api-v3:omis-public:payment-gateway-session:detail', kwargs={ 'public_token': session.order.public_token, 'pk': session.id }, ) client = self.create_api_client( scope=Scope.public_omis_front_end, grant_type=Application.GRANT_CLIENT_CREDENTIALS, ) response = client.get(url) assert response.status_code == status.HTTP_200_OK # check API response assert response.json() == { 'id': str(session.id), 'created_on': format_date_or_datetime(session.created_on), 'status': session.status, 'payment_url': '', } assert not requests_mock.called
def test_500_if_govuk_pay_errors(self, govuk_status_code, requests_mock): """ Test that if GOV.UK Pay errors whilst getting a payment, the endpoint returns 500. Possible GOV.UK errors: - 401 - UNAUTHORIZED - 404 - NOT FOUND - 500 - INTERNAL SERVER ERROR """ order = OrderWithAcceptedQuoteFactory() session = PaymentGatewaySessionFactory( order=order, status=PaymentGatewaySessionStatus.created, ) requests_mock.get( govuk_url(f'payments/{session.govuk_payment_id}'), status_code=govuk_status_code, ) # make API call url = reverse( 'api-v3:omis-public:payment-gateway-session:detail', kwargs={ 'public_token': order.public_token, 'pk': session.id }, ) client = self.create_api_client( scope=Scope.public_omis_front_end, grant_type=Application.GRANT_CLIENT_CREDENTIALS, ) response = client.get(url) assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
def test_atomicity_when_govuk_pay_errors(self, requests_mock): """ Test that if GOV.UK Pay errors, none of the changes persists. """ session = PaymentGatewaySessionFactory() original_session_status = session.status url = govuk_url(f'payments/{session.govuk_payment_id}') requests_mock.get(url, status_code=500) with pytest.raises(GOVUKPayAPIException): assert session.refresh_from_govuk_payment() session.refresh_from_db() assert session.status == original_session_status assert requests_mock.call_count == 1
def test_atomicity_when_session_save_errors(self, requests_mock): """ Test that if the PaymentGatewaySession.save() call fails, none of the changes persists. """ session = PaymentGatewaySessionFactory() original_session_status = session.status url = govuk_url(f'payments/{session.govuk_payment_id}') requests_mock.get( url, status_code=200, json={ 'state': { 'status': 'success' }, }, ) session.save = mock.MagicMock(side_effect=Exception()) with pytest.raises(Exception): session.refresh_from_govuk_payment() session.refresh_from_db() assert session.status == original_session_status assert requests_mock.call_count == 1
def test_500_if_govuk_pay_errors_when_cancelling(self, govuk_status_code, requests_mock): """ Test that if GOV.UK Pay errors whilst cancelling some other ongoing sessions/payments, the endpoint returns 500 to keep the system consistent. Possible GOV.UK errors when cancelling: - 400 - BAD REQUEST - 401 - UNAUTHORIZED - 404 - NOT FOUND - 409 - CONFLICT - 500 - INTERNAL SERVER ERROR In all these cases we return 500 as all those GOV.UK errors are unexpected. """ order = OrderWithAcceptedQuoteFactory() existing_session = PaymentGatewaySessionFactory( order=order, status=PaymentGatewaySessionStatus.created, ) # mock GOV.UK requests used to # - refresh the existing payment gateway session # - cancel the GOV.UK payment requests_mock.get( govuk_url(f'payments/{existing_session.govuk_payment_id}'), status_code=200, json={ 'state': { 'status': existing_session.status }, }, ) requests_mock.post( govuk_url(f'payments/{existing_session.govuk_payment_id}/cancel'), status_code=govuk_status_code, ) # make API call assert PaymentGatewaySession.objects.count() == 1 url = reverse( 'api-v3:omis-public:payment-gateway-session:collection', kwargs={'public_token': order.public_token}, ) client = self.create_api_client( scope=Scope.public_omis_front_end, grant_type=Application.GRANT_CLIENT_CREDENTIALS, ) response = client.post(url) assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR # check no session created assert PaymentGatewaySession.objects.count() == 1
def test_verbs_not_allowed(self, verb, public_omis_api_client): """Test that makes sure the other verbs are not allowed.""" order = OrderWithAcceptedQuoteFactory() session = PaymentGatewaySessionFactory(order=order) url = reverse( 'api-v3:public-omis:payment-gateway-session:detail', kwargs={'public_token': order.public_token, 'pk': session.id}, ) response = getattr(public_omis_api_client, verb)(url, json_={}) assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED
def test_404_if_in_disallowed_status(self, order_status, public_omis_api_client): """Test that if the order is not in an allowed state, the endpoint returns 404.""" order = OrderFactory(status=order_status) session = PaymentGatewaySessionFactory(order=order) # make API call url = reverse( 'api-v3:public-omis:payment-gateway-session:detail', kwargs={'public_token': order.public_token, 'pk': session.id}, ) response = public_omis_api_client.get(url) assert response.status_code == status.HTTP_404_NOT_FOUND
def test_with_govuk_pay_erroring_when_refreshing(self, requests_mock): """ Test that if GOV.UK Pay cancels the payment but errors when refreshing the session, the session object is not updated (but the GOV.UK payment is still cancelled). This is okay as the session object will get refreshed at the next opportunity. """ session = PaymentGatewaySessionFactory() original_session_status = session.status requests_mock.post( govuk_url(f'payments/{session.govuk_payment_id}/cancel'), status_code=204, ) requests_mock.get( govuk_url(f'payments/{session.govuk_payment_id}'), status_code=500, ) with pytest.raises(GOVUKPayAPIException): session.cancel() session.refresh_from_db() assert session.status == original_session_status assert requests_mock.call_count == 2
def test_without_credentials(self, api_client): """Test that making a request without credentials returns an error.""" order = OrderFactory() session = PaymentGatewaySessionFactory( order=order, status=PaymentGatewaySessionStatus.CREATED, ) url = reverse( 'api-v3:public-omis:payment-gateway-session:detail', kwargs={'public_token': order.public_token, 'pk': session.pk}, ) response = api_client.get(url) assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_get(self, order_status, session_status, requests_mock): """Test a successful call to get a payment gateway session.""" order = OrderFactory(status=order_status) session = PaymentGatewaySessionFactory( order=order, status=session_status, ) # mock GOV.UK Pay request used to get the existing session next_url = 'https://payment.example.com/123abc' requests_mock.get( govuk_url(f'payments/{session.govuk_payment_id}'), status_code=200, json={ 'state': { 'status': session.status }, 'payment_id': session.govuk_payment_id, '_links': { 'next_url': { 'href': next_url, 'method': 'GET', }, }, }, ) # make API call url = reverse( 'api-v3:omis-public:payment-gateway-session:detail', kwargs={ 'public_token': order.public_token, 'pk': session.id }, ) client = self.create_api_client( scope=Scope.public_omis_front_end, grant_type=Application.GRANT_CLIENT_CREDENTIALS, ) response = client.get(url) assert response.status_code == status.HTTP_200_OK # check API response assert response.json() == { 'id': str(session.id), 'created_on': format_date_or_datetime(session.created_on), 'status': session.status, 'payment_url': next_url, }
def test_without_whitelisted_ip(self, public_omis_api_client): """Test that making a request without the whitelisted client IP returns an error.""" order = OrderFactory() session = PaymentGatewaySessionFactory( order=order, status=PaymentGatewaySessionStatus.CREATED, ) url = reverse( 'api-v3:public-omis:payment-gateway-session:detail', kwargs={'public_token': order.public_token, 'pk': session.pk}, ) public_omis_api_client.set_http_x_forwarded_for('1.1.1.1') response = public_omis_api_client.get(url) assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_404_if_session_belongs_to_another_order(self, public_omis_api_client): """ Test that if the payment gateway session belongs to another order, the endpoint returns 404. """ orders = OrderWithAcceptedQuoteFactory.create_batch(2) sessions = PaymentGatewaySessionFactory.create_batch(2, order=factory.Iterator(orders)) url = reverse( 'api-v3:public-omis:payment-gateway-session:detail', kwargs={ 'public_token': orders[0].public_token, 'pk': sessions[1].id, }, ) response = public_omis_api_client.get(url) assert response.status_code == status.HTTP_404_NOT_FOUND
def test_one_failed_refresh_doesnt_stop_others(self, requests_mock): """ Test that if one refresh fails, the other ones are still carried on and committed to the databasea. In this example, pay-1 and pay-3 should get refreshed whilst pay-2 errors and shouldn't get refreshed. """ # mock calls to GOV.UK Pay govuk_payment_ids = ['pay-1', 'pay-2', 'pay-3'] requests_mock.get( govuk_url(f'payments/{govuk_payment_ids[0]}'), status_code=200, json={'state': { 'status': 'failed' }}, ) requests_mock.get( govuk_url(f'payments/{govuk_payment_ids[1]}'), status_code=500, ) requests_mock.get( govuk_url(f'payments/{govuk_payment_ids[2]}'), status_code=200, json={'state': { 'status': 'failed' }}, ) # populate db sessions = PaymentGatewaySessionFactory.create_batch( 3, status=PaymentGatewaySessionStatus.STARTED, govuk_payment_id=factory.Iterator(govuk_payment_ids), ) # make call refresh_pending_payment_gateway_sessions(age_check=0) # check result for session in sessions: session.refresh_from_db() assert requests_mock.call_count == 3 assert sessions[0].status == PaymentGatewaySessionStatus.FAILED assert sessions[1].status == PaymentGatewaySessionStatus.STARTED assert sessions[2].status == PaymentGatewaySessionStatus.FAILED
def test_refresh(self, requests_mock): """ Test that only ongoing sessions older than 60 minutes are refreshed against GOV.UK Pay. Note that the value '60 minutes' is a parameter initialised in the test and not part of the logic of the task. """ # mock call to GOV.UK Pay requests_mock.register_uri( 'GET', re.compile(govuk_url('payments/*')), json={'state': { 'status': 'failed' }}, ) # populate db data = ( # shouldn't be included because modified_on == 59 mins ago ('2017-04-18 19:01', PaymentGatewaySessionStatus.STARTED), # shouldn't be included because status != 'ongoing' ('2017-04-18 18:59', PaymentGatewaySessionStatus.SUCCESS), ('2017-04-18 18:59', PaymentGatewaySessionStatus.FAILED), ('2017-04-18 18:59', PaymentGatewaySessionStatus.CANCELLED), ('2017-04-18 18:59', PaymentGatewaySessionStatus.ERROR), # should be included because modified_on >= 60 mins ago ('2017-04-18 19:00', PaymentGatewaySessionStatus.CREATED), ('2017-04-18 18:59', PaymentGatewaySessionStatus.STARTED), ('2017-04-17 20:00', PaymentGatewaySessionStatus.SUBMITTED), ) sessions = [] for frozen_time, session_status in data: with freeze_time(frozen_time): sessions.append( PaymentGatewaySessionFactory(status=session_status)) # make call with freeze_time('2017-04-18 20:00'): # mocking now refresh_pending_payment_gateway_sessions(age_check=60) # check result assert requests_mock.call_count == 3 for session in sessions[-3:]: session.refresh_from_db() assert session.status == PaymentGatewaySessionStatus.FAILED
def test_403_if_scope_not_allowed(self, scope): """Test that other oauth2 scopes are not allowed.""" order = OrderWithAcceptedQuoteFactory() session = PaymentGatewaySessionFactory(order=order) url = reverse( 'api-v3:omis-public:payment-gateway-session:detail', kwargs={ 'public_token': order.public_token, 'pk': session.id }, ) client = self.create_api_client( scope=scope, grant_type=Application.GRANT_CLIENT_CREDENTIALS, ) response = client.get(url) assert response.status_code == status.HTTP_403_FORBIDDEN
def test_verbs_not_allowed(self, verb): """Test that makes sure the other verbs are not allowed.""" order = OrderWithAcceptedQuoteFactory() session = PaymentGatewaySessionFactory(order=order) url = reverse( 'api-v3:omis-public:payment-gateway-session:detail', kwargs={ 'public_token': order.public_token, 'pk': session.id }, ) client = self.create_api_client( scope=Scope.public_omis_front_end, grant_type=Application.GRANT_CLIENT_CREDENTIALS, ) response = getattr(client, verb)(url) assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED
def test_without_scope(self, hawk_api_client): """Test that making a request without the correct Hawk scope returns an error.""" order = OrderFactory() session = PaymentGatewaySessionFactory( order=order, status=PaymentGatewaySessionStatus.CREATED, ) hawk_api_client.set_credentials( 'test-id-without-scope', 'test-key-without-scope', ) url = reverse( 'api-v3:public-omis:payment-gateway-session:detail', kwargs={'public_token': order.public_token, 'pk': session.pk}, ) response = hawk_api_client.get(url) assert response.status_code == status.HTTP_403_FORBIDDEN
def test_404_if_in_disallowed_status(self, order_status): """Test that if the order is not in an allowed state, the endpoint returns 404.""" order = OrderFactory(status=order_status) session = PaymentGatewaySessionFactory(order=order) # make API call url = reverse( 'api-v3:omis-public:payment-gateway-session:detail', kwargs={ 'public_token': order.public_token, 'pk': session.id }, ) client = self.create_api_client( scope=Scope.public_omis_front_end, grant_type=Application.GRANT_CLIENT_CREDENTIALS, ) response = client.get(url) assert response.status_code == status.HTTP_404_NOT_FOUND
def test_exception_if_govuk_pay_errors_when_cancelling( self, govuk_status_code, requests_mock, ): """ Test that if GOV.UK Pay errors whilst cancelling some other ongoing sessions/payments, the method raises GOVUKPayAPIException to keep the system consistent. Possible GOV.UK Pay errors when cancelling: - 400 - BAD REQUEST - 401 - UNAUTHORIZED - 404 - NOT FOUND - 409 - CONFLICT - 500 - INTERNAL SERVER ERROR """ order = OrderWithAcceptedQuoteFactory() existing_session = PaymentGatewaySessionFactory( order=order, status=PaymentGatewaySessionStatus.CREATED, ) # mock GOV.UK requests used to # - refresh the existing payment gateway session # - cancel the GOV.UK payment requests_mock.get( govuk_url(f'payments/{existing_session.govuk_payment_id}'), status_code=200, json={ 'state': {'status': existing_session.status}, }, ) requests_mock.post( govuk_url(f'payments/{existing_session.govuk_payment_id}/cancel'), status_code=govuk_status_code, ) assert PaymentGatewaySession.objects.count() == 1 with pytest.raises(GOVUKPayAPIException): PaymentGatewaySession.objects.create_from_order(order) assert PaymentGatewaySession.objects.count() == 1
def test_404_if_session_belongs_to_another_order(self): """ Test that if the payment gateway session belongs to another order, the endpoint returns 404. """ orders = OrderWithAcceptedQuoteFactory.create_batch(2) sessions = PaymentGatewaySessionFactory.create_batch( 2, order=factory.Iterator(orders)) url = reverse( 'api-v3:omis-public:payment-gateway-session:detail', kwargs={ 'public_token': orders[0].public_token, 'pk': sessions[1].id, }, ) client = self.create_api_client( scope=Scope.public_omis_front_end, grant_type=Application.GRANT_CLIENT_CREDENTIALS, ) response = client.get(url) assert response.status_code == status.HTTP_404_NOT_FOUND
def test_with_govuk_pay_erroring_when_cancelling(self, requests_mock): """ Test that if GOV.UK Pay errors when cancelling the payment, the session object is not updated. """ session = PaymentGatewaySessionFactory() original_session_status = session.status requests_mock.post( govuk_url(f'payments/{session.govuk_payment_id}/cancel'), status_code=500, ) with pytest.raises(GOVUKPayAPIException): session.cancel() session.refresh_from_db() assert session.status == original_session_status assert requests_mock.call_count == 1
def test_cancel_updates_session(self, requests_mock): """ Test that if GOV.UK Pay cancels and acknowledges the change, the session object is updated. """ session = PaymentGatewaySessionFactory() requests_mock.post( govuk_url(f'payments/{session.govuk_payment_id}/cancel'), status_code=204, ) requests_mock.get( govuk_url(f'payments/{session.govuk_payment_id}'), status_code=200, json={'state': {'status': 'cancelled'}}, ) session.cancel() session.refresh_from_db() assert session.status == PaymentGatewaySessionStatus.CANCELLED assert requests_mock.call_count == 2
def test_create_cancels_other_ongoing_sessions(self, requests_mock): """ Test that creating a new payment gateway session cancels the other ongoing sessions and GOV.UK payments. Given: - ongoing session 1 - ongoing session 2 - failed session 3 Calling this endpoint should: - cancel GOV.UK payment related to session 1 - update the payment gateway session 1 status to 'cancelled' - cancel GOV.UK payment related to session 2 - update the payment gateway session 2 status to 'cancelled' - start a new GOV.UK payment - create a payment gateway session related to it """ order = OrderWithAcceptedQuoteFactory() existing_data = PaymentGatewaySessionFactory.create_batch( 3, order=order, status=factory.Iterator([ PaymentGatewaySessionStatus.created, PaymentGatewaySessionStatus.started, PaymentGatewaySessionStatus.failed, ]), ) # mock GOV.UK requests used to: # - refresh the payment gateway sessions # - cancel the GOV.UK payments # - refresh the payment gateway sessions again after the cancellation for session in existing_data: requests_mock.get( govuk_url(f'payments/{session.govuk_payment_id}'), [ # this is for the initial refresh { 'status_code': 200, 'json': { 'state': { 'status': session.status } }, }, # this is for the second refresh after cancelling { 'status_code': 200, 'json': { 'state': { 'status': 'cancelled' } }, }, ], ) requests_mock.post( govuk_url(f'payments/{session.govuk_payment_id}/cancel'), status_code=204, ) # mock GOV.UK call used to create a new payment session govuk_payment_id = '123abc123abc123abc123abc12' next_url = 'https://payment.example.com/123abc' json_response = { 'state': { 'status': 'created', 'finished': False }, 'payment_id': govuk_payment_id, '_links': { 'next_url': { 'href': next_url, 'method': 'GET', }, }, } requests_mock.post( govuk_url('payments'), # create payment status_code=201, json=json_response, ) requests_mock.get( govuk_url(f'payments/{govuk_payment_id}'), # get payment status_code=200, json=json_response, ) # make API call url = reverse( 'api-v3:omis-public:payment-gateway-session:collection', kwargs={'public_token': order.public_token}, ) client = self.create_api_client( scope=Scope.public_omis_front_end, grant_type=Application.GRANT_CLIENT_CREDENTIALS, ) response = client.post(url) assert response.status_code == status.HTTP_201_CREATED # check sessions cancelled for existing_session in existing_data[:-1]: existing_session.refresh_from_db() assert existing_session.status == PaymentGatewaySessionStatus.cancelled # check session record created assert PaymentGatewaySession.objects.ongoing().count() == 1 session = PaymentGatewaySession.objects.ongoing().first() assert session.govuk_payment_id == govuk_payment_id # check API response assert response.json() == { 'id': str(session.id), 'created_on': format_date_or_datetime(session.created_on), 'status': PaymentGatewaySessionStatus.created, 'payment_url': next_url, }
def test_409_if_refresh_updates_order_status_to_paid(self, requests_mock): """ Test that if the system is not up-to-date, the order is in quote_accepted but the GOV.UK payment happens, the endpoint triggers a check on existing sessions, realises that one finished successfully and records the payment marking the order as 'paid'. For this reason, the endpoint returns 409 - Conflict as no other payment can be made. """ # set up db order = OrderWithAcceptedQuoteFactory() existing_session = PaymentGatewaySessionFactory( order=order, status=PaymentGatewaySessionStatus.started, ) # mock GOV.UK requests used to refresh the payment session. # GOV.UK Pay says that the payment completed successfully requests_mock.get( govuk_url(f'payments/{existing_session.govuk_payment_id}'), status_code=200, json={ 'amount': order.total_cost, 'state': { 'status': 'success' }, 'email': '*****@*****.**', 'reference': '12345', 'created_date': '2018-02-13T14:56:56.734Z', 'card_details': { 'last_digits_card_number': '1111', 'cardholder_name': 'John Doe', 'expiry_date': '01/20', 'billing_address': { 'line1': 'line 1 address', 'line2': 'line 2 address', 'postcode': 'SW1A 1AA', 'city': 'London', 'country': 'GB', }, 'card_brand': 'Visa', }, }, ) # make API call url = reverse( 'api-v3:omis-public:payment-gateway-session:collection', kwargs={'public_token': order.public_token}, ) client = self.create_api_client( scope=Scope.public_omis_front_end, grant_type=Application.GRANT_CLIENT_CREDENTIALS, ) response = client.post(url) assert response.status_code == status.HTTP_409_CONFLICT # check session record existing_session.refresh_from_db() assert existing_session.status == PaymentGatewaySessionStatus.success # check order and pyament order.refresh_from_db() assert order.status == OrderStatus.paid assert Payment.objects.filter(order=order).count() == 1
def test_exception_if_refresh_updates_order_status_to_paid(self, requests_mock): """ Test that if the system is not up-to-date, the order is in quote_accepted but the GOV.UK payment happens, the method triggers a check on existing sessions, realises that one finished successfully and records the payment marking the order as 'paid'. For this reason, the method raises APIConflictException as no other payment can be started. """ # set up db order = OrderWithAcceptedQuoteFactory() existing_session = PaymentGatewaySessionFactory( order=order, status=PaymentGatewaySessionStatus.STARTED, ) # mock GOV.UK requests used to refresh the payment session, # GOV.UK Pay says that the payment completed successfully requests_mock.get( govuk_url(f'payments/{existing_session.govuk_payment_id}'), status_code=200, json={ 'amount': order.total_cost, 'state': {'status': 'success'}, 'email': '*****@*****.**', 'created_date': '2018-02-13T14:56:56.734Z', 'reference': '12345', 'card_details': { 'last_digits_card_number': '1111', 'cardholder_name': 'John Doe', 'expiry_date': '01/20', 'billing_address': { 'line1': 'line 1 address', 'line2': 'line 2 address', 'postcode': 'SW1A 1AA', 'city': 'London', 'country': 'GB', }, 'card_brand': 'Visa', }, }, ) with pytest.raises(APIConflictException): PaymentGatewaySession.objects.create_from_order(order) # check session record existing_session.refresh_from_db() assert existing_session.status == PaymentGatewaySessionStatus.SUCCESS # check order and payment order.refresh_from_db() assert order.status == OrderStatus.PAID assert Payment.objects.count() == 1 payment = Payment.objects.first() assert payment.amount == order.total_cost assert payment.method == PaymentMethod.CARD assert payment.received_on == dateutil_parse('2018-02-13').date() assert payment.transaction_reference == '12345' assert payment.cardholder_name == 'John Doe' assert payment.billing_address_1 == 'line 1 address' assert payment.billing_address_2 == 'line 2 address' assert payment.billing_address_town == 'London' assert payment.billing_address_postcode == 'SW1A 1AA' assert payment.billing_address_country == 'GB' assert payment.billing_email == '*****@*****.**' assert payment.card_brand == 'Visa'
def test_with_govuk_payment_success_updates_order(self, requests_mock): """ Test that if the GOV.UK payment status is `success` and the payment gateway session is out of date, the record is updated, the related order marked as `paid` and an OMIS `payment.Payment` record created from the GOV.UK response data one. """ order = OrderWithAcceptedQuoteFactory() session = PaymentGatewaySessionFactory( order=order, status=PaymentGatewaySessionStatus.created, ) # mock GOV.UK calls used to refresh the payment session # Pay says that the payment completed successfully requests_mock.get( govuk_url(f'payments/{session.govuk_payment_id}'), status_code=200, json={ 'amount': order.total_cost, 'state': { 'status': 'success' }, 'email': '*****@*****.**', 'reference': '12345', 'created_date': '2018-02-13T14:56:56.734Z', '_links': { 'next_url': None, }, 'card_details': { 'last_digits_card_number': '1111', 'cardholder_name': 'John Doe', 'expiry_date': '01/20', 'billing_address': { 'line1': 'line 1 address', 'line2': 'line 2 address', 'postcode': 'SW1A 1AA', 'city': 'London', 'country': 'GB', }, 'card_brand': 'Visa', }, }, ) # make API call url = reverse( 'api-v3:omis-public:payment-gateway-session:detail', kwargs={ 'public_token': order.public_token, 'pk': session.id }, ) client = self.create_api_client( scope=Scope.public_omis_front_end, grant_type=Application.GRANT_CLIENT_CREDENTIALS, ) response = client.get(url) assert response.status_code == status.HTTP_200_OK # check API response assert response.json() == { 'id': str(session.id), 'created_on': format_date_or_datetime(session.created_on), 'status': PaymentGatewaySessionStatus.success, 'payment_url': '', } # check session record session.refresh_from_db() assert session.status == PaymentGatewaySessionStatus.success # check order and payment order.refresh_from_db() assert order.status == OrderStatus.paid assert Payment.objects.filter(order=order).count() == 1
def test_with_govuk_payment_success_updates_order(self, requests_mock): """ Test that if the GOV.UK payment status is `success` and the payment gateway session is out of date, the record is updated, the related order marked as `paid` and an OMIS `payment.Payment` record created from the GOV.UK response data one. """ order = OrderWithAcceptedQuoteFactory() session = PaymentGatewaySessionFactory( status=PaymentGatewaySessionStatus.created, order=order, ) url = govuk_url(f'payments/{session.govuk_payment_id}') response_json = { 'amount': order.total_cost, 'state': { 'status': 'success' }, 'email': '*****@*****.**', 'created_date': '2018-02-13T14:56:56.734Z', 'reference': '12345', 'card_details': { 'last_digits_card_number': '1111', 'cardholder_name': 'John Doe', 'expiry_date': '01/20', 'billing_address': { 'line1': 'line 1 address', 'line2': 'line 2 address', 'postcode': 'SW1A 1AA', 'city': 'London', 'country': 'GB', }, 'card_brand': 'Visa', }, } requests_mock.get(url, status_code=200, json=response_json) assert session.refresh_from_govuk_payment() # check session session.refresh_from_db() assert session.status == PaymentGatewaySessionStatus.success # check order order.refresh_from_db() assert order.status == OrderStatus.paid # check payment object assert Payment.objects.filter(order=order).count() == 1 payment = Payment.objects.filter(order=order).first() assert payment.amount == response_json['amount'] assert payment.method == PaymentMethod.card assert payment.received_on == dateutil_parse('2018-02-13').date() assert payment.transaction_reference == '12345' assert payment.cardholder_name == 'John Doe' assert payment.card_brand == 'Visa' assert payment.billing_email == '*****@*****.**' assert payment.billing_address_1 == 'line 1 address' assert payment.billing_address_2 == 'line 2 address' assert payment.billing_address_town == 'London' assert payment.billing_address_postcode == 'SW1A 1AA' assert payment.billing_address_country == 'GB' assert requests_mock.call_count == 1