import { useCallback, createContext, useContext, ReactNode } from 'react'; import { environment } from '../environments/environment.ts'; import { useHttpService } from './HttpService.tsx'; import { buildProofingReturnUrl } from '../utils/proofingFlow'; // Phase 2 (full-proxy identity-proofing hard-gate), client Task 6 - the CLIENT half of Stripe // Identity verification. A related person (or a patient acting for themselves) starts document // verification on AKASHIC'S PLATFORM Stripe account; these two routes live on `fhirBaseApi` // (/R4/proofing/...) alongside RelatedPerson/payment - NOT apiUrl. Both routes are self-scoped: // the server derives the acting person's identity from the caller's own auth context (never a // path/body parameter), so neither call here takes or sends an id. const API_URL = environment.fhirIdentityApi; export type ProofingStatusValue = 'pending' | 'verified' | 'failed' | 'expired' | 'none'; export type ProofingStatusResponse = { status: ProofingStatusValue; }; export const useProofingService = () => { const { get, post } = useHttpService(); // POST {fhirIdentityApi}/proofing/session {returnUrl} -> {url, sessionId}. The server derives // personKey from the caller's auth context (never a body param), but DOES need a same-origin // `returnUrl` to send Stripe Identity back to - built here the same way InvoiceDetail.tsx builds // successUrl/cancelUrl for payment checkout (utils/paymentFlow.ts buildCheckoutUrls: current // origin + pathname + hash, since the app is a HashRouter SPA and dropping the hash would land // the returning caller on the default route instead of wherever they started verification from), // with the `proofing_return=1` marker useProofingReturn watches for appended // (utils/proofingFlow.ts buildProofingReturnUrl). The server validates the host against the same // open-redirect allow-list as checkout and falls back to its own fixed constant on a bad/missing // host - this call never assumes its own URL was accepted. Redirects the browser to Stripe's // hosted verification page on success; mirrors lib/billingCheckout.ts startAddOnCheckout. Left to // throw (not swallowed into a discriminated result like PaymentService.createCheckout) so a // verify-now caller can show its own inline error state; Task 7's banner is the first consumer. const startVerification = useCallback(async (): Promise => { const returnUrl = buildProofingReturnUrl( window.location.origin, window.location.pathname, window.location.hash, ); const response = await post<{ url: string; sessionId: string }>(`${API_URL}/proofing/session`, { returnUrl, }); window.location.href = response.url; }, [post]); // GET {fhirIdentityApi}/proofing/status -> {status}. Self-scoped poll of the CALLER's own // identity-proofing status; always 200 (server resolves an unrecognized/legacy caller or a // missing record to `'none'` rather than throwing) - see infra Proofing/status.ts. const getProofingStatus = useCallback(async (): Promise => { return get(`${API_URL}/proofing/status`); }, [get]); return { startVerification, getProofingStatus }; }; const ProofingServiceContext = createContext | undefined>(undefined); export const ProofingServiceProvider = ({ children }: { children: ReactNode }) => { const proofingService = useProofingService(); return ( {children} ); }; export const useProofingServiceContext = () => { const context = useContext(ProofingServiceContext); if (context === undefined) { throw new Error('useProofingServiceContext must be used within a ProofingServiceProvider'); } return context; };