Add coupon fraud context and analytics tracking
This commit is contained in:
@@ -4,6 +4,10 @@ import { cleanup, render, screen } from '@testing-library/react';
|
||||
import { CheckoutWizardProvider } from '../WizardContext';
|
||||
import { PaymentStep } from '../steps/PaymentStep';
|
||||
|
||||
vi.mock('@/hooks/useAnalytics', () => ({
|
||||
useAnalytics: () => ({ trackEvent: vi.fn() }),
|
||||
}));
|
||||
|
||||
const basePackage = {
|
||||
id: 1,
|
||||
price: 49,
|
||||
|
||||
@@ -15,6 +15,7 @@ import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { useAnalytics } from '@/hooks/useAnalytics';
|
||||
|
||||
type PaymentStatus = 'idle' | 'processing' | 'ready' | 'error';
|
||||
|
||||
@@ -184,6 +185,7 @@ const PaddleCta: React.FC<{ onCheckout: () => Promise<void>; disabled: boolean;
|
||||
|
||||
export const PaymentStep: React.FC = () => {
|
||||
const { t, i18n } = useTranslation('marketing');
|
||||
const { trackEvent } = useAnalytics();
|
||||
const {
|
||||
selectedPackage,
|
||||
nextStep,
|
||||
@@ -283,6 +285,10 @@ export const PaymentStep: React.FC = () => {
|
||||
|
||||
if (RateLimitHelper.isLimited(trimmed)) {
|
||||
setCouponError(t('coupon.errors.too_many_attempts'));
|
||||
trackEvent({
|
||||
category: 'marketing_coupon',
|
||||
action: 'rate_limited',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -299,6 +305,11 @@ export const PaymentStep: React.FC = () => {
|
||||
amount: preview.pricing.formatted.discount,
|
||||
})
|
||||
);
|
||||
trackEvent({
|
||||
category: 'marketing_coupon',
|
||||
action: 'applied',
|
||||
name: preview.coupon.code,
|
||||
});
|
||||
setVoucherExpiry(preview.coupon.expires_at ?? null);
|
||||
setIsGiftVoucher(preview.coupon.code?.toUpperCase().startsWith('GIFT-') ?? false);
|
||||
if (typeof window !== 'undefined') {
|
||||
@@ -308,11 +319,15 @@ export const PaymentStep: React.FC = () => {
|
||||
setCouponPreview(null);
|
||||
setCouponNotice(null);
|
||||
setCouponError(error instanceof Error ? error.message : t('coupon.errors.generic'));
|
||||
trackEvent({
|
||||
category: 'marketing_coupon',
|
||||
action: 'apply_failed',
|
||||
});
|
||||
RateLimitHelper.bump(trimmed);
|
||||
} finally {
|
||||
setCouponLoading(false);
|
||||
}
|
||||
}, [selectedPackage, t]);
|
||||
}, [RateLimitHelper, selectedPackage, t, trackEvent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasAutoAppliedCoupon.current) {
|
||||
@@ -675,6 +690,13 @@ export const PaymentStep: React.FC = () => {
|
||||
}, [applyCoupon, couponCode, selectedPackage]);
|
||||
|
||||
const handleRemoveCoupon = useCallback(() => {
|
||||
if (couponPreview?.coupon.code) {
|
||||
trackEvent({
|
||||
category: 'marketing_coupon',
|
||||
action: 'removed',
|
||||
name: couponPreview.coupon.code,
|
||||
});
|
||||
}
|
||||
setCouponPreview(null);
|
||||
setCouponNotice(null);
|
||||
setCouponError(null);
|
||||
@@ -682,7 +704,7 @@ export const PaymentStep: React.FC = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.removeItem('preferred_coupon_code');
|
||||
}
|
||||
}, []);
|
||||
}, [couponPreview, trackEvent]);
|
||||
|
||||
const openWithdrawalModal = useCallback(async () => {
|
||||
setShowWithdrawalModal(true);
|
||||
|
||||
Reference in New Issue
Block a user