feat: persist LNURL successAction with Lightning payments (LUD-09)#427
feat: persist LNURL successAction with Lightning payments (LUD-09)#427al-munazzim wants to merge 2 commits intoblinkbitcoin:mainfrom
Conversation
…aloyMoney#397) Implements LUD-09 compliance by capturing and storing successAction data from LNURL-pay flows, and exposing it through the GraphQL Transaction API. Changes: - Add LnurlSuccessAction domain types with validation - Update LNURL service to capture successAction from requestInvoice - Thread successAction through payment flow to persistence layer - Add lnurlSuccessAction field to LnPayment MongoDB schema - Expose successAction in GraphQL SettlementViaLn type - Add unit tests for success action validation Fixes GaloyMoney#397 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Implements LUD-09 compliance by modeling, persisting, and exposing LNURL successAction data end-to-end for Lightning payments.
Changes:
- Adds domain types and validation for LNURL success actions (
message,url,aes) plus unit tests forcheckedToLnurlSuccessAction. - Extends the LND/Mongoose
LnPaymentschema and repository layer to storelnurlSuccessActionand wires it through the Lightning payment execution path for LNURL-pay sends. - Introduces a GraphQL
LnurlSuccessActionobject type and aSettlementViaLn.successActionfield to expose stored success actions in the public schema.
Reviewed changes
Copilot reviewed 10 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| core/api/test/unit/domain/bitcoin/lnurl/success-action.spec.ts | Adds unit tests validating checkedToLnurlSuccessAction behavior for all tag types and error/null cases. |
| core/api/src/services/mongoose/ln-payments.ts | Maps the new lnurlSuccessAction field from raw LnPaymentType into partial and persisted domain payment types. |
| core/api/src/services/lnurl-pay/index.ts | Changes LNURL-pay fetcher to return { invoice, successAction }, validating and normalizing successAction via the new domain helper. |
| core/api/src/services/lnd/schema.types.d.ts | Extends LnPaymentType to include an optional lnurlSuccessAction field. |
| core/api/src/services/lnd/schema.ts | Adds a subdocument schema and field for lnurlSuccessAction on the LnPayment Mongoose model. |
| core/api/src/graphql/shared/types/object/lnurl-success-action.ts | Defines the GraphQL LnurlSuccessAction object type used in the schema. |
| core/api/src/graphql/shared/types/abstract/settlement-via.ts | Adds a successAction field to SettlementViaLn, resolving via LnPaymentsRepository by paymentHash. |
| core/api/src/graphql/public/schema.graphql | Updates the public GraphQL schema to declare LnurlSuccessAction and the SettlementViaLn.successAction field. |
| core/api/src/domain/bitcoin/lnurl/index.types.d.ts | Adds ambient types for LnurlSuccessAction, LnurlSuccessActionTag, and the LnurlPayInvoiceResponse return type. |
| core/api/src/domain/bitcoin/lnurl/index.ts | Introduces domain models, error type, and checkedToLnurlSuccessAction validator for LNURL success actions. |
| core/api/src/domain/bitcoin/lightning/payments/index.types.d.ts | Threads lnurlSuccessAction through LnPaymentPartial and PersistedLnPaymentLookup types. |
| core/api/src/app/wallets/index.types.d.ts | Extends PayInvoiceByWalletIdArgs with an optional lnurlSuccessAction parameter. |
| core/api/src/app/payments/send-lnurl.ts | Updates LNURL send flows to consume the richer LNURL-pay response and pass lnurlSuccessAction into the Lightning payment pipeline. |
| core/api/src/app/payments/send-lightning.ts | Threads the optional lnurlSuccessAction through execution and persistence of Lightning payments, storing it with the LnPayment record. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const successAction = checkedToLnurlSuccessAction(response.successAction) | ||
| if (successAction instanceof Error) { | ||
| // Log the error but don't fail the payment - successAction is optional | ||
| // The payment can still proceed without successAction | ||
| return { | ||
| invoice: response.invoice, | ||
| successAction: null, | ||
| } |
There was a problem hiding this comment.
The comment indicates that errors from checkedToLnurlSuccessAction are logged before returning a null successAction, but the implementation only returns { invoice, successAction: null } without any logging. To avoid misleading future maintainers (and to help diagnose malformed LNURL responses), either add appropriate logging/telemetry for the validation error here or update the comment to reflect that the error is intentionally ignored without logging.
| }): Promise<LnurlPayInvoiceResponse | LnurlServiceError> => { | ||
| try { | ||
| const invoice = await requestInvoice({ | ||
| const response = await requestInvoice({ | ||
| lnUrlOrAddress: lnAddressOrLnurl, | ||
| tokens: utils.toSats(toSats(amount.amount)), | ||
| }) | ||
|
|
||
| if (!invoice.hasValidAmount) { | ||
| if (!response.hasValidAmount) { | ||
| return new ErrorFetchingLnurlInvoice( | ||
| "Lnurl service returned an invoice with an invalid amount", | ||
| ) | ||
| } | ||
|
|
||
| return invoice.invoice | ||
| const successAction = checkedToLnurlSuccessAction(response.successAction) | ||
| if (successAction instanceof Error) { | ||
| // Log the error but don't fail the payment - successAction is optional | ||
| // The payment can still proceed without successAction | ||
| return { | ||
| invoice: response.invoice, | ||
| successAction: null, | ||
| } | ||
| } | ||
|
|
||
| return { | ||
| invoice: response.invoice, | ||
| successAction, | ||
| } |
There was a problem hiding this comment.
fetchInvoiceFromLnAddressOrLnurl now parses and returns an LnurlSuccessAction, but there are no tests covering the success path (only an invalid-lnurl error case in core/api/test/integration/services/lnurl-pay.spec.ts). Given this is the core entry point for LNURL-pay and is now responsible for enforcing LUD-09 semantics, please add tests that cover valid successAction payloads (message/url/aes) and invalid payloads (e.g., missing required fields, unknown tags) to ensure the new behavior and error-tolerant fallback (successAction: null) are exercised.
| type: LnurlSuccessAction, | ||
| description: "LNURL success action returned after payment (LUD-09/10)", | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| resolve: async (source: any) => { |
There was a problem hiding this comment.
Unexpected any. Specify a different type.
- Add error logging when successAction validation fails (lnurl-pay service) - Add unit tests for fetchInvoiceFromLnAddressOrLnurl covering all successAction types (message/url/aes), invalid payloads, and null case - Replace `any` type with proper SettlementViaLn intersection type in successAction resolver, remove unnecessary type cast Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
successActiondata with Lightning payment transactionsmessage,url, andaesSettlementViaLn.successActionfield for frontend consumptionTest plan
checkedToLnurlSuccessActionvalidation covering all tag types and error casesFixes #397
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com