diff --git a/src/modules/market/components/OrderHistory.vue b/src/modules/market/components/OrderHistory.vue index ddf4ba0..9bb93c8 100644 --- a/src/modules/market/components/OrderHistory.vue +++ b/src/modules/market/components/OrderHistory.vue @@ -105,16 +105,30 @@ + +
+
+ + Payment Confirmed + + Paid + +
+

+ Paid on {{ formatDate(order.paidAt) }} +

+
+ -
+
Payment Required
- - {{ order.paymentStatus === 'paid' ? 'Paid' : 'Pending' }} + + {{ isOrderPaid(order) ? 'Paid' : 'Pending' }}
@@ -237,7 +251,7 @@ import { auth } from '@/composables/useAuth' import { useLightningPayment } from '../composables/useLightningPayment' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' -import { Package, Store, Zap, Copy, QrCode } from 'lucide-vue-next' +import { Package, Store, Zap, Copy, QrCode, CheckCircle } from 'lucide-vue-next' import { toast } from 'vue-sonner' import type { OrderStatus } from '@/stores/market' @@ -286,11 +300,20 @@ const sortedOrders = computed(() => { const totalOrders = computed(() => allOrders.value.length) const pendingOrders = computed(() => allOrders.value.filter(o => o.status === 'pending').length) const paidOrders = computed(() => allOrders.value.filter(o => o.status === 'paid').length) -const pendingPayments = computed(() => allOrders.value.filter(o => o.paymentStatus === 'pending').length) +const pendingPayments = computed(() => allOrders.value.filter(o => !isOrderPaid(o)).length) const isDevelopment = computed(() => import.meta.env.DEV) // Methods +const isOrderPaid = (order: Order) => { + // Prioritize the 'paid' field from Nostr status updates (type 2) + if (order.paid !== undefined) { + return order.paid + } + // Fallback to paymentStatus field + return order.paymentStatus === 'paid' +} + const formatDate = (timestamp: number) => { return new Date(timestamp * 1000).toLocaleDateString('en-US', { year: 'numeric', diff --git a/src/modules/market/composables/usePaymentStatusChecker.ts b/src/modules/market/composables/usePaymentStatusChecker.ts new file mode 100644 index 0000000..fb2c608 --- /dev/null +++ b/src/modules/market/composables/usePaymentStatusChecker.ts @@ -0,0 +1,139 @@ +import { ref } from 'vue' +import { useAuth } from '@/composables/useAuth' +import { useMarketStore } from '../stores/market' + +// Simplified bolt11 parser to extract payment hash +function parseBolt11(bolt11: string): { paymentHash?: string } { + try { + // Remove lightning: prefix if present + const cleanBolt11 = bolt11.replace(/^lightning:/, '') + + // Very basic bolt11 parsing - in a real app you'd use a proper library + // For now, we'll return empty since this requires complex bech32 decoding + return {} + } catch (error) { + console.error('Failed to parse bolt11:', error) + return {} + } +} + +export function usePaymentStatusChecker() { + const { currentUser } = useAuth() + const marketStore = useMarketStore() + const isChecking = ref(false) + + // Check payment status via LNbits API using payment hash + async function checkPaymentStatusByHash(paymentHash: string, walletId?: string): Promise { + if (!currentUser.value || !paymentHash) { + return false + } + + try { + // Get wallet with admin key + const wallet = walletId + ? currentUser.value.wallets?.find((w: any) => w.id === walletId) + : currentUser.value.wallets?.find((w: any) => w.adminkey) // Use first wallet with admin key + + if (!wallet || !wallet.adminkey) { + console.warn('No wallet with admin key found for payment status check') + return false + } + + // Check payment status via LNbits API + const response = await fetch(`/api/v1/payments/${paymentHash}`, { + headers: { + 'accept': 'application/json', + 'X-API-KEY': wallet.adminkey, + }, + }) + + if (response.ok) { + const payment = await response.json() + return payment.paid === true + } else { + console.warn('Payment not found or API error:', response.status) + return false + } + } catch (error) { + console.error('Failed to check payment status:', error) + return false + } + } + + // Check payment status for a bolt11 invoice + async function checkPaymentStatusByBolt11(bolt11: string): Promise { + const parsed = parseBolt11(bolt11) + if (parsed.paymentHash) { + return await checkPaymentStatusByHash(parsed.paymentHash) + } + return false + } + + // Check and update payment status for an order + async function checkAndUpdateOrderPaymentStatus(orderId: string): Promise { + if (isChecking.value) return false + + const order = marketStore.orders[orderId] + if (!order || !order.paymentRequest) { + return false + } + + try { + isChecking.value = true + console.log('🔍 Checking payment status for order:', orderId.slice(-8)) + + // Method 1: Check by bolt11 (if we can extract payment hash) + const isPaid = await checkPaymentStatusByBolt11(order.paymentRequest) + + if (isPaid && order.paymentStatus !== 'paid') { + console.log('✅ Payment confirmed via API check for order:', orderId.slice(-8)) + + // Update order status + const updatedOrder = { + ...order, + status: 'paid' as const, + paymentStatus: 'paid' as const, + paidAt: Math.floor(Date.now() / 1000), + items: [...order.items] // Convert readonly to mutable + } + + marketStore.updateOrder(orderId, updatedOrder) + return true + } + + return isPaid + } catch (error) { + console.error('Failed to check payment status:', error) + return false + } finally { + isChecking.value = false + } + } + + // Check payment status for all pending orders + async function checkAllPendingOrders(): Promise { + const pendingOrders = Object.values(marketStore.orders).filter( + order => order.paymentStatus === 'pending' && order.paymentRequest + ) + + if (pendingOrders.length === 0) { + return + } + + console.log(`🔍 Checking payment status for ${pendingOrders.length} pending orders`) + + for (const order of pendingOrders) { + await checkAndUpdateOrderPaymentStatus(order.id) + // Add small delay between requests to avoid overwhelming the API + await new Promise(resolve => setTimeout(resolve, 100)) + } + } + + return { + isChecking, + checkPaymentStatusByHash, + checkPaymentStatusByBolt11, + checkAndUpdateOrderPaymentStatus, + checkAllPendingOrders + } +} \ No newline at end of file diff --git a/src/modules/market/services/nostrmarketService.ts b/src/modules/market/services/nostrmarketService.ts index 4b305db..5cb03fb 100644 --- a/src/modules/market/services/nostrmarketService.ts +++ b/src/modules/market/services/nostrmarketService.ts @@ -411,39 +411,39 @@ export class NostrmarketService { ) if (order) { - // Update order status + // Create the updated order object with all status updates + const updatedOrder = { + ...order, + updatedAt: Math.floor(Date.now() / 1000), + items: [...order.items] // Convert readonly to mutable + } + + // Update payment status if (statusUpdate.paid !== undefined) { - const newStatus = statusUpdate.paid ? 'paid' : 'pending' - marketStore.updateOrderStatus(order.id, newStatus) - - // Also update payment status - const updatedOrder = { - ...order, - paymentStatus: (statusUpdate.paid ? 'paid' : 'pending') as 'paid' | 'pending' | 'expired', - paidAt: statusUpdate.paid ? Math.floor(Date.now() / 1000) : undefined, - updatedAt: Math.floor(Date.now() / 1000), - items: [...order.items] // Convert readonly to mutable - } - marketStore.updateOrder(order.id, updatedOrder) + updatedOrder.paid = statusUpdate.paid + updatedOrder.paymentStatus = (statusUpdate.paid ? 'paid' : 'pending') as 'paid' | 'pending' | 'expired' + updatedOrder.status = statusUpdate.paid ? 'paid' : 'pending' + updatedOrder.paidAt = statusUpdate.paid ? Math.floor(Date.now() / 1000) : undefined } + // Update shipping status if (statusUpdate.shipped !== undefined) { - // Update shipping status if you have that field - const updatedOrder = { - ...order, - shipped: statusUpdate.shipped, - status: statusUpdate.shipped ? 'shipped' : order.status, - updatedAt: Math.floor(Date.now() / 1000), - items: [...order.items] // Convert readonly to mutable + updatedOrder.shipped = statusUpdate.shipped + if (statusUpdate.shipped && updatedOrder.status === 'paid') { + updatedOrder.status = 'shipped' } - marketStore.updateOrder(order.id, updatedOrder) } - console.log('Order status updated:', { + // Apply the update - this should trigger persistence + marketStore.updateOrder(order.id, updatedOrder) + + console.log('Order status updated and persisted:', { orderId: statusUpdate.id, - paid: statusUpdate.paid, - shipped: statusUpdate.shipped, - newStatus: statusUpdate.paid ? 'paid' : 'pending' + paid: updatedOrder.paid, + shipped: updatedOrder.shipped, + paymentStatus: updatedOrder.paymentStatus, + status: updatedOrder.status, + paidAt: updatedOrder.paidAt }) } else { console.warn('Status update received for unknown order:', statusUpdate.id) diff --git a/src/modules/market/stores/market.ts b/src/modules/market/stores/market.ts index 404c061..4426339 100644 --- a/src/modules/market/stores/market.ts +++ b/src/modules/market/stores/market.ts @@ -403,7 +403,9 @@ export const useMarketStore = defineStore('market', () => { ...orderData, id: orderData.id || generateOrderId(), createdAt: Math.floor(Date.now() / 1000), - updatedAt: Math.floor(Date.now() / 1000) + updatedAt: Math.floor(Date.now() / 1000), + paid: false, // Initialize as unpaid + paymentStatus: orderData.paymentStatus || 'pending' // Default to pending if not specified } orders.value[order.id] = order @@ -682,7 +684,22 @@ export const useMarketStore = defineStore('market', () => { try { const storageKey = getUserStorageKey('market_orders') localStorage.setItem(storageKey, JSON.stringify(orders.value)) - console.log('Saved orders to localStorage with key:', storageKey) + + // Debug: Check what's being saved + const orderCount = Object.keys(orders.value).length + const paidOrders = Object.values(orders.value).filter(o => o.paymentStatus === 'paid' || o.status === 'paid') + + console.log('💾 Saved orders to localStorage:', { + storageKey, + totalOrders: orderCount, + paidOrders: paidOrders.length, + orderStatuses: Object.values(orders.value).map(o => ({ + id: o.id?.slice(-8), + status: o.status, + paymentStatus: o.paymentStatus, + hasPaymentRequest: !!o.paymentRequest + })) + }) } catch (error) { console.warn('Failed to save orders to localStorage:', error) } @@ -695,7 +712,22 @@ export const useMarketStore = defineStore('market', () => { if (stored) { const parsedOrders = JSON.parse(stored) orders.value = parsedOrders - console.log('Loaded orders from localStorage with key:', storageKey, 'Orders count:', Object.keys(parsedOrders).length) + + // Debug: Check payment status of loaded orders + const orderCount = Object.keys(parsedOrders).length + const paidOrders = Object.values(parsedOrders).filter((o: any) => o.paymentStatus === 'paid' || o.status === 'paid') + + console.log('📦 Loaded orders from localStorage:', { + storageKey, + totalOrders: orderCount, + paidOrders: paidOrders.length, + orderStatuses: Object.values(parsedOrders).map((o: any) => ({ + id: o.id?.slice(-8), + status: o.status, + paymentStatus: o.paymentStatus, + hasPaymentRequest: !!o.paymentRequest + })) + }) } else { console.log('No orders found in localStorage for key:', storageKey) // Clear any existing orders when switching users diff --git a/src/modules/market/types/market.ts b/src/modules/market/types/market.ts index 5c21145..3b038b0 100644 --- a/src/modules/market/types/market.ts +++ b/src/modules/market/types/market.ts @@ -69,6 +69,8 @@ export interface Order { paymentHash?: string paidAt?: number paymentStatus?: 'pending' | 'paid' | 'expired' + paid?: boolean // Direct boolean field matching nostrmarket reference + shipped?: boolean // Shipping status from merchant updates qrCodeDataUrl?: string qrCodeLoading?: boolean qrCodeError?: string | null