feat(lightning): add withdraw link lifecycle management (delete/update/invalidate)

- Add deleteWithdrawLink and updateWithdrawLink RPC methods to LightningPubClient
- Extract shared WithdrawLink type, add Delete/Update request/response types
- Track linkId in LNURL sessions for server-side cleanup
- Invalidate previous LNURL session on new link creation
- Auto-delete expired links on the server

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-03-07 13:01:40 -05:00
commit 99ae5ab3de
4 changed files with 132 additions and 37 deletions

View file

@ -37,6 +37,9 @@ import {
type CreateWithdrawLinkParams,
type CreateWithdrawLinkResponse,
type GetWithdrawLinkResponse,
type DeleteWithdrawLinkResponse,
type UpdateWithdrawLinkParams,
type UpdateWithdrawLinkResponse,
isRPCError,
} from './types.js'
@ -320,6 +323,33 @@ export class LightningPubClient {
return response
}
/**
* Delete a withdraw link via the withdraw extension
*
* Permanently removes the link so it can no longer be claimed.
* Uses the link's `id` (not `unique_hash`).
*/
async deleteWithdrawLink(linkId: string): Promise<DeleteWithdrawLinkResponse> {
console.log('[LightningPub] Deleting withdraw link:', linkId)
const response = await this.sendRPC<DeleteWithdrawLinkResponse>('withdraw.deleteLink', {
id: linkId,
})
console.log('[LightningPub] Withdraw link deleted:', linkId)
return response
}
/**
* Update a withdraw link via the withdraw extension
*
* Can modify title, amounts, uses, or wait_time.
* Uses the link's `id` (not `unique_hash`).
*/
async updateWithdrawLink(params: UpdateWithdrawLinkParams): Promise<UpdateWithdrawLinkResponse> {
console.log('[LightningPub] Updating withdraw link:', params.id)
const response = await this.sendRPC<UpdateWithdrawLinkResponse>('withdraw.updateLink', params)
return response
}
/**
* Get payment state for an invoice
*

View file

@ -71,9 +71,13 @@ export {
type PayInvoiceResponse,
// LNURL types
type LnurlLinkResponse,
type WithdrawLink,
type CreateWithdrawLinkParams,
type CreateWithdrawLinkResponse,
type GetWithdrawLinkResponse,
type DeleteWithdrawLinkResponse,
type UpdateWithdrawLinkParams,
type UpdateWithdrawLinkResponse,
// Exchange types
type ExchangeRate,
// Config

View file

@ -203,48 +203,64 @@ export interface CreateWithdrawLinkParams {
wait_time?: number
}
/** Withdraw link fields shared across responses */
export interface WithdrawLink {
/** Link ID (used for update/delete operations) */
id: string
/** Unique hash identifier (used in LNURL URLs) */
unique_hash: string
/** Bech32-encoded LNURL string */
lnurl: string
/** Title of the link */
title: string
/** Minimum withdrawable in sats */
min_withdrawable: number
/** Maximum withdrawable in sats */
max_withdrawable: number
/** Number of uses allowed */
uses: number
/** Number of times used */
used?: number
/** Wait time between uses */
wait_time: number
/** Whether the link has been fully used */
is_spent?: boolean
}
/** Response from withdraw.createLink RPC */
export interface CreateWithdrawLinkResponse {
/** The created withdraw link */
link: {
/** Unique hash identifier for the link */
unique_hash: string
/** Bech32-encoded LNURL string */
lnurl: string
/** Title of the link */
title: string
/** Minimum withdrawable in sats */
min_withdrawable: number
/** Maximum withdrawable in sats */
max_withdrawable: number
/** Number of uses remaining */
uses: number
/** Wait time between uses */
wait_time: number
}
link: WithdrawLink
}
/** Response from withdraw.getLink RPC */
export interface GetWithdrawLinkResponse {
/** The withdraw link details */
link: {
/** Unique hash identifier */
unique_hash: string
/** Bech32-encoded LNURL string */
lnurl: string
/** Title of the link */
title: string
/** Minimum withdrawable in sats */
min_withdrawable: number
/** Maximum withdrawable in sats */
max_withdrawable: number
/** Number of uses remaining (0 = spent) */
uses: number
/** Wait time between uses */
wait_time: number
/** Whether the link has been used/spent */
is_spent?: boolean
}
link: WithdrawLink
}
/** Response from withdraw.deleteLink RPC */
export interface DeleteWithdrawLinkResponse {
success: boolean
}
/** Parameters for withdraw.updateLink RPC */
export interface UpdateWithdrawLinkParams {
/** Link ID to update */
id: string
/** New title */
title?: string
/** New minimum withdrawable in sats */
min_withdrawable?: number
/** New maximum withdrawable in sats */
max_withdrawable?: number
/** New number of uses (cannot reduce below current used count) */
uses?: number
/** New wait time between uses */
wait_time?: number
}
/** Response from withdraw.updateLink RPC */
export interface UpdateWithdrawLinkResponse {
link: WithdrawLink
}
// ============================================================================