fix: return already-completed send without re-paying on the node [AMB-2985] - #39
Merged
Merged
Conversation
…-2985] When create_send returns an existing COMPLETED transaction (a duplicate payment hash or an idempotency-key replay), short-circuit and resolve the send as a success instead of executing the node payment again, which would otherwise fail or double-pay. Depends on amboss-rails-api PR: fix: return completed transaction instead of throwing on duplicate payment hash [AMB-2985]. Must merge and release before that PR lands, or old SDK consumers will re-pay a settled invoice on the node once the backend starts returning it instead of throwing.
bufo24
force-pushed
the
jesseva/amb-2985-short-circuit-completed-send
branch
from
August 18, 2026 11:41
f5826f3 to
4a9ddd1
Compare
bufo24
marked this pull request as ready for review
August 18, 2026 11:41
wthrajat
approved these changes
Aug 18, 2026
Merged
12 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Transactions.send()now checks the transactioncreate_sendreturns: if itsstatusis alreadyCOMPLETED, the SDK short-circuits and resolves the send as a success instead of executing the node payment.This covers two cases where the backend returns an existing settled transaction instead of creating a new one:
Without this change, the SDK would take that already-settled transaction and try to pay the invoice again on the node, which fails.
Return shape
paymentPreimageis alwaysundefinedon this path. Checkedpackages/core/schema/rails.graphql—PaymentsTransactionhas no preimage field (there's an unmergedamboss-nodifybranch,feat/AMB-2698-paymentstransaction-preimage-adjacent work, that would add one, but it isn't live). Only a payment actually executed on the node (the existing path below the new check) can return a preimage.feeSatmaps directly fromtransaction.feewith no numeric conversion — both are sats. Verified againstamboss-nodify'ssrc/modules/lnd/handlers/offchain.tsonmain, which writespayments_transaction.feefrom LND'ssafe_fee(sats).amboss-nodify#826) was closed unmerged — it would have brokenamboss-cron's routing-fee reimbursement math by 1000x. It has since been decided that the SDK will not handle msat:NodePaymentResult.feeSatstays sats-only and will never carry a/1000conversion, so whatever the ledger stores underneath must still present sats to the SDK. Tracked in AMB-2990.Sequencing (important)
This PR must merge and be released before the sibling
amboss-rails-apiPR (fix: return completed transaction instead of throwing on duplicate payment hash [AMB-2985]).If the rails-api change lands first while consumers are still on an older SDK version, the SDK will receive the completed transaction from
create_sendand attempt to re-pay it on the node — producing a confusing node-side error where users previously got a clean "already paid" style result. Ship this SDK change and let it propagate to consumers first.Test plan
pnpm run typecheckpnpm run buildpnpm run test(36/36 passing, including 2 new cases)COMPLETEDtransaction fromcreate_send→ resolves success without invoking the nodeCOMPLETEDtransaction → still executes the node payment exactly as before (regression check)pnpm run format:check🤖 Generated with Claude Code