feat(extensions): add LNURL-withdraw extension #6
3 changed files with 50 additions and 7 deletions
|
|
@ -20,6 +20,17 @@ export interface MainHandlerInterface {
|
|||
// Application management
|
||||
applicationManager: {
|
||||
getById(id: string): Promise<any>
|
||||
PayAppUserInvoice(appId: string, req: {
|
||||
amount: number
|
||||
invoice: string
|
||||
user_identifier: string
|
||||
debit_npub?: string
|
||||
}): Promise<{
|
||||
preimage: string
|
||||
amount_paid: number
|
||||
network_fee: number
|
||||
service_fee: number
|
||||
}>
|
||||
}
|
||||
|
||||
// Payment operations
|
||||
|
|
@ -41,6 +52,7 @@ export interface MainHandlerInterface {
|
|||
applicationId: string
|
||||
paymentRequest: string
|
||||
maxFeeSats?: number
|
||||
userPubkey?: string
|
||||
}): Promise<{
|
||||
paymentHash: string
|
||||
feeSats: number
|
||||
|
|
@ -156,16 +168,19 @@ export class ExtensionContextImpl implements ExtensionContext {
|
|||
|
||||
/**
|
||||
* Pay a Lightning invoice
|
||||
* If userPubkey is provided, pays from that user's balance instead of app.owner
|
||||
*/
|
||||
async payInvoice(
|
||||
applicationId: string,
|
||||
paymentRequest: string,
|
||||
maxFeeSats?: number
|
||||
maxFeeSats?: number,
|
||||
userPubkey?: string
|
||||
): Promise<{ paymentHash: string; feeSats: number }> {
|
||||
return this.mainHandler.paymentManager.payInvoice({
|
||||
applicationId,
|
||||
paymentRequest,
|
||||
maxFeeSats
|
||||
maxFeeSats,
|
||||
userPubkey
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ export function createMainHandlerAdapter(mainHandler: Main): MainHandlerInterfac
|
|||
// GetApplication throws if not found
|
||||
return null
|
||||
}
|
||||
},
|
||||
|
||||
async PayAppUserInvoice(appId, req) {
|
||||
return mainHandler.applicationManager.PayAppUserInvoice(appId, req)
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -73,6 +77,7 @@ export function createMainHandlerAdapter(mainHandler: Main): MainHandlerInterfac
|
|||
applicationId: string
|
||||
paymentRequest: string
|
||||
maxFeeSats?: number
|
||||
userPubkey?: string
|
||||
}) {
|
||||
// Get the app to find the user ID and app reference
|
||||
const app = await mainHandler.storage.applicationStorage.GetApplication(params.applicationId)
|
||||
|
|
@ -80,19 +85,41 @@ export function createMainHandlerAdapter(mainHandler: Main): MainHandlerInterfac
|
|||
throw new Error(`Application not found: ${params.applicationId}`)
|
||||
}
|
||||
|
||||
// Pay invoice from the app's balance
|
||||
if (params.userPubkey) {
|
||||
// Resolve the Nostr user's ApplicationUser to get their identifier
|
||||
const appUser = await mainHandler.storage.applicationStorage.GetOrCreateNostrAppUser(app, params.userPubkey)
|
||||
console.log(`[MainHandlerAdapter] Paying via PayAppUserInvoice from Nostr user ${params.userPubkey.slice(0, 8)}... (identifier: ${appUser.identifier})`)
|
||||
|
||||
// Use applicationManager.PayAppUserInvoice so notifyAppUserPayment fires
|
||||
// This sends LiveUserOperation events via Nostr for real-time balance updates
|
||||
const result = await mainHandler.applicationManager.PayAppUserInvoice(
|
||||
params.applicationId,
|
||||
{
|
||||
invoice: params.paymentRequest,
|
||||
amount: 0, // Use invoice amount
|
||||
user_identifier: appUser.identifier
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
paymentHash: result.preimage || '',
|
||||
feeSats: result.network_fee || 0
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: pay from app owner's balance (no Nostr user context)
|
||||
const result = await mainHandler.paymentManager.PayInvoice(
|
||||
app.owner.user_id,
|
||||
{
|
||||
invoice: params.paymentRequest,
|
||||
amount: 0 // Use invoice amount
|
||||
amount: 0
|
||||
},
|
||||
app, // linkedApplication
|
||||
app,
|
||||
{}
|
||||
)
|
||||
|
||||
return {
|
||||
paymentHash: result.preimage || '', // preimage serves as proof of payment
|
||||
paymentHash: result.preimage || '',
|
||||
feeSats: result.network_fee || 0
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -140,8 +140,9 @@ export interface ExtensionContext {
|
|||
|
||||
/**
|
||||
* Pay a Lightning invoice (requires sufficient balance)
|
||||
* If userPubkey is provided, pays from that user's balance instead of app.owner
|
||||
*/
|
||||
payInvoice(applicationId: string, paymentRequest: string, maxFeeSats?: number): Promise<{
|
||||
payInvoice(applicationId: string, paymentRequest: string, maxFeeSats?: number, userPubkey?: string): Promise<{
|
||||
paymentHash: string
|
||||
feeSats: number
|
||||
}>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue