import { test, expect } from '@playwright/test'; import { execSync } from 'child_process'; const LOGIN_EMAIL = 'checkout-e2e@example.com'; const LOGIN_PASSWORD = 'Password123!'; test.describe('Checkout Payment Step – Stripe & PayPal states', () => { test.beforeAll(async () => { execSync( `php artisan tenant:add-dummy --email=${LOGIN_EMAIL} --password=${LOGIN_PASSWORD} --first_name=Checkout --last_name=Tester --address="Playwrightstr. 1" --phone="+4912345678"` ); execSync( `php artisan tinker --execute="App\\\\Models\\\\User::where('email', '${LOGIN_EMAIL}')->update(['email_verified_at' => now()]);"` ); }); test.beforeEach(async ({ page }) => { await page.goto('/login'); await page.fill('input[name="email"]', LOGIN_EMAIL); await page.fill('input[name="password"]', LOGIN_PASSWORD); await page.getByRole('button', { name: /Anmelden|Login/ }).click(); await expect(page).toHaveURL(/dashboard/); }); test('Stripe payment intent error surfaces descriptive status', async ({ page }) => { await page.route('**/stripe/create-payment-intent', async (route) => { await route.fulfill({ status: 422, contentType: 'application/json', body: JSON.stringify({ error: 'Test payment intent failure' }), }); }); await openCheckoutPaymentStep(page); await expect( page.locator('text=/Fehler beim Laden der Zahlungsdaten|Error loading payment data/') ).toBeVisible(); await expect( page.locator('text=/Zahlungsformular bereit|Payment form ready/') ).not.toBeVisible(); }); test('Stripe payment intent ready state renders when backend responds', async ({ page }) => { await page.route('**/stripe/create-payment-intent', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ client_secret: 'pi_test_secret' }), }); }); await openCheckoutPaymentStep(page); await expect( page.locator('text=/Zahlungsformular bereit\\. Bitte gib deine Daten ein\\.|Payment form ready\\./') ).toBeVisible(); }); test('PayPal approval success updates status', async ({ page }) => { await stubPayPalSdk(page); await page.route('**/paypal/create-order', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ id: 'ORDER_TEST', status: 'CREATED' }), }); }); await page.route('**/paypal/capture-order', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ status: 'captured' }), }); }); await openCheckoutPaymentStep(page); await selectPayPalMethod(page); await page.waitForFunction(() => window.__paypalButtonsConfig !== undefined); await page.evaluate(async () => { const config = window.__paypalButtonsConfig; if (!config) return; await config.createOrder(); await config.onApprove({ orderID: 'ORDER_TEST' }); }); await expect( page.locator('text=/Zahlung bestätigt\\. Bestellung wird abgeschlossen|Payment confirmed/') ).toBeVisible(); }); test('PayPal capture failure notifies user', async ({ page }) => { await stubPayPalSdk(page); await page.route('**/paypal/create-order', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ id: 'ORDER_FAIL', status: 'CREATED' }), }); }); await page.route('**/paypal/capture-order', async (route) => { await route.fulfill({ status: 500, contentType: 'application/json', body: JSON.stringify({ error: 'capture_failed' }), }); }); await openCheckoutPaymentStep(page); await selectPayPalMethod(page); await page.waitForFunction(() => window.__paypalButtonsConfig !== undefined); await page.evaluate(async () => { const config = window.__paypalButtonsConfig; if (!config) return; await config.createOrder(); await config.onApprove({ orderID: 'ORDER_FAIL' }); }); await expect( page.locator('text=/PayPal capture failed|PayPal-Abbuchung fehlgeschlagen|PayPal capture error/') ).toBeVisible(); }); }); async function openCheckoutPaymentStep(page: import('@playwright/test').Page) { await page.goto('/packages'); const checkoutLink = page.locator('a[href^="/checkout/"]').first(); const href = await checkoutLink.getAttribute('href'); if (!href) { throw new Error('No checkout link found on packages page.'); } await page.goto(href); const nextButton = page.getByRole('button', { name: /Weiter zum Zahlungsschritt|Continue to Payment/, }); if (await nextButton.isVisible()) { await nextButton.click(); } await page.waitForSelector('text=/Zahlung|Payment/'); } async function selectPayPalMethod(page: import('@playwright/test').Page) { const paypalButton = page.getByRole('button', { name: /PayPal/ }); if (await paypalButton.isVisible()) { await paypalButton.click(); } } async function stubPayPalSdk(page: import('@playwright/test').Page) { await page.route('https://www.paypal.com/sdk/js**', async (route) => { await route.fulfill({ status: 200, contentType: 'application/javascript', body: ` window.paypal = { Buttons: function (config) { window.__paypalButtonsConfig = config; return { render: function () { // noop }, }; }, }; `, }); }); } declare global { interface Window { __paypalButtonsConfig?: { createOrder: () => Promise; onApprove: (data: { orderID: string }) => Promise; onError?: (error: unknown) => void; }; } }