Skip to content

Add outgoing payments API and frontend#1

Merged
martinsaposnic merged 3 commits intomasterfrom
feat/outgoing-payments
Mar 30, 2026
Merged

Add outgoing payments API and frontend#1
martinsaposnic merged 3 commits intomasterfrom
feat/outgoing-payments

Conversation

@martinsaposnic
Copy link
Copy Markdown
Contributor

Summary

Adds the ability to list and track outgoing payments (on-chain sends) through both the REST API and the demo wallet frontend.

Problem

After calling POST /sendtoaddress, the only feedback was the TXID string in the response. There was no way to:

  • List past outgoing payments
  • See their confirmation status
  • Track them in the wallet UI

Additionally, LDK's internal payment store doesn't register on-chain sends until the next chain sync cycle, meaning there was a gap between "transaction broadcast" and "transaction visible" - sometimes several minutes on signet/mainnet.

Solution

New API endpoints (read-only, both auth tiers):

  • GET /payments/outgoing - List all outgoing payments with limit/offset pagination. Returns on-chain sends and any outbound Lightning payments.
  • GET /payments/outgoing/{payment_id} - Get a single outgoing payment by LDK payment ID.

Both are auto-documented at /scalar (OpenAPI 3.1).

Response shape:

{
  "id": "hex-encoded payment id",
  "kind": { "onchain": { "txid": "..." } },
  "status": "pending" | "succeeded" | "failed",
  "amountSat": 1000,
  "feeSat": 143,
  "updatedAt": 1774888707
}

For Lightning outbound payments, kind would be { "lightning": { "paymentHash": "..." } }.

Immediate visibility for on-chain sends:

A new mdk_outgoing_sends SQLite table stores the txid, address, amount, and timestamp immediately when sendtoaddress returns. The outgoing list merges LDK's payment store with these local records (deduplicating by txid), so sends appear instantly as PENDING before LDK chain sync confirms them.

Frontend (wallet.html):

  • "Outgoing Payments" section added below "Incoming Payments" on the Payments tab
  • Table columns: Status, Type (On-chain/Lightning), Amount, Fee, Updated, ID
  • Txid/ID is click-to-copy (hover highlights orange)
  • Pagination with Prev/Next
  • Auto-refreshes after on-chain sends

Files changed

File What changed
src/types.rs Added OutgoingPaymentResponse, OutgoingPaymentKind, OutgoingPaymentStatus, ListOutgoingPaymentsRequest types
src/api/mod.rs Added routes and handler wrappers for list_outgoing_payments and get_outgoing_payment
src/api/invoices.rs Added handle_list_outgoing_payments, handle_get_outgoing_payment, and payment_to_outgoing conversion logic
src/api/onchain.rs handle_send_to_address now stores the send in mdk_outgoing_sends immediately after broadcast
src/store/invoice_metadata.rs Added mdk_outgoing_sends table, OutgoingSendRecord struct, insert_outgoing_send and list_outgoing_sends methods
wallet.html Outgoing payments section with table, pagination, click-to-copy txid, auto-refresh

Design decisions

  • Merge strategy over separate store: Rather than only using local SQLite, the listing merges LDK's authoritative payment data with locally stored sends. LDK provides accurate status (pending/succeeded/failed) and fee info once it picks up the tx. Local records fill the gap before that happens.
  • Read-only endpoints: Outgoing payment listing uses the same read-only auth tier as incoming payments - no need for full access to view payment history.
  • No separate migration system: Follows the existing pattern of CREATE TABLE IF NOT EXISTS for idempotent schema creation.

Test plan

  • cargo build --features demo passes
  • Verified on staging/signet: send on-chain, tx appears immediately as PENDING
  • After chain sync, status updates to SUCCEEDED with correct fee
  • GET /payments/outgoing returns correct data
  • Frontend pagination works
  • Click-to-copy on txid works
  • Existing incoming payments unaffected
  • CI checks (fmt, clippy, unit tests) - will run on Linux in CI

Note: The pre-commit hook (just check) includes unit tests that fail on macOS due to secret.rs tests using /proc/self/fd/ (Linux-only). This is a pre-existing issue unrelated to this PR. CI runs on ubuntu-latest where these tests pass.

Add REST endpoints to list and query outgoing payments (on-chain and
Lightning), with immediate visibility for on-chain sends before chain
sync picks them up.

API:
- GET /payments/outgoing - list outgoing payments with pagination
- GET /payments/outgoing/{payment_id} - get single outgoing payment
- Both are read-only (work with either auth tier)
- Response includes kind (onchain with txid, or lightning with
  payment_hash), status, amount, fee, and timestamp
- Auto-documented via OpenAPI at /scalar

On-chain send tracking:
- New mdk_outgoing_sends SQLite table stores txid, address, amount,
  and timestamp immediately when sendtoaddress returns
- Outgoing list merges LDK payment store with local records,
  deduplicating by txid, so sends appear instantly as PENDING
  before LDK chain sync confirms them

Frontend (wallet.html):
- Outgoing Payments section added to Payments tab with pagination
- Columns: Status, Type, Amount, Fee, Updated, ID
- Txid is click-to-copy with hover highlight
- Auto-refreshes after on-chain sends
@martinsaposnic martinsaposnic force-pushed the feat/outgoing-payments branch from 3aaf38a to 8bb561b Compare March 30, 2026 16:56
Flatten response shape to match phoenixd's outgoing payment format:
- paymentId, paymentHash, preimage, txId, isPaid, sent, fees,
  invoice, completedAt, createdAt
- Add from/to/all query params to GET /payments/outgoing
- Update frontend to use new field names
Verify that:
- The outgoing payment appears immediately after send (before chain sync)
- The txid and amount match the send request
- After confirmation, isPaid is true with fees and completedAt set
@martinsaposnic martinsaposnic merged commit 866f495 into master Mar 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants