From 8764915fcd747195cc6e0b52dea017c97643134a Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Sat, 3 Jan 2026 19:15:37 +0100 Subject: [PATCH] Handle inline Paddle checkout responses --- tests/ui/purchase/paddle-sandbox-full.test.ts | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/tests/ui/purchase/paddle-sandbox-full.test.ts b/tests/ui/purchase/paddle-sandbox-full.test.ts index 773737d..e73b63f 100644 --- a/tests/ui/purchase/paddle-sandbox-full.test.ts +++ b/tests/ui/purchase/paddle-sandbox-full.test.ts @@ -49,14 +49,32 @@ test.describe('Paddle sandbox full flow (staging)', () => { checkoutCta.click(), ]); - const checkoutPayload = await apiResponse.json(); - const checkoutUrl: string = checkoutPayload.checkout_url ?? checkoutPayload.url ?? ''; + const rawBody = await apiResponse.text(); + let checkoutPayload: Record | null = null; + try { + checkoutPayload = rawBody && rawBody.trim().startsWith('{') ? JSON.parse(rawBody) : null; + } catch { + checkoutPayload = null; + } - expect(checkoutUrl).toContain('paddle'); + const inlineMode = checkoutPayload?.mode === 'inline' || checkoutPayload?.inline === true; + const checkoutUrl = extractCheckoutUrl(checkoutPayload, rawBody); + + if (!inlineMode) { + expect(checkoutUrl).toContain('paddle'); + } // Navigate to Paddle hosted checkout and complete payment. - await page.goto(checkoutUrl); - await expect(page).toHaveURL(/paddle/); + if (inlineMode) { + await expect( + page.getByText(/Checkout geƶffnet|Checkout opened|Paddle-Checkout/i).first() + ).toBeVisible({ timeout: 20_000 }); + } else if (checkoutUrl) { + await page.goto(checkoutUrl); + await expect(page).toHaveURL(/paddle/); + } else { + throw new Error(`Missing Paddle checkout URL. Response: ${rawBody}`); + } await completeHostedPaddleCheckout(page, sandboxCard); @@ -178,6 +196,29 @@ async function completeHostedPaddleCheckout( await payButton.click(); } +function extractCheckoutUrl(payload: Record | null, rawBody: string): string | null { + if (payload) { + const directUrl = payload.checkout_url ?? payload.url ?? payload.checkoutUrl; + if (typeof directUrl === 'string') { + return directUrl; + } + } + + const trimmed = rawBody.trim(); + if (/^https?:\/\//i.test(trimmed)) { + return trimmed; + } + + if (trimmed.startsWith('<')) { + const match = trimmed.match(/https?:\/\/["'a-zA-Z0-9._~:/?#@!$&'()*+,;=%-]+/); + if (match) { + return match[0]; + } + } + + return null; +} + function buildTenantEmail(): string | null { const rawEmail = process.env.E2E_TENANT_EMAIL ?? process.env.E2E_PADDLE_EMAIL ?? null; if (!rawEmail) {