import { authenticate } from '../../plugins/auth.js' import { getCustomerById } from '../../services/customer.service.js' import { isCustomerEligibleForFirstSessionDiscount } from '../../services/pricing.service.js' import { getDb } from '../../db/client.js' import { UserType, SessionStatus } from '../../constants.js' const sql = getDb() /** * Phase 4 onboarding-state endpoint. Drives: * - Verif Choice Sheet visibility on the post-name screen. * - S6 paywall vs Pilih cara routing decision. * * Eligibility predicate (server-authoritative — client never decides): * first_session_discount_enabled AND phone-verified AND no completed sessions. * * NOTE: deviates from the plan's `users.phone_verified_at` reference — there is no * such column. `customers.phone IS NOT NULL` is equivalent in this schema (phone is * only ever set by the OTP-verify path). */ export const clientOnboardingRoutes = async (app) => { app.get('/onboarding-state', { preHandler: authenticate }, async (request, reply) => { if (request.auth?.userType !== UserType.CUSTOMER) { return reply.code(403).send({ success: false, error: { code: 'FORBIDDEN', message: 'Customer account required' }, }) } const customer = await getCustomerById(request.auth.userId) if (!customer) { return reply.code(404).send({ success: false, error: { code: 'ACCOUNT_NOT_FOUND', message: 'Customer account not found' }, }) } const isPhoneVerified = !!customer.phone const [prior] = await sql` SELECT id FROM chat_sessions WHERE customer_id = ${customer.id} AND status IN (${SessionStatus.COMPLETED}, ${SessionStatus.CLOSING}) LIMIT 1 ` const hasConsultedBefore = !!prior // Use the same predicate the pricing endpoint uses, so the two stay in lock-step. const isFirstSessionDiscountEligible = await isCustomerEligibleForFirstSessionDiscount(customer.id) return reply.send({ success: true, data: { has_consulted_before: hasConsultedBefore, is_phone_verified: isPhoneVerified, is_first_session_discount_eligible: isFirstSessionDiscountEligible, is_anonymous: !!customer.is_anonymous, }, }) }) }