- 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>
7.3 KiB
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):
- CTA Curhat on customer home is gated by mitra availability — pulled every 5 seconds.
- New session flow: CTA → payment screen → payment confirmed → blast → mitra accept (idempotent) → chat starts. (Today: blast happens immediately on CTA, no payment in path.)
- Customer can start a new session with the same mitra via a CTA on chat history.
- Returning-chat (same-mitra) requests need mitra approval. 20-second window, auto-reject on timeout. Timeout configurable via control center.
- 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.