feat(capi): server-side Meta Conversions API for signup (CompleteRegistration)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled

The browser pixel under-counts signups badly: ~96% of paid traffic is mobile
in-app browsers where iOS ITP / ad-blockers drop client events, and the OAuth
path never fired it reliably. Meta recorded ~0 registrations for a 7.5K-spend
campaign while PostHog saw 98 facebook signups — so Meta could neither optimize
toward nor attribute signups, which is the main driver of the low signup rate.

This adds a server-side CAPI CompleteRegistration:
- MetaCapiService + @Global module. Fail-open: no-ops unless META_CAPI_PIXEL_ID
  + META_CAPI_ACCESS_TOKEN are set; never throws (signup must not break).
  SHA-256 hashed email + fbp/fbc/IP/UA.
- Fired from the better-auth user.create.after hook for ALL signups (reliable,
  covers Google OAuth which the browser pixel missed entirely).
- A session-gated POST /analytics/meta/complete-registration endpoint adds
  fbp/fbc/IP/UA (ad-click attribution) for the email path.
- The browser pixel now passes a shared event_id (signup_<userId>); the
  premature Google client-pixel fire (fired on click, before completion) is
  removed.
- All sources dedupe via event_id=signup_<userId>.

Activate by setting META_CAPI_PIXEL_ID + META_CAPI_ACCESS_TOKEN (Events Manager)
in the api env; META_CAPI_TEST_EVENT_CODE routes to Test Events for verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 15:19:58 +03:00
parent 3325747428
commit 145689a391
10 changed files with 223 additions and 5 deletions

View File

@@ -150,7 +150,13 @@ function RegisterPage() {
// onboarding modal on the search page is the single place that applies it
// (covers both email and Google OAuth signups).
capture("user_signed_up", { method: "email" });
trackMeta("CompleteRegistration", { method: "email" });
// CompleteRegistration: browser pixel + server-side CAPI (adds fbp/fbc/IP/UA
// for ad-click attribution), deduped via the shared event id `signup_<userId>`.
const metaEventId = data?.user ? `signup_${data.user.id}` : undefined;
trackMeta("CompleteRegistration", { method: "email" }, metaEventId);
api
.post("/analytics/meta/complete-registration", { eventSourceUrl: window.location.href })
.catch(() => {});
if (plan) localStorage.setItem(PENDING_PLAN_KEY, plan);
toast.success("Hesap oluşturuldu!");
window.location.href = redirectUrl;
@@ -237,7 +243,10 @@ function RegisterPage() {
onClick={() => {
startAction("register", { method: "google" });
capture("user_signed_up", { method: "google" });
trackMeta("CompleteRegistration", { method: "google" });
// Meta CompleteRegistration for Google is sent SERVER-SIDE on actual
// account creation (auth databaseHook). The browser pixel here fired on
// click (before the user even finished Google auth) and would double-
// count, so it's intentionally not sent client-side for OAuth.
if (plan) localStorage.setItem(PENDING_PLAN_KEY, plan);
signIn.social({ provider: "google", callbackURL: redirectUrl });
}}