import { NextRequest, NextResponse } from "next/server"; import { getApiUrlFromRequest, getPaymentConfig } from "@/config/services.config"; import { checkPaymentsByUserId, checkSiteVipByUserId, findLinkedMemberFromOrder, findVipEmailLinkByAnonymousUserId, getAdminToken, isAnonymousUserId, upsertVipEmailLink, } from "@/app/api/vip/_helpers"; type PayjsVipResult = { vip: boolean; email?: string; }; function resolvePayApiUrl(request: NextRequest): string { const payConfig = getPaymentConfig(); const hostHeader = request.headers.get("host") || request.headers.get("x-forwarded-host"); return ( (hostHeader ? getApiUrlFromRequest(hostHeader) : null) || process.env.PAYMENT_API_URL || process.env.NEXT_PUBLIC_API_URL || payConfig.apiUrl ).replace(/\/$/, ""); } async function checkVipFromPayjsapi( request: NextRequest, userId: string ): Promise { try { const apiUrl = resolvePayApiUrl(request); const url = `${apiUrl}/nomadvip/check_vip?user_id=${encodeURIComponent( userId )}`; console.log("[check-by-user] payjsapi request user_id=%s url=%s", userId, url); const res = await fetch( url, { cache: "no-store", signal: AbortSignal.timeout(5000) } ); if (!res.ok) { console.log( "[check-by-user] payjsapi non-ok user_id=%s status=%s", userId, res.status ); return null; } const data = await res.json().catch(() => ({})); const result = { vip: !!data?.vip, email: typeof data?.email === "string" && data.email.trim() ? data.email.trim().toLowerCase() : undefined, }; console.log( "[check-by-user] payjsapi response user_id=%s vip=%s email=%s", userId, result.vip, result.email ?? "(none)" ); return result; } catch (error) { console.log( "[check-by-user] payjsapi request failed user_id=%s error=%s", userId, error instanceof Error ? error.message : String(error) ); return null; } } export async function POST(request: NextRequest) { const body = await request.json().catch(() => ({})); const userId = String(body?.user_id ?? body?.userId ?? "").trim(); console.log("[check-by-user] request user_id=%s", userId || "(empty)"); if (!userId) { return NextResponse.json( { ok: false, vip: false, error: "缺少 user_id" }, { status: 400 } ); } const payjsResult = await checkVipFromPayjsapi(request, userId); let vip = !!payjsResult?.vip; let email = payjsResult?.email ?? null; let linkedUserId: string | undefined; let vipFromSite = false; let vipFromPayments = false; let vipSource = vip ? "payjsapi" : "none"; try { if (vip) { console.log( "[check-by-user] hit payjsapi user_id=%s alreadyLinked=%s", userId, !!email ); return NextResponse.json({ ok: true, vip: true, alreadyLinked: !!email, email: email ?? undefined, }); } const adminToken = await getAdminToken(); if (!adminToken) { console.error("[check-by-user] missing PocketBase admin token"); return NextResponse.json({ ok: false, vip: false }, { status: 500 }); } if (!vip) { vipFromSite = await checkSiteVipByUserId(adminToken, userId); vip = vipFromSite; if (!vip) { vipFromPayments = await checkPaymentsByUserId(adminToken, userId); vip = vipFromPayments; } if (vipFromSite) { vipSource = "pocketbase:site_vip"; } else if (vipFromPayments) { vipSource = "pocketbase:payments"; } console.log( "[check-by-user] pb fallback user_id=%s site_vip=%s payments=%s", userId, vipFromSite, vipFromPayments ); } if (vip && !email && isAnonymousUserId(userId)) { const linkRecord = await findVipEmailLinkByAnonymousUserId( adminToken, userId ); const linkEmail = typeof linkRecord?.email === "string" ? linkRecord.email.trim().toLowerCase() : ""; const linkUserId = typeof linkRecord?.user_id === "string" ? linkRecord.user_id.trim() : ""; if (linkEmail) { email = linkEmail; if (linkUserId && !isAnonymousUserId(linkUserId)) { linkedUserId = linkUserId; } vipSource = "vip_email_link"; console.log( "[check-by-user] linked by vip_email_link user_id=%s linkedUserId=%s email=%s", userId, linkedUserId || "(none)", email ); } else { const linkedMember = await findLinkedMemberFromOrder(adminToken, userId); if (linkedMember) { email = linkedMember.email; linkedUserId = linkedMember.userId; await upsertVipEmailLink( adminToken, linkedMember.email, linkedMember.userId, userId ); vipSource = "linked_from_order"; console.log( "[check-by-user] linked by order user_id=%s linkedUserId=%s email=%s", userId, linkedUserId, email ); } } } console.log( "[check-by-user] result user_id=%s vip=%s source=%s alreadyLinked=%s linkedUserId=%s", userId, vip, vipSource, !!email, linkedUserId || "(none)" ); return NextResponse.json({ ok: true, vip, alreadyLinked: !!email, email: email ?? undefined, linkedUserId, }); } catch (error) { console.error("[check-by-user] failed:", error); return NextResponse.json({ ok: false, vip: false }, { status: 500 }); } }