Add payment module, BFF proxy, hub/dashboard/ops/PSP/merchant screens wired to real APIs, and mark payment-frontend complete in project status. Co-authored-by: Cursor <cursoragent@cursor.com>
77 lines
3.0 KiB
TypeScript
77 lines
3.0 KiB
TypeScript
"use client";
|
||
|
||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||
import Link from "next/link";
|
||
import { toast } from "sonner";
|
||
import { useTenantId } from "@/hooks/useTenantId";
|
||
import { paymentApi } from "@/modules/payment/services/payment-api";
|
||
import { PaymentPageLoader, PaymentPageError, PaymentEmptyState } from "@/modules/payment/pages/shared";
|
||
import { PageHeader, Card, CardContent, Button } from "@/components/ds";
|
||
import { PaymentStatusChip } from "@/modules/payment/components/PaymentStatusChip";
|
||
|
||
export const PaymentVerification = function PaymentVerificationPage() {
|
||
const { tenantId } = useTenantId();
|
||
const qc = useQueryClient();
|
||
|
||
const q = useQuery({
|
||
queryKey: ["payment", tenantId, "verification-queue"],
|
||
queryFn: async () => {
|
||
const requests = await paymentApi.requests.list(tenantId!);
|
||
return requests.filter((r) => ["redirect_issued", "pending"].includes(String(r.status)));
|
||
},
|
||
enabled: !!tenantId,
|
||
});
|
||
|
||
const verifyM = useMutation({
|
||
mutationFn: (id: string) => paymentApi.requests.verify(tenantId!, id, { status: "paid" }),
|
||
onSuccess: () => {
|
||
toast.success("پرداخت تأیید شد");
|
||
qc.invalidateQueries({ queryKey: ["payment", tenantId, "verification-queue"] });
|
||
},
|
||
onError: (e: Error) => toast.error(e.message),
|
||
});
|
||
|
||
if (!tenantId || q.isLoading) return <PaymentPageLoader />;
|
||
if (q.error) return <PaymentPageError error={q.error} onRetry={() => q.refetch()} />;
|
||
|
||
const queue = q.data ?? [];
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<PageHeader
|
||
title="تأیید پرداخت"
|
||
description="درخواستهای نیازمند verify دستی یا پس از callback."
|
||
/>
|
||
{queue.length === 0 ? (
|
||
<PaymentEmptyState title="صف تأیید خالی است" />
|
||
) : (
|
||
<div className="grid gap-3">
|
||
{queue.map((r) => (
|
||
<Card key={String(r.id)}>
|
||
<CardContent className="flex flex-wrap items-center justify-between gap-2 p-4">
|
||
<div>
|
||
<p className="font-medium">
|
||
{String(r.amount_minor)} {String(r.currency)}
|
||
</p>
|
||
<p className="text-xs text-[var(--muted)]">{String(r.provider_code)}</p>
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
<PaymentStatusChip status={String(r.status)} />
|
||
<Link href={`/payment/requests/${r.id}`}>
|
||
<Button size="sm" variant="outline">جزئیات</Button>
|
||
</Link>
|
||
<Button size="sm" loading={verifyM.isPending} onClick={() => verifyM.mutate(String(r.id))}>
|
||
تأیید
|
||
</Button>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default PaymentVerification;
|