feat(withdraw): add HTTP API for creating withdraw links
Add POST /api/v1/withdraw/create endpoint to allow external apps (ATM, web clients) to create LNURL-withdraw links via HTTP instead of RPC. Changes: - Add handleCreateWithdrawLink HTTP handler - Fix route ordering: callback routes before wildcard :unique_hash - Extract app_id from Authorization header (Bearer app_<id>) - Use is_unique=false for simple single-use ATM links Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5ecb8166b1
commit
df2df9bc26
1 changed files with 85 additions and 6 deletions
|
|
@ -82,11 +82,17 @@ export default class WithdrawExtension implements Extension {
|
||||||
*/
|
*/
|
||||||
getHttpRoutes(): HttpRoute[] {
|
getHttpRoutes(): HttpRoute[] {
|
||||||
return [
|
return [
|
||||||
// Initial LNURL request (simple link)
|
// Create withdraw link (HTTP API for ATM/external integrations)
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
path: '/api/v1/withdraw/create',
|
||||||
|
handler: this.handleCreateWithdrawLink.bind(this)
|
||||||
|
},
|
||||||
|
// LNURL callback (user submits invoice) - MUST be before :unique_hash routes
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/api/v1/lnurl/:unique_hash',
|
path: '/api/v1/lnurl/cb/:unique_hash',
|
||||||
handler: this.handleLnurlRequest.bind(this)
|
handler: this.handleLnurlCallback.bind(this)
|
||||||
},
|
},
|
||||||
// Initial LNURL request (unique link with use hash)
|
// Initial LNURL request (unique link with use hash)
|
||||||
{
|
{
|
||||||
|
|
@ -94,11 +100,11 @@ export default class WithdrawExtension implements Extension {
|
||||||
path: '/api/v1/lnurl/:unique_hash/:id_unique_hash',
|
path: '/api/v1/lnurl/:unique_hash/:id_unique_hash',
|
||||||
handler: this.handleLnurlUniqueRequest.bind(this)
|
handler: this.handleLnurlUniqueRequest.bind(this)
|
||||||
},
|
},
|
||||||
// LNURL callback (user submits invoice)
|
// Initial LNURL request (simple link) - MUST be last (catches all)
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/api/v1/lnurl/cb/:unique_hash',
|
path: '/api/v1/lnurl/:unique_hash',
|
||||||
handler: this.handleLnurlCallback.bind(this)
|
handler: this.handleLnurlRequest.bind(this)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -231,6 +237,79 @@ export default class WithdrawExtension implements Extension {
|
||||||
// HTTP Route Handlers
|
// HTTP Route Handlers
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle create withdraw link request (HTTP API)
|
||||||
|
* POST /api/v1/withdraw/create
|
||||||
|
*
|
||||||
|
* Body: {
|
||||||
|
* title: string
|
||||||
|
* min_withdrawable: number (sats)
|
||||||
|
* max_withdrawable: number (sats)
|
||||||
|
* uses?: number (defaults to 1)
|
||||||
|
* wait_time?: number (seconds between uses, defaults to 0)
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Auth: Bearer token in Authorization header (app_<app_id>)
|
||||||
|
*
|
||||||
|
* Returns: {
|
||||||
|
* link: { lnurl, unique_hash, id, ... }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
private async handleCreateWithdrawLink(req: HttpRequest): Promise<HttpResponse> {
|
||||||
|
try {
|
||||||
|
const { title, min_withdrawable, max_withdrawable, uses, wait_time } = req.body
|
||||||
|
|
||||||
|
// Extract app_id from Authorization header (Bearer app_<app_id>)
|
||||||
|
const authHeader = req.headers?.authorization || req.headers?.Authorization || ''
|
||||||
|
let app_id = 'default'
|
||||||
|
if (authHeader.startsWith('Bearer app_')) {
|
||||||
|
app_id = authHeader.replace('Bearer app_', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!title || !min_withdrawable) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
body: { status: 'ERROR', reason: 'Missing required fields: title, min_withdrawable' },
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const link = await this.manager.create(app_id, {
|
||||||
|
title,
|
||||||
|
min_withdrawable,
|
||||||
|
max_withdrawable: max_withdrawable || min_withdrawable,
|
||||||
|
uses: uses || 1,
|
||||||
|
wait_time: wait_time || 0,
|
||||||
|
is_unique: false // Simple single-use links for ATM
|
||||||
|
})
|
||||||
|
|
||||||
|
// Return in format expected by ATM client
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
status: 'OK',
|
||||||
|
link: {
|
||||||
|
lnurl: link.lnurl,
|
||||||
|
unique_hash: link.unique_hash,
|
||||||
|
id: link.id,
|
||||||
|
title: link.title,
|
||||||
|
min_withdrawable: link.min_withdrawable,
|
||||||
|
max_withdrawable: link.max_withdrawable,
|
||||||
|
uses: link.uses,
|
||||||
|
used: link.used
|
||||||
|
}
|
||||||
|
},
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
body: { status: 'ERROR', reason: error.message },
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle initial LNURL request (simple link)
|
* Handle initial LNURL request (simple link)
|
||||||
* GET /api/v1/lnurl/:unique_hash
|
* GET /api/v1/lnurl/:unique_hash
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue