fix storing in/out trans.

This commit is contained in:
9qeklajc
2026-03-14 16:56:44 +01:00
parent 2c2124952f
commit 4723b9db4d
5 changed files with 114 additions and 60 deletions
+5 -5
View File
@@ -372,12 +372,12 @@ async def validate_bearer_key(
},
)
key_preview = bearer_key[:10] + "..." if len(bearer_key) > 10 else bearer_key
logger.error(
"Invalid API key format",
f"Invalid API key format: preview={key_preview!r} length={len(bearer_key)} "
f"(expected 'sk-...' or 'cashu...' token)",
extra={
"key_preview": bearer_key[:10] + "..."
if len(bearer_key) > 10
else bearer_key,
"key_preview": key_preview,
"key_length": len(bearer_key),
},
)
@@ -386,7 +386,7 @@ async def validate_bearer_key(
status_code=401,
detail={
"error": {
"message": "Invalid API key",
"message": "Invalid API key format. Expected an 'sk-...' API key or a 'cashu...' token.",
"type": "invalid_request_error",
"code": "invalid_api_key",
}
+8 -2
View File
@@ -16,7 +16,13 @@ from ..wallet import (
send_token,
slow_filter_spend_proofs,
)
from .db import ApiKey, CashuTransaction, ModelRow, UpstreamProviderRow, create_session
from .db import (
ApiKey,
CashuTransaction,
ModelRow,
UpstreamProviderRow,
create_session,
)
from .log_manager import log_manager
from .logging import get_logger
from .settings import SettingsService, settings
@@ -1234,7 +1240,7 @@ async def get_transactions_api(
stmt = stmt.where(CashuTransaction.swept == True) # noqa: E712
elif status == "pending":
stmt = stmt.where(
not CashuTransaction.collected,
CashuTransaction.collected == False,
CashuTransaction.swept == False, # noqa: E712
)
+11 -7
View File
@@ -2,6 +2,7 @@ import os
import pathlib
import sqlite3
import time
import uuid
from contextlib import asynccontextmanager
from typing import AsyncGenerator
@@ -134,7 +135,8 @@ class CashuTransaction(SQLModel, table=True): # type: ignore
id: str = Field(
primary_key=True,
description="SHA-256 hash of the x-cashu token or unique identifier",
default_factory=lambda: uuid.uuid4().hex,
description="Unique transaction identifier",
)
token: str = Field(description="Serialized Cashu token")
amount: int = Field(description="Amount in the token's unit")
@@ -151,31 +153,33 @@ class CashuTransaction(SQLModel, table=True): # type: ignore
async def store_cashu_transaction(
id: str,
token: str,
amount: int,
unit: str,
mint_url: str | None = None,
type: str = "out",
typ: str = "out",
request_id: str | None = None,
collected: bool = False,
created_at: int | None = None,
) -> None:
try:
async with create_session() as session:
tx = CashuTransaction(
id=id,
token=token,
amount=amount,
unit=unit,
mint_url=mint_url,
type=type,
type=typ,
request_id=request_id,
collected=collected,
created_at=created_at or int(time.time()),
)
session.add(tx)
await session.commit()
except Exception as e:
logger.warning(
"Failed to store cashu transaction",
extra={"error": str(e), "id": id, "type": type},
f"Failed to store cashu transaction: {e} (type={typ})",
extra={"error": str(e), "type": typ},
)
+3 -4
View File
@@ -455,15 +455,14 @@ async def get_bearer_token_key(
)
return key
except Exception as e:
key_preview = bearer_key[:20] + "..." if len(bearer_key) > 20 else bearer_key
logger.error(
"Bearer token validation failed",
f"Bearer token validation failed: {type(e).__name__}: {e} path={path} key={key_preview!r}",
extra={
"error": str(e),
"error_type": type(e).__name__,
"path": path,
"bearer_key_preview": bearer_key[:20] + "..."
if len(bearer_key) > 20
else bearer_key,
"bearer_key_preview": key_preview,
},
)
raise
+87 -42
View File
@@ -1686,6 +1686,7 @@ class BaseUpstreamProvider:
unit: str,
mint: str | None = None,
payment_token_hash: str | None = None,
request_id: str | None = None,
) -> str:
"""Create and send a refund token to the user.
@@ -1694,6 +1695,7 @@ class BaseUpstreamProvider:
unit: Unit of the refund (sat or msat)
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
Returns:
Refund token string
@@ -1723,18 +1725,17 @@ class BaseUpstreamProvider:
},
)
if payment_token_hash:
try:
await store_cashu_transaction(
id=payment_token_hash,
token=refund_token,
amount=amount,
unit=unit,
mint_url=mint,
type="out",
)
except Exception:
pass # store_cashu_transaction already logs
try:
await store_cashu_transaction(
token=refund_token,
amount=amount,
unit=unit,
mint_url=mint,
typ="out",
request_id=request_id,
)
except Exception:
pass # store_cashu_transaction already logs
return refund_token
except Exception as e:
@@ -1786,6 +1787,7 @@ class BaseUpstreamProvider:
max_cost_for_model: int,
mint: str | None = None,
payment_token_hash: str | None = None,
request_id: str | None = None,
) -> StreamingResponse:
"""Handle streaming response for X-Cashu payment, calculating refund if needed.
@@ -1868,7 +1870,8 @@ class BaseUpstreamProvider:
)
refund_token = await self.send_refund(
refund_amount, unit, mint, payment_token_hash
refund_amount, unit, mint, payment_token_hash,
request_id=request_id,
)
response_headers["X-Cashu"] = refund_token
@@ -1923,6 +1926,7 @@ class BaseUpstreamProvider:
max_cost_for_model: int,
mint: str | None = None,
payment_token_hash: str | None = None,
request_id: str | None = None,
) -> Response:
"""Handle non-streaming response for X-Cashu payment, calculating refund if needed.
@@ -1995,7 +1999,8 @@ class BaseUpstreamProvider:
if refund_amount > 0:
refund_token = await self.send_refund(
refund_amount, unit, mint, payment_token_hash
refund_amount, unit, mint, payment_token_hash,
request_id=request_id,
)
response_headers["X-Cashu"] = refund_token
@@ -2032,18 +2037,17 @@ class BaseUpstreamProvider:
emergency_refund = amount
refund_token = await send_token(emergency_refund, unit=unit, mint_url=mint)
response.headers["X-Cashu"] = refund_token
if payment_token_hash:
try:
await store_cashu_transaction(
id=payment_token_hash,
token=refund_token,
amount=emergency_refund,
unit=unit,
mint_url=mint,
type="out",
)
except Exception:
pass
try:
await store_cashu_transaction(
token=refund_token,
amount=emergency_refund,
unit=unit,
mint_url=mint,
typ="out",
request_id=getattr(request.state, "request_id", None),
)
except Exception:
pass
logger.warning(
"Emergency refund issued due to JSON parse error",
@@ -2069,6 +2073,7 @@ class BaseUpstreamProvider:
max_cost_for_model: int,
mint: str | None = None,
payment_token_hash: str | None = None,
request_id: str | None = None,
) -> StreamingResponse | Response:
"""Handle chat completion response for X-Cashu payment, detecting streaming vs non-streaming.
@@ -2112,6 +2117,7 @@ class BaseUpstreamProvider:
max_cost_for_model,
mint,
payment_token_hash,
request_id=request_id,
)
else:
return await self.handle_x_cashu_non_streaming_response(
@@ -2122,6 +2128,7 @@ class BaseUpstreamProvider:
max_cost_for_model,
mint,
payment_token_hash,
request_id=request_id,
)
except Exception as e:
@@ -2222,7 +2229,8 @@ class BaseUpstreamProvider:
)
refund_token = await self.send_refund(
amount - 60, unit, mint, payment_token_hash
amount - 60, unit, mint, payment_token_hash,
request_id=getattr(request.state, "request_id", None),
)
logger.info(
@@ -2267,6 +2275,7 @@ class BaseUpstreamProvider:
max_cost_for_model,
mint,
payment_token_hash,
request_id=getattr(request.state, "request_id", None),
)
background_tasks = BackgroundTasks()
background_tasks.add_task(response.aclose)
@@ -2346,6 +2355,20 @@ class BaseUpstreamProvider:
amount, unit, mint = await recieve_token(x_cashu_token)
headers = self.prepare_headers(dict(request.headers))
request_id = getattr(request.state, "request_id", None)
try:
await store_cashu_transaction(
token=x_cashu_token,
amount=amount,
unit=unit,
mint_url=mint,
typ="in",
request_id=request_id,
collected=True,
)
except Exception:
pass
logger.info(
"X-Cashu token redeemed for Responses API",
extra={"amount": amount, "unit": unit, "path": path, "mint": mint},
@@ -2493,7 +2516,8 @@ class BaseUpstreamProvider:
)
refund_token = await self.send_refund(
amount - 60, unit, mint, payment_token_hash
amount - 60, unit, mint, payment_token_hash,
request_id=getattr(request.state, "request_id", None),
)
logger.info(
@@ -2538,6 +2562,7 @@ class BaseUpstreamProvider:
max_cost_for_model,
mint,
payment_token_hash,
request_id=getattr(request.state, "request_id", None),
)
background_tasks = BackgroundTasks()
background_tasks.add_task(response.aclose)
@@ -2588,6 +2613,7 @@ class BaseUpstreamProvider:
max_cost_for_model: int,
mint: str | None = None,
payment_token_hash: str | None = None,
request_id: str | None = None,
) -> StreamingResponse | Response:
"""Handle Responses API completion response for X-Cashu payment.
@@ -2632,6 +2658,7 @@ class BaseUpstreamProvider:
max_cost_for_model,
mint,
payment_token_hash,
request_id=request_id,
)
else:
return await self.handle_x_cashu_non_streaming_responses_response(
@@ -2642,6 +2669,7 @@ class BaseUpstreamProvider:
max_cost_for_model,
mint,
payment_token_hash,
request_id=request_id,
)
except Exception as e:
@@ -2669,6 +2697,7 @@ class BaseUpstreamProvider:
max_cost_for_model: int,
mint: str | None = None,
payment_token_hash: str | None = None,
request_id: str | None = None,
) -> StreamingResponse:
"""Handle streaming Responses API response for X-Cashu payment.
@@ -2751,7 +2780,8 @@ class BaseUpstreamProvider:
)
refund_token = await self.send_refund(
refund_amount, unit, mint, payment_token_hash
refund_amount, unit, mint, payment_token_hash,
request_id=request_id,
)
response_headers["X-Cashu"] = refund_token
@@ -2806,6 +2836,7 @@ class BaseUpstreamProvider:
max_cost_for_model: int,
mint: str | None = None,
payment_token_hash: str | None = None,
request_id: str | None = None,
) -> Response:
"""Handle non-streaming Responses API response for X-Cashu payment."""
logger.debug(
@@ -2866,7 +2897,8 @@ class BaseUpstreamProvider:
if refund_amount > 0:
refund_token = await self.send_refund(
refund_amount, unit, mint, payment_token_hash
refund_amount, unit, mint, payment_token_hash,
request_id=request_id,
)
response_headers["X-Cashu"] = refund_token
@@ -2903,18 +2935,17 @@ class BaseUpstreamProvider:
emergency_refund = amount
refund_token = await send_token(emergency_refund, unit=unit, mint_url=mint)
response.headers["X-Cashu"] = refund_token
if payment_token_hash:
try:
await store_cashu_transaction(
id=payment_token_hash,
token=refund_token,
amount=emergency_refund,
unit=unit,
mint_url=mint,
type="out",
)
except Exception:
pass
try:
await store_cashu_transaction(
token=refund_token,
amount=emergency_refund,
unit=unit,
mint_url=mint,
typ="out",
request_id=getattr(request.state, "request_id", None),
)
except Exception:
pass
logger.warning(
"Emergency refund issued for Responses API due to JSON parse error",
@@ -2969,6 +3000,20 @@ class BaseUpstreamProvider:
amount, unit, mint = await recieve_token(x_cashu_token)
headers = self.prepare_headers(dict(request.headers))
request_id = getattr(request.state, "request_id", None)
try:
await store_cashu_transaction(
token=x_cashu_token,
amount=amount,
unit=unit,
mint_url=mint,
typ="in",
request_id=request_id,
collected=True,
)
except Exception:
pass
logger.info(
"X-Cashu token redeemed successfully",
extra={"amount": amount, "unit": unit, "path": path, "mint": mint},