implement free plan age verification via Stripe SetupIntent; personalize soul demo greeting with user name and timezone
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m49s

This commit is contained in:
2026-05-11 02:03:39 -05:00
parent ac2d00d653
commit c966f2b455
7 changed files with 167 additions and 74 deletions
+12 -6
View File
@@ -144,8 +144,8 @@ test('[founding] Stripe.js script tag present in page', async ({ page }) => {
await expect(stripeScript).toBeAttached();
});
test('[free] Stripe.js is still loaded (though not used)', async ({ page }) => {
// Free plan still includes Stripe.js for forward compat
test('[free] Stripe.js is loaded (used for age verification SetupIntent)', async ({ page }) => {
// Free plan now creates a SetupIntent for age verification
await page.goto('/checkout?plan=free');
const stripeScript = page.locator('script[src*="stripe.com"]');
await expect(stripeScript).toBeAttached();
@@ -186,6 +186,7 @@ test('[professional] payment-message div starts hidden', async ({ page }) => {
test('[professional] buyer-name input is present and fillable', async ({ page }) => {
await page.goto('/checkout?plan=professional');
await page.waitForFunction(() => typeof (window as any).signUpWithEmail === 'function', { timeout: 10000 });
await expect(page.locator('#buyer-name')).toBeAttached();
await page.fill('#buyer-name', 'Test User');
expect(await page.locator('#buyer-name').inputValue()).toBe('Test User');
@@ -193,6 +194,7 @@ test('[professional] buyer-name input is present and fillable', async ({ page })
test('[professional] buyer-email input is present and fillable', async ({ page }) => {
await page.goto('/checkout?plan=professional');
await page.waitForFunction(() => typeof (window as any).signUpWithEmail === 'function', { timeout: 10000 });
await expect(page.locator('#buyer-email')).toBeAttached();
await page.fill('#buyer-email', 'test@example.com');
expect(await page.locator('#buyer-email').inputValue()).toBe('test@example.com');
@@ -416,14 +418,18 @@ test('[professional] successful payment: submit-btn shows spinner then loading s
// ─── /api/payment-intent endpoint contracts ───────────────────────────────────
test('POST /api/payment-intent free plan returns no_payment_required', async ({ request }) => {
test('POST /api/payment-intent free plan returns setup_mode (age verification)', async ({ request }) => {
const res = await request.post('/api/payment-intent', {
data: JSON.stringify({ plan: 'free', email: 'test@example.com' }),
headers: { 'Content-Type': 'application/json' },
});
expect(res.status()).toBe(200);
const body = await res.json();
expect(body.no_payment_required ?? body.free).toBeTruthy();
// Free plan creates a SetupIntent for age verification — must not 500
expect(res.status()).toBeLessThan(500);
if (res.status() === 200) {
const body = await res.json();
expect('setup_mode' in body || 'client_secret' in body || 'error' in body).toBeTruthy();
expect(body.no_payment_required).toBeFalsy();
}
});
test('POST /api/payment-intent professional returns client_secret or stripe error (not 500)', async ({ request }) => {