Scoring Rules
MAIN Score Generators
Overview
Score Generators are a functional module of the anti-fraud system — a rule-based constructor for transaction scoring. The system works on the principle of cumulative risk: each check is assigned a weight, points are summed, and based on the total value a decision of PASS or REJECT is made.
The system contains two categories of generators:
| Characteristic | BASIC | MAIN |
|---|---|---|
| Logic | Binary: 0 or scoreValue | Branching: multiple outcomes |
| Execution | Parallel (Basic Batch) | Sequential with early-exit |
| Data source | Internal (Redis, PostgreSQL) | External providers (HTTP, Graph DB) |
| Scope | Per-gate (separate for each gate) | Per-transaction (once per transaction) |
| Count | 22 types | 4 types |
All generators (BASIC and MAIN) are stored in a single ScoreGenerators table, differentiated by the generatorCategory column (BASIC / MAIN).
When MAIN generators are enabled, the following 3 BASIC generators should be disabled:
System Black/White ListClient's Black/White ListVPN usage
MAIN Generator Types
There are 4 MAIN Score Generators, defined in PGScoreGeneratorTypeEnum with category PGScoreGeneratorCategoryEnum.MAIN:
| Type | Purpose | Provider | Required inputs |
|---|---|---|---|
TRUSTLAYER_SP | Card reputation check via TrustLayer (trusted / fraud) | providerTrustLayer | cardToken |
TRUSTLAYER_EMAIL_CHECK_SP | Email reputation check via TrustLayer (trusted / not found / fraud) | providerTrustLayer (requestType: email) | email |
THREEFP_CHECK | Fingerprint check via TigerGraph graph DB (trusted device / fraud) | providerTigerGraphFp | fingerprint |
GENERATED_CARD_CHECK_SP | Generated card detection via Smart Payments (valid / generated) | providerSmartPayments | gccCardToken, gccSessionId |
TRUSTLAYER_SP and TRUSTLAYER_EMAIL_CHECK_SP are separate database entries and separate steps in the scoring chain, but they share the same external TrustLayer API and common ENV variables for mode/mocks.
Each MAIN SG is created with a config containing a set of outcomes — a mapping from provider result to scoring action:
NO_SCORE— do not add pointsSCORE_VALUE— addscoreValuepointsTHRESHOLD— set the threshold value (badScoreBorder), guaranteeing REJECT
Each outcome also has an earlyExit flag — whether to interrupt the chain.
They are registered in the SCORE_GENERATOR_CREATE_TYPE_OBJ template.
Execution Flow
Sequential Execution and Early-Exit
MAIN generators execute strictly sequentially in the order defined by executionOrder. If a generator returns a specific result (e.g. trusted or fraud), the chain is interrupted (early-exit) — subsequent generators are not called.
Default execution order (from utils-sg-template.ts):
const MAIN_SG_DEFAULT_EXECUTION_ORDERS: Record<string, number> = {
[PGScoreGeneratorTypeEnum.TRUSTLAYER_SP]: 1,
[PGScoreGeneratorTypeEnum.TRUSTLAYER_EMAIL_CHECK_SP]: 2,
[PGScoreGeneratorTypeEnum.THREEFP_CHECK]: 3,
[PGScoreGeneratorTypeEnum.GENERATED_CARD_CHECK_SP]: 4,
};That is, by default: TrustLayer (card) -> TrustLayer (email) -> 3FP Check -> Generated Card Check.
Early-exit logic per step:
1. TRUSTLAYER_SP -> trusted? exit : fraud? exit(scoreValue) : continue
2. TRUSTLAYER_EMAIL_CHECK -> trusted? exit : fraud? exit(scoreValue) : continue
3. THREEFP_CHECK -> trusted? exit : fraud? exit(scoreValue) : continue
4. GENERATED_CARD_CHECK -> valid? exit : generated? exit(>=threshold)
If a transaction is missing a required input (e.g. email for TRUSTLAYER_EMAIL_CHECK_SP), the orchestrator does not call the provider for that step and returns an error of the kind "missing required input".
Transaction Scope
All MAIN generators operate in the scope of a transaction:
- Called once per transaction (not per gate)
- Result is cached and applied to all gates in the cascade
- No repeated provider calls when switching between gateways
Parallel Execution with Basic Batch
The MAIN pipeline and Basic Batch pipeline run in parallel (Promise.all). Basic Batch does not wait for MAIN and vice versa. After both pipelines complete, results are merged per-gate:
totalScore = MAIN.totalScore + BasicBatch[gateId].totalScore
SLA Timeline
0ms 500ms 800ms 2800ms
|---------|----------|------------------|
| Trust | FP Check | SP Card |
| Layer | (Graph) | Check |
| <=500ms | <=300ms | <=2000ms |
| | | |
|====================================== | Basic Batch (<=500ms, parallel)
| | | |
|------------------- Total MAIN: <=2800ms (worst case, no earlyExit)
| Total Basic Batch: <=500ms (finishes before MAIN)
| Total scoring: max(MAIN, BasicBatch) ~ <=2800ms worst case
With earlyExit at Phase 1 (Trust Layer) — total time ~ 500ms.
Card Block (Blocked Cards Cache)
Concept
When a provider returns a specific negative result (FRAUD_MATCH from Trust Layer, GENERATED from Smart Payments), the card is written to the global ScoringCardBlocks table. On subsequent checks, the system first checks this table — if the card is found, the provider is not called, saving time and resources.
Characteristics
| Parameter | Value |
|---|---|
| Scope | Global (not tied to a company) |
| Uniqueness | By pair (cardToken, sourceType) |
| Supported generators | TRUSTLAYER_SP, GENERATED_CARD_CHECK_SP |
| Not supported | THREEFP_CHECK (checks device, not card) |
Check-Before-Call Flow
Before calling external providers (Trust Layer, Smart Payments) for TRUSTLAYER_SP and GENERATED_CARD_CHECK_SP, a preliminary check of the local blocked cards cache (ScoringCardBlocks) is performed by the pair (cardToken, sourceType). If the card is already blocked, the provider call is skipped and the block reason and result are used from cache. This optimization does not apply to THREEFP_CHECK (it works with fingerprint, not card). The check-before-call logic executes at the beginning of each supported MAIN check in the Main Orchestrator.
1. Start MAIN generator phase
2. Does the generator support card-block? (TRUSTLAYER_SP / GENERATED_CARD_CHECK_SP)
- No (THREEFP_CHECK) -> call provider directly
- Yes -> check cardBlockCheck(cardToken)
3. Card found in ScoringCardBlocks?
- Yes -> provider is NOT called, saved reason is used
- No -> call provider
4. Provider result = FRAUD_MATCH / GENERATED?
- Yes -> side-effect: write to ScoringCardBlocks
5. scoreEvaluate(result, config) -> scoreValue + earlyExit
Architecture (Module Responsibilities)
The key principle: Score Generator is responsible only for scoring. Provider interaction logic and blocked card cache logic are outside the scoring operation.
Four Modules
| Module | Location | Responsibility |
|---|---|---|
| Main Orchestrator | transaction-score/main/ | Execution sequence, early-exit, caching |
| Score Evaluator | transaction-score/main/ | Pure mapping: outcome -> scoreValue (no side-effects) |
| Card Block | transaction-score/card-block/ | Check and write to ScoringCardBlockModel |
| Provider Adapters | scoring-providers/ | HTTP / TigerGraph calls to external services |
Module Relationship
transactionScore() --- entry point
├── Basic Batch Pipeline (22 BASIC, parallel)
└── Main Orchestrator
├── Card Block -> ScoringCardBlockModel (PostgreSQL)
├── Provider Adapters -> External Services (Trust Layer, TigerGraph, Smart Payments)
└── Score Evaluator -> pure function: outcome -> scoreValue
Score Evaluator (Pure Function)
Score Evaluator has no knowledge of providers, HTTP, database, or card-block. It receives a result and configuration — returns a score:
Input: providerResult + outcomeList + scoreValue + badScoreBorder
Output: { scoreValue, earlyExit, flagValue }
scoreAction mapping:
NO_SCORE -> scoreValue: 0
SCORE_VALUE -> scoreValue: sg.scoreValue (configured weight)
THRESHOLD -> scoreValue: badScoreBorder (guaranteed REJECT)
Data Models
ScoreGenerators (existing table, extended)
New column added:
| Column | Type | Description |
|---|---|---|
generatorCategory | ENUM (BASIC / MAIN) | Generator category. Default: BASIC |
New values in PGScoreGeneratorTypeEnum: TRUSTLAYER_SP, TRUSTLAYER_EMAIL_CHECK_SP, THREEFP_CHECK, GENERATED_CARD_CHECK_SP.
Full table schema:
┌──────────────────────────────────────────┐
│ ScoreGenerators │
├──────────────────────────────────────────┤
│ id UUID PK │
│ createdAt TIMESTAMP │
│ type ENUM (25 values) │
│ createType ENUM (SYSTEM/MANUAL) │
│ generatorCategory ENUM (BASIC/MAIN) │ <-- NEW
│ title STRING │
│ scoreValue INTEGER │
│ actionNoParam ENUM │
│ isPriority BOOLEAN │
│ isActive BOOLEAN │
│ optCount INTEGER nullable │
│ optPercent INTEGER nullable │
│ optPeriodSec INTEGER nullable │
│ companyId UUID FK -> Companies │
└──────────────────┬───────────────────────┘
│ 1:1 (MAIN only)
▼
┌──────────────────────────────────────────┐
│ ScoreGeneratorConfigs │ <-- NEW
├──────────────────────────────────────────┤
│ id UUID PK │
│ scoreGeneratorId UUID FK UNIQUE │
│ -> ScoreGenerators.id │
│ ON DELETE CASCADE │
│ executionOrder INTEGER NOT NULL │
│ config JSONB NOT NULL │
│ createdAt TIMESTAMP │
│ updatedAt TIMESTAMP │
└──────────────────────────────────────────┘
┌──────────────────────────────────────────┐
│ ScoringCardBlocks │ <-- NEW
├──────────────────────────────────────────┤
│ id UUID PK │
│ cardToken STRING NOT NULL │
│ blockReason ENUM (FRAUD_MATCH / │
│ GENERATED) NOT NULL │
│ sourceType ENUM (TRUSTLAYER_SP / │
│ GENERATED_CARD_CHECK_SP)│
│ NOT NULL │
│ createdAt TIMESTAMP │
│ │
│ UNIQUE(cardToken, sourceType) │
│ Scope: global (no companyId) │
└──────────────────────────────────────────┘
ScoreGeneratorConfigs (new table)
Extended configuration for MAIN generators. 1:1 relationship with ScoreGeneratorModel:
| Column | Type | Description |
|---|---|---|
id | UUID, PK | Primary key |
scoreGeneratorId | UUID, FK, UNIQUE | Link to ScoreGenerators.id, ON DELETE CASCADE |
executionOrder | INTEGER, NOT NULL | Execution order (1, 2, 3...) |
config | JSONB, NOT NULL | Configuration: outcomes, timeoutMs, fallbackOnError |
createdAt | TIMESTAMP | Created date |
updatedAt | TIMESTAMP | Updated date |
ScoringCardBlocks (new table)
Global blocked cards cache:
| Column | Type | Description |
|---|---|---|
id | UUID, PK | Primary key |
cardToken | STRING, NOT NULL | Card token |
blockReason | ENUM (FRAUD_MATCH / GENERATED) | Block reason |
sourceType | ENUM (TRUSTLAYER_SP / GENERATED_CARD_CHECK_SP) | Source generator |
createdAt | TIMESTAMP | Block date |
Config JSONB Structure
Example for TRUSTLAYER_SP:
{
"timeoutMs": 500,
"outcomes": [
{ "result": "TRUSTED", "scoreAction": "NO_SCORE", "earlyExit": true },
{ "result": "NOT_FOUND", "scoreAction": "SCORE_VALUE", "earlyExit": false },
{ "result": "FRAUD_MATCH", "scoreAction": "THRESHOLD", "earlyExit": true }
],
"fallbackOnError": "NO_SCORE"
}Example for TRUSTLAYER_EMAIL_CHECK_SP (same structure as TRUSTLAYER_SP):
{
"timeoutMs": 500,
"outcomes": [
{ "result": "TRUSTED", "scoreAction": "NO_SCORE", "earlyExit": true },
{ "result": "NOT_FOUND", "scoreAction": "SCORE_VALUE", "earlyExit": false },
{ "result": "FRAUD_MATCH", "scoreAction": "THRESHOLD", "earlyExit": true }
],
"fallbackOnError": "NO_SCORE"
}Example for THREEFP_CHECK:
{
"timeoutMs": 300,
"outcomes": [
{ "result": "TRUSTED_DEVICE", "scoreAction": "NO_SCORE", "earlyExit": true },
{ "result": "UNTRUSTED", "scoreAction": "SCORE_VALUE", "earlyExit": false }
],
"fallbackOnError": "NO_SCORE"
}Example for GENERATED_CARD_CHECK_SP:
{
"timeoutMs": 2000,
"outcomes": [
{ "result": "VALID", "scoreAction": "NO_SCORE", "earlyExit": true },
{ "result": "GENERATED", "scoreAction": "THRESHOLD", "earlyExit": true }
],
"fallbackOnError": "NO_SCORE"
}API Endpoints
Extended Existing Endpoints
| Endpoint | Changes |
|---|---|
GET /type-list | Adds generatorCategory for each type + defaultConfig for MAIN |
POST /get-list | Response includes generatorCategory and scoreGeneratorConfig (for MAIN) |
POST /get-one | Response includes generatorCategory and scoreGeneratorConfig (for MAIN) |
POST /create | For MAIN: automatic creation of ScoreGeneratorConfigModel |
POST /remove | For MAIN: cascading config deletion + recalculation of executionOrder |
POST /toggle-active | Sets isActive field on ScoreGeneratorModel |
New Endpoint: Update MAIN Order
Atomic update of the execution order for all MAIN generators of a company in a single transaction. Designed for drag-and-drop on the frontend.
POST /api/v1/client/score-generators/update-main-order
Request body:
{
"orderList": [
{ "scoreGeneratorId": "uuid-1", "executionOrder": 1 },
{ "scoreGeneratorId": "uuid-2", "executionOrder": 2 },
{ "scoreGeneratorId": "uuid-3", "executionOrder": 3 },
{ "scoreGeneratorId": "uuid-4", "executionOrder": 4 }
]
}Validations:
- All
scoreGeneratorIdbelong to the current company - All have
generatorCategory = MAIN executionOrdervalues are unique (no duplicates)executionOrdervalues are contiguous starting from 1 (i.e. 1, 2, 3, 4 — no gaps for four MAIN generators)
Setup and Provisioning
Creation
MAIN SG are created automatically when a company is created via sgCreateFromTemplateTransaction. For each MAIN SG a record is created in ScoreGeneratorModel + a linked record in ScoreGeneratorConfigModel with default config and execution order.
A migration has been added that also creates these generators for existing companies with default values (isActive=true). A separate migration adds the TRUSTLAYER_EMAIL_CHECK_SP type to the enum and ScoreGenerator / config rows for existing companies (following the same pattern as other MAIN generators).
Enabling / Disabling
Via API endpoint POST /toggle-active — sets the isActive field on the ScoreGeneratorModel.
Environment Variables
All variables are defined in src/utils/utils-env-config.ts.
Common (affect all MAIN SG)
| ENV | Type | Default | Description |
|---|---|---|---|
MAIN_SG_STUB_RANDOM | boolean | false | In stub mode — return random outcome instead of deterministic |
TRUSTLAYER_SP
| ENV | Type | Default | Description |
|---|---|---|---|
MAIN_SG_TRUSTLAYER_PROVIDER_MODE | string | 'real' | Provider mode: real / stub / mock_service |
MAIN_SG_TRUSTLAYER_HOST | string | '' | TrustLayer API host |
MAIN_SG_TRUSTLAYER_API_KEY | string | '' | TrustLayer API key (Bearer token) |
MAIN_SG_TRUSTLAYER_MOCK_SCENARIO | string | '' | Fixed mock scenario: trusted / not_found / fraud_match / timeout / http_500 / invalid_json |
MAIN_SG_TRUSTLAYER_TIMEOUT_MS | number | 500 | Request timeout to TrustLayer (ms) |
TRUSTLAYER_EMAIL_CHECK_SP
A separate MAIN SG in the database and in the scoring chain, but uses the same variables as TRUSTLAYER_SP: host, key, MAIN_SG_TRUSTLAYER_PROVIDER_MODE, MAIN_SG_TRUSTLAYER_MOCK_SCENARIO, MAIN_SG_TRUSTLAYER_TIMEOUT_MS. There is no separate ENV for email timeout — both types use resolveMainSgTimeoutMs -> MAIN_SG_TRUSTLAYER_TIMEOUT_MS.
THREEFP_CHECK
| ENV | Type | Default | Description |
|---|---|---|---|
MAIN_SG_THREEFP_TIMEOUT_MS | number | 300 | Request timeout to TigerGraph (ms) |
Additionally, depthLevel and minSuccessCount parameters are taken from the SG config in the database (not from ENV). Defaults (from utils-main-types.ts):
export const THREEFP_CHECK_CONFIG_DEFAULTS = {
depthLevel: 1,
minSuccessCount: 3,
} as const;GENERATED_CARD_CHECK_SP
| ENV | Type | Default | Description |
|---|---|---|---|
MAIN_SG_GCC_PROVIDER_MODE | string | 'real' | Provider mode: real / stub / mock_service |
MAIN_SG_GCC_HOST | string | '' | SmartPayments API host |
MAIN_SG_GCC_SECRET | string | '' | Secret key (x-af-secret header) |
MAIN_SG_GCC_CONFIG_ID | string | '' | Configuration ID on SmartPayments side |
MAIN_SG_GCC_MOCK_SCENARIO | string | '' | Fixed mock scenario: valid / generated / in_process / timeout / http_500 / invalid_json |
MAIN_SG_GENERATED_CARD_TIMEOUT_MS | number | 10000 | Request timeout (ms) |
Default Timeouts Summary
| SG | ENV | Default |
|---|---|---|
TRUSTLAYER_SP | MAIN_SG_TRUSTLAYER_TIMEOUT_MS | 500 ms |
TRUSTLAYER_EMAIL_CHECK_SP | MAIN_SG_TRUSTLAYER_TIMEOUT_MS | 500 ms (same as card) |
THREEFP_CHECK | MAIN_SG_THREEFP_TIMEOUT_MS | 300 ms |
GENERATED_CARD_CHECK_SP | MAIN_SG_GENERATED_CARD_TIMEOUT_MS | 10 000 ms |
Default timeouts (from utils-main-types.ts):
const MAIN_SG_TRUSTLAYER_TIMEOUT_MS_DEFAULT = 500;
const MAIN_SG_THREEFP_TIMEOUT_MS_DEFAULT = 300;
const MAIN_SG_GENERATED_CARD_TIMEOUT_MS_DEFAULT = 10000;ENV Summary for Mocks
| ENV | Description | Default |
|---|---|---|
MAIN_SG_TRUSTLAYER_PROVIDER_MODE | TrustLayer mode | real |
MAIN_SG_GCC_PROVIDER_MODE | GCC mode | real |
MAIN_SG_TRUSTLAYER_MOCK_SCENARIO | Global TrustLayer scenario | (empty) |
MAIN_SG_GCC_MOCK_SCENARIO | Global GCC scenario | (empty) |
MAIN_SG_STUB_RANDOM | Random result in stub mode | false |
Mock and Stub Modes
MAIN SG providers (TrustLayer and GCC/SmartPayments) support three operating modes, controlled via ENV. THREEFP_CHECK has no mock mode — it always calls TigerGraph directly. For emulation, a local TigerGraph (Docker) or data in a test environment is needed.
Provider Modes
| Mode | ENV value | Behavior |
|---|---|---|
| Production | real | Real HTTP request to provider |
| Stub | stub | Instant response without network, fixed result |
| Mock service | mock_service | Local emulation with scenario selection |
Setting the Mode
MAIN_SG_TRUSTLAYER_PROVIDER_MODE=mock_service
MAIN_SG_GCC_PROVIDER_MODE=mock_serviceStub Mode
The simplest option — instantly returns a default result without network calls:
- TrustLayer (card and email): always
NOT_FOUND - GCC: always
VALID
If MAIN_SG_STUB_RANDOM=true, the result will be random from the allowed values.
Mock Service Mode
Full-featured emulation, allowing any provider response including errors and timeouts.
The scenario is selected in two ways (by priority):
1. Globally via ENV (one scenario for all requests)
MAIN_SG_TRUSTLAYER_MOCK_SCENARIO=trusted
MAIN_SG_GCC_MOCK_SCENARIO=generatedIf these variables are set, they override any prefixes in tokens. All requests will return the same result.
2. Per-transaction via token prefix
Leave MAIN_SG_TRUSTLAYER_MOCK_SCENARIO and MAIN_SG_GCC_MOCK_SCENARIO empty. The scenario is determined by a prefix in the cardToken / email (TrustLayer) and gccCardToken (GCC) fields.
TrustLayer Mock Scenarios
Defined in TrustLayerMockScenarioEnum:
| Scenario | Emulates |
|---|---|
trusted | found: true — card/email found, trusted |
not_found | found: false — card/email not found |
fraud_match | found: true, has_chargeback: true — fraud |
timeout | Timeout (sleep > timeoutMs) |
http_500 | HTTP 500 from provider |
invalid_json | Invalid JSON response (missing found field) |
Per-transaction prefix selection (for TRUSTLAYER_SP uses cardToken, for TRUSTLAYER_EMAIL_CHECK_SP uses email — same prefixes):
| Prefix | Scenario | Result |
|---|---|---|
tl_mock_trusted_ | trusted | Card/email trusted |
tl_mock_not_found_ | not_found | Card/email not found |
tl_mock_fraud_match_ | fraud_match | Fraud |
tl_mock_timeout_ | timeout | Provider timeout |
tl_mock_http_500_ | http_500 | HTTP 500 |
tl_mock_invalid_json_ | invalid_json | Invalid response |
| (no prefix) | — | NOT_FOUND (default) |
Example for card: cardToken = "tl_mock_fraud_match_card123" -> provider returns FRAUD_MATCH.
Example for email: email = "[email protected]" -> scenario fraud_match.
Or globally: MAIN_SG_TRUSTLAYER_MOCK_SCENARIO=trusted (applies to all TrustLayer calls — both card and email).
GCC (SmartPayments) Mock Scenarios
Defined in GccMockScenarioEnum:
| Scenario | Emulates |
|---|---|
valid | status: 2 (COMPLETE) — card is valid |
generated | status: -1 (ERROR) — card is generated |
in_process | status: 1 — check not completed (indeterminate) |
timeout | Timeout |
http_500 | HTTP 500 |
invalid_json | Response without status field |
Per-transaction prefix selection (by gccCardToken field):
| Prefix | Scenario | Result |
|---|---|---|
gcc_mock_valid_ | valid | Card is valid |
gcc_mock_generated_ | generated | Card is generated |
gcc_mock_in_process_ | in_process | Check not completed |
gcc_mock_timeout_ | timeout | Provider timeout |
gcc_mock_http_500_ | http_500 | HTTP 500 |
gcc_mock_invalid_json_ | invalid_json | Invalid response |
| (no prefix) | — | VALID (default) |
Example: gccCardToken = "gcc_mock_generated_token456" -> provider returns GENERATED.
Or globally: MAIN_SG_GCC_MOCK_SCENARIO=generated.
Typical Usage Scenarios
Check happy path (all trusted, early exit):
cardToken = "tl_mock_trusted_test1"Check email happy path (with empty global MAIN_SG_TRUSTLAYER_MOCK_SCENARIO):
email = "[email protected]"Check fraud blocking:
cardToken = "tl_mock_fraud_match_test2"
gccCardToken = "gcc_mock_generated_test2"Check behavior when provider is unavailable:
cardToken = "tl_mock_timeout_test3"
gccCardToken = "gcc_mock_http_500_test3"Check different transactions with different scenarios in one test:
Transaction 1: cardToken = "tl_mock_trusted_aaa" -> TRUSTED, earlyExit
Transaction 2: cardToken = "tl_mock_not_found_bbb" -> NOT_FOUND, scoring continues
Transaction 3: cardToken = "tl_mock_fraud_match_ccc" -> FRAUD_MATCH, blocked
Scoring Outcomes and EarlyExit Reference
Default execution order: TRUSTLAYER_SP (1) -> TRUSTLAYER_EMAIL_CHECK_SP (2) -> THREEFP_CHECK (3) -> GENERATED_CARD_CHECK_SP (4).
| Generator | Provider outcome | Action | Score | EarlyExit | Description |
|---|---|---|---|---|---|
TRUSTLAYER_SP | TRUSTED | NO_SCORE | 0 | true | Card trusted, skip remaining |
TRUSTLAYER_SP | NOT_FOUND | SCORE_VALUE | scoreValue | false | Card not found, add points |
TRUSTLAYER_SP | FRAUD_MATCH | THRESHOLD | badScoreBorder | true | Fraud, block |
TRUSTLAYER_SP | provider error | SCORE_VALUE | scoreValue | false | Provider unavailable, add points |
TRUSTLAYER_EMAIL_CHECK_SP | TRUSTED | NO_SCORE | 0 | true | Email trusted, skip remaining |
TRUSTLAYER_EMAIL_CHECK_SP | NOT_FOUND | SCORE_VALUE | scoreValue | false | Email not found, add points |
TRUSTLAYER_EMAIL_CHECK_SP | FRAUD_MATCH | THRESHOLD | badScoreBorder | true | Fraud, block |
TRUSTLAYER_EMAIL_CHECK_SP | provider error | SCORE_VALUE | scoreValue | false | Provider unavailable, add points |
THREEFP_CHECK | TRUSTED | NO_SCORE | 0 | true | Fingerprint trusted |
THREEFP_CHECK | TRUSTED_DEVICE | NO_SCORE | 0 | true | Device trusted |
THREEFP_CHECK | UNTRUSTED | SCORE_VALUE | scoreValue | false | Device unknown, add points |
THREEFP_CHECK | FRAUD | THRESHOLD | badScoreBorder | true | Fraud, block |
THREEFP_CHECK | provider error | NO_SCORE | 0 | false | TigerGraph unavailable, no penalty |
GENERATED_CARD_CHECK_SP | VALID | NO_SCORE | 0 | true | Card is genuine, skip |
GENERATED_CARD_CHECK_SP | GENERATED | THRESHOLD | badScoreBorder | true | Card is generated, block |
GENERATED_CARD_CHECK_SP | provider error | SCORE_VALUE | scoreValue | false | Provider unavailable, add points |
Error Handling
At the base level, each MAIN generator has a timeout (timeoutMs in the config JSONB). If a response is not received in time or the provider returns an error, fallbackOnError from the configuration is applied:
| fallbackOnError | Behavior | Purpose |
|---|---|---|
NO_SCORE | scoreValue = 0, earlyExit = false | Maximum conversion (Flexible Mode) |
In the transaction notes the following is logged: "Provider Timeout. Applied default score: 0".
Module Structure in Code
src/utils/
├── transaction-score/
│ ├── utils-transaction-score.ts # EXISTING: entry point + basic batch
│ ├── utils-scoring-constants.ts # EXISTING: Redis keys
│ ├── utils-scoring-read.ts # EXISTING: Redis ZSET read
│ ├── utils-scoring-write.ts # EXISTING: Redis ZSET write
│ ├── sql/postgres/ # EXISTING: SQL queries
│ │
│ ├── main/ # NEW: MAIN generators
│ │ ├── utils-main-types.ts # Types for MAIN pipeline
│ │ ├── utils-main-orchestrator.ts # Orchestrator: sequential execution, early-exit
│ │ └── utils-main-score-evaluator.ts # Pure mapping: outcome -> scoreValue
│ │
│ └── card-block/ # NEW: Local block-list
│ └── utils-card-block.ts # check(cardToken) / create(cardToken, reason)
│
└── scoring-providers/ # NEW: Provider adapters
├── utils-provider-trustlayer.ts # HTTP -> Trust Layer
├── utils-provider-tigergraph-fp.ts # Query -> TigerGraph (fingerprint)
└── utils-provider-smartpayments.ts # HTTP -> Smart PaymentsUpdated 5 months ago