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
d3f00fde18
commit
b27e272dc1
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