Sync newsletter opt-ins to Brevo
Both standalone signup forms (Newsletter.tsx on Home/newsletter page, NewsletterModal.tsx from the Navbar CTA) were previously non-functional — static markup with no onSubmit/state at all, nothing was ever captured. They're now real client forms posting to the new /api/newsletter/subscribe route, which upserts the contact into Brevo's Contacts API (list id from BREVO_LIST_ID). Checkout's existing newsletterOptIn checkbox gets the same sync, fire-and-forget alongside the order-confirmation email — a failed marketing sync must never fail checkout. lib/brevo.ts is the only thing that talks to Brevo; this app still never sends marketing mail itself. Whatever automation Brevo has configured on the list (Welcome Flow etc.) runs entirely on their side — Brevo's Automation workflows aren't manageable via their public API at all, so that part can't be wired up from here. Needs BREVO_API_KEY and BREVO_LIST_ID set in the frontend's Coolify environment — not yet added there. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import { sendOrderConfirmationEmail } from "../../lib/orderEmail";
|
||||
import { normalizeVatId, isValidVatId } from "../../lib/vatId";
|
||||
import { checkVatIdViaVies } from "../../lib/vies";
|
||||
import { computeExemptTotals, destinationCountry, isExemptionEligibleCountry } from "../../lib/vatExemption";
|
||||
import { upsertNewsletterContact } from "../../lib/brevo";
|
||||
|
||||
// Plain float arithmetic on money (quantity × unitPrice summed across
|
||||
// lines, a percent discount, subtracting/adding those together) drifts
|
||||
@@ -376,6 +377,13 @@ export async function POST(request: Request) {
|
||||
});
|
||||
});
|
||||
|
||||
// Fire-and-forget, same reasoning as the confirmation email above — a
|
||||
// failed marketing sync is not worth failing checkout over, and doesn't
|
||||
// even need a critical alert (nothing customer-facing depends on it).
|
||||
if (body.newsletterOptIn) {
|
||||
upsertNewsletterContact(body.email, "checkout").catch(() => {});
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
orderNumber: order.orderNumber,
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { upsertNewsletterContact } from "../../../lib/brevo";
|
||||
|
||||
type SubscribeBody = {
|
||||
email?: string;
|
||||
consent?: boolean;
|
||||
source?: "newsletter-page" | "newsletter-modal";
|
||||
};
|
||||
|
||||
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const body: SubscribeBody = await req.json();
|
||||
const email = body.email?.trim() ?? "";
|
||||
|
||||
if (!EMAIL_PATTERN.test(email)) {
|
||||
return NextResponse.json({ ok: false, reason: "Bitte gib eine gültige E-Mail-Adresse ein." }, { status: 400 });
|
||||
}
|
||||
if (!body.consent) {
|
||||
return NextResponse.json({ ok: false, reason: "Bitte akzeptiere die Datenschutzerklärung." }, { status: 400 });
|
||||
}
|
||||
|
||||
const source = body.source === "newsletter-modal" ? "newsletter-modal" : "newsletter-page";
|
||||
const result = await upsertNewsletterContact(email, source);
|
||||
if (!result.ok) {
|
||||
return NextResponse.json({ ok: false, reason: "Anmeldung ist fehlgeschlagen. Bitte versuche es später erneut." }, { status: 502 });
|
||||
}
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
Reference in New Issue
Block a user