mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-10-05 12:28:22 +00:00
refactor(balance): remove unreachable cashu-refund-by-hash endpoint
This commit is contained in:
@@ -155,7 +155,7 @@ The response includes your change in the same header:
|
|||||||
X-Cashu: cashuA7k2mNp4...
|
X-Cashu: cashuA7k2mNp4...
|
||||||
```
|
```
|
||||||
|
|
||||||
This is fully stateless—no session, no `/v1/balance/refund` call needed. However, **streaming does not work with `X-Cashu`** because the refund can only be calculated after the full response is generated.
|
This is fully stateless—no session, no `/v1/balance/refund` call needed. However, **streaming does not work with `X-Cashu`** because the refund can only be calculated after the full response is generated. If you lose the `X-Cashu` response header before claiming your change, you can reclaim the refund via `POST /v1/wallet/refund` by supplying the original payment token in the `x-cashu` header.
|
||||||
|
|
||||||
## Response Headers
|
## Response Headers
|
||||||
|
|
||||||
|
|||||||
@@ -609,26 +609,6 @@ async def reset_child_key_spent(
|
|||||||
return {"success": True, "message": "Child key balance reset successfully."}
|
return {"success": True, "message": "Child key balance reset successfully."}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/cashu-refund/{payment_token_hash}")
|
|
||||||
async def get_cashu_refund(
|
|
||||||
payment_token_hash: str,
|
|
||||||
session: AsyncSession = Depends(get_session),
|
|
||||||
) -> dict:
|
|
||||||
"""Retrieve a stored Cashu refund token by the hash of the original payment token."""
|
|
||||||
result = await session.get(CashuTransaction, payment_token_hash)
|
|
||||||
if result is None:
|
|
||||||
raise HTTPException(status_code=404, detail="Refund not found")
|
|
||||||
if result.swept:
|
|
||||||
raise HTTPException(status_code=410, detail="Refund has been swept")
|
|
||||||
result.collected = True
|
|
||||||
session.add(result)
|
|
||||||
await session.commit()
|
|
||||||
return {
|
|
||||||
"refund_token": result.token,
|
|
||||||
"amount": result.amount,
|
|
||||||
"unit": result.unit,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@router.api_route(
|
@router.api_route(
|
||||||
"/{path:path}",
|
"/{path:path}",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import hashlib
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
@@ -1835,7 +1834,6 @@ class BaseUpstreamProvider:
|
|||||||
max_cost_for_model: int,
|
max_cost_for_model: int,
|
||||||
model_obj: Model,
|
model_obj: Model,
|
||||||
mint: str | None = None,
|
mint: str | None = None,
|
||||||
payment_token_hash: str | None = None,
|
|
||||||
request_id: str | None = None,
|
request_id: str | None = None,
|
||||||
) -> Response | StreamingResponse:
|
) -> Response | StreamingResponse:
|
||||||
"""Dispatch /v1/messages via litellm for x-cashu payments.
|
"""Dispatch /v1/messages via litellm for x-cashu payments.
|
||||||
@@ -1857,7 +1855,6 @@ class BaseUpstreamProvider:
|
|||||||
max_cost_for_model,
|
max_cost_for_model,
|
||||||
requested_model,
|
requested_model,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id,
|
request_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1884,7 +1881,6 @@ class BaseUpstreamProvider:
|
|||||||
refund_amount,
|
refund_amount,
|
||||||
unit,
|
unit,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=request_id,
|
request_id=request_id,
|
||||||
)
|
)
|
||||||
response_headers["X-Cashu"] = refund_token
|
response_headers["X-Cashu"] = refund_token
|
||||||
@@ -2071,7 +2067,6 @@ class BaseUpstreamProvider:
|
|||||||
max_cost_for_model: int,
|
max_cost_for_model: int,
|
||||||
requested_model: str | None,
|
requested_model: str | None,
|
||||||
mint: str | None,
|
mint: str | None,
|
||||||
payment_token_hash: str | None,
|
|
||||||
request_id: str | None,
|
request_id: str | None,
|
||||||
) -> StreamingResponse:
|
) -> StreamingResponse:
|
||||||
"""Buffer a litellm stream end-to-end, compute cost, then replay.
|
"""Buffer a litellm stream end-to-end, compute cost, then replay.
|
||||||
@@ -2175,7 +2170,6 @@ class BaseUpstreamProvider:
|
|||||||
refund_amount,
|
refund_amount,
|
||||||
unit,
|
unit,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=request_id,
|
request_id=request_id,
|
||||||
)
|
)
|
||||||
response_headers["X-Cashu"] = refund_token
|
response_headers["X-Cashu"] = refund_token
|
||||||
@@ -2940,7 +2934,6 @@ class BaseUpstreamProvider:
|
|||||||
amount: int,
|
amount: int,
|
||||||
unit: str,
|
unit: str,
|
||||||
mint: str | None = None,
|
mint: str | None = None,
|
||||||
payment_token_hash: str | None = None,
|
|
||||||
request_id: str | None = None,
|
request_id: str | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Create and send a refund token to the user.
|
"""Create and send a refund token to the user.
|
||||||
@@ -2949,7 +2942,6 @@ class BaseUpstreamProvider:
|
|||||||
amount: Refund amount
|
amount: Refund amount
|
||||||
unit: Unit of the refund (sat or msat)
|
unit: Unit of the refund (sat or msat)
|
||||||
mint: Optional mint URL for the refund token
|
mint: Optional mint URL for the refund token
|
||||||
payment_token_hash: Optional SHA-256 hash of the original payment token for storage
|
|
||||||
request_id: Optional HTTP request ID for tracking
|
request_id: Optional HTTP request ID for tracking
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -3041,7 +3033,6 @@ class BaseUpstreamProvider:
|
|||||||
unit: str,
|
unit: str,
|
||||||
max_cost_for_model: int,
|
max_cost_for_model: int,
|
||||||
mint: str | None = None,
|
mint: str | None = None,
|
||||||
payment_token_hash: str | None = None,
|
|
||||||
request_id: str | None = None,
|
request_id: str | None = None,
|
||||||
) -> StreamingResponse:
|
) -> StreamingResponse:
|
||||||
"""Handle streaming response for X-Cashu payment, calculating refund if needed.
|
"""Handle streaming response for X-Cashu payment, calculating refund if needed.
|
||||||
@@ -3052,7 +3043,6 @@ class BaseUpstreamProvider:
|
|||||||
amount: Payment amount received
|
amount: Payment amount received
|
||||||
unit: Payment unit (sat or msat)
|
unit: Payment unit (sat or msat)
|
||||||
max_cost_for_model: Maximum cost for the model
|
max_cost_for_model: Maximum cost for the model
|
||||||
payment_token_hash: Optional hash of original payment token for refund storage
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
StreamingResponse with refund token in header if applicable
|
StreamingResponse with refund token in header if applicable
|
||||||
@@ -3143,7 +3133,6 @@ class BaseUpstreamProvider:
|
|||||||
refund_amount,
|
refund_amount,
|
||||||
unit,
|
unit,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=request_id,
|
request_id=request_id,
|
||||||
)
|
)
|
||||||
response_headers["X-Cashu"] = refund_token
|
response_headers["X-Cashu"] = refund_token
|
||||||
@@ -3222,7 +3211,6 @@ class BaseUpstreamProvider:
|
|||||||
unit: str,
|
unit: str,
|
||||||
max_cost_for_model: int,
|
max_cost_for_model: int,
|
||||||
mint: str | None = None,
|
mint: str | None = None,
|
||||||
payment_token_hash: str | None = None,
|
|
||||||
request_id: str | None = None,
|
request_id: str | None = None,
|
||||||
) -> Response:
|
) -> Response:
|
||||||
"""Handle non-streaming response for X-Cashu payment, calculating refund if needed.
|
"""Handle non-streaming response for X-Cashu payment, calculating refund if needed.
|
||||||
@@ -3233,7 +3221,6 @@ class BaseUpstreamProvider:
|
|||||||
amount: Payment amount received
|
amount: Payment amount received
|
||||||
unit: Payment unit (sat or msat)
|
unit: Payment unit (sat or msat)
|
||||||
max_cost_for_model: Maximum cost for the model
|
max_cost_for_model: Maximum cost for the model
|
||||||
payment_token_hash: Optional hash of original payment token for refund storage
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Response with refund token in header if applicable
|
Response with refund token in header if applicable
|
||||||
@@ -3303,7 +3290,6 @@ class BaseUpstreamProvider:
|
|||||||
refund_amount,
|
refund_amount,
|
||||||
unit,
|
unit,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=request_id,
|
request_id=request_id,
|
||||||
)
|
)
|
||||||
response_headers["X-Cashu"] = refund_token
|
response_headers["X-Cashu"] = refund_token
|
||||||
@@ -3375,7 +3361,6 @@ class BaseUpstreamProvider:
|
|||||||
unit: str,
|
unit: str,
|
||||||
max_cost_for_model: int,
|
max_cost_for_model: int,
|
||||||
mint: str | None = None,
|
mint: str | None = None,
|
||||||
payment_token_hash: str | None = None,
|
|
||||||
request_id: str | None = None,
|
request_id: str | None = None,
|
||||||
) -> StreamingResponse | Response:
|
) -> StreamingResponse | Response:
|
||||||
"""Handle chat completion response for X-Cashu payment, detecting streaming vs non-streaming.
|
"""Handle chat completion response for X-Cashu payment, detecting streaming vs non-streaming.
|
||||||
@@ -3419,7 +3404,6 @@ class BaseUpstreamProvider:
|
|||||||
unit,
|
unit,
|
||||||
max_cost_for_model,
|
max_cost_for_model,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=request_id,
|
request_id=request_id,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -3430,7 +3414,6 @@ class BaseUpstreamProvider:
|
|||||||
unit,
|
unit,
|
||||||
max_cost_for_model,
|
max_cost_for_model,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=request_id,
|
request_id=request_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -3460,7 +3443,6 @@ class BaseUpstreamProvider:
|
|||||||
max_cost_for_model: int,
|
max_cost_for_model: int,
|
||||||
model_obj: Model,
|
model_obj: Model,
|
||||||
mint: str | None = None,
|
mint: str | None = None,
|
||||||
payment_token_hash: str | None = None,
|
|
||||||
) -> Response | StreamingResponse:
|
) -> Response | StreamingResponse:
|
||||||
"""Forward request paid with X-Cashu token to upstream service.
|
"""Forward request paid with X-Cashu token to upstream service.
|
||||||
|
|
||||||
@@ -3499,7 +3481,6 @@ class BaseUpstreamProvider:
|
|||||||
max_cost_for_model=max_cost_for_model,
|
max_cost_for_model=max_cost_for_model,
|
||||||
model_obj=model_obj,
|
model_obj=model_obj,
|
||||||
mint=mint,
|
mint=mint,
|
||||||
payment_token_hash=payment_token_hash,
|
|
||||||
request_id=getattr(request.state, "request_id", None),
|
request_id=getattr(request.state, "request_id", None),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -3569,7 +3550,6 @@ class BaseUpstreamProvider:
|
|||||||
amount,
|
amount,
|
||||||
unit,
|
unit,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=getattr(request.state, "request_id", None),
|
request_id=getattr(request.state, "request_id", None),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -3619,7 +3599,6 @@ class BaseUpstreamProvider:
|
|||||||
unit,
|
unit,
|
||||||
max_cost_for_model,
|
max_cost_for_model,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=getattr(request.state, "request_id", None),
|
request_id=getattr(request.state, "request_id", None),
|
||||||
)
|
)
|
||||||
background_tasks = BackgroundTasks()
|
background_tasks = BackgroundTasks()
|
||||||
@@ -3695,7 +3674,6 @@ class BaseUpstreamProvider:
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
payment_token_hash = hashlib.sha256(x_cashu_token.encode()).hexdigest()
|
|
||||||
headers = dict(request.headers)
|
headers = dict(request.headers)
|
||||||
amount, unit, mint = await recieve_token(x_cashu_token)
|
amount, unit, mint = await recieve_token(x_cashu_token)
|
||||||
headers = self.prepare_headers(dict(request.headers))
|
headers = self.prepare_headers(dict(request.headers))
|
||||||
@@ -3728,7 +3706,6 @@ class BaseUpstreamProvider:
|
|||||||
max_cost_for_model,
|
max_cost_for_model,
|
||||||
model_obj,
|
model_obj,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_message = str(e)
|
error_message = str(e)
|
||||||
@@ -3788,7 +3765,6 @@ class BaseUpstreamProvider:
|
|||||||
max_cost_for_model: int,
|
max_cost_for_model: int,
|
||||||
model_obj: Model,
|
model_obj: Model,
|
||||||
mint: str | None = None,
|
mint: str | None = None,
|
||||||
payment_token_hash: str | None = None,
|
|
||||||
) -> Response | StreamingResponse:
|
) -> Response | StreamingResponse:
|
||||||
"""Forward Responses API request paid with X-Cashu token to upstream service.
|
"""Forward Responses API request paid with X-Cashu token to upstream service.
|
||||||
|
|
||||||
@@ -3864,7 +3840,6 @@ class BaseUpstreamProvider:
|
|||||||
amount,
|
amount,
|
||||||
unit,
|
unit,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=getattr(request.state, "request_id", None),
|
request_id=getattr(request.state, "request_id", None),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -3909,7 +3884,6 @@ class BaseUpstreamProvider:
|
|||||||
unit,
|
unit,
|
||||||
max_cost_for_model,
|
max_cost_for_model,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=getattr(request.state, "request_id", None),
|
request_id=getattr(request.state, "request_id", None),
|
||||||
)
|
)
|
||||||
background_tasks = BackgroundTasks()
|
background_tasks = BackgroundTasks()
|
||||||
@@ -3960,7 +3934,6 @@ class BaseUpstreamProvider:
|
|||||||
unit: str,
|
unit: str,
|
||||||
max_cost_for_model: int,
|
max_cost_for_model: int,
|
||||||
mint: str | None = None,
|
mint: str | None = None,
|
||||||
payment_token_hash: str | None = None,
|
|
||||||
request_id: str | None = None,
|
request_id: str | None = None,
|
||||||
) -> StreamingResponse | Response:
|
) -> StreamingResponse | Response:
|
||||||
"""Handle Responses API completion response for X-Cashu payment.
|
"""Handle Responses API completion response for X-Cashu payment.
|
||||||
@@ -4005,7 +3978,6 @@ class BaseUpstreamProvider:
|
|||||||
unit,
|
unit,
|
||||||
max_cost_for_model,
|
max_cost_for_model,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=request_id,
|
request_id=request_id,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -4016,7 +3988,6 @@ class BaseUpstreamProvider:
|
|||||||
unit,
|
unit,
|
||||||
max_cost_for_model,
|
max_cost_for_model,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=request_id,
|
request_id=request_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -4044,7 +4015,6 @@ class BaseUpstreamProvider:
|
|||||||
unit: str,
|
unit: str,
|
||||||
max_cost_for_model: int,
|
max_cost_for_model: int,
|
||||||
mint: str | None = None,
|
mint: str | None = None,
|
||||||
payment_token_hash: str | None = None,
|
|
||||||
request_id: str | None = None,
|
request_id: str | None = None,
|
||||||
) -> StreamingResponse:
|
) -> StreamingResponse:
|
||||||
"""Handle streaming Responses API response for X-Cashu payment.
|
"""Handle streaming Responses API response for X-Cashu payment.
|
||||||
@@ -4131,7 +4101,6 @@ class BaseUpstreamProvider:
|
|||||||
refund_amount,
|
refund_amount,
|
||||||
unit,
|
unit,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=request_id,
|
request_id=request_id,
|
||||||
)
|
)
|
||||||
response_headers["X-Cashu"] = refund_token
|
response_headers["X-Cashu"] = refund_token
|
||||||
@@ -4210,7 +4179,6 @@ class BaseUpstreamProvider:
|
|||||||
unit: str,
|
unit: str,
|
||||||
max_cost_for_model: int,
|
max_cost_for_model: int,
|
||||||
mint: str | None = None,
|
mint: str | None = None,
|
||||||
payment_token_hash: str | None = None,
|
|
||||||
request_id: str | None = None,
|
request_id: str | None = None,
|
||||||
) -> Response:
|
) -> Response:
|
||||||
"""Handle non-streaming Responses API response for X-Cashu payment."""
|
"""Handle non-streaming Responses API response for X-Cashu payment."""
|
||||||
@@ -4279,7 +4247,6 @@ class BaseUpstreamProvider:
|
|||||||
refund_amount,
|
refund_amount,
|
||||||
unit,
|
unit,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
request_id=request_id,
|
request_id=request_id,
|
||||||
)
|
)
|
||||||
response_headers["X-Cashu"] = refund_token
|
response_headers["X-Cashu"] = refund_token
|
||||||
@@ -4376,7 +4343,6 @@ class BaseUpstreamProvider:
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
payment_token_hash = hashlib.sha256(x_cashu_token.encode()).hexdigest()
|
|
||||||
headers = dict(request.headers)
|
headers = dict(request.headers)
|
||||||
amount, unit, mint = await recieve_token(x_cashu_token)
|
amount, unit, mint = await recieve_token(x_cashu_token)
|
||||||
headers = self.prepare_headers(dict(request.headers))
|
headers = self.prepare_headers(dict(request.headers))
|
||||||
@@ -4409,7 +4375,6 @@ class BaseUpstreamProvider:
|
|||||||
max_cost_for_model,
|
max_cost_for_model,
|
||||||
model_obj,
|
model_obj,
|
||||||
mint,
|
mint,
|
||||||
payment_token_hash,
|
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_message = str(e)
|
error_message = str(e)
|
||||||
|
|||||||
@@ -693,7 +693,6 @@ async def test_x_cashu_non_streaming_dispatches_and_refunds_overpaid_amount() ->
|
|||||||
max_cost_for_model=10_000,
|
max_cost_for_model=10_000,
|
||||||
model_obj=model,
|
model_obj=model,
|
||||||
mint="https://mint.example",
|
mint="https://mint.example",
|
||||||
payment_token_hash="hash123",
|
|
||||||
request_id="req-1",
|
request_id="req-1",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -972,7 +971,6 @@ async def test_forward_x_cashu_request_routes_messages_via_litellm() -> None:
|
|||||||
max_cost_for_model=10_000,
|
max_cost_for_model=10_000,
|
||||||
model_obj=model,
|
model_obj=model,
|
||||||
mint="https://mint",
|
mint="https://mint",
|
||||||
payment_token_hash="h",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
mock_helper.assert_awaited_once()
|
mock_helper.assert_awaited_once()
|
||||||
@@ -1041,7 +1039,6 @@ async def test_forward_x_cashu_request_handles_count_tokens_locally() -> None:
|
|||||||
max_cost_for_model=10_000,
|
max_cost_for_model=10_000,
|
||||||
model_obj=model,
|
model_obj=model,
|
||||||
mint="https://mint",
|
mint="https://mint",
|
||||||
payment_token_hash="h",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ async def test_non_streaming_includes_cost_sats() -> None:
|
|||||||
unit="msat",
|
unit="msat",
|
||||||
max_cost_for_model=10000,
|
max_cost_for_model=10000,
|
||||||
mint=None,
|
mint=None,
|
||||||
payment_token_hash=None,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
body = json.loads(response.body)
|
body = json.loads(response.body)
|
||||||
@@ -170,7 +169,6 @@ async def test_streaming_includes_cost_sats_in_usage_chunk() -> None:
|
|||||||
unit="msat",
|
unit="msat",
|
||||||
max_cost_for_model=10000,
|
max_cost_for_model=10000,
|
||||||
mint=None,
|
mint=None,
|
||||||
payment_token_hash=None,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
chunks = await _collect_streaming(response)
|
chunks = await _collect_streaming(response)
|
||||||
|
|||||||
Reference in New Issue
Block a user