diff --git a/lnbits/core/views/callback_api.py b/lnbits/core/views/callback_api.py index 9107bf0a4..80b7ed4c4 100644 --- a/lnbits/core/views/callback_api.py +++ b/lnbits/core/views/callback_api.py @@ -439,7 +439,7 @@ async def _handle_revolut_subscription( extra = { **(reference.extra or {}), - "subscription_request_id": reference.subscription_request_id, + "subscription_request_id": subscription_id, "fiat_method": "subscription", "tag": reference.tag, "subscription": { diff --git a/lnbits/fiat/revolut.py b/lnbits/fiat/revolut.py index f8d54b6bd..fd3454f07 100644 --- a/lnbits/fiat/revolut.py +++ b/lnbits/fiat/revolut.py @@ -283,7 +283,7 @@ class RevolutWallet(FiatProvider): return FiatSubscriptionResponse( ok=True, checkout_session_url=checkout_url, - subscription_request_id=payment_options.subscription_request_id, + subscription_request_id=revolut_subscription_id, ) except json.JSONDecodeError: return FiatSubscriptionResponse( @@ -302,6 +302,15 @@ class RevolutWallet(FiatProvider): **kwargs, ) -> FiatSubscriptionResponse: try: + subscription = await self.get_subscription(subscription_id) + reference = self.deserialize_subscription_reference( + subscription.get("external_reference") + ) + if not reference or reference.wallet_id != correlation_id: + return FiatSubscriptionResponse( + ok=False, error_message="Subscription not found." + ) + r = await self.client.post( f"/api/subscriptions/{subscription_id}/cancel", timeout=REVOLUT_REQUEST_TIMEOUT, diff --git a/tests/unit/test_fiat_providers.py b/tests/unit/test_fiat_providers.py index babfe254a..13338ce01 100644 --- a/tests/unit/test_fiat_providers.py +++ b/tests/unit/test_fiat_providers.py @@ -919,8 +919,10 @@ async def test_revolut_wallet_create_subscription(settings: Settings): "PLAN_VARIATION_123", 1, payment_options ) + subscription_request_id = payment_options.subscription_request_id assert response.ok is True - assert response.subscription_request_id is not None + assert response.subscription_request_id == "SUBSCRIPTION123" + assert subscription_request_id is not None assert ( response.checkout_session_url == "https://checkout.revolut.com/payment-link/sub_123" @@ -933,16 +935,14 @@ async def test_revolut_wallet_create_subscription(settings: Settings): assert payload["plan_variation_id"] == "PLAN_VARIATION_123" assert payload["customer_id"] == "CUSTOMER123" assert client.calls[1][1]["timeout"] == 30 - assert client.calls[1][1]["headers"]["Idempotency-Key"] == ( - response.subscription_request_id - ) + assert client.calls[1][1]["headers"]["Idempotency-Key"] == (subscription_request_id) assert payload["setup_order_redirect_url"] == ( "https://lnbits.example/subscription-success" ) reference = json.loads(payload["external_reference"]) assert reference["wallet_id"] == "wallet_1" assert reference["tag"] == "gold" - assert reference["subscription_request_id"] == response.subscription_request_id + assert reference["subscription_request_id"] == subscription_request_id assert reference["memo"] == "Monthly Gold" assert reference["extra"]["link"] == "link-1" assert client.calls[2][0] == "/api/orders/ORDER123" @@ -1235,13 +1235,55 @@ async def test_revolut_wallet_cancel_subscription(settings: Settings): settings.revolut_api_version = "2026-04-20" wallet = RevolutWallet() - client = MockHTTPClient([MockHTTPResponse(json_data={})]) + client = MockHTTPClient( + [ + MockHTTPResponse( + json_data={ + "external_reference": json.dumps({"wallet_id": "wallet_1"}), + } + ), + MockHTTPResponse(json_data={}), + ] + ) wallet.client = client # type: ignore[assignment] response = await wallet.cancel_subscription("SUBSCRIPTION123", "wallet_1") assert response.ok is True - assert client.calls[0][0] == "/api/subscriptions/SUBSCRIPTION123/cancel" + assert client.calls[0][0] == "/api/subscriptions/SUBSCRIPTION123" + assert client.calls[1][0] == "/api/subscriptions/SUBSCRIPTION123/cancel" + + +@pytest.mark.anyio +async def test_revolut_wallet_cancel_subscription_checks_wallet_id( + settings: Settings, +): + settings.revolut_api_endpoint = "https://sandbox-merchant.revolut.com" + settings.revolut_api_secret_key = "revolut-secret" + settings.revolut_api_version = "2026-04-20" + + wallet = RevolutWallet() + client = MockHTTPClient( + [ + MockHTTPResponse( + json_data={ + "external_reference": json.dumps({"wallet_id": "wallet_2"}), + } + ), + ] + ) + wallet.client = client # type: ignore[assignment] + + response = await wallet.cancel_subscription("SUBSCRIPTION123", "wallet_1") + + assert response.ok is False + assert response.error_message == "Subscription not found." + assert client.calls == [ + ( + "/api/subscriptions/SUBSCRIPTION123", + {"timeout": 30}, + ) + ] @pytest.mark.anyio