Skip to main content

Self Top-up — Task Breakdown (as-built reconciliation)

Mode change vs prior version. Previous breakdown was a forward plan against mocked BE (hub_core not checked out). This rewrite is a reconciliation against the as-built code in five repos (hub-chat-v2, hub-core, hub-service, qontak-billing, modpanel/moderator-be) and a direct read of every PRD-cited Jira ticket. The build is materially complete; the leftover is entirely in the QA / PO-sign-off lane.

Sources of truth, grounded:

  • PRD: prds/self-topup.md (Confluence 50190516285).
  • RFC (now linked): "Self Top-up Technical Documentation" — Confluence 51046842375. Describes the Ruby/Rails hub-core flow + Go qontak-billing config source as actually shipped.
  • Epic: BIF-7797 (not BIF-5840 — see Drift §0).
  • All ticket statuses verified via Jira on 2026-06-30.

0. Drift correction before reading any task

Three drift items invalidate the prior breakdown's framing:

  1. PRD says Epic = BIF-5840. That Epic is Done (it's the 25Q4 BE-only forerunner). The actual delivery Epic for everything in this PRD is BIF-7797 "26Q2 Self Topup (Call, AI, User, Email, Recording)" — status To Do, target 26Q2, deprio'd 2026-04-21 per Addo's comment. Every PRD-cited Story (BIF-7869…BIF-7879) is a child of BIF-7797. README.md jira_epic and the PRD header must be updated to BIF-7797.
  2. PRD says "RFC Link = TBD". The RFC exists: 51046842375 "Self Top-up Technical Documentation" (Hafriz, Feb 2026, v3). It describes the as-built system. It is not linked from the Epic or the PRD — pure linkage gap, not a missing artefact.
  3. The build is not blocked on hub_core. The Go service qontak-billing already owns topup_configurations end-to-end and already calls Mekari Billing's /api/v4/packages/{id} from internal/app/api/mekari_billing/get_detail_package.go:20. The prior breakdown's "STP-S09 fully blocked" framing was wrong.

1. Build status — as-built evidence per story

Legend: ✅ Implemented (file + line) · ⚠️ Implemented with caveat · ❌ Not built · 🟡 Code done, awaiting PO confirm / QA sign-off.

StoryJira (status, 2026-06-30)Code evidenceVerdict
STP-S01 Feature flag + V3 gateBIF-7879In ProgressTwo-layer flag: hub-core/spec/dummy/db/data/20200219071220_default_data_flag.rb:1648,1666 (billing_new_topup_page, billing_new_topup); per-CID organization.settings['bifrost_new_topup'] evaluated in hub-core/app/apps/mekari_pay/interactors/user_pay_invoiced.rb:35. FE consumes via hub-chat-v2/common/store/BillingStore.ts:17 (is_new_topup_page?: boolean) and hub-chat-v2/features/finances/topup/FinancesTopupPage.vue:314. Sub-tasks BIF-8248 (BE) Done, BIF-8249 (FE) Done, BIF-8250 (QA) Done, BIF-8457 (Nuxt4 toggle) Done.🟡 Code complete; Story still In Progress because it doubles as the rollout-by-CID coordination ticket.
STP-S02 WA min 1,000,000 + excess (UPDATE)BIF-7871 — Waiting Confirmationhub-chat-v2/features/finances/topup/FinancesTopupPage.vue:373 (minimumTopup.value.wa_balance = mcc?.is_enabled ? mcc.minimum_value : 1000000); spec components/__tests__/FinancesTopupPage.spec.ts:19; BE row task BIF-7900 (Done) added wa_balance to topup_configurations.🟡
STP-S03 MUV predefined options + hide on Unlimited (UPDATE)BIF-7872 — Waiting ConfirmationOptions stored in qontak-billing/db/migrations/20251009090000_create_topup_configurations.up.sql:12 (option_value JSONB). Hide-by-component in qontak-billing/internal/app/usecase/topup_configuration/fetch_configuration.go:60-66 (isHideComponent/isShowComponent against hide_by_component/show_by_component JSONB). FE: hub-chat-v2/features/finances/topup/store/useTopupStore.ts:118 (isToggleForHideMUV); components/TopupStepTwo.vue:77 MUV row. BE row task BIF-7899 (Done).🟡
STP-S04 User Quota (NEW)BIF-7873 — Waiting ConfirmationBE row task BIF-7898 (Done). Package code field present in qontak-billing/db/migrations/20251015093000_add_fields_to_topup_configurations.up.sql (package_code, quota_type, billing_version_for_pi, excess_*); period semantics carried via quota_type column. PRD's "period = contract end_date" rule is not visible in the moderator-be paid_pi payload at modpanel/moderator-be/app/controllers/api/v1/billing/subscriptions_controller.rb:41 (action takes active_from + paid_date; no period_end/contract_end_date parameter mapped).⚠️ Code in qontak-billing complete; verification gap: confirm contract_end_date actually reaches Mekari Billing via paid_pi.
STP-S05 Voice Balance (NEW)BIF-7874 — Waiting ConfirmationBE row task BIF-7897 (Done). Component-code gate via show_by_component JSONB (migration 20251015093000). hub-core add-on mapping: app/core/domains/services/billing/bulk_add_on.rb:42 (voice_call_extra mapped behind is_voice_call_enabled flag billing_voice_call_topup).🟡
STP-S06 AI Dialog (NEW)BIF-7875 — Waiting ConfirmationBE row task BIF-7896 (Done). hub-core add-on mapping: app/core/domains/services/billing/bulk_add_on.rb:30 (chatbot_ai_extra behind billing_chatbot_ai_topup). ×1000 multiplier is encoded in seed data via option_value JSONB — not in app code; cross-check seed when CID rollout begins.⚠️ Code complete; verification gap: assert seeded option_value for the AI row produces N → N×1,000 dialogs end-to-end.
STP-S07 Review Order + confirmation popup (NEW)BIF-7876 — Waiting ConfirmationFE: hub-chat-v2/features/finances/topup/components/TopupDynamicStep2.vue, TopupStepTwo.vue. Confirmation modal evidenced by BIF-8558 (Done) ("Missing close button in confirmation self top up modal"). UI revision BIF-8324 (Done) "Adjust UI Self top up based on new UI Step two".🟡
STP-S08 Excess usage on Topup page (NEW)BIF-8175In Progress (separate Story; PRD row 12 has empty Jira cell — PRD↔Jira link gap)FE composable hub-chat-v2/features/finances/topup/composables/useExcessBalance.ts; rendered at FinancesTopupPage.vue:196 (:excess-balances="excessBalances"). BE: dedicated endpoint GET /iag/v1/topup-configurations/excess-quota at qontak-billing/internal/app/server/rest_router.go:65; usecase internal/app/usecase/topup_configuration/excess_quota_topup.go. Migration 20251015093000 added excess_key, excess_package_id, excess_package_code. Executor sub-tasks BIF-8023 (FE) Done and BIF-8024 (BE) Done — they are split-from BIF-8175, not from a PRD-cited Story.🟡
STP-S09 Mekari Billing pricing API + Redis cache (BE)BIF-7869 — Waiting ConfirmationAlready implemented in qontak-billing. Client: internal/app/api/mekari_billing/get_detail_package.go:19-20 (GET /api/v4/packages/{id}). Caller: internal/app/usecase/topup_configuration/fetch_configuration.go:144 (s.mekariBillingClient.GetDetailPackage(ctx, packageID)). Cache: same file, lines 21-22 (cacheTopupConfigurationDuration = 1 * time.Hour, key topup_configuration:). Fallback on cache miss → DB → API → re-cache; on API failure, the row is skipped with a logged error. Dynamic placeholders in label/description replaced via {{mekari_billing}} token (fetch_configuration.go:replacePlaceholder).

Non-PRD scope additions on Epic BIF-7797 (also worth tracking)

TicketStatusWhy it matters
BIF-7889 (BE) / BIF-7891 (FE) Placeholder: partial toggle to several CIDsDone / Won't FixRollout-list plumbing.
BIF-7892 (BE) add label and description to topup_configurationsDoneMigration 20260331120000_add_label_and_description_to_topup_configurations.up.sql (qontak-billing).
BIF-7893 (FE) Create a new topup pageDoneNet-new hub-chat-v2/features/finances/topup/FinancesTopupPage.vue shell.
BIF-7894 (FE) Hit and map topup_configurations from BEDoneBillingStore.ts:228 (getTopupConfigurations).
BIF-7895 (FE) Create/adjust review top-up page for POST ordersDoneReview step.
BIF-7901 (BE) Add label and description to fetch endpointDoneResponse field label, description (response/topup_configuration.go:24-25).
BIF-7960 (BE) Adjust wording all email notificationDoneTopup notification copy.
BIF-8198 (FE) Recheck API integrationDonePost-rewire smoke.
BIF-8324 (FE) Adjust UI Self top up based on new UI Step twoDoneStep-2 redesign — not in PRD AC table.
BIF-8342 (BE) add component_name_excess to topup_configurationsWon't FixDeferred refinement.
BIF-8456 (QA) Testing new Nuxt4 Self Top Up page + automationDoneNuxt4 migration QA.
BIF-8457 (FE) Toggle new Self Top Up nuxt4 pageDoneNuxt4 migration toggle.
BIF-8382 (QA) E2E Testing Self Top UpIn TestingThis is the actual blocker.
BIF-8557 (Bug) WA top-up button shown without WABADoneComponent-code gating regression fixed.
BIF-8558 (Bug) Missing close button in confirmation popupDoneReview-popup polish.

2. Effort Summary (rewritten on actuals)

The prior breakdown estimated 22 person-days remaining (FE 12.5 + BE 5 + QA 4.5). Reality:

BucketForward estimate (prior breakdown)Actual outcome
Phase 1 — UI mockedFE 10.5 + QA 2.5 = 13 dShipped under BIF-7893/7894/7895/8023/8324/8457. Closed.
Phase 2 — API integrationFE 2 + BE 5 + QA 2 = 9 dShipped under BIF-7896/7897/7898/7899/7900/7901/7892/8024/8248/8249. Closed.
Remaining work~3–5 person-days of verification + PO sign-off + rollout coordination. See §3.

Effort confidence on the remaining work: high. All five repos inspected on disk; every PRD-cited Story has either Done sub-tasks shipping the change, or is itself paused only on PO confirmation. The two outstanding code questions (STP-S04 contract-end date in paid_pi, STP-S06 ×1000 multiplier seed) are localized investigations, not features-to-write.


3. Leftover work (the actual backlog)

Everything below is what stands between code-as-built and BIF-7797 closing. No new features.

Task L1: [PO/PM] Review and confirm the 10 Stories awaiting PO sign-off

Status: ✅ Done — 10 of 10. Verified against Jira 2026-07-01, in two passes the same day: BIF-7871–7878 resolved first (PO walkthrough session), then BIF-7869 and BIF-7870 also resolved Done on a follow-up review. All ten PRD-cited Stories are now closed.

What to docomplete.

Effort: 0.5 d (PM) + 1 d (PO) — spent.

Depends on: nothing.


Task L2: [QA] Finish BIF-8382 E2E Testing Self Top Up

Status: 🟡 In Testing on the board, actively in motion. This is the only QA ticket not yet Done. The other two QA tickets in scope (BIF-8250 testcase, BIF-8456 Nuxt4 testing) are Done. Target: closed on/before the 2026-08-31 hard deadline — ~2 md against a ~2-month runway, no capacity concern (Yoddi/Izzul have headroom).

What to verify in this E2E pass — these are the gaps surfaced by the code reconciliation:

  1. STP-S04 contract-end propagation. For a User Quota (agent_extra) top-up on a V3 CID, assert that the resulting Paid PI in Mekari Billing carries period_end = contract.end_date. Inspect the paid_pi payload sent from hub-core to modpanel/moderator-be/app/controllers/api/v1/billing/subscriptions_controller.rb:41. If the payload lacks an end_date field, file a follow-up to hub-core's CreatePaidPi.
  2. STP-S06 AI ×1000 multiplier. Submit qty = 5 for the AI Dialog row and assert chatbot_ai_extra increases by 5,000 on OrganizationPackage. The multiplier is data-only (topup_configurations.option_value), so a regression here is a seed/data issue, not code.
  3. STP-S05 + STP-S06 component-code gating. For three CIDs (with Voice component, with AI component, with neither), assert that the qontak-billing GET /iag/v1/topup-configurations response filters rows per show_by_component evaluated in qontak-billing/internal/app/usecase/topup_configuration/fetch_configuration.go:66-71. The BIF-8557 bug (WA shown without WABA) means this gating path has had at least one regression.
  4. STP-S09 Mekari Billing pricing. Force an is_fixed_cost = false row and assert base_price in the response reflects the current Mekari Billing /api/v4/packages/{id} price, not the stale DB column. Validate fallback when the upstream returns an error (fetch_configuration.go:84-87 — the row is dropped, not stubbed).
  5. STP-S01 + S07 happy-path E2E. V3 CID with flag ON → top-up → Review Order → confirm popup → Mekari Pay redirect → webhook → balance increment → Paid PI created in Jurnal. End-to-end.
  6. Idempotency. Replay a paid webhook and assert no double-credit (Redis lock at hub-core/app/apps/mekari_pay/services/redis/set_invoice_webhook.rb-style flow per RFC §2.3).

Effort: 2 d (QA — Yoddi/Izzul; this is the existing BIF-8382 ticket, just needs to finish).

Depends on: nothing — environment already exists.


Task L3: [PM/TPM] Fix PRD ↔ Jira ↔ RFC linkage drift

Status: 🟡 3 of 4 done (2026-07-01). First three fixed directly in the PRD; the fourth (scope-addition note) is optional and still open.

FixWhereActionStatus
PRD Epic field is wrongprds/self-topup.md HEADER BLOCK + README.md jira_epicChange BIF-5840BIF-7797.✅ Done
PRD row for "Show excess Usage on Topup Page" has empty Jira cellprds/self-topup.md §8.2 row 12Add BIF-8175 link; note its split-from chain to BIF-8023 / BIF-8024.✅ Done
RFC unlinked from Epic + PRDBIF-7797 description; prds/self-topup.md HEADER RFC LinkLink Confluence 51046842375 from both.✅ Done in the PRD. Still open on the BIF-7797 Jira description — Jira-side edit not yet made.
PRD missing scope additionsprds/self-topup.md §8.2 or a new "Post-PRD additions" noteEither fold BIF-7960 (email), BIF-8324 (Step-2 UI), BIF-8456/8457 (Nuxt4) into ACs, or list them explicitly as accepted scope drift.❌ Open (optional)

Effort: 0.5 d (PM) — ~0.4 d spent, ~0.1 d remaining (Jira description link + optional scope note).

Depends on: nothing.


Task L4: [BE] Resolve STP-S04 paid_pi end-date question (only if L2 verification fails)

Status: ⚠️ Conditional. Run only if L2 item 1 fails.

If paid_pi does not carry the contract end_date for User Quota top-ups, add the field in:

  • hub-core (path TBD on inspection; the CreatePaidPi service identified in RFC §1.4 Step B);
  • and accept it in modpanel/moderator-be/app/controllers/api/v1/billing/subscriptions_controller.rb (the paid_pi action signature).

Effort: 1 d BE + 0.5 d QA. Triggered only by L2 outcome.

Depends on: L2 result.


Task L5: [TPM] Reconcile Epic priority + close

Status: ✅ Resolved 2026-07-01, and mirrored into Jira the same day. TPM confirmed Self Top-up stays in 26Q3 — deprio lifted, target_quarter: 2026-Q3 / commitment_date: 2026-08-31 stand. See delivery/decisions/0001-lift-bif-7797-deprio-confirm-26q3.md. BIF-7797 itself moved To DoIn Progress in Jira (verified 10:49 same day) — the deprio is lifted on the board, not just documented here.

BIF-7797 was To Do + P2, deprio'd on 2026-04-21. With the code done and L1 now fully resolved, the TPM lifted the deprio and closed on 26Q3 rather than slipping target_quarter — only L2 (BIF-8382) remains, well inside the quarter.

Effort: 0.5 d (TPM).

Depends on: L1 outcome.


4. Updated Effort Summary (leftover only, as of 2026-07-01)

TaskDisciplineDaysStatus
L1 PO sign-off coordinationPM0.5✅ Done
L1 PO sign-off itselfPO1✅ Done (10/10 Stories)
L2 E2E QA (BIF-8382)QA2🟡 Open — In Testing
L3 doc/link fixesPM0.5🟡 3 of 4 done (~0.1 d left, optional)
L4 paid_pi end-date fixBE+QA1.5Conditional — only if L2 #1 fails
L5 Epic priority decisionTPM0.5✅ Done, mirrored into Jira
Total remaining — unconditional~2.1 d
Total remaining — if L4 triggers~3.6 d

progress_done_days should be updated in README.md from 0 to a value close to `effort_total_days

  • 4.5 ≈ 17.5(out of22), progress_pct ≈ 80, progress_as_of: 2026-06-30. The earlier progress_done_days: 0` reflects the prior breakdown's "nothing built yet" framing, which the code disagrees with.

5. Ordering rationale

The bottleneck is organisational, not technical:

  1. L3 first (0.5 d, no deps). Fix the linkage drift so reviewers walking the docs land on BIF-7797 and 51046842375, not BIF-5840 and "RFC TBD". Cheapest, highest-leverage.
  2. L1 + L2 in parallel. L1 clears the 10 Stories' review checkpoint with one PO session; L2 finishes the only open QA ticket. Both can start the same day.
  3. L4 only if L2 surfaces the end-date gap. Don't pre-emptively touch hub-core / moderator-be.
  4. L5 last. Once L1+L2 land, BIF-7797 is closable; TPM owns the priority cleanup.

6. Skipped — none

The prior breakdown skipped STP-S09 and the BE half of STP-S04 as "blocked on external contracts". Both are shipped in qontak-billing (fetch_configuration.go + the add_fields_to_topup_configurations migration). No items remain skipped.


7. Changelog

VersionDateAuthorSummary
2.32026-07-01L1 fully closedSecond Jira pass same day: BIF-7869 and BIF-7870 also resolved Done — all 10 L1 Stories now closed. BIF-7797 Epic moved To DoIn Progress in Jira (deprio lifted on the board, not just in this repo). L5 fully resolved. Only L2 (BIF-8382 QA E2E, In Testing) remains open. Initiative moved Tentative → Commit on the team readiness dashboard.
2.22026-07-01Jira verification + L3 fixVerified live against Jira: 8 of 10 L1 Stories (BIF-7871–7878) resolved Done today. BIF-7869 and BIF-7870 remain To Do, untouched — not part of the completed walkthrough. BIF-8382 QA E2E still In Testing. BIF-7797 Epic still To Do/P2 with no comment reflecting the 26Q3 decision — the ADR (0001) is recorded in this repo but not yet mirrored into Jira. Also closed 3 of 4 L3 items: prds/self-topup.md HEADER Epic corrected to BIF-7797, RFC Link added, and the excess-usage row's empty Jira cell filled with BIF-8175.
2.12026-07-01TPM decisionTask L5 resolved: TPM confirmed BIF-7797 stays in 26Q3 (deprio lifted). See delivery/decisions/0001-lift-bif-7797-deprio-confirm-26q3.md. L1 (PO sign-off) + L2 (BIF-8382 E2E) remain the only open blockers.
2.02026-06-30ReconciliationRewrote against as-built code across all five repos and verified Jira ticket statuses. Found build materially complete; leftover is PO sign-off (10 Stories in Waiting Confirmation) and one QA ticket (BIF-8382 In Testing). Corrected three drift items (wrong Epic, missing RFC link, PRD↔Jira gap on excess row). Effort drops from 22 d forward-estimate to 4.5 d remaining (6 d if a conditional BE fix triggers).
1.02026-06-29Forward planHorizontal slice plan against mocked BE; hub_core not checked out.