/**
* PaymentProvider — module-local state for the customer Payments portal.
*
* Invoices and transactions are sourced from the shared paymentStore (localStorage)
* so the customer sees payment requests created on the owner side, and payments
* made here flow back to the owner's Payment Management page. The server-side
* (webhook-written) transaction history is merged in on top when available.
*/
import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
import { stripeConfig } from '../config/stripeConfig';
import {
loadInvoices,
loadTransactions,
saveTransactions,
subscribe,
} from '../data/paymentStore';
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, setInvoices] = useState(loadInvoices);
const [transactions, setTransactions] = useState(loadTransactions);
// Re-read from the shared store whenever the owner adds an invoice, marks
// one paid, or a payment is recorded (same-tab and cross-tab).
useEffect(
() =>
subscribe(() => {
setInvoices(loadInvoices());
setTransactions((prev) => mergeById(prev, loadTransactions()));
}),
[]
);
// 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]);
saveTransactions(next); // persists to the shared store + notifies the owner side
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;
};