diff --git a/apps/api/src/services/credits-client.ts b/apps/api/src/services/credits-client.ts index 530bbd5..db743ef 100644 --- a/apps/api/src/services/credits-client.ts +++ b/apps/api/src/services/credits-client.ts @@ -39,27 +39,32 @@ export class CreditsClient { /** * Reserviert Credits — `commit` oder `refund-reservation` muss * folgen. Pattern für teure asynchrone Operationen. + * + * Verifiziert gegen mana-credits internalReserveSchema (2026-05-19): + * camelCase-Felder, appId Pflicht seit Verein-Konsolidierung + * 2026-05-05. Korrektur vom 2026-05-19 (vorher snake_case — wäre + * 400 geworden, fiel nur nicht auf weil Paid-Decks dormant sind). */ async reserve(input: { userId: string; amount: number; reason: string }) { const r = await fetch(`${CREDITS_URL}/api/v1/internal/credits/reserve`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHeader() }, body: JSON.stringify({ - user_id: input.userId, + userId: input.userId, amount: input.amount, reason: input.reason, - app_id: 'cards', + appId: 'wordeck', }), }); if (!r.ok) throw new Error(`mana-credits reserve ${r.status}`); - return r.json() as Promise<{ reservation_id: string }>; + return r.json() as Promise<{ reservationId: string; balance: number }>; } - async commit(reservationId: string) { + async commit(reservationId: string, description?: string) { const r = await fetch(`${CREDITS_URL}/api/v1/internal/credits/commit`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHeader() }, - body: JSON.stringify({ reservation_id: reservationId }), + body: JSON.stringify({ reservationId, description }), }); if (!r.ok) throw new Error(`mana-credits commit ${r.status}`); return r.json(); @@ -69,11 +74,39 @@ export class CreditsClient { const r = await fetch(`${CREDITS_URL}/api/v1/internal/credits/refund-reservation`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHeader() }, - body: JSON.stringify({ reservation_id: reservationId }), + body: JSON.stringify({ reservationId }), }); if (!r.ok) throw new Error(`mana-credits refund ${r.status}`); return r.json(); } + + /** + * Gewährt Credits an einen User (Author-Payout). `referenceId` ist + * Pflicht (Reconcile-Anker), `reason` max 64 chars. + * + * Reason-Konvention: `wordeck.payout.`. + */ + async grant(input: { + userId: string; + amount: number; + reason: string; + referenceId: string; + description?: string; + }) { + const r = await fetch(`${CREDITS_URL}/api/v1/internal/credits/grant`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', ...authHeader() }, + body: JSON.stringify({ + userId: input.userId, + amount: input.amount, + reason: input.reason, + referenceId: input.referenceId, + description: input.description, + }), + }); + if (!r.ok) throw new Error(`mana-credits grant ${r.status}`); + return r.json() as Promise<{ grantId: string }>; + } } let cached: CreditsClient | null = null;