Skip to content

feat(router): add blocklist count and lookup endpoints - #13887

Open
ayush22667 wants to merge 2 commits into
feat/blocklist-card-bin-6-10from
feat/blocklist-count-lookup-apis
Open

feat(router): add blocklist count and lookup endpoints#13887
ayush22667 wants to merge 2 commits into
feat/blocklist-card-bin-6-10from
feat/blocklist-count-lookup-apis

Conversation

@ayush22667

Copy link
Copy Markdown
Contributor

Type of Change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring
  • Dependency updates
  • Documentation
  • CI/CD

Description

Base branch is feat/blocklist-card-bin-6-10, not main. This stacks on that branch's 6-10
digit BIN work and profile scoping, and should be merged after it.

Two read-only endpoints on the profile-scoped blocklist:

GET /blocklist/count?data_kind=<kind> returns

{ "data_kind": "card_bin", "total_count": 16, "counts_by_length": { "6": 12, "8": 3, "10": 1 } }

counts_by_length buckets by BIN digit length, computed in Postgres with
GROUP BY length(fingerprint_id) rather than paging every row (and its metadata blob) into the
app to count them.

GET /blocklist/lookup?data=<value> returns { "data": "411111", "blocked": true } - one yes/no
check for a single value, across every data_kind.

Both resolve the business profile exactly as add/delete already do, through
get_profile_id_from_business_details: X-Profile-Id when present, otherwise the merchant's
default_profile, otherwise IR_04. That also validates the profile belongs to the caller's
merchant, so one merchant cannot count or probe another's entries.

Two supporting changes:

  • common_utils::types::BlocklistLookupData, a LengthString<20, 1> newtype bounding the lookup
    value at the API boundary. 20 is the longest a fingerprint_id can legitimately be - a card BIN is
    at most 10 digits, and a locker fingerprint id is a 20-character nano id. Validating in the type
    rather than in core means a future caller cannot skip it.
  • expand_data_kinds in diesel_models, collapsing the CardBin -> [CardBin, ExtendedCardBin]
    expansion that was copy-pasted at six call sites in that file.

Additional Changes

  • This PR modifies the API contract
  • This PR modifies the database schema
  • This PR modifies application configuration/environment variables

Two additive endpoints; no existing request or response shape changes, no schema change, no config
change. Both new routes sit inside the existing #[cfg(all(feature = "olap", feature = "v1"))]
blocklist scope, so there is no v2 surface.

Motivation and Context

GET /blocklist pages full rows, which is the only way to read the blocklist today. Answering "how
many entries do I have, and of what BIN lengths?" means paging everything and counting client-side,
and there is no way at all to ask "is this one value blocked?" without scanning. Both are cheap
questions that deserve cheap answers, and counts_by_length in particular is what makes the 6-10
digit BIN work legible to a merchant.

Linked Issues

How did you test it?

Compile-verified under v1: cargo check passes for common_utils, api_models, diesel_models,
router and openapi; cargo +nightly fmt clean; no lint warnings from the new code.

Postman requests covering both endpoints are staged in the Blocklist Profile Scoping folder
(13. Count blocked BINs, 14. Look up whether a BIN is blocked), asserting status, that
counts_by_length sums to total_count, and that a BIN blocked under one profile reports
blocked: true for that profile.

Not yet done, and worth a reviewer's attention before merge:

  • The Postman requests have not been executed against a running server - their assertions are derived
    from the response types, not observed output.
  • just check_v2, just clippy and just clippy_v2 have not been run. common_utils is touched
    and feature-gated, so v2 is the one to check.

Checklist

  • I formatted the code cargo +nightly fmt --all
  • I addressed lints thrown by cargo clippy
  • I reviewed the submitted code
  • I added unit tests for my changes where possible

Add GET /blocklist/count, returning a total plus a counts_by_length
breakdown aggregated with GROUP BY length(fingerprint_id), and
GET /blocklist/lookup, a single yes/no check for one value across every
data_kind.

Both resolve the business profile the same way add/delete do, via
get_profile_id_from_business_details, so X-Profile-Id falls back to
default_profile and the profile is validated against the caller's
merchant.

Bound the lookup value with a new BlocklistLookupData newtype
(LengthString<20, 1>), 20 being the longest a fingerprint_id can be, and
collapse the CardBin -> [CardBin, ExtendedCardBin] expansion duplicated
at six call sites into expand_data_kinds.
@ayush22667
ayush22667 requested review from a team as code owners August 27, 2026 11:20
@semanticdiff-com

semanticdiff-com Bot commented Aug 27, 2026

Copy link
Copy Markdown

@hyperswitch-bot hyperswitch-bot Bot added the M-api-contract-changes Metadata: This PR involves API contract changes label Aug 27, 2026
@ayush22667 ayush22667 self-assigned this Aug 27, 2026
@ayush22667 ayush22667 added the S-test-ready Status: This PR is ready for cypress-tests label Aug 27, 2026
@github-actions github-actions Bot removed the S-test-ready Status: This PR is ready for cypress-tests label Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

M-api-contract-changes Metadata: This PR involves API contract changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant