OTP overhaul: test-user bypass + hash-at-rest + Fazpass integration

- Test-OTP bypass allowlist for Apple reviewers / QA: phone-scoped static OTPs
  managed in CC (Settings → Test OTP Bypass), bcrypt-hashed on save, kill-switch
  toggle, per-entry expires_at. New `otp_requests` columns (is_bypass, code_hash)
  + DB CHECK enforcing bypass-row shape.
- Hash-at-rest for stub OTPs: replaced plaintext `<ref>:<code>` storage with
  bcrypt(code_hash); reference goes to fazpass_reference alone. Verify routes on
  sovereign is_bypass flag, defers code_hash-NULL rows to Fazpass.
- Fazpass integration (gated by FAZPASS_ENABLED env, default off): new
  fazpass.service.js calling /v1/otp/{request,verify}; distinct errors for wrong
  OTP (CODE_MISMATCH 401) vs provider outage (OTP_PROVIDER_FAILED 502).
- Removed redundant Free Trial CC section (was a back-compat shim for the same
  pricing_promotions row as "Diskon Sesi Pertama") + unused alias in
  pricing.service.js.

208 tests green (34 new for OTP + Fazpass). Fazpass API + dashboard PDFs added
at project root for reference (docs are auth-gated).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-29 22:39:34 +08:00
parent 3a0cdf5c4e
commit 6fd98ca99c
15 changed files with 1958 additions and 158 deletions

View File

@@ -424,7 +424,217 @@
"Bash(tee /tmp/playwright-debug.log)",
"Bash(curl -s -i -X OPTIONS http://localhost:3001/internal/config/payment-session-timeout -H 'Origin: http://localhost:5173' -H 'Access-Control-Request-Method: PATCH' -H 'Access-Control-Request-Headers: authorization,content-type')",
"Bash(tee /tmp/playwright-run-6.log)",
"Bash(kill 882584)"
"Bash(kill 882584)",
"Bash(adb version *)",
"Bash(adb connect *)",
"Bash(sudo apt-get update -qq)",
"Bash(sudo apt-get install -y android-tools-adb)",
"Read(//home/ramad/flutter/bin/**)",
"Read(//home/ramad/development/flutter/bin/**)",
"Read(//snap/bin/**)",
"Read(//home/ramad/**)",
"Read(//mnt/c/src/flutter/bin/**)",
"Read(//mnt/c/flutter/bin/**)",
"Read(//mnt/c/Users/ramad/flutter/bin/**)",
"Read(//mnt/c/Users/ramad/**)",
"Read(//mnt/c/dev/flutter/**)",
"Bash(grep -E '^\\\\..*env|env$')",
"Bash(curl -fsSL https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json)",
"Bash(python3 -c \"import sys, json; d=json.load\\(sys.stdin\\); stable=d['current_release']['stable']; rel=next\\(r for r in d['releases'] if r['hash']==stable\\); print\\(rel['version']\\); print\\('https://storage.googleapis.com/' + d['base_url'].split\\('//'\\)[-1].split\\('storage.googleapis.com/'\\)[-1] + '/' + rel['archive']\\)\")",
"Bash(curl -fL -o flutter_linux.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.41.9-stable.tar.xz)",
"Bash(command -v node)",
"Bash(command -v npm)",
"Read(//usr/local/bin/**)",
"Bash(tar xf *)",
"Bash(export PATH=\"$HOME/flutter/bin:$PATH\")",
"Bash(~/flutter/bin/flutter --version)",
"Bash(~/flutter/bin/flutter doctor *)",
"Bash(mkdir -p ~/Android/Sdk/cmdline-tools)",
"Bash(curl -fL -o cmdline-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip)",
"Bash(unzip -q cmdline-tools.zip -d /tmp/cmdline-extract)",
"Bash(mv /tmp/cmdline-extract/cmdline-tools ~/Android/Sdk/cmdline-tools/latest)",
"Bash(python3 -m zipfile -e cmdline-tools.zip /tmp/cmdline-extract)",
"Bash(command -v java)",
"Bash(java -version)",
"Bash(chmod +x ~/Android/Sdk/cmdline-tools/latest/bin/*)",
"Bash(export ANDROID_HOME=\"$HOME/Android/Sdk\")",
"Bash(export PATH=\"$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH\")",
"Bash(sdkmanager --version)",
"Bash(sdkmanager --licenses)",
"Bash(sdkmanager \"platform-tools\" \"platforms;android-36\" \"build-tools;36.0.0\")",
"Bash(export PATH=\"$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$HOME/flutter/bin:$PATH\")",
"Bash(adb -s 192.168.88.247:5555 reverse tcp:3000 tcp:3000)",
"Bash(adb -s 192.168.88.247:5555 reverse --list)",
"Bash(adb reverse *)",
"Bash(adb kill-server *)",
"Bash(adb start-server *)",
"Bash(/usr/bin/adb version *)",
"Bash(~/Android/Sdk/platform-tools/adb version *)",
"Bash(export PATH=\"$HOME/Android/Sdk/platform-tools:$PATH\")",
"Bash(echo \"exit=$?\")",
"Bash(adb -t 1 reverse tcp:3000 tcp:3000)",
"Bash(adb shell *)",
"Bash(curl *)",
"Bash(awk '{print $1}')",
"Bash(ip route *)",
"Bash(awk '/default/ {print $3}')",
"Bash(export ADB_SERVER_SOCKET=tcp:172.22.240.1:5037)",
"Read(//etc/**)",
"Read(//mnt/wsl/**)",
"Bash(ip -br addr)",
"Bash(awk '{printf \"%s %s %s %s %s %s\\\\n\", $2, $8, $9, $10, $11, $12}')",
"Bash(adb forward *)",
"Bash(kill 3639 3663)",
"Bash(adb -s emulator-5554 forward --remove-all)",
"Bash(adb -s emulator-5556 forward --remove-all)",
"Bash(timeout 1 bash -c '</dev/tcp/127.0.0.1/7777')",
"Bash(echo \"WSL 127.0.0.1:7777 -> $\\(timeout 1 bash -c '</dev/tcp/127.0.0.1/7777' 2>&1 && echo OPEN || echo CLOSED\\)\")",
"Bash(timeout 1 bash -c '</dev/tcp/127.0.0.1/8765')",
"Bash(echo \"WSL 127.0.0.1:8765 -> $\\(timeout 1 bash -c '</dev/tcp/127.0.0.1/8765' 2>&1 && echo OPEN || echo CLOSED\\)\")",
"Bash(timeout 1 bash -c '</dev/tcp/172.22.240.1/7777')",
"Bash(awk '{print $4}')",
"Bash(awk '/\\(node --watch|flutter_tools\\\\.snapshot run -d|wsl_tcp_relay\\)/ && !/awk/ {printf \"PID %s: %s\\\\n\", $2, substr\\($0, index\\($0, $8\\)\\)}')",
"Bash(cat)",
"Bash(node /tmp/list-pending.mjs)",
"Bash(node _tmp-list-pending.mjs)",
"Bash(rm _tmp-list-pending.mjs)",
"Bash(node _tmp-check.mjs)",
"Bash(node _tmp-mitras.mjs)",
"Bash(kill %1)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 adb -s emulator-5556 logcat -d -t 800 '*:E' flutter:V)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 adb -s emulator-5556 shell \"ps -A | grep -iE 'halobestie|mitra' \")",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 adb -s emulator-5556 shell \"pm list packages 2>/dev/null | grep -iE 'halo|mitra'\")",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 adb -s emulator-5556 shell \"ps -A 2>/dev/null | grep -iE 'halo|mitra'\")",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 adb -s emulator-5556 logcat -d -b crash -t 200)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 adb -s emulator-5556 logcat -d -t 3000)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 adb -s emulator-5556 logcat -d -t 5000)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 adb -s emulator-5556 logcat -c)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 adb -s emulator-5556 logcat flutter:V AndroidRuntime:E '*:S')",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 adb -s emulator-5556 logcat -d)",
"Bash(awk '{ts=$0; sub\\(/.*\"time\":/,\"\",ts\\); sub\\(/,.*/,\"\",ts\\); url=$0; sub\\(/.*\"url\":\"/,\"\",url\\); sub\\(/\".*/,\"\",url\\); print ts, url}')",
"Bash(awk -F'\"time\":|,\"pid\"|\"url\":\"|\"host\"' '{print $2, $4}')",
"Bash(awk -F'\"time\":|\"url\":\"|\"' '{print $2, $4}')",
"Bash(node --input-type=module -e ' *)",
"Bash(npx vitest *)",
"Bash(node --env-file=/home/ramad/workspace/halobestie/halobestie-clone/backend/.env -e ' *)",
"Bash(node -e \"require\\('dotenv'\\).config\\(\\); const jwt = require\\('jsonwebtoken'\\); const { randomUUID } = require\\('crypto'\\); console.log\\(jwt.sign\\({ user_type: 'customer', session_id: randomUUID\\(\\) }, process.env.AUTH_JWT_SECRET, { algorithm: 'HS256', expiresIn: 3600, subject: '10ebeb45-7e77-45e7-8177-d5db62539cce' }\\)\\)\")",
"Bash(tee /tmp/pricing-before.json)",
"Bash(tee /tmp/pricing-after.json)",
"Bash(node -e \"require\\('dotenv'\\).config\\(\\); const jwt = require\\('jsonwebtoken'\\); const { randomUUID } = require\\('crypto'\\); console.log\\(jwt.sign\\({ user_type: 'cc_user', session_id: randomUUID\\(\\) }, process.env.AUTH_JWT_SECRET, { algorithm: 'HS256', expiresIn: 3600, subject: '54d90715-d456-4bbe-a31d-a9ae4839b379' }\\)\\)\")",
"Bash(python3 -m json.tool)",
"Bash(node --check src/pages/settings/SettingsPage.jsx)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone status)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone log --oneline -5)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone remote -v)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone diff --stat backend/package-lock.json control_center/package-lock.json)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone diff backend/package.json control_center/package.json)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone diff backend/package-lock.json)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone commit -m ' *)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone log -1 --pretty=format:\"%an <%ae>\")",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone config --local --list)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone config --global --list)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone -c 'user.name=ramadhan sjamsani' -c user.email=ramadhan.sjamsani@gmail.com commit -m ' *)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone push origin master)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 npm run dev)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 python3 .dev/wsl_tcp_relay.py --watch-adb 172.22.240.1)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 adb devices)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 timeout 15 adb devices)",
"Bash(ip addr *)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 flutter run -d emulator-5554 --dart-define=API_BASE_URL=http://192.168.88.247:3000)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 flutter run -d emulator-5556 --dart-define=API_BASE_URL=http://192.168.88.247:3000)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 timeout 10 adb -s emulator-5556 shell getprop sys.boot_completed)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 timeout 10 adb -s emulator-5556 shell pm list packages com.halobestie)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 timeout 5 adb -s emulator-5556 shell \"ps -A | grep -E 'install|pm '\")",
"Bash(kill 23571)",
"Bash(ls ~/.maestro/bin/ 2>/dev/null; ls /opt/maestro/bin 2>/dev/null; find ~ -maxdepth 5 -name \"maestro\" -type f -executable 2>/dev/null | head -5; find /usr -maxdepth 5 -name \"maestro\" -type f -executable 2>/dev/null | head -5)",
"Read(//usr/**)",
"Bash(bash)",
"Bash(wget --no-verbose -O /tmp/maestro.zip \"https://github.com/mobile-dev-inc/maestro/releases/latest/download/maestro.zip\")",
"Bash(openssl version *)",
"Bash(mkdir -p ~/.maestro)",
"Bash(unzip -qo /tmp/maestro.zip -d ~/.maestro/tmp)",
"Bash(mv ~/.maestro/tmp/maestro/* ~/.maestro/)",
"Bash(~/.maestro/bin/maestro --version)",
"Bash(export PATH=\"$HOME/.maestro/bin:$PATH\")",
"Bash(export MAESTRO_CLI_NO_ANALYTICS=1)",
"Bash(maestro --device emulator-5554 test client_app/.maestro/flows/10_returning_repays.yaml)",
"Bash(~/.maestro/bin/maestro test *)",
"Bash(~/.maestro/bin/maestro --help)",
"Bash(~/.maestro/bin/maestro list-devices *)",
"Bash(python3 .dev/wsl_tcp_relay.py 5555 172.22.240.1 5555)",
"Bash(python3 .dev/wsl_tcp_relay.py 5557 172.22.240.1 5557)",
"Bash(timeout 5 bash -c 'echo \"test\" | nc -v 127.0.0.1 5555')",
"Bash(timeout 5 bash -c 'echo \"test\" | nc -v 172.22.240.1 5555')",
"Bash(timeout 30 ~/.maestro/bin/maestro --udid emulator-5554 list-devices)",
"Bash(timeout 5 bash -c 'exec 3<>/dev/tcp/172.22.240.1/5555 && echo OK_5555')",
"Bash(timeout 5 bash -c 'exec 3<>/dev/tcp/172.22.240.1/5557 && echo OK_5557')",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 ~/.maestro/bin/maestro test client_app/.maestro/flows/10_returning_repays.yaml)",
"Bash(python3 -c \"import json,sys; d=json.load\\(open\\('/home/ramad/.maestro/tests/2026-05-17_000444/commands-\\(10_returning_repays.yaml\\).json'\\)\\); print\\(json.dumps\\(d.get\\('commands',[d]\\)[-1] if isinstance\\(d, dict\\) else d[-1], indent=2\\)[:3000]\\)\")",
"Bash(timeout 30 ~/.maestro/bin/maestro hierarchy)",
"Bash(python3 -c ' *)",
"Bash(ADB_SERVER_SOCKET=tcp:172.22.240.1:5037 timeout 5 adb devices)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone diff --stat requirement/phase4-customer-flow.md)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone diff requirement/phase4-customer-flow.md)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone diff backend/src/routes/internal/_test.routes.js)",
"Bash(awk '{print $3}')",
"Bash(kill 89692)",
"Bash(echo \"started pid $!\")",
"Bash(PGPASSWORD=postgres psql -h localhost -U postgres -d halobestie -c \"SELECT mitra_id, is_online, last_heartbeat_at FROM mitra_online_status WHERE is_online = true ORDER BY last_heartbeat_at DESC;\")",
"Bash(~/.maestro/bin/maestro --device emulator-5554 test client_app/.maestro/flows/ts-01_returning_lama_online.yaml)",
"Bash(~/.maestro/bin/maestro --udid 127.0.0.1:5554 test client_app/.maestro/flows/ts-01_returning_lama_online.yaml)",
"Bash(~/.maestro/bin/maestro --udid emulator-5554 test client_app/.maestro/flows/ts-01_returning_lama_online.yaml)",
"Bash(nohup python3 .dev/wsl_tcp_relay.py 5037 172.22.240.1 5037)",
"Bash(echo \"relay started pid $!\")",
"Bash(unset ADB_SERVER_SOCKET)",
"Bash(pkill -f \"maestro\")",
"Bash(kill 96710)",
"Bash(timeout 3 bash -c '</dev/tcp/172.22.240.1/5037')",
"Bash(ip -4 addr show eth0)",
"Bash(timeout 3 bash -c '</dev/tcp/127.0.0.1/5037')",
"Bash(powershell.exe -NoProfile -Command \"adb.exe start-server\")",
"Bash(timeout 2 bash -c '</dev/tcp/172.22.240.1/__TRACKED_VAR__')",
"Bash(env -u ADB_SERVER_SOCKET ~/.maestro/bin/maestro --udid emulator-5554 test client_app/.maestro/flows/ts-01_returning_lama_online.yaml)",
"Bash(xargs -I {} sh -c 'echo \"=== {} ===\" && grep -o \"\\\\\"text\\\\\":\\\\\"[^\\\\\"]*\\\\\"\" {} | sort -u | head -30')",
"Bash(env -u ADB_SERVER_SOCKET adb -s emulator-5554 install -r build/app/outputs/flutter-apk/app-debug.apk)",
"Bash(env -u ADB_SERVER_SOCKET ~/.maestro/bin/maestro --udid emulator-5554 test client_app/.maestro/flows/ts-04_returning_baru_blast.yaml)",
"Bash(env -u ADB_SERVER_SOCKET ~/.maestro/bin/maestro --udid emulator-5554 test client_app/.maestro/flows/ts-03_returning_lama_offline_tanya_admin.yaml)",
"Bash(env -u ADB_SERVER_SOCKET ~/.maestro/bin/maestro --udid emulator-5554 test client_app/.maestro/flows/ts-05_payment_expired_retry_preserves_targeting.yaml)",
"Bash(sed -i 's|\\\\${output.MITRA_NAME}|${output.MITRA_NAME_RE}|g' client_app/.maestro/flows/ts-0*.yaml *)",
"Bash(env -u ADB_SERVER_SOCKET ~/.maestro/bin/maestro --udid emulator-5554 test client_app/.maestro/flows/ts-02_returning_lama_offline_blast.yaml)",
"Bash(grep -v \"^--$\\\\|kotlin\\\\|graalvm\\\\|jvm\")",
"Bash(kill 130976)",
"Bash(echo \"restarted pid $!\")",
"Bash(env -u ADB_SERVER_SOCKET ~/.maestro/bin/maestro --udid emulator-5554 test client_app/.maestro/flows/ts-06_targeted_reject_fallback_to_blast.yaml)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone log --oneline -10)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone diff --stat)",
"Bash(git -c user.email=ramadhan.sjamsani@gmail.com -c 'user.name=Ramadhan Sjamsani' commit -m ' *)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone status --short)",
"Bash(git -C /home/ramad/workspace/halobestie/halobestie-clone log --oneline -3)",
"Bash(env -u ADB_SERVER_SOCKET ~/.maestro/bin/maestro --udid emulator-5554 test client_app/.maestro/flows/ts-07_returning_existing_name_skips_setname.yaml)",
"Bash(kill 133811)",
"Bash(python3 .dev/wsl_tcp_relay.py --watch-adb 172.22.240.1)",
"Bash(grep -rEn \"INSERT INTO mitras|phone_number.*[0-9]{8,}\" src/db/migrations/)",
"Bash(ps -eo pid,cmd)",
"Bash(awk '/inet / {print $2}')",
"Bash(powershell.exe -NoProfile -Command \"netsh interface portproxy show v4tov4\")",
"Bash(tee /tmp/confirm.json)",
"Bash(adb -s emulator-5554 shell pm path com.halobestie.client.client_app)",
"Bash(python3 .dev/wsl_tcp_relay.py 5037 172.22.240.1 5037)",
"Bash(env -u ADB_SERVER_SOCKET adb devices)",
"Bash(adb -s emulator-5554 exec-out screencap -p)",
"Bash(adb -s emulator-5554 shell dumpsys activity activities)",
"Bash(adb -s emulator-5554 logcat -d -t '60.0')",
"Bash(dart format *)",
"Bash(adb -s emulator-5554 install -r build/app/outputs/flutter-apk/app-debug.apk)",
"Bash(adb -s emulator-5554 shell am force-stop com.halobestie.client.client_app)",
"Bash(adb -s emulator-5554 shell monkey -p com.halobestie.client.client_app -c android.intent.category.LAUNCHER 1)",
"Bash(env -u ADB_SERVER_SOCKET ~/.maestro/bin/maestro --udid emulator-5554 test /tmp/verify_register_overflow.yaml)",
"Bash(adb -s emulator-5554 shell input tap 800 1530)",
"Bash(adb -s emulator-5554 shell wm size)",
"Bash(adb -s emulator-5554 logcat -d -t '30.0')",
"Bash(adb -s emulator-5554 shell input tap 720 1450)",
"Bash(env -u ADB_SERVER_SOCKET ~/.maestro/bin/maestro --udid emulator-5554 test client_app/.maestro/flows/verify_register_overflow.yaml)",
"Bash(adb -s emulator-5554 shell \"run-as com.halobestie.client.client_app cat /data/data/com.halobestie.client.client_app/shared_prefs/FlutterSharedPreferences.xml\")"
],
"additionalDirectories": [
"/home/rama/workspaces/workspace-claude/halobestie-clone/backend/src",
@@ -434,7 +644,8 @@
"/proc/5649/fd",
"/home/rama/.android/avd/Medium_Phone.avd",
"/tmp",
"/home/rama/.android/avd"
"/home/rama/.android/avd",
"/home/ramad/workspace/halobestie/halobestie-clone/client_app/.maestro/flows"
]
}
}