Phase 4 Stage 1: backend foundation (additive endpoints + schema)
Schema (idempotent migration): - payment_sessions.is_free_trial -> is_first_session_discount (data copied) - payment_sessions.mode TEXT NOT NULL DEFAULT 'chat' CHECK (chat|call) - chat_sessions.topics TEXT[] for ESP picks (info-only) New endpoints: - GET /api/client/onboarding-state (drives verif sheet + S6 paywall gate) - GET /api/client/chat-pricing (rewrite: chat+call groups + first-session discount block, per-customer eligibility) - GET /api/shared/auth-providers (env-probed; replaces ENABLE_SOCIAL_AUTH build flag — frontend cutover lands in stage 2) - GET /api/client/support-handles (Tanya Admin handles, CC-config-driven) session_warning WS event fires once at 180s remaining. app_config seeds (mock pricing tiers, first-session discount, support handles, payment method order, end-session 2-step toggle). CC SettingsPage: 3 new sections (first-session discount, pricing tiers JSON editors, support handles). 15/15 Vitest passing. chat_sessions.is_free_trial also renamed for consistency (plan only specified payment_sessions; pairing.service.js read both). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -136,7 +136,7 @@ const requireConfirmedPaymentSession = async (paymentSessionId, customerId, { al
|
||||
/**
|
||||
* General-blast pairing request. Requires a confirmed payment_session_id.
|
||||
*
|
||||
* The duration_minutes / price / is_free_trial values for the chat_session row are
|
||||
* The duration_minutes / price / is_first_session_discount values for the chat_session row are
|
||||
* sourced from the payment session — the client does not dictate pricing here.
|
||||
*
|
||||
* `allowTargetedPayment` is set true by the fallback-to-blast path: the original payment
|
||||
@@ -183,14 +183,14 @@ export const createPairingRequest = async (customerId, { paymentSessionId, topic
|
||||
// Create session sourced from the payment session.
|
||||
const [session] = await sql`
|
||||
INSERT INTO chat_sessions (
|
||||
customer_id, status, duration_minutes, price, is_free_trial, topic_sensitivity, payment_session_id
|
||||
customer_id, status, duration_minutes, price, is_first_session_discount, topic_sensitivity, payment_session_id
|
||||
)
|
||||
VALUES (
|
||||
${customerId}, ${SessionStatus.PENDING_ACCEPTANCE},
|
||||
${paySession.duration_minutes}, ${paySession.amount}, ${paySession.is_free_trial},
|
||||
${paySession.duration_minutes}, ${paySession.amount}, ${paySession.is_first_session_discount},
|
||||
${resolvedTopic}, ${paymentSessionId}
|
||||
)
|
||||
RETURNING id, customer_id, status, duration_minutes, price, is_free_trial, topic_sensitivity, payment_session_id, created_at
|
||||
RETURNING id, customer_id, status, duration_minutes, price, is_first_session_discount, topic_sensitivity, payment_session_id, created_at
|
||||
`
|
||||
|
||||
// Fan out to all available mitras in parallel — DB inserts and notifications are
|
||||
@@ -206,7 +206,7 @@ export const createPairingRequest = async (customerId, { paymentSessionId, topic
|
||||
request_type: PairingRequestType.GENERAL,
|
||||
created_at: session.created_at,
|
||||
duration_minutes: session.duration_minutes,
|
||||
is_free_trial: session.is_free_trial,
|
||||
is_first_session_discount: session.is_first_session_discount,
|
||||
topic_sensitivity: session.topic_sensitivity,
|
||||
})
|
||||
}))
|
||||
@@ -305,14 +305,14 @@ export const createTargetedPairingRequest = async (customerId, { paymentSessionI
|
||||
// Create session sourced from the payment session, status = pending_acceptance.
|
||||
const [session] = await sql`
|
||||
INSERT INTO chat_sessions (
|
||||
customer_id, status, duration_minutes, price, is_free_trial, topic_sensitivity, payment_session_id
|
||||
customer_id, status, duration_minutes, price, is_first_session_discount, topic_sensitivity, payment_session_id
|
||||
)
|
||||
VALUES (
|
||||
${customerId}, ${SessionStatus.PENDING_ACCEPTANCE},
|
||||
${paySession.duration_minutes}, ${paySession.amount}, ${paySession.is_free_trial},
|
||||
${paySession.duration_minutes}, ${paySession.amount}, ${paySession.is_first_session_discount},
|
||||
${resolvedTopic}, ${paymentSessionId}
|
||||
)
|
||||
RETURNING id, customer_id, status, duration_minutes, price, is_free_trial, topic_sensitivity, payment_session_id, created_at
|
||||
RETURNING id, customer_id, status, duration_minutes, price, is_first_session_discount, topic_sensitivity, payment_session_id, created_at
|
||||
`
|
||||
|
||||
// Single notification to the targeted mitra
|
||||
@@ -330,7 +330,7 @@ export const createTargetedPairingRequest = async (customerId, { paymentSessionI
|
||||
request_type: PairingRequestType.RETURNING,
|
||||
created_at: session.created_at,
|
||||
duration_minutes: session.duration_minutes,
|
||||
is_free_trial: session.is_free_trial,
|
||||
is_first_session_discount: session.is_first_session_discount,
|
||||
topic_sensitivity: session.topic_sensitivity,
|
||||
confirmation_timeout_seconds: returning_chat_confirmation_timeout_seconds,
|
||||
})
|
||||
@@ -399,12 +399,12 @@ export const acceptPairingRequest = async (sessionId, mitraId) => {
|
||||
ELSE NULL
|
||||
END
|
||||
WHERE id = ${sessionId}
|
||||
RETURNING id, customer_id, mitra_id, status, paired_at, duration_minutes, price, is_free_trial, expires_at, payment_session_id
|
||||
RETURNING id, customer_id, mitra_id, status, paired_at, duration_minutes, price, is_first_session_discount, expires_at, payment_session_id
|
||||
`
|
||||
|
||||
// Record transaction
|
||||
if (activeSession.duration_minutes) {
|
||||
const txType = activeSession.is_free_trial ? TransactionType.FREE_TRIAL : TransactionType.PAID
|
||||
const txType = activeSession.is_first_session_discount ? TransactionType.FIRST_SESSION_DISCOUNT : TransactionType.PAID
|
||||
await sql`
|
||||
INSERT INTO customer_transactions (customer_id, session_id, type, amount)
|
||||
VALUES (${activeSession.customer_id}, ${sessionId}, ${txType}, ${activeSession.price || 0})
|
||||
@@ -787,7 +787,7 @@ export const getPendingRequestsForMitra = async (mitraId) => {
|
||||
SELECT
|
||||
cs.id AS session_id,
|
||||
cs.duration_minutes,
|
||||
cs.is_free_trial,
|
||||
cs.is_first_session_discount,
|
||||
cs.topic_sensitivity,
|
||||
cs.created_at,
|
||||
CASE
|
||||
|
||||
Reference in New Issue
Block a user