Compare commits

...

1 Commits

Author SHA1 Message Date
9qeklajc
dc0e56e450 add new status code for refund not ready 2026-07-07 15:57:33 +02:00
2 changed files with 67 additions and 5 deletions

View File

@@ -259,11 +259,26 @@ async def refund_wallet_endpoint(
)
)
in_tx = in_tx_result.first()
cashu_fingerprint = hashlib.sha256(x_cashu.encode()).hexdigest()[:16]
if in_tx is None:
logger.info(
"refund_wallet_endpoint: x-cashu inbound transaction not found",
extra={"cashu_fingerprint": cashu_fingerprint},
)
raise HTTPException(status_code=404, detail="Refund not found")
# Use the request_id to find the associated "out" (refund) transaction
if in_tx.request_id is None:
logger.info(
"refund_wallet_endpoint: x-cashu inbound transaction has no request_id",
extra={
"cashu_fingerprint": cashu_fingerprint,
"cashu_transaction_id": in_tx.id,
"amount": in_tx.amount,
"unit": in_tx.unit,
"mint_url": in_tx.mint_url,
},
)
raise HTTPException(status_code=404, detail="Refund not found")
out_tx_result = await session.exec(
@@ -274,8 +289,33 @@ async def refund_wallet_endpoint(
)
out_tx = out_tx_result.first()
if out_tx is None:
raise HTTPException(status_code=404, detail="Refund not found")
logger.info(
"refund_wallet_endpoint: x-cashu refund transaction not ready",
extra={
"cashu_fingerprint": cashu_fingerprint,
"cashu_transaction_id": in_tx.id,
"refund_request_id": in_tx.request_id,
"amount": in_tx.amount,
"unit": in_tx.unit,
"mint_url": in_tx.mint_url,
"age_seconds": max(0, int(time.time()) - in_tx.created_at),
},
)
raise HTTPException(
status_code=425,
detail="Refund not ready. Retry later.",
headers={"Retry-After": "5"},
)
if out_tx.swept:
logger.info(
"refund_wallet_endpoint: x-cashu refund transaction already swept",
extra={
"cashu_fingerprint": cashu_fingerprint,
"cashu_transaction_id": in_tx.id,
"refund_transaction_id": out_tx.id,
"refund_request_id": in_tx.request_id,
},
)
raise HTTPException(status_code=410, detail="Refund has been swept")
out_tx.collected = True

View File

@@ -2,6 +2,7 @@ import json
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from fastapi import HTTPException
from fastapi.responses import JSONResponse
from routstr.balance import refund_wallet_endpoint
@@ -88,8 +89,6 @@ async def test_refund_x_cashu_sat_unit() -> None:
@pytest.mark.asyncio
async def test_refund_x_cashu_not_found_raises_404() -> None:
from fastapi import HTTPException
session = MagicMock()
session.exec = AsyncMock(return_value=_exec_result(None))
@@ -104,9 +103,32 @@ async def test_refund_x_cashu_not_found_raises_404() -> None:
@pytest.mark.asyncio
async def test_refund_x_cashu_swept_raises_410() -> None:
from fastapi import HTTPException
async def test_refund_x_cashu_pending_out_tx_raises_425() -> None:
in_tx = _make_cashu_tx(
token="cashuApending_token",
amount=0,
unit="msat",
type="in",
request_id="req-pending",
)
session = MagicMock()
session.exec = AsyncMock(side_effect=[_exec_result(in_tx), _exec_result(None)])
with pytest.raises(HTTPException) as exc_info:
await refund_wallet_endpoint(
authorization="Bearer sk-somekey",
x_cashu="cashuApending_token",
session=session,
)
assert exc_info.value.status_code == 425
assert exc_info.value.detail == "Refund not ready. Retry later."
assert exc_info.value.headers == {"Retry-After": "5"}
@pytest.mark.asyncio
async def test_refund_x_cashu_swept_raises_410() -> None:
in_tx = _make_cashu_tx(token="cashuAswept_token", amount=0, unit="msat", type="in", request_id="req-swept")
out_tx = _make_cashu_tx(token="cashuAswept", amount=100, unit="msat", type="out", request_id="req-swept", swept=True)