fix: send Cashu token creation payload in request body

This commit is contained in:
9qeklajc
2026-07-24 23:01:10 +02:00
parent b94d95fc2b
commit b0c70ecddc
4 changed files with 102 additions and 25 deletions
+43 -6
View File
@@ -109,13 +109,19 @@ async def account_info(
# Note: validate_bearer_key already supports refund_address and key_expiry_time params # Note: validate_bearer_key already supports refund_address and key_expiry_time params
@router.get("/create") class BalanceCreateRequest(BaseModel):
async def create_balance( initial_balance_token: str
balance_limit: int | None = None
balance_limit_reset: str | None = None
validity_date: int | None = None
async def _create_balance(
initial_balance_token: str, initial_balance_token: str,
balance_limit: int | None = None, balance_limit: int | None,
balance_limit_reset: str | None = None, balance_limit_reset: str | None,
validity_date: int | None = None, validity_date: int | None,
session: AsyncSession = Depends(get_session), session: AsyncSession,
) -> dict: ) -> dict:
key = await validate_bearer_key(initial_balance_token, session) key = await validate_bearer_key(initial_balance_token, session)
@@ -135,6 +141,37 @@ async def create_balance(
} }
@router.post("/create")
async def create_balance_from_body(
payload: BalanceCreateRequest,
session: AsyncSession = Depends(get_session),
) -> dict:
return await _create_balance(
payload.initial_balance_token,
payload.balance_limit,
payload.balance_limit_reset,
payload.validity_date,
session,
)
@router.get("/create")
async def create_balance(
initial_balance_token: str,
balance_limit: int | None = None,
balance_limit_reset: str | None = None,
validity_date: int | None = None,
session: AsyncSession = Depends(get_session),
) -> dict:
return await _create_balance(
initial_balance_token,
balance_limit,
balance_limit_reset,
validity_date,
session,
)
@router.get("/info") @router.get("/info")
async def wallet_info( async def wallet_info(
key: ApiKey = Depends(get_key_from_header), key: ApiKey = Depends(get_key_from_header),
+40
View File
@@ -0,0 +1,40 @@
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from fastapi import FastAPI
from httpx import ASGITransport, AsyncClient
from routstr import balance as balance_module
from routstr.core.db import get_session
@pytest.mark.asyncio
async def test_create_balance_accepts_large_cashu_token_in_post_body(
monkeypatch: pytest.MonkeyPatch,
) -> None:
token = "cashuA" + "x" * 20_000
key = SimpleNamespace(hashed_key="hashed", balance=123_000)
validate_bearer_key = AsyncMock(return_value=key)
session = AsyncMock()
monkeypatch.setattr(balance_module, "validate_bearer_key", validate_bearer_key)
async def override_get_session(): # type: ignore[no-untyped-def]
yield session
app = FastAPI()
app.include_router(balance_module.balance_router)
app.dependency_overrides[get_session] = override_get_session
async with AsyncClient(
transport=ASGITransport(app=app), # type: ignore[arg-type]
base_url="http://test",
) as client:
response = await client.post(
"/v1/balance/create",
json={"initial_balance_token": token},
)
assert response.status_code == 200
assert response.json() == {"api_key": "sk-hashed", "balance": 123_000}
validate_bearer_key.assert_awaited_once_with(token, session)
@@ -91,25 +91,27 @@ export function CashuPaymentWorkflow({
setIsCreatingKey(true); setIsCreatingKey(true);
try { try {
const params = new URLSearchParams({ const requestPayload: {
initial_balance_token: string;
balance_limit?: number;
balance_limit_reset?: string;
validity_date?: number;
} = {
initial_balance_token: initialToken.trim(), initial_balance_token: initialToken.trim(),
}); };
if (balanceLimit) params.append('balance_limit', balanceLimit); if (balanceLimit) requestPayload.balance_limit = Number(balanceLimit);
if (balanceLimitReset) if (balanceLimitReset)
params.append('balance_limit_reset', balanceLimitReset); requestPayload.balance_limit_reset = balanceLimitReset;
if (validityDate) { if (validityDate) {
const timestamp = Math.floor( requestPayload.validity_date = Math.floor(
new Date(validityDate + 'T23:59:59').getTime() / 1000 new Date(validityDate + 'T23:59:59').getTime() / 1000
); );
params.append('validity_date', timestamp.toString());
} }
const response = await fetch( const response = await fetch(`${baseUrl}/v1/balance/create`, {
`${baseUrl}/v1/balance/create?${params.toString()}`, method: 'POST',
{ headers: { 'Content-Type': 'application/json' },
method: 'GET', body: JSON.stringify(requestPayload),
headers: { 'Content-Type': 'application/json' }, });
}
);
if (!response.ok) { if (!response.ok) {
const errorText = await response.text(); const errorText = await response.text();
throw new Error(errorText || 'Failed to create API key'); throw new Error(errorText || 'Failed to create API key');
@@ -161,13 +161,11 @@ export function RoutstrCreateKeySection({
setIsCreatingCashu(true); setIsCreatingCashu(true);
try { try {
const params = new URLSearchParams({ const resp = await fetch(`${cleanUrl}/v1/balance/create`, {
initial_balance_token: cashuToken.trim(), method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ initial_balance_token: cashuToken.trim() }),
}); });
const resp = await fetch(
`${cleanUrl}/v1/balance/create?${params.toString()}`,
{ method: 'GET', headers: { 'Content-Type': 'application/json' } }
);
if (!resp.ok) { if (!resp.ok) {
const errorText = await resp.text(); const errorText = await resp.text();