Files
halobestie-clone/requirement/phase3.7-questions.md
ramadhan sjamsani d09e50af55 Phase 3.7: paid pairing flow + returning chat + extension flip
- Backend: payment_sessions + pairing_failures tables; payment.service.js
  and pairing-failure.service.js (new); rewritten pairing.service.js
  (payment-gated blast + targeted "Curhat lagi" + cancel + fallback);
  rewritten extension.service.js (data-driven auto-approve with offline
  safeguard, charge-at-approval); pricing.service.js (extension tiers
  without free trial); mitra-status.service.js (countAvailableMitras
  cached path); 60s sweeper for stale payment sessions
- Backend routes: client.payment.routes, client.mitra-availability.routes,
  internal/failed-pairings.routes; client.chat.routes rewritten for
  payment-gated start + /returning + /cancel + /fallback-to-blast;
  internal/config.routes adds 4 new keys with Valkey invalidate publish
- client_app: mitra-availability poll, payment screen + notifier, pairing
  notifier rewrite (PairingTargetedWaiting + PairingFailed states),
  targeted-waiting overlay + bestie-unavailable dialog, "Curhat lagi"
  CTA, failed-pairing terminal, extension via payment-session
- mitra_app: PairingRequestType enum, returning-chat 20s countdown
  auto-dismiss, extension card "otomatis disetujui" copy
- control_center: 4 new config rows in Settings, Failed Pairings page
  (filter + paginate + action menu), sidebar + route registered
- Test infrastructure: Vitest backend (7/7 pass), Playwright CC (4/4
  pass), Maestro mobile scaffold (CLI install pending)
- Bugs found via Playwright + fixed: LoginPage labels not associated
  with inputs (a11y); backend internal CORS missing PATCH/PUT/DELETE
  in allow-methods (silent settings breakage in browsers since Stage 4)
- Docs: phase3.7.md PRD, phase3.7-plan.md, phase3.7-questions.md (Q&A),
  phase3.7-testing.md (E2E checklist), phase3.7-test-run-2026-05-03.md
  (today's run results)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 23:02:49 +08:00

7.3 KiB

status, captured, answered
status captured answered
ANSWERED — ready for PRD (phase3.7.md) 2026-05-02 2026-05-03

Phase 3.7 — Clarifying Questions (Answered)

Raw asks from user (2026-05-02 chat):

  1. CTA Curhat on customer home is gated by mitra availability — pulled every 5 seconds.
  2. New session flow: CTA → payment screen → payment confirmed → blast → mitra accept (idempotent) → chat starts. (Today: blast happens immediately on CTA, no payment in path.)
  3. Customer can start a new session with the same mitra via a CTA on chat history.
  4. Returning-chat (same-mitra) requests need mitra approval. 20-second window, auto-reject on timeout. Timeout configurable via control center.
  5. Session extension still requires approval, but 10-second window with auto-approve on timeout (flip from today's auto-reject). Configurable via control center.

Phase numbering — DECIDED

  • Called Phase 3.7 (next free after 3.6). Originally drafted as "Phase 4" but user prefers to keep 3.x numbering for this scope.

Section 1 — CTA gated by mitra availability (5s poll)

# Q Answer
1.1 Signal definition (b) at least 1 mitra online AND below max-customer capacity
1.2 Endpoint shape New lightweight GET /api/client/mitra-availability{ available: bool, count?: number }. Backend reads from Valkey only — must not hit Postgres on every poll. Count optional in payload (CC debugging), client only reads available
1.3 Polling lifecycle Foreground only. Pause on background, resume on foreground
1.4 Disabled-state UX (a) Greyed CTA with subtitle "Belum ada bestie tersedia"
1.5 Visible count Binary only — no number shown to user
1.6 Stale data on poll fail (b) Default to disabled

Section 2 — New flow: CTA → Payment → Blast → Accept → Chat

2a. Payment screen

# Q Answer
2a.1 Integration depth Mocked — no real Xendit in 3.7. Real Xendit deferred to a later phase
2a.2 Pricing source Keep existing Phase 3 mock pricing. Real pricing/tiers later (saved to memory)
2a.3 Free trial UX (b) "Gratis" Rp 0 confirmation step on the same payment screen — does not skip to blast
2a.4 Abandonment (b) Persist a "pending payment" / "abandoned" row; auto-expire after configurable timeout
2a.5 Payment timeout 20 minutes default, CC-configurable (payment_session_timeout_minutes)

2b. Blast → Accept

# Q Answer
2b.1 Customer screen during blast Reuse existing "Searching for bestie..." screen. Real design later via Claude design
2b.2 Blast timeout Same as Phase 2; verify it is CC-configurable, otherwise add pairing_blast_timeout_seconds
2b.3 No mitra accepts within window Persist payment + log failed-pairing event with a tag (e.g. no_mitra_available, all_mitras_rejected, targeted_mitra_offline, targeted_mitra_rejected, targeted_mitra_timeout, payment_session_expired, customer_cancelled). Surface to Control Center for manual review/refund decision. Customer-facing: hard-fail message for now (CTA copy will be revised later)
2b.4 All mitras explicitly reject Same as 2b.3 — different tag value
2b.5 Idempotency on accept Confirmed — keep Phase 2's DB-level uniqueness on session acceptance

2c. Migration

# Q Answer
2c.1 Replacement strategy Replace entirely — delete the old instant-blast path. No feature flag
2c.2 Existing screens Reuse where possible; replace only when reuse is impractical

Section 3 — "Curhat lagi" with the same mitra (from chat history)

# Q Answer
3.1 Per row or per partner (a) CTA on every chat history row (simpler)
3.2 Payment first Yes — same payment screen as regular curhat
3.3 Mitra offline at tap (a) but with popup — show "Bestie sedang tidak online" popup. Offer "Chat dengan bestie lain" if any other mitra is available; otherwise just show the offline message
3.4 Mitra at capacity Same as 3.3
3.5 On rejection / 20s auto-reject Same as 3.3. Important: payment is already taken by this point — the same payment carries over to the general blast fallback (no double-charge). If fallback also fails, treat as 2b.3 (logged + CC review with appropriate tag)
3.6 Bypass general gating Independent — depends only on the targeted mitra's status, not the section 1 availability poll
3.7 Anonymity Unchanged (mitra always sees customer call_name)

Section 4 — Returning-chat approval window (20s, auto-reject)

# Q Answer
4.1 Mitra UX Reuse existing incoming-request notification component (FCM + foreground card), add visible 20s countdown
4.2 Customer UX during 20s (a) Overlay "Menunggu konfirmasi bestie..." with cancel button
4.3 Auto-reject downstream Same as 3.5 (popup → offer general blast fallback or fail; payment carries over)
4.4 Mitra offline at request time (a) Auto-reject immediately, do not wait 20s
4.5 Control center config New config row: returning_chat_confirmation_timeout_seconds (default 20). Use a clear label and explanation in the CC UI
4.6 Concurrency (mitra mid-session with someone else) (c) Send the card and let mitra decide

Section 5 — Extension approval flip (10s, auto-approve)

# Q Answer
5.1 Behavioral flip — confirm Confirmed intentional — flip from auto-reject → auto-approve
5.2 Default value & config (c) Keep existing extension-timeout row; add new extension_default_action_on_timeout enum (auto_reject | auto_approve), default auto_approve
5.3 Customer overlay during 10s Same overlay as today, just shorter timer
5.4 Charge timing for extension (b) Charge at approval moment (auto-approve fires charge; explicit reject within 10s = no charge). Important: extension is NOT auto-charged — customer chooses time + price first (same UX as initial chat request, without trial)
5.5 Mitra UX Same extension card; copy adjusted to reflect auto-approve
5.6 Mitra disconnected/offline during 10s (b) Treat as auto-reject (safer for customer). Domain rule (saved to memory): mitra can flip to offline mid-session — never use "in-session" as proxy for "online"

Cross-cutting

# Q Answer
X.1 Refund / failed-pairing model Single consistent model across 2b.3 / 3.5 / 4.3: payment row persists, event logged with tag (cause) for filtering/audit, surfaced to CC for manual review
X.2 Old instant-blast code Delete entirely, no kill-switch, no feature flag
X.3 Free trial Same payment screen UI, Rp 0 / "Gratis", with duration/tier picker shown
X.4 Anonymity Unchanged
X.5 New CC configs pairing_blast_timeout_seconds (only if not already), payment_session_timeout_minutes (default 20), returning_chat_confirmation_timeout_seconds (default 20), extension_default_action_on_timeout enum (default auto_approve) — no others
X.6 Phase numbering Phase 3.7

Next step

PRD phase3.7.md, then phase3.7-plan.md, then code.