mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-08-09 02:54:37 +00:00
Merge pull request #644 from Routstr/fix/api-key-refund-token-retrieval-main
fix: return persisted API-key refund token
This commit is contained in:
@@ -259,6 +259,36 @@ async def _lookup_key_no_create(
|
||||
return None
|
||||
|
||||
|
||||
async def _get_persisted_api_key_refund(
|
||||
key: ApiKey, session: AsyncSession
|
||||
) -> dict[str, str] | None:
|
||||
result = await session.exec(
|
||||
select(CashuTransaction)
|
||||
.where(
|
||||
CashuTransaction.api_key_hashed_key == key.hashed_key,
|
||||
CashuTransaction.type == "out",
|
||||
CashuTransaction.source == "apikey",
|
||||
)
|
||||
.order_by(col(CashuTransaction.created_at).desc())
|
||||
)
|
||||
refund = result.first()
|
||||
if refund is None:
|
||||
return None
|
||||
if refund.swept:
|
||||
raise HTTPException(status_code=410, detail="Refund has been swept")
|
||||
|
||||
refund.collected = True
|
||||
session.add(refund)
|
||||
await session.commit()
|
||||
|
||||
persisted = {"token": refund.token}
|
||||
if refund.unit == "sat":
|
||||
persisted["sats"] = str(refund.amount)
|
||||
else:
|
||||
persisted["msats"] = str(refund.amount)
|
||||
return persisted
|
||||
|
||||
|
||||
async def _restore_balance(
|
||||
session: AsyncSession, hashed_key: str, balance: int, reserved_balance: int, mint_url: str
|
||||
) -> None:
|
||||
@@ -353,6 +383,8 @@ async def refund_wallet_endpoint(
|
||||
if key.total_balance <= 0:
|
||||
if cached := await _refund_cache_get(bearer_value):
|
||||
return cached
|
||||
if persisted := await _get_persisted_api_key_refund(key, session):
|
||||
return persisted
|
||||
|
||||
if key.parent_key_hash:
|
||||
raise HTTPException(
|
||||
|
||||
@@ -221,6 +221,78 @@ def _make_api_key(
|
||||
return key
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_apikey_refund_returns_persisted_token_after_cache_loss() -> None:
|
||||
key = _make_api_key(balance=0, refund_currency="sat")
|
||||
refund_token = "cashuApersisted_refund_token"
|
||||
refund_tx = _make_cashu_tx(
|
||||
token=refund_token,
|
||||
amount=5,
|
||||
unit="sat",
|
||||
type="out",
|
||||
request_id=None,
|
||||
)
|
||||
refund_tx.source = "apikey"
|
||||
refund_tx.api_key_hashed_key = key.hashed_key
|
||||
|
||||
session = MagicMock()
|
||||
session.get = AsyncMock(return_value=key)
|
||||
session.exec = AsyncMock(return_value=_exec_result(refund_tx))
|
||||
session.add = MagicMock()
|
||||
session.commit = AsyncMock()
|
||||
|
||||
with (
|
||||
patch("routstr.balance._refund_cache_get", AsyncMock(return_value=None)),
|
||||
patch("routstr.balance.send_token", AsyncMock()) as mock_send_token,
|
||||
):
|
||||
result = await refund_wallet_endpoint(
|
||||
authorization="Bearer sk-testhash",
|
||||
x_cashu=None,
|
||||
session=session,
|
||||
)
|
||||
|
||||
assert result == {"token": refund_token, "sats": "5"}
|
||||
assert refund_tx.collected is True
|
||||
session.add.assert_called_once_with(refund_tx)
|
||||
session.commit.assert_awaited_once()
|
||||
mock_send_token.assert_not_awaited()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_apikey_refund_rejects_persisted_token_after_sweep() -> None:
|
||||
from fastapi import HTTPException
|
||||
|
||||
key = _make_api_key(balance=0, refund_currency="sat")
|
||||
refund_tx = _make_cashu_tx(
|
||||
token="cashuAswept_apikey_refund",
|
||||
amount=5,
|
||||
unit="sat",
|
||||
request_id=None,
|
||||
swept=True,
|
||||
)
|
||||
refund_tx.source = "apikey"
|
||||
refund_tx.api_key_hashed_key = key.hashed_key
|
||||
|
||||
session = MagicMock()
|
||||
session.get = AsyncMock(return_value=key)
|
||||
session.exec = AsyncMock(return_value=_exec_result(refund_tx))
|
||||
session.add = MagicMock()
|
||||
session.commit = AsyncMock()
|
||||
|
||||
with patch("routstr.balance._refund_cache_get", AsyncMock(return_value=None)):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await refund_wallet_endpoint(
|
||||
authorization="Bearer sk-testhash",
|
||||
x_cashu=None,
|
||||
session=session,
|
||||
)
|
||||
|
||||
assert exc_info.value.status_code == 410
|
||||
assert exc_info.value.detail == "Refund has been swept"
|
||||
session.add.assert_not_called()
|
||||
session.commit.assert_not_awaited()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_apikey_refund_stores_cashu_transaction_with_apikey_source() -> None:
|
||||
key = _make_api_key(balance=5000, refund_currency="sat")
|
||||
|
||||
Reference in New Issue
Block a user