Phase 4 Stage 10 Maestro: 09_chat_tab.yaml + seed-pending-payment endpoint

Closes the Stage 10 acceptance criterion §10.11 #13 (Maestro coverage).

- New dev-only `POST /internal/_test/seed-pending-payment` — inserts a
  payment_sessions row in `pending` status with expires_at 20m out, so
  the Pembayaran sub-tab has a deterministic row to render. Body
  accepts { phone, isExtension?, amount?, durationMinutes?, mode? }.
  Gated on NODE_ENV != 'production' like the other test routes.

- New Maestro helper script `seed_pending_payment.js` mirrors the
  existing seed_history_session pattern.

- New flow `09_chat_tab.yaml`:
    cold-start onboarding → home (returning view) →
    seed completed session + seed pending payment →
    tap "💬 chat" bottom-nav → lands on /chat/aktif via redirect →
    assert "aktif" / "pembayaran" / "selesai" pills + empty-state copy →
    tap pembayaran → assert "menunggu pembayaran sesi" + "bayar Rp..." →
    tap selesai → assert "X menit" duration row → tap row → assert
    "Transkrip Chat" appbar → back → still on /chat/selesai.

  Maestro parsed the YAML cleanly and started executing against the
  device; full run requires backend + online mitra in dev DB (same
  pre-reqs as flows 03/05/06/08).

- TECH_DEBT entry: Stage 10 retired the standalone bestie-history list
  screen, which means (a) the "curhat lagi" targeted-payment entry
  point has no UI affordance anywhere in the app — its plumbing in
  payment_notifier / payment_screen is now orphaned, and (b) the
  Stage 8 flow `08_returning_targeted.yaml` will fail at
  `assertVisible: "Riwayat Chat"` because it expects the deleted
  screen. Three fix paths listed in the entry for product to pick.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 20:24:50 +08:00
parent e3ea1d793e
commit 1908e98012
4 changed files with 328 additions and 0 deletions

View File

@@ -245,4 +245,45 @@ export const internalTestRoutes = async (fastify) => {
`
return { ok: true, session_id: session.id, mitra_id: mitra.id, mitra_name: mitra.display_name }
})
// Seed a payment_sessions row in `pending` status for the customer linked
// to `phone`, with expires_at safely in the future. Used by Maestro Stage
// 10 flow (09_chat_tab.yaml) to populate the Pembayaran sub-tab without
// walking the multi-screen S6 paywall → method → duration → method flow.
//
// Body: { phone, isExtension?, amount?, durationMinutes?, mode? }
// - isExtension: defaults false (initial-session payment)
// - amount: defaults 5000 IDR
// - durationMinutes: defaults 15
// - mode: 'chat' (default) | 'call'
fastify.post('/seed-pending-payment', async (request, reply) => {
const phone = request.body?.phone
if (!phone) {
return reply.code(400).send({ error: 'phone required in body' })
}
const isExtension = request.body?.isExtension === true
const amount = Number.isFinite(request.body?.amount) ? request.body.amount : 5000
const durationMinutes = Number.isFinite(request.body?.durationMinutes)
? request.body.durationMinutes
: 15
const mode = request.body?.mode === 'call' ? 'call' : 'chat'
const [customer] = await sql`
SELECT id FROM customers WHERE phone = ${phone} LIMIT 1
`
if (!customer) {
return reply.code(404).send({ error: 'no_customer_for_phone', phone })
}
const [row] = await sql`
INSERT INTO payment_sessions (
customer_id, amount, duration_minutes, is_first_session_discount,
is_extension, status, mode, expires_at
) VALUES (
${customer.id}, ${amount}, ${durationMinutes}, false,
${isExtension}, 'pending', ${mode}, NOW() + INTERVAL '20 minutes'
)
RETURNING id, customer_id, amount, is_extension, status, expires_at
`
return { ok: true, payment_id: row.id, ...row }
})
}