fix(lnbits): normalize invoice response (fallback to bolt11) and update payment status check; chore: add .gitignore
This commit is contained in:
64
.gitignore
vendored
Normal file
64
.gitignore
vendored
Normal 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
|
||||||
|
|
||||||
|
|
||||||
@@ -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> {
|
||||||
|
// Fallback to authenticated polling against V1 status endpoint to ensure compatibility
|
||||||
|
const start = Date.now();
|
||||||
|
const pollIntervalMs = 2000;
|
||||||
|
while (Date.now() - start < timeout) {
|
||||||
try {
|
try {
|
||||||
const response = await api.get(`/public/v1/payment/${paymentHash}`, {
|
const status = await this.checkPayment(paymentHash);
|
||||||
timeout,
|
if (status.paid) return true;
|
||||||
});
|
|
||||||
return response.data.paid;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (axios.isAxiosError(error) && error.code === 'ECONNABORTED') {
|
// If transient error, keep polling until timeout
|
||||||
return false; // Timeout reached
|
if (axios.isAxiosError(error) && (error.response?.status ?? 0) >= 500) {
|
||||||
}
|
// continue
|
||||||
|
} else {
|
||||||
console.error('Error polling payment:', error);
|
console.error('Error polling payment:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user