Skip to content

fix(upi): M4.3.1 remediation of independent review findings REV-01 through REV-07 on PR #30 - #31

Open
Satelink-Protocol wants to merge 1 commit into
feature/m4-3-upi-security-remediationfrom
feature/m4-3-1-review-remediation
Open

Satelink-Protocol wants to merge 1 commit into
feature/m4-3-upi-security-remediationfrom
feature/m4-3-1-review-remediation

Conversation

@Satelink-Protocol

Copy link
Copy Markdown
Contributor

Summary

This PR delivers Milestone M4.3.1 — Independent Review Remediation for PR #30, addressing and fully resolving all seven defects (REV-01 through REV-07, including 4 merge blockers) identified during the independent review of PR #30 (feature/m4-3-upi-security-remediation).

Target base branch: feature/m4-3-upi-security-remediation
Associated Documentation: See remediation matrix in docs/UPI_SECURITY_AUDIT.md, docs/UPI_PROVIDER_READINESS.md, and docs/PROJECT_STATE.md.


Remediated Findings (REV-01 through REV-07)

1. REV-01 (Merge Blocker) — QR Alignment Patterns & Independent Decoder Validation

  • Root Cause: Alignment pattern centers on row 6 / col 6 were skipped due to timing pattern reservations before alignment placement; Reed-Solomon polynomial generation inverted term multiplication; format info placement inverted horizontal/vertical bits and Table 12 indexing; missing Golay BCH(18,6) version information blocks for QR versions ≥ 7.
  • Fix: Reordered module placement so alignment patterns are drawn before timing modules (skipping finder corners); implemented ISO/IEC 18004 Table 12 bit placement preserving the Dark Module at (size - 8, 8); added BCH(18,6) version blocks for V7–V14; corrected RS polynomial math.
  • Verification: Verified with automated independent jsQR decoding across all versions V1–V14 and real 243-byte UPI URI payloads.

2. REV-02 (Merge Blocker) — Duplicate Active Bookings & SQLSTATE 23505 Recovery

  • Root Cause: Lack of active booking pre-check before payment intent; confirmation race conditions escaped transactions with HTTP 500 when unique constraint violations occurred.
  • Fix:
    • Pre-Intent check in createUpiPaymentIntent rejecting users with active bookings (pending_approval, pending_payment, confirmed, checked_in) with HTTP 409 already_booked.
    • Locked transaction check in applySuccessfulUpiPayment; transitions payment to refund_pending with reason 'duplicate_booking' if active booking exists.
    • Defensive SAVEPOINT sp_confirm_booking wrapping booking insertion; catches 23505 unique violations and cleanly transitions payment to refund_pending without throwing HTTP 500.
    • Guest claim check in claimUpiBooking checking for active bookings (HTTP 409) and wrapped in SAVEPOINT sp_claim_booking.

3. REV-03 (Merge Blocker) — refund_pending Operational Recovery

  • Root Cause: refundUpiOrder only permitted refunds for payments in succeeded status; external gateway network calls were inside database transactions holding locks.
  • Fix:
    • Expanded refundUpiOrder to support payments in both succeeded and refund_pending status.
    • Preserved idempotency ledger (status: 'refunded', duplicate: true).
    • Isolated external gateway API calls outside database holding transactions.
    • Added failure persistence: on gateway failure, payment remains refund_pending with failure_reason = 'refund_failed: ...'.
    • Added authorized manual finance reconciliation (manualReconciliation: true) allowing finance officers to complete refunds with external transaction proof.
    • Linked lib/services/refunds.js to dispatch UPI refunds to refundUpiOrder.

4. REV-04 (Merge Blocker) — Mandatory Verified Amount Exact Match

  • Root Cause: applySuccessfulUpiPayment could receive missing, non-numeric, or malformed amounts without strict type and equality guards.
  • Fix:
    • Enforced strict validation: verifiedAmountPaise must be a safe positive integer (Number.isSafeInteger(n) && n > 0).
    • Verified exact match with expected payment amount (verifiedAmountPaise === Number(payment.amount)).
    • Verified exact currency (INR) and merchant ID match.
    • Any mismatch transitions payment to disputed, writes immutable audit ledger entry, and creates 0 confirmed bookings or Telegram invites.

5. REV-05 (Medium) — QR Payload Byte Limits & User-Facing Error

  • Root Cause: QR capacity stopped at Version 10 (213 bytes); UTF-8 byte count was not checked; long payloads threw unhandled 500 errors.
  • Fix: Expanded QR capacity through Version 14 (362 bytes); added Buffer.byteLength(uri, 'utf8') check; throws structured HTTP 400 QrPayloadTooLargeError (payload_too_large); ensured buildUpiUri never truncates parameters.

6. REV-06 (Medium) — Strict Webhook Merchant ID Verification

  • Root Cause: Webhook ingestion did not strictly assert merchantId, risking spoofed callbacks.
  • Fix: In PhonePeAdapter.verifyWebhook and MockUpiAdapter, verified data.merchantId === this.merchantId; missing, null, or mismatched merchant IDs are rejected with HTTP 401 PaymentAdapterError.

7. REV-07 (Low) — Ticket-Tier Active Hold Index (Migration 017)

  • Root Cause: Query activeTierPaymentHolds lacked an exact matching composite index.
  • Fix: Added additive migration migrations/017_payments_tier_holds_index.sql:
CREATE INDEX IF NOT EXISTS idx_payments_active_tier_holds
  ON payments (((raw_ref->>'ticketTypeId')::uuid), status, expires_at);

Verified query plan executes Bitmap Index Scan via EXPLAIN.


Migration 017 Details

  • File: migrations/017_payments_tier_holds_index.sql
  • Purpose: Creates composite partial index idx_payments_active_tier_holds on payments (((raw_ref->>'ticketTypeId')::uuid), status, expires_at).
  • Integrity: Purely additive, fully idempotent (IF NOT EXISTS), maintains sequential migration ordering (003 through 017), zero modification to historical migrations.
  • Execution Plan: Verified locally using EXPLAIN query in test/migrations.test.js showing index scan utilization.

Test Results

  • UPI Checkout Suite (test/upi-checkout.test.js): 31/31 passed (100%)
    • QR V1–V14 independent jsQR decode: PASS
    • Duplicate booking prevention & savepoint 23505 handling: PASS
    • refund_pending recovery & manual finance reconciliation: PASS
    • Mandatory verified amount matching: PASS
    • QR byte limits & 400 error handling: PASS
    • Webhook merchantId validation: PASS
  • Migration Suite (test/migrations.test.js): 8/8 passed (100%)
    • Sequential ordering (003–017) & idempotency: PASS
    • Migration 017 index & EXPLAIN Bitmap Index Scan: PASS
  • Full Test Suite (npm test): 464 passed, 2 skipped, 0 failed
  • Linter (npm run lint): 140/140 files clean
  • Playwright E2E Suite (npm run test:e2e): 157 passed, 11 skipped, 0 regressions

Absolute Safety Gates Preserved

  • Target branch is feature/m4-3-upi-security-remediation (does not touch launch-mvp or main).
  • ENABLE_LIVE_PAYMENTS=false strictly preserved across all environments.
  • Zero live payment or banking transactions.
  • Zero modifications to production, DNS, or Telegram settings.
  • This PR will NOT be merged automatically and requires independent owner review.

… on PR #30

- REV-01: Correct ISO/IEC 18004 QR alignment patterns, RS polynomial generator, Table 12 Format Info, and BCH(18,6) Version Info across V1-V14 with independent jsQR validation
- REV-02: Prevent duplicate active bookings at intent creation, confirmation locked tx, guest claims, and handle SQLSTATE 23505 via defensive savepoints
- REV-03: Enable operational recovery for refund_pending payments, isolate gateway calls outside holding transactions, and support manual finance reconciliation
- REV-04: Enforce strict safe positive integer paise amount matching, exact currency match, and merchant association, preventing underpayment attacks
- REV-05: Expand QR capacity to V14 (362B), enforce byte-length checks, and return HTTP 400 QrPayloadTooLargeError without truncating payment details
- REV-06: Strictly verify webhook merchantId against configured identity, rejecting missing or mismatched IDs with 401
- REV-07: Add additive migration 017 with idx_payments_active_tier_holds index, verified via EXPLAIN Bitmap Index Scan
- docs: update UPI_SECURITY_AUDIT.md, UPI_PROVIDER_READINESS.md, and PROJECT_STATE.md with M4.3.1 remediation matrix
@vercel

vercel Bot commented Nov 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
strangersmeetup Ready Ready Preview Nov 3, 2026 7:54pm UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant