def test_handle_notify_request_payment_failed(provider_base_config, order): """Test request notify helper returns http 204 and order status is correct when payment fails""" order.order_number = "abc123" order.status = OrderStatus.PAID order.save() refund = OrderRefundFactory( order=order, refund_id="1234567", amount=order.total_price ) params = { "AUTHCODE": "8CF2D0EA9947D09B707E3C2953EF3014F1AD12D2BB0DCDBAC3ABD4601B50462B", "RETURN_CODE": "1", "REFUND_ID": "1234567", } rf = RequestFactory() request = rf.get("/payments/notify_refund/", params) payment_provider = create_bambora_provider(provider_base_config, request) assert refund.status == OrderRefundStatus.PENDING lease_status = refund.order.lease.status returned = payment_provider.handle_notify_refund_request() refund = OrderRefund.objects.get(refund_id=params.get("REFUND_ID")) order = refund.order assert refund.status == OrderRefundStatus.REJECTED # The order status shouldn't change assert order.status == OrderStatus.PAID assert order.lease.status == lease_status assert isinstance(returned, HttpResponse) assert returned.status_code == 204
def test_initiate_duplicated_payment_new_token_after_expiry( provider_base_config, order): request = RequestFactory().request() payment_provider = create_bambora_provider(provider_base_config, request) assert OrderToken.objects.count() == 0 with mock.patch( "payments.providers.bambora_payform.requests.post", side_effect=mocked_response_create, ) as mock_call: payment_provider.initiate_payment(order) assert OrderToken.objects.count() == 1 token = OrderToken.objects.first() assert token.order == order assert token.valid_until.isoformat( ) == "2019-01-20T21:59:59.999999+00:00" # Try to pay again 7 days after the day when the payment was created with freeze_time("2019-01-21T08:00:00Z"): url = payment_provider.initiate_payment(order) # Verify that the return URL passed includes the application language assert order.lease.application.language in mock_call.call_args.kwargs.get( "json").get("payment_method").get("return_url") assert OrderToken.objects.count() == 2 assert url.startswith(payment_provider.url_payment_api) assert "token/token123" in url
def test_handle_notify_request_payment_failed(provider_base_config, order, order_status, expected_order_status): """Test request notify helper returns http 204 and order status is correct when payment fails""" order.order_number = "abc123" order.status = order_status order.save() params = { "VENE_UI_RETURN_URL": "http%3A%2F%2F127.0.0.1%3A8000%2Fv1", "AUTHCODE": "ED754E8F2E7FE0CC269B9F6A1C197F19B8393F37A1B63BE1E889D53F87A5FCA1", "RETURN_CODE": "1", "ORDER_NUMBER": "abc123-1602145394.662132", "SETTLED": "1", } rf = RequestFactory() request = rf.get("/payments/notify/", params) payment_provider = create_bambora_provider(provider_base_config, request) returned = payment_provider.handle_notify_request() order_after = Order.objects.get( order_number=params.get("ORDER_NUMBER").split("-")[0]) assert order_after.status == expected_order_status assert isinstance(returned, HttpResponse) assert returned.status_code == 204
def test_handle_notify_request_success( provider_base_config, order: Order, ): """Test request notify helper returns http 204 and order status is correct when successful""" order.order_number = "abc123" order.status = OrderStatus.PAID order.lease.status = LeaseStatus.PAID order.lease.save() order.save() refund = OrderRefundFactory( order=order, refund_id="1234567", amount=order.total_price ) rf = RequestFactory() request = rf.get("/payments/notify_refund/", notify_success_params) payment_provider = create_bambora_provider(provider_base_config, request) assert refund.status == OrderRefundStatus.PENDING returned = payment_provider.handle_notify_refund_request() refund = OrderRefund.objects.get(refund_id=notify_success_params.get("REFUND_ID")) order = refund.order assert refund.status == OrderRefundStatus.ACCEPTED assert order.status == OrderStatus.REFUNDED assert order.lease.status == LeaseStatus.TERMINATED assert isinstance(returned, HttpResponse) assert returned.status_code == 204
def test_generate_sticker_number_for_ws_lease(provider_base_config, order: Order): create_ws_sticker_sequences() order.status = OrderStatus.OFFERED order.order_number = "abc123" order.save() application = order.lease.application application.area_type = ApplicationAreaType.UNMARKED application.save() rf = RequestFactory() request = rf.get("/payments/success/", success_params) payment_provider = create_bambora_provider(provider_base_config, request) returned = payment_provider.handle_success_request() order_after = Order.objects.get( order_number=success_params.get("ORDER_NUMBER").split("-")[0]) assert order_after.status == OrderStatus.PAID assert order_after.lease.application.status == ApplicationStatus.HANDLED assert order_after.lease.sticker_number == 1 assert isinstance(returned, HttpResponse) url = returned.url assert "/payment-result" in url assert "payment_status=success" in url
def test_get_payment_email_url(provider_base_config, order): request = RequestFactory().request() payment_provider = create_bambora_provider(provider_base_config, request) lang = "fi" url = payment_provider.get_payment_email_url(order, lang) ui_return_url = settings.VENE_UI_RETURN_URL assert ( url == f"{ui_return_url.format(LANG=lang)}/payment?order_number={order.order_number}" )
def test_initiate_refund_no_order_email(provider_base_config: dict, order: Order): """Test the request creator constructs the payload base and returns a url that contains a token""" request = RequestFactory().request() order.status = OrderStatus.PAID order.email = None order.lease.status = LeaseStatus.PAID order.lease.save() order.save() OrderToken.objects.create( order=order, token="12345", valid_until=now() - relativedelta(days=7) ) if hasattr(order.product, "price_for_tier"): place_price = order.product.price_for_tier(order.lease.berth.pier.price_tier) area = order.lease.berth.pier.harbor else: # Winter products are priced per m2 place_price = rounded( order.product.price_value * order.lease.place.place_type.width * order.lease.place.place_type.length, round_to_nearest=1, ) area = order.lease.place.winter_storage_section.area products = [ { "id": get_talpa_product_id(order.product.id, area, False), "product_id": 1123, "title": order.product.name, "count": 1, "pretax_price": price_as_fractional_int( convert_aftertax_to_pretax(place_price, order.product.tax_percentage) ), "tax": int(order.product.tax_percentage), "price": price_as_fractional_int(place_price), "type": 1, } ] payment_provider = create_bambora_provider(provider_base_config, request) with mock.patch( "payments.providers.bambora_payform.requests.post", side_effect=mocked_refund_response_create, ), mock.patch( "payments.providers.bambora_payform.BamboraPayformProvider.get_payment_details", side_effect=mocked_refund_payment_details(products=products), ): refund = payment_provider.initiate_refund(order) assert refund.refund_id == "123456"
def test_initiate_refund_refunded_amount_does_not_match( provider_base_config: dict, order: Order ): """Test the request creator constructs the payload base and returns a url that contains a token""" request = RequestFactory().request() order.status = OrderStatus.PAID order.lease.status = LeaseStatus.PAID order.lease.save() order.save() OrderToken.objects.create( order=order, token="98765", valid_until=now() - relativedelta(hours=1) ) OrderToken.objects.create( order=order, token="12345", valid_until=now() - relativedelta(days=7) ) place_price = order.total_price + 10 products = [ { "id": "123123123", "product_id": 1123, "title": order.product.name, "count": 1, "pretax_price": price_as_fractional_int( convert_aftertax_to_pretax(place_price, order.product.tax_percentage) ), "tax": int(order.product.tax_percentage), "price": price_as_fractional_int(place_price), "type": 1, } ] payment_provider = create_bambora_provider(provider_base_config, request) with mock.patch( "payments.providers.bambora_payform.requests.post", side_effect=mocked_refund_response_create, ), mock.patch( "payments.providers.bambora_payform.BamboraPayformProvider.get_payment_details", side_effect=mocked_refund_payment_details(products=products), ), pytest.raises( RefundPriceError ) as exception: payment_provider.initiate_refund(order) assert ( f"The amount to be refunded ({currency_format(place_price)}) " f"does not match the amount paid ({currency_format(order.total_price)})" ) in str(exception) assert not OrderRefund.objects.exists()
def test_initiate_payment_success(provider_base_config: dict, order: Order): """Test the request creator constructs the payload base and returns a url that contains a token""" request = RequestFactory().request() payment_provider = create_bambora_provider(provider_base_config, request) with mock.patch( "payments.providers.bambora_payform.requests.post", side_effect=mocked_response_create, ) as mock_call: url = payment_provider.initiate_payment(order) assert url.startswith(payment_provider.url_payment_api) assert "token/token123" in url # Verify that the return URL passed includes the application language assert order.lease.application.language in mock_call.call_args.kwargs.get( "json").get("payment_method").get("return_url")
def test_handle_notify_request_unknown_error(provider_base_config, order): """Test request notify helper returns http 204 when status code is unknown""" params = { "VENE_UI_RETURN_URL": "http%3A%2F%2F127.0.0.1%3A8000%2Fv1", "AUTHCODE": "3CD17A51E89C0A6DDDCA743AFCBD5DC40E8FF8AB97756089E75EC953A19A938C", "RETURN_CODE": "15", "ORDER_NUMBER": "abc123-1602145394.662132", "SETTLED": "1", } rf = RequestFactory() request = rf.get("/payments/notify/", params) payment_provider = create_bambora_provider(provider_base_config, request) returned = payment_provider.handle_notify_request() assert isinstance(returned, HttpResponse) assert returned.status_code == 204
def test_handle_notify_request_order_not_found(provider_base_config, order): """Test request notify helper returns http 204 when order can't be found""" params = { "VENE_UI_RETURN_URL": "http%3A%2F%2F127.0.0.1%3A8000%2Fv1", "AUTHCODE": "83F6C12E8D894B433CB2B6A2A56709CF0AE26665768ED74FB28922F6ED128301", "RETURN_CODE": "0", "ORDER_NUMBER": "abc567-1602145394.662132", "SETTLED": "1", } rf = RequestFactory() request = rf.get("/payments/notify/", params) payment_provider = create_bambora_provider(provider_base_config, request) returned = payment_provider.handle_notify_request() assert isinstance(returned, HttpResponse) assert returned.status_code == 204
def test_initiate_refund_error_unavailable(provider_base_config, order: Order): """Test the request creator raises service unavailable if request doesn't go through""" request = RequestFactory().request() invalid_config = { **provider_base_config, "VENE_PAYMENTS_BAMBORA_API_URL": FAKE_BAMBORA_API_URL, } unavailable_payment_provider = create_bambora_provider(invalid_config, request) with mock.patch( "payments.providers.bambora_payform.requests.post", side_effect=mocked_response_create, ): with pytest.raises(ServiceUnavailableError): unavailable_payment_provider.initiate_payment(order)
def test_initiate_refund_another_pending_refund( provider_base_config: dict, order: Order ): """Test the request creator constructs the payload base and returns a url that contains a token""" request = RequestFactory().request() order.status = OrderStatus.PAID order.lease.status = LeaseStatus.PAID order.lease.save() order.save() OrderRefundFactory(order=order, status=OrderRefundStatus.PENDING) OrderToken.objects.create( order=order, token="98765", valid_until=now() - relativedelta(hours=1) ) OrderToken.objects.create( order=order, token="12345", valid_until=now() - relativedelta(days=7) ) products = [ { "id": "123123123", "product_id": 1123, "title": order.product.name, "count": 1, "pretax_price": 100, "tax": 24, "price": 100, "type": 1, } ] assert OrderRefund.objects.count() == 1 payment_provider = create_bambora_provider(provider_base_config, request) with mock.patch( "payments.providers.bambora_payform.requests.post", side_effect=mocked_refund_response_create, ), mock.patch( "payments.providers.bambora_payform.BamboraPayformProvider.get_payment_details", side_effect=mocked_refund_payment_details(products=products), ), pytest.raises( ValidationError ) as exception: payment_provider.initiate_refund(order) assert "Cannot refund an order that has another pending refund" in str(exception) assert OrderRefund.objects.count() == 1
def test_handle_success_request_maintenance_break(provider_base_config, order): """Test request helper reacts to maintenance break error by returning a failure url""" params = { "VENE_UI_RETURN_URL": "http%3A%2F%2F127.0.0.1%3A8000%2Fv1", "AUTHCODE": "144662CEBD9861D4526C4147D10FB6C50FE1E02453701F8972B699CFD1F4A99E", "RETURN_CODE": "10", "ORDER_NUMBER": "abc123-1602145394.662132", "SETTLED": "1", } rf = RequestFactory() request = rf.get("/payments/success/", params) payment_provider = create_bambora_provider(provider_base_config, request) returned = payment_provider.handle_success_request() # TODO Handling isn't final yet so there might be something extra that needs to be tested here assert isinstance(returned, HttpResponse) assert "payment_status=failure" in returned.url
def test_handle_success_request_return_url_missing(provider_base_config, order): """Test the handler returns a bad request object if return URL is missing from params""" params = { "AUTHCODE": "DD789BA71ACD627892517745AF4C4CE2068F006C602CD54264E1FC5E4C2EE6CF", "RETURN_CODE": "0", "ORDER_NUMBER": "abc123-1602145394.662132", "SETTLED": "0", } rf = RequestFactory() request = rf.get("/payments/success/", params) payment_provider = create_bambora_provider(provider_base_config, request) returned = payment_provider.handle_success_request() assert isinstance(returned, HttpResponseServerError) assert returned.status_code == 500
def test_handle_success_request_status_not_updated(provider_base_config, order): """Test request helper reacts to transaction status update error by returning a failure url""" params = { "VENE_UI_RETURN_URL": "http%3A%2F%2F127.0.0.1%3A8000%2Fv1", "AUTHCODE": "D9170B2C0C0F36E467517E0DF2FC7D89BBC7237597B6BE3B0DE11518F93D7342", "RETURN_CODE": "4", "ORDER_NUMBER": "abc123-1602145394.662132", "SETTLED": "1", } rf = RequestFactory() request = rf.get("/payments/success/", params) payment_provider = create_bambora_provider(provider_base_config, request) returned = payment_provider.handle_success_request() # TODO Handling isn't final yet so there might be something extra that needs to be tested here assert isinstance(returned, HttpResponse) assert "payment_status=failure" in returned.url
def test_handle_notify_request_success( provider_base_config, berth_order: Order, order_status: OrderStatus, expected_order_status: OrderStatus, ): """Test request notify helper returns http 204 and order status is correct when successful""" berth_order.order_number = "abc123" berth_order.status = order_status berth_order.save() rf = RequestFactory() request = rf.get("/payments/notify/", success_params) payment_provider = create_bambora_provider(provider_base_config, request) returned = payment_provider.handle_notify_request() order_after = Order.objects.get( order_number=success_params.get("ORDER_NUMBER").split("-")[0]) assert order_after.status == expected_order_status assert isinstance(returned, HttpResponse) assert returned.status_code == 204
def test_initiate_refund_invalid_order_status( provider_base_config: dict, order: Order, order_status ): """Test the request creator constructs the payload base and returns a url that contains a token""" request = RequestFactory().request() order.status = order_status order.save() OrderToken.objects.create( order=order, token="98765", valid_until=now() - relativedelta(hours=1) ) OrderToken.objects.create( order=order, token="12345", valid_until=now() - relativedelta(days=7) ) payment_provider = create_bambora_provider(provider_base_config, request) with pytest.raises(ValidationError) as exception: payment_provider.initiate_refund(order) assert "Cannot refund an order that is not paid" in str(exception)
def test_initiate_refund_no_order_email_or_application( provider_base_config: dict, order: Order ): """Test the request creator constructs the payload base and returns a url that contains a token""" request = RequestFactory().request() order.status = OrderStatus.PAID order.customer_email = None order.lease.status = LeaseStatus.PAID order.lease.application = None order.lease.save() order.save() OrderToken.objects.create( order=order, token="12345", valid_until=now() - relativedelta(days=7) ) payment_provider = create_bambora_provider(provider_base_config, request) with pytest.raises(ValidationError) as exception: payment_provider.initiate_refund(order) assert "Cannot refund an order that has no email" in str(exception)
def test_handle_initiate_refund_error_validation(order, provider_base_config): """Test the response handler raises PayloadValidationError as expected""" r = {"result": 1, "type": "e-payment", "errors": ["Invalid auth code"]} OrderToken.objects.create( order=order, token="12345", valid_until=now() - relativedelta(days=7) ) order.status = OrderStatus.PAID order.customer_email = "*****@*****.**" order.lease.status = LeaseStatus.PAID order.lease.save() order.save() request = RequestFactory().request() payment_provider = create_bambora_provider(provider_base_config, request) with mock.patch( "payments.providers.bambora_payform.requests.post", side_effect=[MockResponse(data=r)], ): with pytest.raises(PayloadValidationError): payment_provider.initiate_refund(order)
def test_initiate_duplicated_payment(provider_base_config, order): request = RequestFactory().request() payment_provider = create_bambora_provider(provider_base_config, request) assert OrderToken.objects.count() == 0 with mock.patch( "payments.providers.bambora_payform.requests.post", side_effect=mocked_response_create, ) as mock_call: payment_provider.initiate_payment(order) assert OrderToken.objects.count() == 1 url = payment_provider.initiate_payment(order) # Verify that the return URL passed includes the application language assert order.lease.application.language in mock_call.call_args.kwargs.get( "json").get("payment_method").get("return_url") assert OrderToken.objects.count() == 1 assert OrderToken.objects.first().order == order assert url.startswith(payment_provider.url_payment_api) assert "token/token123" in url
def test_handle_notify_request_success_for_ap_order( provider_base_config, berth_order: Order, ): berth_order.order_number = "abc123" berth_order.status = OrderStatus.OFFERED berth_order.order_type = OrderType.ADDITIONAL_PRODUCT_ORDER berth_order.save() rf = RequestFactory() request = rf.get("/payments/notify/", success_params) payment_provider = create_bambora_provider(provider_base_config, request) returned = payment_provider.handle_notify_request() order_after = Order.objects.get( order_number=success_params.get("ORDER_NUMBER").split("-")[0]) assert order_after.status == OrderStatus.PAID # it should not change the application and lease status in case of additional product order assert order_after.lease.application.status == ApplicationStatus.PENDING assert order_after.lease.status == LeaseStatus.DRAFTED assert isinstance(returned, HttpResponse) assert returned.status_code == 204
def test_handle_success_request_success(provider_base_config, order: Order): """Test request helper changes the order status to PAID Also check it returns a success url with order number""" order.status = OrderStatus.OFFERED order.order_number = "abc123" order.save() rf = RequestFactory() request = rf.get("/payments/success/", success_params) payment_provider = create_bambora_provider(provider_base_config, request) returned = payment_provider.handle_success_request() order_after = Order.objects.get( order_number=success_params.get("ORDER_NUMBER").split("-")[0]) assert order_after.status == OrderStatus.PAID assert order_after.lease.application.status == ApplicationStatus.HANDLED assert isinstance(returned, HttpResponse) url = returned.url assert "/payment-result" in url assert "payment_status=success" in url
def test_handle_success_request_payment_failed(provider_base_config, order): """Test request helper changes the order status to rejected and returns a failure url""" order.status = OrderStatus.OFFERED order.order_number = "abc123-1602145394.662132" order.save() params = { "VENE_UI_RETURN_URL": "http%3A%2F%2F127.0.0.1%3A8000%2Fv1", "AUTHCODE": "ED754E8F2E7FE0CC269B9F6A1C197F19B8393F37A1B63BE1E889D53F87A5FCA1", "RETURN_CODE": "1", "ORDER_NUMBER": "abc123-1602145394.662132", "SETTLED": "1", } rf = RequestFactory() request = rf.get("/payments/success/", params) payment_provider = create_bambora_provider(provider_base_config, request) returned = payment_provider.handle_success_request() order_after = Order.objects.get(order_number=params.get("ORDER_NUMBER")) assert order_after.status == OrderStatus.OFFERED assert isinstance(returned, HttpResponse) assert "payment_status=failure" in returned.url
def test_duplicate_payments_tokens_cancelled_notify_payment( provider_base_config, order: Order): # Fake the Payment initiate flow order.status = OrderStatus.OFFERED order.order_number = "abc123" order.save() request = RequestFactory().request() payment_provider = create_bambora_provider(provider_base_config, request) assert OrderToken.objects.count() == 0 with mock.patch( "payments.providers.bambora_payform.requests.post", side_effect=mocked_response_create, ) as mock_call: payment_provider.initiate_payment(order) assert OrderToken.objects.count() == 1 token = OrderToken.objects.first() assert token.order == order assert token.valid_until.isoformat( ) == "2019-01-20T21:59:59.999999+00:00" # Try to pay again 7 days after the day when the payment was created with freeze_time("2019-01-21T08:00:00Z"): url = payment_provider.initiate_payment(order) # Verify that the return URL passed includes the application language assert order.lease.application.language in mock_call.call_args.kwargs.get( "json").get("payment_method").get("return_url") assert OrderToken.objects.count() == 2 assert url.startswith(payment_provider.url_payment_api) assert "token/token123" in url # Fake the part where Bambora notifies the success/failure params = { "VENE_UI_RETURN_URL": "http%3A%2F%2F127.0.0.1%3A8000%2Fv1", "AUTHCODE": "DD789BA71ACD627892517745AF4C4CE2068F006C602CD54264E1FC5E4C2EE6CF", "RETURN_CODE": "1", "ORDER_NUMBER": "abc123-1602145394.662132", } rf = RequestFactory() request = rf.get("/payments/success/", params) payment_provider = create_bambora_provider(provider_base_config, request) payment_provider.handle_notify_request() order_after = Order.objects.get( order_number=params.get("ORDER_NUMBER").split("-")[0]) assert all([token.cancelled for token in order_after.tokens.all()]) with mock.patch( "payments.providers.bambora_payform.requests.post", side_effect=mocked_response_create, ): with freeze_time("2019-01-31T08:00:00Z"): payment_provider.initiate_payment(order) # The last token shouldn't be cancelled assert OrderToken.objects.count() == 3 assert OrderToken.objects.filter(cancelled=True).count() == 2 assert OrderToken.objects.filter(cancelled=False).count() == 1