3 Commits

Author SHA1 Message Date
Noderunners
0ab66479d9 Move terms.txt to public/ to fix Terms page loading 2025-11-11 01:21:52 +01:00
Noderunners
4afe3f6d3e fix(lnbits): normalize invoice response (fallback to bolt11) and update payment status check; chore: add .gitignore 2025-11-10 20:47:58 +01:00
Michilis
bb3b5604a1 Fix API calls to use npub instead of identifier
- Updated getUserInfo to send npub field instead of identifier
- Updated whitelistUser to send npub field instead of identifier
- Resolves 422 Unprocessable Content error from backend API
2025-07-01 19:18:29 +00:00
4 changed files with 109 additions and 23 deletions

64
.gitignore vendored Normal file
View File

@@ -0,0 +1,64 @@
# Dependencies
node_modules/
# Production builds
dist/
dist-ssr/
build/
# Vite and tooling caches
.vite/
.esbuild/
.rollup.cache/
.parcel-cache/
.turbo/
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Environment files
.env
.env.local
.env.*.local
.env.production.local
.env.development.local
.env.test.local
!.env.example
# Coverage
coverage/
# TypeScript build info
*.tsbuildinfo
# Editor directories and files
.vscode/
.idea/
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*~
# OS metadata
.DS_Store
Thumbs.db
# Misc secrets/keys (if present)
*.pem
*.key
*.crt
*.p12
*.pfx
# Package tarballs
*.tgz

View File

@@ -46,3 +46,4 @@ We reserve the right to terminate or suspend access to our service immediately,
## 8. Changes to Terms ## 8. Changes to Terms
We reserve the right to modify these terms at any time. We will notify users of any changes by updating the date at the top of this page. We reserve the right to modify these terms at any time. We will notify users of any changes by updating the date at the top of this page.

View File

@@ -11,7 +11,7 @@ export const apiService = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ body: JSON.stringify({
identifier: pubkey, npub: pubkey,
}), }),
}); });
@@ -48,7 +48,7 @@ export const apiService = {
'X-Api-Key': apiKey, 'X-Api-Key': apiKey,
}, },
body: JSON.stringify({ body: JSON.stringify({
identifier: pubkey, npub: pubkey,
}), }),
}); });

View File

@@ -8,6 +8,7 @@ const api = axios.create({
headers: { headers: {
'X-Api-Key': API_KEY, 'X-Api-Key': API_KEY,
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Accept: 'application/json',
}, },
}); });
@@ -62,16 +63,26 @@ export const lnbitsService = {
extra = {}, extra = {},
}: CreateInvoiceParams): Promise<Invoice> { }: CreateInvoiceParams): Promise<Invoice> {
try { try {
const response = await api.post('/api/v1/payments', { // Build a V1-safe payload: only include known/supported fields when defined
const payload: Record<string, any> = {
out: false, out: false,
amount, amount,
memo, memo,
unit, };
webhook, if (unit) payload.unit = unit;
internal, if (webhook) payload.webhook = webhook;
extra, if (typeof internal === 'boolean') payload.internal = internal;
}); if (extra && Object.keys(extra).length > 0) payload.extra = extra;
return response.data;
const response = await api.post('/api/v1/payments', payload);
const data = response.data ?? {};
// Normalize response to ensure payment_request is always populated for the UI
const normalized: Invoice = {
...data,
payment_request: data.payment_request || data.bolt11,
bolt11: data.bolt11 || data.payment_request,
};
return normalized;
} catch (error) { } catch (error) {
console.error('Error creating invoice:', error); console.error('Error creating invoice:', error);
throw error; throw error;
@@ -82,10 +93,13 @@ export const lnbitsService = {
async checkPayment(paymentHash: string): Promise<PaymentStatus> { async checkPayment(paymentHash: string): Promise<PaymentStatus> {
try { try {
const response = await api.get(`/api/v1/payments/${paymentHash}`); const response = await api.get(`/api/v1/payments/${paymentHash}`);
const data = response.data || {};
// Normalize potential V1 shapes
const paid: boolean = (data.paid === true) || (data.status === 'paid') || (data.settled === true);
return { return {
paid: response.data.paid, paid,
preimage: response.data.preimage, preimage: data.preimage,
details: response.data.details, details: data.details,
}; };
} catch (error) { } catch (error) {
console.error('Error checking payment:', error); console.error('Error checking payment:', error);
@@ -150,17 +164,24 @@ export const lnbitsService = {
// Long poll payment status // Long poll payment status
async longPollPayment(paymentHash: string, timeout = 60000): Promise<boolean> { async longPollPayment(paymentHash: string, timeout = 60000): Promise<boolean> {
try { // Fallback to authenticated polling against V1 status endpoint to ensure compatibility
const response = await api.get(`/public/v1/payment/${paymentHash}`, { const start = Date.now();
timeout, const pollIntervalMs = 2000;
}); while (Date.now() - start < timeout) {
return response.data.paid; try {
} catch (error) { const status = await this.checkPayment(paymentHash);
if (axios.isAxiosError(error) && error.code === 'ECONNABORTED') { if (status.paid) return true;
return false; // Timeout reached } catch (error) {
// If transient error, keep polling until timeout
if (axios.isAxiosError(error) && (error.response?.status ?? 0) >= 500) {
// continue
} else {
console.error('Error polling payment:', error);
throw error;
}
} }
console.error('Error polling payment:', error); await new Promise((r) => setTimeout(r, pollIntervalMs));
throw error;
} }
return false;
}, },
}; };