diff --git a/lnbits/core/views/callback_api.py b/lnbits/core/views/callback_api.py index f6c185d79..a7abc3a1e 100644 --- a/lnbits/core/views/callback_api.py +++ b/lnbits/core/views/callback_api.py @@ -357,16 +357,20 @@ async def handle_revolut_event(event: dict): return payment = await get_standalone_payment(f"fiat_revolut_order_{order_id}") - if not payment: + if payment: + await check_fiat_status(payment) + return + + if event_type == "ORDER_COMPLETED": logger.warning(f"No payment found for Revolut order: '{order_id}'.") await _handle_revolut_subscription_order_paid(order_id) return - await check_fiat_status(payment) + logger.info(f"Ignoring Revolut authorised order without payment: '{order_id}'.") return if event_type == "SUBSCRIPTION_INITIATED": - await _handle_revolut_subscription_initiated(event) + logger.info("Revolut subscription initiated event received.") return if event_type in [ @@ -406,7 +410,10 @@ async def _get_revolut_provider() -> RevolutWallet | None: async def _handle_revolut_subscription( - subscription: dict, fiat_provider: RevolutWallet + subscription: dict, + fiat_provider: RevolutWallet, + order_id: str | None = None, + order: dict | None = None, ): subscription_id = subscription.get("id") if not subscription_id: @@ -420,16 +427,17 @@ async def _handle_revolut_subscription( logger.warning("Revolut subscription event missing LNbits metadata.") return - cycle_id = subscription.get("current_cycle_id") - if not cycle_id: - logger.warning("Revolut subscription missing current_cycle_id.") - return - - cycle = await fiat_provider.get_subscription_cycle(subscription_id, cycle_id) - order_id = cycle.get("order_id") if not order_id: - logger.warning("Revolut subscription cycle missing order_id.") - return + cycle_id = subscription.get("current_cycle_id") + if not cycle_id: + logger.warning("Revolut subscription missing current_cycle_id.") + return + + cycle = await fiat_provider.get_subscription_cycle(subscription_id, cycle_id) + order_id = cycle.get("order_id") + if not order_id: + logger.warning("Revolut subscription cycle missing order_id.") + return existing_payment = await get_standalone_payment(f"fiat_revolut_order_{order_id}") if existing_payment: @@ -439,7 +447,8 @@ async def _handle_revolut_subscription( await check_fiat_status(existing_payment) return - order = await fiat_provider.get_order(order_id) + if not order: + order = await fiat_provider.get_order(order_id) amount_minor = order.get("amount") currency = (order.get("currency") or "").upper() if amount_minor is None or not currency: @@ -475,7 +484,9 @@ async def _handle_revolut_subscription_order_paid(order_id: str): return order = await fiat_provider.get_order(order_id) - if order.get("type") != "payment" or order.get("state") != "completed": + order_type = (order.get("type") or "").lower() + order_state = (order.get("state") or "").upper() + if order_type != "payment" or order_state != "COMPLETED": logger.warning(f"Revolut order is not a completed payment: '{order_id}'.") return @@ -490,7 +501,9 @@ async def _handle_revolut_subscription_order_paid(order_id: str): logger.warning(f"Revolut subscription is not active: '{subscription_id}'.") return - await _handle_revolut_subscription_initiated(subscription) + await _handle_revolut_subscription( + subscription, fiat_provider, order_id=order_id, order=order + ) async def _create_revolut_subscription_payment( diff --git a/tests/api/test_callback_api.py b/tests/api/test_callback_api.py index d7731912e..a910cd729 100644 --- a/tests/api/test_callback_api.py +++ b/tests/api/test_callback_api.py @@ -194,7 +194,7 @@ async def test_callback_api_handles_revolut_subscription_event( settings.revolut_api_secret_key = "revolut-secret" settings.revolut_api_version = "2026-04-20" revolut_provider = RevolutWallet() - mocker.patch.object( + get_subscription_mock = mocker.patch.object( revolut_provider, "get_subscription", return_value={ @@ -253,23 +253,10 @@ async def test_callback_api_handles_revolut_subscription_event( } ) - assert create_wallet_invoice_mock.await_count == 1 - called_wallet_id, invoice = create_wallet_invoice_mock.await_args.args - assert called_wallet_id == "wallet_1" - assert invoice.amount == 9.25 - assert invoice.memo == "Revolut Members" - assert invoice.external_id == "SUBSCRIPTION_1" - assert invoice.internal is True - assert invoice.extra["fiat_method"] == "subscription" - assert invoice.extra["subscription"]["checking_id"] == "order_ORDER_SUB_1" - assert payment.fiat_provider == "revolut" - assert payment.fee == -2 - assert payment.extra["fiat_checking_id"] == "order_ORDER_SUB_1" - assert payment.checking_id == "fiat_revolut_order_ORDER_SUB_1" - update_payment_mock.assert_awaited_once_with( - payment, "fiat_revolut_order_ORDER_SUB_1" - ) - fiat_status_mock.assert_awaited_once_with(payment) + get_subscription_mock.assert_not_awaited() + create_wallet_invoice_mock.assert_not_awaited() + update_payment_mock.assert_not_awaited() + fiat_status_mock.assert_not_awaited() @pytest.mark.anyio @@ -353,10 +340,9 @@ async def test_callback_api_handles_revolut_subscription_order_event( assert get_payment_mock.await_count == 2 get_payment_mock.assert_any_await("fiat_revolut_order_ORDER_SUB_1") - assert get_order_mock.await_count == 2 + assert get_order_mock.await_count == 1 assert [call.args for call in get_subscription_mock.await_args_list] == [ ("SUBSCRIPTION_1",), - ("SUBSCRIPTION_1",), ] assert create_wallet_invoice_mock.await_count == 1 called_wallet_id, invoice = create_wallet_invoice_mock.await_args.args