fix: surface LDK Node payment failure reasons to user
This commit is contained in:
+34
-17
@@ -20,6 +20,7 @@ import type {
|
||||
ClosedChannelDetails,
|
||||
PaymentDetails,
|
||||
LdkNodeEvent,
|
||||
PaymentFailureReason,
|
||||
ClosureReason,
|
||||
Lsps1OrderResponse,
|
||||
Lsps1OrderStatus
|
||||
@@ -1783,6 +1784,7 @@ export default class LdkNode {
|
||||
|
||||
/**
|
||||
* Poll listPayments until the given payment succeeds or fails.
|
||||
* Captures failure reason from events for better error messages.
|
||||
* Returns the completed payment or throws on failure/timeout.
|
||||
*/
|
||||
private awaitPaymentCompletion = async (
|
||||
@@ -1791,29 +1793,44 @@ export default class LdkNode {
|
||||
const maxAttempts = 60;
|
||||
const delayMs = 1000;
|
||||
let payment = null;
|
||||
let failureReason: PaymentFailureReason | undefined;
|
||||
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
const payments = await LdkNodeInjection.payments.listPayments();
|
||||
payment = payments.find((p) => p.id === paymentId);
|
||||
|
||||
if (payment?.status === 'succeeded') {
|
||||
break;
|
||||
// Subscribe to events to capture the failure reason
|
||||
const unsubscribe = this.subscribeToEvents((event: LdkNodeEvent) => {
|
||||
if (
|
||||
event.type === 'paymentFailed' &&
|
||||
event.paymentId === paymentId
|
||||
) {
|
||||
failureReason = event.reason;
|
||||
}
|
||||
if (payment?.status === 'failed') {
|
||||
throw new Error(localeString('error.paymentFailed'));
|
||||
});
|
||||
|
||||
try {
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
const payments = await LdkNodeInjection.payments.listPayments();
|
||||
payment = payments.find((p) => p.id === paymentId);
|
||||
|
||||
if (payment?.status === 'succeeded') {
|
||||
break;
|
||||
}
|
||||
if (payment?.status === 'failed') {
|
||||
throw new Error(failureReason || 'PAYMENT_FAILED_UNKNOWN');
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||
}
|
||||
if (payment?.status !== 'succeeded') {
|
||||
throw new Error(localeString('error.paymentTimedOut'));
|
||||
}
|
||||
|
||||
if (payment?.status !== 'succeeded') {
|
||||
throw new Error(localeString('error.paymentTimedOut'));
|
||||
return {
|
||||
hash: payment?.kind.hash || paymentId,
|
||||
preimage: payment?.kind.preimage || ''
|
||||
};
|
||||
} finally {
|
||||
unsubscribe();
|
||||
}
|
||||
|
||||
return {
|
||||
hash: payment?.kind.hash || paymentId,
|
||||
preimage: payment?.kind.preimage || ''
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1753,6 +1753,14 @@
|
||||
"error.failureReasonIncorrectPaymentDetails": "Payment failed: Payment details incorrect (unknown payment hash, invalid amount or invalid final CLTV delta).",
|
||||
"error.failureReasonIncorrectPaymentDetailsKeysend": "The receiving node might not accept keysend payments.",
|
||||
"error.failureReasonInsufficientBalance": "Insufficient local balance",
|
||||
"error.ldk.recipientRejected": "Payment rejected by recipient",
|
||||
"error.ldk.retriesExhausted": "All routing attempts exhausted",
|
||||
"error.ldk.routeNotFound": "No route found to destination",
|
||||
"error.ldk.paymentExpired": "Payment expired before completion",
|
||||
"error.ldk.unknownRequiredFeatures": "Invoice requires unsupported features",
|
||||
"error.ldk.invoiceRequestExpired": "Invoice request expired",
|
||||
"error.ldk.invoiceRequestRejected": "Invoice request was rejected",
|
||||
"error.ldk.blindedPathCreationFailed": "Failed to create blinded payment path",
|
||||
"error.invalidMacaroon": "Invalid macaroon. Please check that you've entered the correct macaroon for this node.",
|
||||
"error.invalidResponse": "Received invalid response data from the server",
|
||||
"error.paymentFailed": "Payment failed",
|
||||
|
||||
@@ -18,6 +18,16 @@ const userFriendlyErrors: any = {
|
||||
'error.failureReasonIncorrectPaymentDetails',
|
||||
FAILURE_REASON_INSUFFICIENT_BALANCE:
|
||||
'error.failureReasonInsufficientBalance',
|
||||
// LDK Node payment failure reasons
|
||||
recipientRejected: 'error.ldk.recipientRejected',
|
||||
retriesExhausted: 'error.ldk.retriesExhausted',
|
||||
routeNotFound: 'error.ldk.routeNotFound',
|
||||
paymentExpired: 'error.ldk.paymentExpired',
|
||||
unknownRequiredFeatures: 'error.ldk.unknownRequiredFeatures',
|
||||
invoiceRequestExpired: 'error.ldk.invoiceRequestExpired',
|
||||
invoiceRequestRejected: 'error.ldk.invoiceRequestRejected',
|
||||
blindedPathCreationFailed: 'error.ldk.blindedPathCreationFailed',
|
||||
PAYMENT_FAILED_UNKNOWN: 'error.paymentFailed',
|
||||
Error: 'general.error'
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user