nook: capture trial email and run the two lifecycle emails
Trials were anonymous — a 14-day trial you cannot talk to. trials gains an email column (idempotent ALTER), POST /api/the-nook/trial-email attaches the optional first-launch prompt's answer, and the address lands in a Brevo list when NOOK_TRIAL_LIST_ID is set. The lifecycle emails follow: a D+11 trial expiry reminder with a buy link, a purchase thank-you from the Stripe webhook, and scheduled testimonial (D+3) and review (D+14) asks via the daily /api/the-nook/lifecycle cron. Rows are marked before sending so retries never double-send; trial addresses that already bought get no buy reminder; the endpoint requires a Bearer secret (CRON_SECRET / NOOK_LIFECYCLE_SECRET) and fails closed when unconfigured — an open trigger would email real users. All sends are best-effort and logged, never thrown. Tests: trial-email (3) and lifecycle (6) suites; full run 539 pass. Issues: FRE-10, FRE-11
This commit is contained in:
@@ -2,6 +2,7 @@ import type { APIEvent } from "@solidjs/start/server";
|
||||
import { env } from "~/env/server";
|
||||
import { NookConnectionFactory } from "~/server/db-connections";
|
||||
import { nookSchemaBootstrap, issueLicense, emailLicenseKey } from "~/server/nook";
|
||||
import { emailThankYou } from "~/server/nook-lifecycle";
|
||||
import { json } from "../_lib";
|
||||
import { createHmac, timingSafeEqual } from "node:crypto";
|
||||
|
||||
@@ -92,6 +93,9 @@ export async function POST(event: APIEvent) {
|
||||
try {
|
||||
const { key } = await issueLicense(email, sessionId);
|
||||
await emailLicenseKey(email, key);
|
||||
// Purchase thank-you (the D+3 testimonial and D+14 review asks are
|
||||
// scheduled from the daily lifecycle job, not here).
|
||||
await emailThankYou(email);
|
||||
} catch (error) {
|
||||
// UNIQUE stripe_session_id conflict from a racing duplicate delivery.
|
||||
console.error("Failed to issue The Nook license (webhook):", error);
|
||||
|
||||
Reference in New Issue
Block a user