Evaluate Lightning.Pub triggers as backup payment confirmation #32

Open
opened 2026-06-13 22:02:53 +00:00 by padreug · 1 comment
Owner

Migrated from aiolabs/lamassu-next#32 — opened by @padreug on 2026-02-24.\n\n## Summary

Lightning.Pub supports HTTP callback triggers — when an invoice is paid, it fires an HTTP GET to a registered URL. Evaluate whether this is worth adding as a redundant payment confirmation path alongside the existing Nostr subscription.

Current Approach

The ATM watches for invoice payment via Nostr relay subscription (kind 21000 LiveUserOperation events). When Lightning.Pub detects payment, it publishes an event → ATM receives it → fires INVOICE_PAID into the state machine. There's also polling-based balance watching as a fallback.

How Triggers Would Work

When creating an invoice, pass http_callback_url:

POST /api/user/invoice/new
{
  "amountSats": 500,
  "http_callback_url": "http://localhost:3100/hooks/paid?amount={amount}"
}

Lightning.Pub hits that URL on payment settlement. The ATM would run a small local HTTP server to receive callbacks.

Trade-offs

Pros:

  • Second confirmation path independent of Nostr relay connectivity
  • Instant — no relay latency or reconnection delay
  • Simple — Lightning.Pub already supports this, just needs a callback URL at invoice creation

Against (current assessment):

  • The Nostr subscription already reconnects aggressively
  • Missed events during brief disconnects should arrive on reconnect
  • Adds an HTTP server to the machine — currently the ATM has no need for one
  • Extra complexity for a failure mode that may not manifest in practice

Recommendation

Low priority. Revisit if relay reliability becomes an issue during field testing. The better investment is ensuring aggressive Nostr reconnection logic during active transactions (see the relay connection handling in packages/lightning/src/client.ts).

  • Lightning.Pub trigger docs: triggerPaidCallback() in lightning-pub/dev/src/services/main/index.ts
  • ATM invoice watching: watchInvoice() in packages/lightning/src/client.ts:376
> _Migrated from [aiolabs/lamassu-next#32](https://git.atitlan.io/aiolabs/lamassu-next/issues/32) — opened by @padreug on 2026-02-24._\n\n## Summary Lightning.Pub supports HTTP callback triggers — when an invoice is paid, it fires an HTTP GET to a registered URL. Evaluate whether this is worth adding as a redundant payment confirmation path alongside the existing Nostr subscription. ## Current Approach The ATM watches for invoice payment via Nostr relay subscription (`kind 21000` LiveUserOperation events). When Lightning.Pub detects payment, it publishes an event → ATM receives it → fires `INVOICE_PAID` into the state machine. There's also polling-based balance watching as a fallback. ## How Triggers Would Work When creating an invoice, pass `http_callback_url`: ``` POST /api/user/invoice/new { "amountSats": 500, "http_callback_url": "http://localhost:3100/hooks/paid?amount={amount}" } ``` Lightning.Pub hits that URL on payment settlement. The ATM would run a small local HTTP server to receive callbacks. ## Trade-offs **Pros:** - Second confirmation path independent of Nostr relay connectivity - Instant — no relay latency or reconnection delay - Simple — Lightning.Pub already supports this, just needs a callback URL at invoice creation **Against (current assessment):** - The Nostr subscription already reconnects aggressively - Missed events during brief disconnects should arrive on reconnect - Adds an HTTP server to the machine — currently the ATM has no need for one - Extra complexity for a failure mode that may not manifest in practice ## Recommendation Low priority. Revisit if relay reliability becomes an issue during field testing. The better investment is ensuring aggressive Nostr reconnection logic during active transactions (see the relay connection handling in `packages/lightning/src/client.ts`). ## Related - Lightning.Pub trigger docs: `triggerPaidCallback()` in `lightning-pub/dev/src/services/main/index.ts` - ATM invoice watching: `watchInvoice()` in `packages/lightning/src/client.ts:376`
Author
Owner

@padreug commented on 2026-05-13 (lamassu-next#32):

Rescope: under LNbits the backup-confirmation question is webhooks vs subscribe_payments

The original write-up evaluated Lightning.Pub's http_callback_url on /api/user/invoice/new as a redundant path alongside the LP kind 21000 GetLiveUserOperations subscription. With the ATM moving to LNbits as the payment backend (see #22), the LP-specific framing no longer applies. The underlying question stays valid; the surfaces change.

LNbits equivalent surfaces:

  1. Primary (nostr push): subscribe_payments({payment_hash}) over the nostr transport. Real-time, encrypted, no public HTTP endpoint, mirrors the LP subscription pattern. This is the recommended primary path.

  2. Backup A — LNbits per-invoice webhook: create_invoice({webhook: "https://..."}) accepts a callback URL that LNbits POSTs on settlement. Requires the ATM to expose an HTTP endpoint, which conflicts with the no-HTTP stance. Not recommended.

  3. Backup B — LNURL-withdraw webhook: the withdraw extension's link-level webhook_url fires on redemption (views_lnurl.py:152-196). Same trade-off — exposes a public ATM endpoint. Important caveat from the withdraw-extension review: webhook dispatch failures are silently logged (views_lnurl.py:191-196); the link's used counter is already incremented and LnurlSuccessResponse() already returned. The ATM must not treat webhook delivery as ground truth for settlement.

  4. Backup C — poll get_payment({payment_hash}): worst-case fallback if the subscription drops mid-flow and reconnect is in progress.

Recommendation given the new architecture:

  • Stick with subscribe_payments as the primary path — same shape as the LP subscription this issue contemplated, with the new outgoing-payment fanout covering ndebit-equivalent and LNURL-withdraw cases (LNbits PR #4, commit 085fd501).
  • Treat any HTTP webhook as observability-only, never settlement truth.
  • Poll get_payment only after subscription reconnect, to catch settlements that fired during the relay disconnect window.

This shifts the issue from "LP triggers as backup" to "LNbits webhook surface as backup," with a stronger recommendation against using webhooks at all on a no-HTTP ATM. Worth keeping the issue open if a "subscription dropped mid-flow, what's the recovery story" exercise is needed; otherwise can be closed.

> _@padreug commented on 2026-05-13 ([lamassu-next#32](https://git.atitlan.io/aiolabs/lamassu-next/issues/32#issuecomment-557)):_ ## Rescope: under LNbits the backup-confirmation question is webhooks vs `subscribe_payments` The original write-up evaluated **Lightning.Pub's `http_callback_url`** on `/api/user/invoice/new` as a redundant path alongside the LP `kind 21000 GetLiveUserOperations` subscription. With the ATM moving to LNbits as the payment backend (see #22), the LP-specific framing no longer applies. The underlying question stays valid; the surfaces change. **LNbits equivalent surfaces:** 1. **Primary (nostr push):** `subscribe_payments({payment_hash})` over the nostr transport. Real-time, encrypted, no public HTTP endpoint, mirrors the LP subscription pattern. This is the recommended primary path. 2. **Backup A — LNbits per-invoice webhook:** `create_invoice({webhook: "https://..."})` accepts a callback URL that LNbits POSTs on settlement. Requires the ATM to expose an HTTP endpoint, which conflicts with the no-HTTP stance. Not recommended. 3. **Backup B — LNURL-withdraw webhook:** the `withdraw` extension's link-level `webhook_url` fires on redemption (`views_lnurl.py:152-196`). Same trade-off — exposes a public ATM endpoint. **Important caveat from the withdraw-extension review:** webhook dispatch failures are silently logged (`views_lnurl.py:191-196`); the link's `used` counter is already incremented and `LnurlSuccessResponse()` already returned. The ATM **must not** treat webhook delivery as ground truth for settlement. 4. **Backup C — poll `get_payment({payment_hash})`:** worst-case fallback if the subscription drops mid-flow and reconnect is in progress. **Recommendation given the new architecture:** - Stick with **`subscribe_payments` as the primary path** — same shape as the LP subscription this issue contemplated, with the new outgoing-payment fanout covering ndebit-equivalent and LNURL-withdraw cases (LNbits PR #4, commit `085fd501`). - Treat any HTTP webhook as **observability-only**, never settlement truth. - Poll `get_payment` only after subscription reconnect, to catch settlements that fired during the relay disconnect window. This shifts the issue from "LP triggers as backup" to "LNbits webhook surface as backup," with a stronger recommendation against using webhooks at all on a no-HTTP ATM. Worth keeping the issue open if a "subscription dropped mid-flow, what's the recovery story" exercise is needed; otherwise can be closed.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/bitspire#32
No description provided.