import { useCallback, useMemo, useState } from 'react'; import { getSikshyaApi, getErrorSummary, SIKSHYA_ENDPOINTS } from '../api'; import { EmbeddableShell } from '../components/shared/EmbeddableShell'; import { ApiErrorPanel } from '../components/shared/ApiErrorPanel'; import { ButtonPrimary, ButtonSecondary } from '../components/shared/buttons'; import { StatusBadge } from '../components/shared/list/StatusBadge'; import { Modal } from '../components/shared/Modal'; import { useAsyncData } from '../hooks/useAsyncData'; import { useAdminRouting } from '../lib/adminRouting'; import { useSikshyaDialog } from '../components/shared/SikshyaDialogContext'; import { appViewHref } from '../lib/appUrl'; import { formatPostDate } from '../lib/formatPostDate'; import type { SikshyaReactConfig } from '../types'; import { __ } from '../lib/i18n'; type PaymentDetails = { id: number; user_id: number; course_id: number; course_title: string; amount: number; currency: string; payment_method: string; transaction_id: string; status: string; payment_date: string; payer_name: string; payer_email: string; gateway_response?: unknown; /** Checkout vs automated subscription renewal (see payments table migration). */ charge_kind?: string; /** Checkout order id stored by fulfillment when gateway snapshot is missing (see legacy payment row). */ related_order_id?: number; }; type DetailsResponse = { ok?: boolean; payment?: PaymentDetails; message?: string }; export function PaymentDetailsPage(props: { config: SikshyaReactConfig; title: string; embedded?: boolean }) { const { config, embedded, title } = props; const dialog = useSikshyaDialog(); const { route, navigateView } = useAdminRouting(); const adminBase = config.adminUrl.replace(/\/?$/, '/'); const paymentId = useMemo(() => parseInt(route.query?.id || '0', 10) || 0, [route.query]); const loader = useCallback(async () => { if (!paymentId) throw new Error(__('Missing payment id.', 'sikshya')); return getSikshyaApi().get(SIKSHYA_ENDPOINTS.admin.payment(paymentId)); }, [paymentId]); const { loading, data, error, refetch } = useAsyncData(loader, [paymentId]); const p = data?.payment; const [editOpen, setEditOpen] = useState(false); const [editStatus, setEditStatus] = useState('pending'); const [saving, setSaving] = useState(false); return ( navigateView('sales', { tab: 'payments' })}> Back to payments { if (!p) return; setEditStatus(p.status || 'pending'); setEditOpen(true); }} > Edit refetch()}> Refresh } > {error ? (
refetch()} />
) : null} {loading ? (
Loading payment…
) : !p ? (
Payment not found.
) : (
{ if (!saving) setEditOpen(false); }} footer={
{ setSaving(true); try { await getSikshyaApi().patch<{ ok?: boolean; message?: string }>( SIKSHYA_ENDPOINTS.admin.paymentUpdate(p.id), { status: editStatus } ); setEditOpen(false); await refetch(); } catch (err) { void dialog.alert({ title: __('Something went wrong', 'sikshya'), message: getErrorSummary(err) }); } finally { setSaving(false); } }} > {saving ? __('Saving…', 'sikshya') : __('Save', 'sikshya')}
} >
Status
{formatPostDate(p.payment_date)}
{(p.charge_kind || '').toLowerCase() === 'renewal' ? (
Renewal
) : null}
Amount
{p.amount.toFixed(2)} {p.currency}
Method
{(p.payment_method || '—').toUpperCase()}
{p.transaction_id ? (
{p.transaction_id}
) : null}
Payer
{p.payer_name || `User #${p.user_id}`}
{p.payer_email || '—'}
{p.user_id ? ( Open user ) : null}
Course / plan
{p.course_id > 0 ? ( {p.course_title || `Course #${p.course_id}`} ) : (p.charge_kind || '').toLowerCase() === 'renewal' ? (
{(() => { const gw = p.gateway_response && typeof p.gateway_response === 'object' && p.gateway_response !== null ? (p.gateway_response as Record) : null; const name = gw && typeof gw.plan_name === 'string' ? gw.plan_name.trim() : ''; return name || 'Subscription renewal'; })()}
) : (
)} {(p.charge_kind || '').toLowerCase() === 'renewal' && p.gateway_response && typeof p.gateway_response === 'object' && p.gateway_response !== null ? (
{(() => { const gw = p.gateway_response as Record; const pid = typeof gw.subscription_plan_id === 'number' ? gw.subscription_plan_id : null; return pid ? `Plan ID: ${pid}` : 'Linked to Sikshya subscription plan'; })()}
) : null} {p.course_id > 0 ? (
Course ID: {p.course_id}
) : null}
{p.related_order_id && p.related_order_id > 0 ? (
Linked checkout order

This legacy payment row was written when the order was fulfilled. Subscription context (plan id, recurring id) lives on the order meta and gateway intent — open the order for the full picture.

) : null}
{__('Gateway response', 'sikshya')}
                {(() => {
                  try {
                    return JSON.stringify(p.gateway_response ?? null, null, 2);
                  } catch (err) {
                    return String(p.gateway_response ?? '') || getErrorSummary(err);
                  }
                })()}
              
)}
); }