feat(FN-281): add payment_success and payment_failed PostHog events (+1 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged: - fix(FN-281): make result search param truly optional to fix typecheck - feat(FN-281): add payment_success and payment_failed PostHog events Files changed: apps/api/package.json | 1 + apps/api/src/app.module.ts | 2 + apps/api/src/payments/payments.service.spec.ts | 99 +- apps/api/src/payments/payments.service.ts | 40 + apps/api/src/posthog/posthog.module.ts | 9 + apps/api/src/posthog/posthog.service.ts | 64 ++ .../web/src/components/payment/payment-content.tsx | 24 +- apps/web/src/routeTree.gen.ts | 1110 ++++++++++---------- apps/web/src/routes/dashboard/subscription/pay.tsx | 24 +- packages/config/src/index.ts | 4 + pnpm-lock.yaml | 30 + 11 files changed, 827 insertions(+), 580 deletions(-) Fusion-Task-Id: FN-281
This commit is contained in:
@@ -23,7 +23,7 @@ import {
|
||||
FileText,
|
||||
Upload,
|
||||
} from "lucide-react";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
interface Brand {
|
||||
id: string;
|
||||
@@ -36,6 +36,7 @@ interface PaymentContentProps {
|
||||
planKey: string;
|
||||
period: "monthly" | "yearly";
|
||||
brandIds: string[];
|
||||
result?: string;
|
||||
}
|
||||
|
||||
const planConfig: Record<string, { priceMonthly: number; priceYearly: number }> = {
|
||||
@@ -54,7 +55,7 @@ const bankDetails = {
|
||||
|
||||
type Step = "summary" | "payment" | "confirmation";
|
||||
|
||||
export function PaymentContent({ planKey, period, brandIds }: PaymentContentProps) {
|
||||
export function PaymentContent({ planKey, period, brandIds, result }: PaymentContentProps) {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
@@ -65,6 +66,18 @@ export function PaymentContent({ planKey, period, brandIds }: PaymentContentProp
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [eftPaymentId, setEftPaymentId] = useState<string | null>(null);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: only re-run when result changes
|
||||
useEffect(() => {
|
||||
if (!result) return;
|
||||
if (result === "success") {
|
||||
capture("payment_success", { method: "iyzico", plan: planKey, period, amount: totalAmount });
|
||||
setStep("confirmation");
|
||||
} else {
|
||||
capture("payment_failed", { method: "iyzico", plan: planKey, period, reason: result });
|
||||
toast.error(t("payment.paymentFailed"));
|
||||
}
|
||||
}, [result]);
|
||||
|
||||
const config = planConfig[planKey];
|
||||
const totalAmount = config
|
||||
? period === "monthly"
|
||||
@@ -119,6 +132,13 @@ export function PaymentContent({ planKey, period, brandIds }: PaymentContentProp
|
||||
return api.upload<{ success: boolean }>(`/payments/eft/${eftPaymentId}/receipt`, formData);
|
||||
},
|
||||
onSuccess: () => {
|
||||
capture("payment_success", {
|
||||
method: "eft",
|
||||
plan: planKey,
|
||||
period,
|
||||
amount: totalAmount,
|
||||
payment_id: eftPaymentId,
|
||||
});
|
||||
toast.success(t("payment.receiptUploaded"));
|
||||
setStep("confirmation");
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,16 +9,26 @@ const PaymentContent = lazy(() =>
|
||||
);
|
||||
|
||||
export const Route = createFileRoute("/dashboard/subscription/pay")({
|
||||
validateSearch: (search: Record<string, unknown>) => ({
|
||||
plan: (search.plan as string) || "",
|
||||
period: ((search.period as string) || "monthly") as "monthly" | "yearly",
|
||||
brands: (search.brands as string) || "",
|
||||
}),
|
||||
validateSearch: (search: Record<string, unknown>) => {
|
||||
const params: {
|
||||
plan: string;
|
||||
period: "monthly" | "yearly";
|
||||
brands: string;
|
||||
result?: string;
|
||||
} = {
|
||||
plan: (search.plan as string) || "",
|
||||
period: ((search.period as string) || "monthly") as "monthly" | "yearly",
|
||||
brands: (search.brands as string) || "",
|
||||
};
|
||||
const result = search.result as string | undefined;
|
||||
if (result) params.result = result;
|
||||
return params;
|
||||
},
|
||||
component: PaymentPage,
|
||||
});
|
||||
|
||||
function PaymentPage() {
|
||||
const { plan, period, brands } = Route.useSearch();
|
||||
const { plan, period, brands, result } = Route.useSearch();
|
||||
const brandIds = brands.split(",").filter(Boolean);
|
||||
|
||||
return (
|
||||
@@ -37,7 +47,7 @@ function PaymentPage() {
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<PaymentContent planKey={plan} period={period} brandIds={brandIds} />
|
||||
<PaymentContent planKey={plan} period={period} brandIds={brandIds} result={result} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user