/** * PaymentProvider — module-local state, fully independent from the app's global * mockStore so the Payments module can be added/removed in isolation. * * Transaction history is sourced from two places and merged by id: * 1. The server (api/payments/transactions → Vercel KV, written by the * webhook) — the production source of truth. * 2. A local record (localStorage) written when a customer returns to the * success page — gives instant feedback and works without KV in the demo. */ import React, { createContext, useContext, useState, useEffect, useCallback } from 'react'; import { INVOICES } from '../data/invoices'; import { stripeConfig } from '../config/stripeConfig'; const STORAGE_KEY = 'lynkeduppro.payments.transactions'; const loadLocal = () => { try { const raw = localStorage.getItem(STORAGE_KEY); return raw ? JSON.parse(raw) : []; } catch { return []; } }; const persistLocal = (txns) => { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(txns)); } catch { /* localStorage unavailable (private mode) — keep in-memory only. */ } }; const mergeById = (a, b) => { const map = new Map(); [...a, ...b].forEach((t) => { if (t && t.id) map.set(t.id, { ...map.get(t.id), ...t }); }); return Array.from(map.values()).sort( (x, y) => new Date(y.createdAt || 0) - new Date(x.createdAt || 0) ); }; const PaymentContext = createContext(null); export const PaymentProvider = ({ children }) => { const [invoices] = useState(INVOICES); const [transactions, setTransactions] = useState(loadLocal); // Pull the server-side (webhook-written) history and merge it in. useEffect(() => { let cancelled = false; (async () => { try { const res = await fetch(`${stripeConfig.apiBase}/transactions`); if (!res.ok) return; const { transactions: server = [] } = await res.json(); if (cancelled || !server.length) return; setTransactions((prev) => mergeById(prev, server)); } catch { /* offline / endpoint unavailable — local history is fine. */ } })(); return () => { cancelled = true; }; }, []); /** Record a transaction confirmed on the success return page. */ const recordTransaction = useCallback((txn) => { if (!txn?.id) return; setTransactions((prev) => { const next = mergeById(prev, [txn]); persistLocal(next); return next; }); }, []); const value = { invoices, transactions, recordTransaction }; return {children}; }; export const usePayments = () => { const ctx = useContext(PaymentContext); if (!ctx) throw new Error('usePayments must be used within a .'); return ctx; };