Migrate billing from Paddle to Lemon Squeezy

This commit is contained in:
Codex Agent
2026-02-03 10:59:54 +01:00
parent 2f4ebfefd4
commit a0ef90e13a
228 changed files with 4369 additions and 4067 deletions

View File

@@ -5,29 +5,29 @@ const demoTenantCredentials = {
password: process.env.E2E_DEMO_TENANT_PASSWORD ?? 'Demo1234!',
};
test.describe('Checkout Payment Step Paddle flow', () => {
test('opens Paddle checkout and shows success notice', async ({ page }) => {
await page.route('**/paddle/create-checkout', async (route) => {
test.describe('Checkout Payment Step Lemon Squeezy flow', () => {
test('opens Lemon Squeezy checkout and shows success notice', async ({ page }) => {
await page.route('**/lemonsqueezy/create-checkout', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
checkout_url: 'https://paddle.test/checkout/success',
checkout_url: 'https://fotospiel.lemonsqueezy.com/checkout/success',
}),
});
});
await page.route('https://cdn.paddle.com/paddle/v2/paddle.js', async (route) => {
await page.route('https://app.lemonsqueezy.com/js/lemon.js', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/javascript',
body: `
window.Paddle = {
Environment: { set: function(env) { window.__paddleEnv = env; } },
Initialize: function(opts) { window.__paddleInit = opts; },
Checkout: {
open: function(config) {
window.__paddleOpenConfig = config;
window.createLemonSqueezy = function() {};
window.LemonSqueezy = {
Setup: function(options) { window.__lemonEventHandler = options?.eventHandler || null; },
Url: {
Open: function(url) {
window.__lemonOpenedUrl = url;
}
}
};
@@ -46,7 +46,7 @@ test.describe('Checkout Payment Step Paddle flow', () => {
};
});
await page.getByRole('button', { name: /Continue with Paddle|Weiter mit Paddle/ }).first().click();
await page.getByRole('button', { name: /Continue with Lemon Squeezy|Weiter mit Lemon Squeezy/ }).first().click();
await expect(
page.locator(
@@ -57,7 +57,7 @@ test.describe('Checkout Payment Step Paddle flow', () => {
let mode: 'inline' | 'hosted' | null = null;
for (let i = 0; i < 8; i++) {
const state = await page.evaluate(() => ({
inline: Boolean(window.__paddleOpenConfig),
inline: Boolean(window.__lemonOpenedUrl),
opened: window.__openedUrls?.length ?? 0,
}));
@@ -77,19 +77,19 @@ test.describe('Checkout Payment Step Paddle flow', () => {
expect(mode).not.toBeNull();
if (mode === 'inline') {
const inlineConfig = await page.evaluate(() => window.__paddleOpenConfig ?? null);
expect(inlineConfig).not.toBeNull();
const inlineUrl = await page.evaluate(() => window.__lemonOpenedUrl ?? null);
expect(inlineUrl).not.toBeNull();
}
if (mode === 'hosted') {
await expect.poll(async () => {
return page.evaluate(() => window.__openedUrls?.[0]?.url ?? null);
}).toContain('paddle');
}).toContain('lemonsqueezy');
}
});
test('shows error state when Paddle checkout creation fails', async ({ page }) => {
await page.route('**/paddle/create-checkout', async (route) => {
test('shows error state when Lemon Squeezy checkout creation fails', async ({ page }) => {
await page.route('**/lemonsqueezy/create-checkout', async (route) => {
await route.fulfill({
status: 500,
contentType: 'application/json',
@@ -97,17 +97,17 @@ test.describe('Checkout Payment Step Paddle flow', () => {
});
});
await page.route('https://cdn.paddle.com/paddle/v2/paddle.js', async (route) => {
await page.route('https://app.lemonsqueezy.com/js/lemon.js', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/javascript',
body: `
window.Paddle = {
Environment: { set: function(env) { window.__paddleEnv = env; } },
Initialize: function(opts) { window.__paddleInit = opts; },
Checkout: {
open: function() {
throw new Error('forced paddle failure');
window.createLemonSqueezy = function() {};
window.LemonSqueezy = {
Setup: function(options) { window.__lemonEventHandler = options?.eventHandler || null; },
Url: {
Open: function() {
throw new Error('forced Lemon Squeezy failure');
}
}
};
@@ -118,10 +118,10 @@ test.describe('Checkout Payment Step Paddle flow', () => {
await openCheckoutPaymentStep(page, demoTenantCredentials);
await acceptCheckoutTerms(page);
await page.getByRole('button', { name: /Continue with Paddle|Weiter mit Paddle/ }).first().click();
await page.getByRole('button', { name: /Continue with Lemon Squeezy|Weiter mit Lemon Squeezy/ }).first().click();
await expect(
page.locator('text=/Paddle-Checkout konnte nicht gestartet werden|Paddle checkout could not be started/i')
page.locator('text=/Lemon Squeezy-Checkout konnte nicht gestartet werden|Lemon Squeezy checkout could not be started/i')
).toBeVisible();
});
});
@@ -169,8 +169,7 @@ async function acceptCheckoutTerms(page: import('@playwright/test').Page) {
declare global {
interface Window {
__openedUrls?: Array<{ url: string; target?: string | null; features?: string | null }>;
__paddleOpenConfig?: { url?: string; items?: Array<{ priceId: string; quantity: number }>; settings?: { displayMode?: string } };
__paddleEnv?: string;
__paddleInit?: Record<string, unknown>;
__lemonOpenedUrl?: string | null;
__lemonEventHandler?: ((event: { event: string; data?: unknown }) => void) | null;
}
}