Claude Project Tracker

refreshed 2026-08-08 20:35:59Z

What's pending across every project

17 open · sorted by priority then due-date
priority all urgent high low
Filtered by tag backend  clear
todo urgent
Rotate Razorpay webhook secret — 'hopewell' is guessable
The RAZORPAY_SECRET_WEBHOOK value in /opt/ocpp/.env on the production VM is literally 'hopewell' — a common English word, effectively no protection. HMAC verification against a guessable secret means anyone who learns the plaintext can forge webhook calls and credit any wallet. Fix: 1. Razorpay Dashboard → Settings → Webhooks → regenerate a long random secret (32+ char base64). 2. Update /opt/ocpp/.env on the VM (SMTP_PASSWORD-style edit). 3. sudo systemctl restart ocpp.service. 4. Send a test webhook from Razorpay dashboard and confirm it verifies. This is the single most impactful hardening we can do.
/opt/ocpp/.envinternal/handlers/wallet_handler.go:239-254↗ https://dashboard.razorpay.com/app/webhooks
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
from the conversationclaude: RAZORPAY_SECRET_WEBHOOK=hopewell in .env is weak - anyone who guesses the plaintext can forge webhooks (i.e., forge topups). Regenerate a long random secret in the Razorpay dashboard and update .env, then restart the service. This is the single most impactful hardening you can do. saravanan: put them in my project tracker - scrumclaw.ai
8d ago
07-31 02:51
todo high
Fix float equality in webhook amount check (can silently reject real topups)
ProcessTopupWebhook uses `if topup.Amount != amount` — direct float64 equality. A tiny rounding drift (e.g., 500.00 stored vs 499.99999998 derived from Razorpay's paise/100) will falsely reject the webhook as 'amount mismatch', leaving the user paying but not credited. Change to: if math.Abs(topup.Amount - amount) > 0.01 { ... } Or better: store amount as int64 paise everywhere and compare integers.
internal/repositories/wallet_repo.go:100
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
8d ago
07-31 02:51
todo high
Extend ledger reconciliation migration to handle negative balances
Migration 0027 only inserts positive ADJUSTMENT entries when `wallets.balance > 0 AND gap.amount >= 0.01`. Users whose legacy wallets.balance is 0 (never populated) but who accumulated CHARGE entries end up with negative ledger balances and no automatic reconciliation. Concrete case: saravanan.hp@gmail.com currently shows balance = -11152.52. Ledger has CHARGE entries but no matching TOPUP history was migrated in. Options: (a) Backfill missing legacy topups from external Razorpay reports before running the reconcile. (b) Add a companion migration that reconciles from a manually-curated CSV of legacy balances. (c) Add an admin credit tool + audit trail so support can fix these case-by-case. Track down the actual topup history for affected users (query Razorpay payments API filtered by our account) and decide the right long-term reconciliation approach.
migrations/0027_wallet_ledger_balance_reconcile.up.sqlinternal/repositories/wallet_repo.go:30-48
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
8d ago
07-31 02:51
todo high
Migration 0028: walk-in user schema (nullable phone + user_upi_handles)
Per BRD v1 §7.3 + Appendix B.\n\n- ALTER users ALTER COLUMN phone_number DROP NOT NULL\n- Recreate phone UNIQUE as partial index (WHERE phone_number IS NOT NULL)\n- CREATE TABLE user_upi_handles (per WIP schema in #51)\n- Register 'upi_walkin' as an accepted auth_provider value (no enum in Postgres — code-side check only)\n\nDoes NOT touch existing wallets/ledger/sessions/reservations tables.\n\nBlocked on TDD lock (#50).</body>
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
6d ago
08-02 11:52
todo high
Migration 0029: charger_qr_codes table
Per BRD v1 §7.1 FR3.\n\nCREATE TABLE charger_qr_codes (\n id UUID PK,\n charger_id VARCHAR(50) NOT NULL,\n connector_id INT NOT NULL,\n razorpay_qr_id VARCHAR(100) UNIQUE NOT NULL,\n razorpay_qr_short_url TEXT,\n razorpay_qr_image_url TEXT,\n status VARCHAR(20) NOT NULL DEFAULT 'active',\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n UNIQUE (charger_id, connector_id)\n);\n\nBlocked on TDD lock (#50).</body>
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
6d ago
08-02 11:52
todo high
UserRepository walk-in methods + resolveOrCreateUser logic
Per BRD v1 §7.3 FR10–FR15. New Go methods on UserRepository:\n\n- GetByVPA(vpa string) (*User, error) — JOIN with user_upi_handles\n- GetByEmail(email string) (*User, error) — if not already present\n- InsertUPIHandle(userID, vpa, source string, primary bool) error\n- TouchUPIHandle(userID, vpa string) error — bump last_seen_at + payment_count++\n- New service function resolveOrCreateUser(contact, email, vpa string) — implements the phone > email > VPA priority + backfill-on-match + walk-in creation logic\n\nBlocked on migration 0028 (#57) landing.</body>
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
6d ago
08-02 11:52
todo high
Razorpay QR admin provisioning endpoint (create + regen QR per gun)
Per BRD v1 §7.1 FR1–FR4.\n\nNew backend endpoints under /internal/admin/:\n\n- POST /admin/chargers/:id/connectors/:cid/qr — create if not exists; returns razorpay_qr_id + image_url\n- POST /admin/chargers/:id/connectors/:cid/qr/regen — close existing QR (via Razorpay POST /v1/payments/qr_codes/:id/close) and create new one; updates charger_qr_codes row\n- GET /admin/chargers/:id/connectors/:cid/qr — return current QR + image URL for reprint\n- GET /admin/chargers/qr — list all QRs across all stations, for admin console listing\n\nWraps Razorpay QR Codes API. All calls go server-to-server with Razorpay API key basic auth (reuse pattern in wallet_handler.go createRazorpayOrder).\n\nBlocked on Razorpay QR API enablement (#52) and TDD (#50).</body>
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
6d ago
08-02 11:52
todo high
qr_code.credited webhook handler in existing PaymentWebhook
Per BRD v1 §7.2 FR6–FR9.\n\nExtend existing internal/handlers/wallet_handler.go PaymentWebhook to switch on hook.Event:\n\n switch hook.Event {\n case "payment.captured": // existing app topup path\n case "qr_code.credited": // NEW walk-in path\n h.handleQRCredit(payload)\n }\n\nhandleQRCredit implements:\n1. Lookup gun via notes.charger_id + notes.connector_id (cross-check charger_qr_codes row exists)\n2. Call resolveOrCreateUser(contact, email, vpa)\n3. Insert TOPUP ledger entry (idempotency key: qrpay:<payment_id>)\n4. Check OCPP status of connector — if Available, trigger RemoteStart via existing internal path with 3x5s retry (see #62)\n5. If not Available, credit persists in wallet only\n6. If WALKIN_QR_SMS_ENABLED flag on → dispatch appropriate SMS template (Phase 1.5)\n7. Return 200 to Razorpay\n\nBlocked on TDD (#50), migrations 0028–0029.</body>
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
6d ago
08-02 11:52
todo high
OCPP RemoteStart internal trigger — verify reusable for walk-in flow
The app-user "Start Charging" button calls some Go function that ultimately sends OCPP RemoteStartTransaction to the charger. Same code path needs to be triggerable from the webhook handler (server-initiated, no user JWT context).\n\nTasks:\n1. Locate the current Go function/method for starting a session (likely inside internal/handlers/charging_handler.go or an internal service).\n2. Confirm it can be called with (user_id, charger_id, connector_id, reserved_amount) purely from server code — no gin.Context assumption.\n3. If it currently requires a gin.Context, refactor to extract a pure service function chargingService.StartSession(...).\n4. Wire the walk-in webhook to call this shared entry point.\n5. Add 3x5s retry wrapper per BRD FR19.\n\nBlocked on TDD (#50).</body>
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
6d ago
08-02 11:52
todo high
Admin ops API endpoints (user/payment/refund/QR mgmt)
Backend API for the admin web page (#55). Under /admin/* (or /internal/admin/*) behind role-gated middleware.\n\n- GET /admin/users?search=... — search by phone/email/VPA/user_id\n- GET /admin/users/:id — user detail including wallet balance, ledger, sessions, UPI handles\n- GET /admin/payments/:razorpay_payment_id — payment lookup\n- POST /admin/payments/:razorpay_payment_id/refund — full/partial refund via Razorpay Refunds API + REVERSAL ledger entry\n- QR management endpoints (see #61)\n- GET /admin/audit — recent admin actions log\n\nRole gate: reuse existing JWT + add role claim, or a dedicated admin_users table. TDD to decide.\n\nBlocked on TDD (#50).</body>
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
6d ago
08-02 11:52
todo
Fix topup amount range mismatch (min=1 vs error message '100')
Backend constant minTopupAmount is 1 but the error message and frontend both say the minimum is 100. Change minTopupAmount to 100 so the backend actually enforces what the message claims — otherwise a bespoke client can top up ₹1 (bypasses the intended floor). - const minTopupAmount = 1 + const minTopupAmount = 100
internal/handlers/wallet_handler.go:29-30internal/handlers/wallet_handler.go:87
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
8d ago
07-31 02:51
todo
Rate-limit / IP-allowlist /webhooks/payment endpoint
The webhook endpoint currently accepts any POST from anywhere. HMAC signature check is the only defense — cryptographically sound, but each invalid request still triggers signature compute + JSON parse + DB lookup, so it's a cheap DoS vector. Two things worth adding (nginx-level is easiest): 1. Restrict source IPs to Razorpay's published webhook IPs (they publish a list): allow only those in the nginx server block for /webhooks/*. 2. Rate-limit /webhooks/* at nginx (limit_req_zone). Even a modest 20 rps burst limit stops volumetric noise. Doesn't change functionality, just hardens.
internal/routes/routes.go:161-163internal/handlers/wallet_handler.go:239-303/etc/nginx/sites-available/vajraev
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
8d ago
07-31 02:51
todo
Migration 0030: session_code column on charging_sessions
Per BRD v1 §7.6 FR26 — for phone-less walk-in recovery.\n\nALTER TABLE charging_sessions ADD COLUMN session_code VARCHAR(10);\nCREATE UNIQUE INDEX charging_sessions_session_code_idx ON charging_sessions (session_code) WHERE session_code IS NOT NULL;\n\nOnly populated for walk-in sessions where contact is missing. Short human-readable code (e.g. V-4F2A9) customer can quote to support.\n\nBlocked on TDD lock (#50).</body>
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
6d ago
08-02 11:52
todo
Feature flag config: WALKIN_QR_ENABLED + WALKIN_QR_SMS_ENABLED
Both flags default false, set via /opt/ocpp/.env:\n\n- WALKIN_QR_ENABLED — master gate. When false, /webhooks/payment ignores qr_code.credited events (returns 200 "ignored"). Enable per station rollout.\n- WALKIN_QR_SMS_ENABLED — separate switch for Phase 1.5. When true, dispatch SMS via Fast2SMS on start/end/etc.\n\nAdd to internal/config/config.go loader. Wire into wallet_handler.go and any SMS-sending code.\n\nAllows Phase 1 → Phase 1.5 rollout without redeploy.</body>
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
6d ago
08-02 11:52
todo
SMS templates + Fast2SMS integration for walk-in (Phase 1.5)
Behind WALKIN_QR_SMS_ENABLED flag. 5 templates × 4 languages (see #53).\n\nBackend changes:\n- Templates stored in Go source (map[templateKey]map[lang]string) with DLT template IDs from #53\n- Language selection: prefer user.preferred_language if set; fallback to English\n- Dispatch service reuses existing Fast2SMS integration (already in codebase for OTP)\n- Fire-and-forget from webhook handler (async goroutine); log failures but don't block webhook response\n\nBlocked on DLT approval (#53).</body>
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
6d ago
08-02 11:52
todo
Admin console: "list payments for QR" endpoint (Razorpay debug aid)
Useful for ops debugging (missing webhook, dispute lookup, reconciliation). Wraps Razorpay: GET https://api.razorpay.com/v1/payments/qr_codes/:qr_id/payments Backend endpoint: GET /admin/chargers/:charger_id/connectors/:connector_id/payments Returns array of payments captured against that gun's QR (all-time), each with vpa, contact, email, amount, timestamp, status. Enables ops to answer "who paid at this gun today" without querying our DB. Small addition to the admin endpoints in #65. Roughly 1 hour of Go + admin console UI work.</body>
scrumclaw#61scrumclaw#65
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
6d ago
08-02 12:10
todo low
Remove deprecated Razorpay 'payment_capture: 1' from order create
Razorpay deprecated the payment_capture parameter for the Orders API — capture behavior is now controlled at the account/merchant level. Not a functional bug today, but noise, and Razorpay may eventually reject unknown fields in future API versions. Drop the key from the payload in createRazorpayOrder.
internal/handlers/wallet_handler.go:340
Vajra Volt Mobile App · vajra-mobile-app · by saravanan@scrumclaw.ai
8d ago
07-31 02:51

Add an item