feat(extensions): pay from caller's balance via PayAppUserInvoice
When userPubkey is provided, resolve the ApplicationUser and call applicationManager.PayAppUserInvoice instead of paymentManager.PayInvoice directly. This ensures notifyAppUserPayment fires, sending LiveUserOperation events via Nostr for real-time balance updates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9d352e5f07
commit
ed7e0d1f86
3 changed files with 50 additions and 7 deletions
|
|
@ -20,6 +20,17 @@ export interface MainHandlerInterface {
|
||||||
// Application management
|
// Application management
|
||||||
applicationManager: {
|
applicationManager: {
|
||||||
getById(id: string): Promise<any>
|
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
|
// Payment operations
|
||||||
|
|
@ -41,6 +52,7 @@ export interface MainHandlerInterface {
|
||||||
applicationId: string
|
applicationId: string
|
||||||
paymentRequest: string
|
paymentRequest: string
|
||||||
maxFeeSats?: number
|
maxFeeSats?: number
|
||||||
|
userPubkey?: string
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
paymentHash: string
|
paymentHash: string
|
||||||
feeSats: number
|
feeSats: number
|
||||||
|
|
@ -156,16 +168,19 @@ export class ExtensionContextImpl implements ExtensionContext {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pay a Lightning invoice
|
* Pay a Lightning invoice
|
||||||
|
* If userPubkey is provided, pays from that user's balance instead of app.owner
|
||||||
*/
|
*/
|
||||||
async payInvoice(
|
async payInvoice(
|
||||||
applicationId: string,
|
applicationId: string,
|
||||||
paymentRequest: string,
|
paymentRequest: string,
|
||||||
maxFeeSats?: number
|
maxFeeSats?: number,
|
||||||
|
userPubkey?: string
|
||||||
): Promise<{ paymentHash: string; feeSats: number }> {
|
): Promise<{ paymentHash: string; feeSats: number }> {
|
||||||
return this.mainHandler.paymentManager.payInvoice({
|
return this.mainHandler.paymentManager.payInvoice({
|
||||||
applicationId,
|
applicationId,
|
||||||
paymentRequest,
|
paymentRequest,
|
||||||
maxFeeSats
|
maxFeeSats,
|
||||||
|
userPubkey
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ export function createMainHandlerAdapter(mainHandler: Main): MainHandlerInterfac
|
||||||
// GetApplication throws if not found
|
// GetApplication throws if not found
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async PayAppUserInvoice(appId, req) {
|
||||||
|
return mainHandler.applicationManager.PayAppUserInvoice(appId, req)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -73,6 +77,7 @@ export function createMainHandlerAdapter(mainHandler: Main): MainHandlerInterfac
|
||||||
applicationId: string
|
applicationId: string
|
||||||
paymentRequest: string
|
paymentRequest: string
|
||||||
maxFeeSats?: number
|
maxFeeSats?: number
|
||||||
|
userPubkey?: string
|
||||||
}) {
|
}) {
|
||||||
// Get the app to find the user ID and app reference
|
// Get the app to find the user ID and app reference
|
||||||
const app = await mainHandler.storage.applicationStorage.GetApplication(params.applicationId)
|
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}`)
|
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(
|
const result = await mainHandler.paymentManager.PayInvoice(
|
||||||
app.owner.user_id,
|
app.owner.user_id,
|
||||||
{
|
{
|
||||||
invoice: params.paymentRequest,
|
invoice: params.paymentRequest,
|
||||||
amount: 0 // Use invoice amount
|
amount: 0
|
||||||
},
|
},
|
||||||
app, // linkedApplication
|
app,
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
paymentHash: result.preimage || '', // preimage serves as proof of payment
|
paymentHash: result.preimage || '',
|
||||||
feeSats: result.network_fee || 0
|
feeSats: result.network_fee || 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -140,8 +140,9 @@ export interface ExtensionContext {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pay a Lightning invoice (requires sufficient balance)
|
* 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
|
paymentHash: string
|
||||||
feeSats: number
|
feeSats: number
|
||||||
}>
|
}>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue