- Delete src/stores/market.ts compatibility re-export file - Update 15 files to import from proper module path @/modules/market/stores/market - Add necessary type exports to market store for external consumers - Remove empty src/stores/ directory completely - Enforce clean modular architecture without global store shortcuts 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
188 lines
5.5 KiB
TypeScript
188 lines
5.5 KiB
TypeScript
import { getApiUrl } from '@/lib/config/lnbits'
|
|
import type { Order } from '@/modules/market/stores/market'
|
|
|
|
export interface LightningInvoice {
|
|
checking_id: string
|
|
payment_hash: string
|
|
wallet_id: string
|
|
amount: number
|
|
fee: number
|
|
bolt11: string // This is the payment request/invoice
|
|
status: string
|
|
memo?: string
|
|
expiry?: string
|
|
preimage?: string
|
|
extra?: Record<string, any>
|
|
created_at?: string
|
|
updated_at?: string
|
|
}
|
|
|
|
export interface CreateInvoiceRequest {
|
|
amount: number
|
|
memo: string
|
|
unit?: 'sat' | 'btc'
|
|
expiry?: number
|
|
extra?: Record<string, any>
|
|
}
|
|
|
|
export interface PaymentStatus {
|
|
paid: boolean
|
|
amount_paid: number
|
|
paid_at?: number
|
|
payment_hash: string
|
|
}
|
|
|
|
export class InvoiceService {
|
|
private baseUrl: string
|
|
|
|
constructor() {
|
|
// Use the payments endpoint for invoice creation
|
|
this.baseUrl = getApiUrl('/payments')
|
|
}
|
|
|
|
private async request<T>(
|
|
endpoint: string,
|
|
adminKey: string,
|
|
options: RequestInit = {}
|
|
): Promise<T> {
|
|
// Construct the URL - for payments, we just append the endpoint
|
|
const url = `${this.baseUrl}${endpoint}`
|
|
console.log('Invoice Service Request:', { url, endpoint })
|
|
|
|
const headers: HeadersInit = {
|
|
'Content-Type': 'application/json',
|
|
'X-Api-Key': adminKey, // Use the wallet's admin key
|
|
...options.headers,
|
|
}
|
|
|
|
const response = await fetch(url, {
|
|
...options,
|
|
headers,
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text()
|
|
console.error('Invoice Service Error:', {
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
errorText,
|
|
url
|
|
})
|
|
throw new Error(`Invoice request failed: ${response.status} ${response.statusText}`)
|
|
}
|
|
|
|
return response.json()
|
|
}
|
|
|
|
/**
|
|
* Create a Lightning invoice for an order
|
|
*/
|
|
async createInvoice(order: Order, adminKey: string, extra?: Record<string, any>): Promise<LightningInvoice> {
|
|
const invoiceData: CreateInvoiceRequest = {
|
|
amount: order.total,
|
|
unit: 'sat',
|
|
memo: `Order ${order.id} - ${order.items.length} items`,
|
|
expiry: extra?.expiry || 3600, // Allow configurable expiry, default 1 hour
|
|
extra: {
|
|
tag: 'nostrmarket', // Use nostrmarket tag for compatibility
|
|
order_id: extra?.order_id || order.id, // Use passed order_id or fallback to order.id
|
|
merchant_pubkey: extra?.merchant_pubkey || order.sellerPubkey, // Use passed merchant_pubkey or fallback
|
|
...extra // Allow additional metadata to be passed in
|
|
}
|
|
}
|
|
|
|
try {
|
|
// Log the exact data being sent to LNBits
|
|
const requestBody = {
|
|
out: false, // Incoming payment
|
|
...invoiceData
|
|
}
|
|
|
|
console.log('Sending invoice request to LNBits:', {
|
|
url: `${this.baseUrl}`,
|
|
body: requestBody,
|
|
extra: requestBody.extra
|
|
})
|
|
|
|
// Use the correct LNBits payments endpoint
|
|
const response = await this.request<LightningInvoice>('', adminKey, {
|
|
method: 'POST',
|
|
body: JSON.stringify(requestBody)
|
|
})
|
|
|
|
console.log('Full LNBits response:', response)
|
|
console.log('Response type:', typeof response)
|
|
console.log('Response keys:', Object.keys(response))
|
|
console.log('Response expiry field:', response.expiry)
|
|
console.log('Response created_at field:', response.created_at)
|
|
|
|
// Check if we have the expected fields
|
|
if (!response.bolt11) {
|
|
console.error('Missing bolt11 in response:', response)
|
|
throw new Error('Invalid invoice response: missing bolt11')
|
|
}
|
|
|
|
console.log('Lightning invoice created with nostrmarket tag:', {
|
|
orderId: order.id,
|
|
paymentHash: response.payment_hash,
|
|
amount: response.amount,
|
|
paymentRequest: response.bolt11.substring(0, 50) + '...',
|
|
extra: invoiceData.extra
|
|
})
|
|
|
|
return response
|
|
} catch (error) {
|
|
console.error('Failed to create Lightning invoice:', error)
|
|
throw new Error('Failed to create payment invoice')
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check payment status of an invoice
|
|
*/
|
|
async checkPaymentStatus(paymentHash: string, adminKey: string): Promise<PaymentStatus> {
|
|
try {
|
|
const response = await this.request<PaymentStatus>(`/${paymentHash}`, adminKey, {})
|
|
return response
|
|
} catch (error) {
|
|
console.error('Failed to check payment status:', error)
|
|
throw new Error('Failed to check payment status')
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all payments for a wallet
|
|
*/
|
|
async getWalletPayments(adminKey: string, limit: number = 100): Promise<PaymentStatus[]> {
|
|
try {
|
|
const response = await this.request<PaymentStatus[]>(`?limit=${limit}`, adminKey, {})
|
|
return response
|
|
} catch (error) {
|
|
console.error('Failed to get wallet payments:', error)
|
|
throw new Error('Failed to get wallet payments')
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate a Lightning payment request
|
|
*/
|
|
validatePaymentRequest(paymentRequest: string): boolean {
|
|
// Basic validation - should start with 'lnbc' and be a valid length
|
|
return paymentRequest.startsWith('lnbc') && paymentRequest.length > 50
|
|
}
|
|
|
|
/**
|
|
* Extract payment hash from a payment request
|
|
*/
|
|
extractPaymentHash(paymentRequest: string): string | null {
|
|
try {
|
|
// This is a simplified extraction - in production you'd use a proper BOLT11 decoder
|
|
const match = paymentRequest.match(/lnbc[0-9]+[a-z0-9]+/i)
|
|
return match ? match[0] : null
|
|
} catch (error) {
|
|
console.error('Failed to extract payment hash:', error)
|
|
return null
|
|
}
|
|
}
|
|
}
|
|
|