verschieben des sofortigen verzichts auf das Widerrrufsrecht zum Anlegen des Events

This commit is contained in:
Codex Agent
2025-12-22 13:11:16 +01:00
parent 84234bfb8e
commit c947e638eb
29 changed files with 877 additions and 374 deletions

View File

@@ -0,0 +1,37 @@
import React from 'react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { cleanup, render, screen } from '@testing-library/react';
import { CheckoutWizardProvider } from '../WizardContext';
import { PaymentStep } from '../steps/PaymentStep';
const basePackage = {
id: 1,
price: 49,
paddle_price_id: 'pri_test_123',
};
describe('PaymentStep', () => {
beforeEach(() => {
localStorage.clear();
window.Paddle = {
Environment: { set: vi.fn() },
Checkout: { open: vi.fn() },
};
});
afterEach(() => {
cleanup();
delete window.Paddle;
});
it('renders the payment experience without crashing', async () => {
render(
<CheckoutWizardProvider initialPackage={basePackage} packageOptions={[basePackage]}>
<PaymentStep />
</CheckoutWizardProvider>,
);
expect(await screen.findByText('checkout.payment_step.guided_title')).toBeInTheDocument();
expect(screen.queryByText('checkout.legal.checkbox_digital_content_label')).not.toBeInTheDocument();
});
});

View File

@@ -120,13 +120,12 @@ const PaddleCta: React.FC<{ onCheckout: () => Promise<void>; disabled: boolean;
export const PaymentStep: React.FC = () => {
const { t, i18n } = useTranslation('marketing');
const { selectedPackage, nextStep, paddleConfig, authUser, setPaymentCompleted } = useCheckoutWizard();
const { selectedPackage, nextStep, paddleConfig, authUser, paymentCompleted, setPaymentCompleted } = useCheckoutWizard();
const [status, setStatus] = useState<PaymentStatus>('idle');
const [message, setMessage] = useState<string>('');
const [initialised, setInitialised] = useState(false);
const [inlineActive, setInlineActive] = useState(false);
const [acceptedTerms, setAcceptedTerms] = useState(false);
const [acceptedWaiver, setAcceptedWaiver] = useState(false);
const [consentError, setConsentError] = useState<string | null>(null);
const [couponCode, setCouponCode] = useState<string>(() => {
if (typeof window === 'undefined') {
@@ -171,7 +170,6 @@ export const PaymentStep: React.FC = () => {
}, [i18n.language]);
const isFree = useMemo(() => (selectedPackage ? Number(selectedPackage.price) <= 0 : false), [selectedPackage]);
const requiresImmediateWaiver = useMemo(() => Boolean(selectedPackage?.activates_immediately), [selectedPackage]);
const applyCoupon = useCallback(async (code: string) => {
if (!selectedPackage) {
@@ -263,7 +261,7 @@ export const PaymentStep: React.FC = () => {
return;
}
if (!acceptedTerms || (requiresImmediateWaiver && !acceptedWaiver)) {
if (!acceptedTerms) {
setConsentError(t('checkout.legal.checkbox_terms_error'));
return;
}
@@ -285,7 +283,6 @@ export const PaymentStep: React.FC = () => {
body: JSON.stringify({
package_id: selectedPackage.id,
accepted_terms: acceptedTerms,
accepted_waiver: requiresImmediateWaiver ? acceptedWaiver : false,
locale: paddleLocale,
}),
});
@@ -317,7 +314,7 @@ export const PaymentStep: React.FC = () => {
return;
}
if (!acceptedTerms || (requiresImmediateWaiver && !acceptedWaiver)) {
if (!acceptedTerms) {
setConsentError(t('checkout.legal.checkbox_terms_error'));
return;
}
@@ -362,7 +359,6 @@ export const PaymentStep: React.FC = () => {
locale: paddleLocale,
coupon_code: couponPreview?.coupon.code ?? undefined,
accepted_terms: acceptedTerms,
accepted_waiver: requiresImmediateWaiver ? acceptedWaiver : false,
inline: inlineSupported,
}),
});
@@ -792,29 +788,6 @@ export const PaymentStep: React.FC = () => {
</div>
</div>
{requiresImmediateWaiver && (
<div className="flex items-start gap-3">
<Checkbox
id="checkout-waiver-free"
checked={acceptedWaiver}
onCheckedChange={(checked) => {
setAcceptedWaiver(Boolean(checked));
if (consentError) {
setConsentError(null);
}
}}
/>
<div className="space-y-1 text-sm">
<Label htmlFor="checkout-waiver-free" className="cursor-pointer">
{t('checkout.legal.checkbox_digital_content_label')}
</Label>
<p className="text-xs text-muted-foreground">
{t('checkout.legal.hint_subscription_withdrawal')}
</p>
</div>
</div>
)}
{consentError && (
<div className="flex items-center gap-2 text-sm text-destructive">
<XCircle className="h-4 w-4" />
@@ -827,7 +800,7 @@ export const PaymentStep: React.FC = () => {
<Button
size="lg"
onClick={handleFreeActivation}
disabled={freeActivationBusy || !acceptedTerms || (requiresImmediateWaiver && !acceptedWaiver)}
disabled={freeActivationBusy || !acceptedTerms}
>
{freeActivationBusy && <LoaderCircle className="mr-2 h-4 w-4 animate-spin" />}
{t('checkout.payment_step.activate_package')}
@@ -940,30 +913,6 @@ export const PaymentStep: React.FC = () => {
</div>
</div>
{requiresImmediateWaiver && (
<div className="flex items-start gap-3">
<Checkbox
id="checkout-waiver-hero"
checked={acceptedWaiver}
onCheckedChange={(checked) => {
setAcceptedWaiver(Boolean(checked));
if (consentError) {
setConsentError(null);
}
}}
className="border-white/60 data-[state=checked]:bg-white data-[state=checked]:text-[#001835]"
/>
<div className="space-y-1 text-sm">
<Label htmlFor="checkout-waiver-hero" className="cursor-pointer text-white">
{t('checkout.legal.checkbox_digital_content_label')}
</Label>
<p className="text-xs text-white/80">
{t('checkout.legal.hint_subscription_withdrawal')}
</p>
</div>
</div>
)}
{consentError && (
<div className="flex items-center gap-2 text-sm text-red-200">
<XCircle className="h-4 w-4" />
@@ -975,7 +924,7 @@ export const PaymentStep: React.FC = () => {
<div className="space-y-2">
<PaddleCta
onCheckout={startPaddleCheckout}
disabled={status === 'processing' || !acceptedTerms || (requiresImmediateWaiver && !acceptedWaiver)}
disabled={status === 'processing' || !acceptedTerms}
isProcessing={status === 'processing'}
className={cn('bg-white text-[#001835] hover:bg-white/90', PRIMARY_CTA_STYLES)}
/>
@@ -1070,7 +1019,7 @@ export const PaymentStep: React.FC = () => {
</p>
<PaddleCta
onCheckout={startPaddleCheckout}
disabled={status === 'processing' || !acceptedTerms || (requiresImmediateWaiver && !acceptedWaiver)}
disabled={status === 'processing' || !acceptedTerms}
isProcessing={status === 'processing'}
className={PRIMARY_CTA_STYLES}
/>