Skip to main content

Merchant ID Architecture

Complete technical documentation for merchant ID generation, validation, and related systems.

Merchant ID Format

New Format (Current)

  • Format: MXXXXXX where X is a digit
  • Example: M000101
  • Range: M000001 to M999999
  • Pattern: /^M\d{6}$/

Legacy Format (Supported)

  • Format: FM-XXXX-XXXX-XXXX where X is a digit
  • Example: FM-0294-8617-5039
  • Pattern: /^FM-\d{4}-\d{4}-\d{4}$/

Backward Compatibility

The system supports both formats:

  • New merchants: Automatically receive MXXXXXX format
  • Existing merchants: Continue to use FM-XXXX-XXXX-XXXX format
  • Validation: Both formats are accepted throughout the system

Node Modules and Dependencies

Merchant API (cloud-run/merchant-api/)

Location: /cloud-run/merchant-api/

Node Modules:

  • express (^4.18.2) - Web framework for API endpoints
  • cors (^2.8.5) - CORS middleware
  • @google-cloud/storage (^7.7.0) - Google Cloud Storage client

Package File: cloud-run/merchant-api/package.json

Key Files:

  • index.js - Main API server and merchant ID generation
  • lib/jsonStore.js - Atomic write operations for GCS
  • lib/validation.js - Merchant data validation

Merchant ID Generation:

  • Function: generateMerchantId(existingIds)
  • Location: cloud-run/merchant-api/index.js (lines 54-74)
  • Algorithm: Generates random 6-digit number, zero-padded, prefixed with 'M'

Partner API (cloud-run/partner-api/)

Location: /cloud-run/partner-api/

Validation Module:

  • File: cloud-run/partner-api/lib/validation.js
  • Function: validateMerchantId(merchant_id)
  • Supports: Both MXXXXXX and FM-XXXX-XXXX-XXXX formats
  • Pattern: /^M\d{6}$/ or /^FM-\d{4}-\d{4}-\d{4}$/

Merchant Creation Flow

1. ID Generation

Location: cloud-run/merchant-api/index.jsgenerateMerchantId()

function generateMerchantId(existingIds) {
let attempts = 0;
let merchantId;

do {
// Generate MXXXXXX format (6 digits, zero-padded)
const numericPart = Math.floor(Math.random() * 999999) + 1;
merchantId = `M${String(numericPart).padStart(6, '0')}`;
attempts++;
} while (existingIds.includes(merchantId) && attempts < 100);

return merchantId;
}

Process:

  1. Generates random number 1-999999
  2. Zero-pads to 6 digits
  3. Prefixes with 'M'
  4. Checks uniqueness against existing IDs
  5. Retries up to 100 times if collision occurs

2. Storage

Merchants JSON:

  • Location: gs://finmatch-shared/merchants.json
  • Structure: { profiles: { [merchantId]: { ... } } }
  • Fields: finmatchId, merchantName, domain, environment, etc.

Merchant Router JSON:

  • Location: gs://finmatch-shared/merchant-router.json
  • Structure: { [merchantId]: { environment: 'p|s|t', domain: '...' } }
  • Purpose: Domain-based routing for merchant identification

3. CORS Policy Update

Automatic Update: ✅ YES (new feature)

Location: cloud-run/merchant-api/index.jsupdateCorsPolicy()

Process:

  1. Extracts domain from merchant profile
  2. Reads current CORS config from gs://finmatch-shared/cors.json
  3. Adds merchant domain (and www variant) to origins array
  4. Saves updated config to gs://finmatch-shared/cors.json (backup)
  5. Applies CORS policy to gs://finmatch-finance-marketing-assets bucket

CORS Bucket: finmatch-finance-marketing-assets (different from data bucket)

Domains Added:

  • Base domain: https://example.com
  • WWW variant: https://www.example.com (if applicable)

Error Handling: CORS update failures do not prevent merchant creation

Files That Handle Merchant IDs

Backend (Node.js)

  1. cloud-run/merchant-api/index.js

    • Generates merchant IDs (generateMerchantId())
    • Creates merchants (POST /api/merchants)
    • Updates merchants (PUT /api/merchants/:id)
    • Updates CORS policy (updateCorsPolicy())
  2. cloud-run/merchant-api/lib/validation.js

    • Validates merchant data
    • Does NOT validate merchant ID format (handled elsewhere)
  3. cloud-run/partner-api/lib/validation.js

    • Validates merchant ID format for API requests
    • Supports both MXXXXXX and FM-XXXX-XXXX-XXXX
  4. cloud-run/partner-api/lib/cache.js

    • Caches merchant data
    • Looks up merchants by ID (supports both formats)

Frontend (JavaScript)

  1. admin/js/merchants.js

    • submitAddMerchant() - Creates new merchant
    • editMerchant() - Loads merchant for editing
    • submitEditMerchant() - Updates merchant (validates ID format)
    • ID format validation: /^(M\d{6}|FM-[0-9]{4}-[0-9]{4}-[0-9]{4})$/
  2. admin/merchants/index.html

    • Edit merchant form with ID input
    • Pattern validation: (M\d{6}|FM-[0-9]{4}-[0-9]{4}-[0-9]{4})

Documentation

  1. admin-docs/docs/merchants/adding-merchants.md - User guide
  2. admin-docs/docs/merchants/editing-merchants.md - Edit guide
  3. admin-docs/docs/merchants/merchant-creation-architecture.md - Architecture
  4. MERCHANT_CREATION_REQUIREMENTS.md - Requirements doc

Validation Rules

Format Validation

Backend (Partner API):

const validFormats = [
/^M\d{6}$/, // M123456
/^FM-\d{4}-\d{4}-\d{4}$/ // FM-1234-5678-9012
];

Frontend (Admin Dashboard):

const finmatchIdPattern = /^(M\d{6}|FM-[0-9]{4}-[0-9]{4}-[0-9]{4})$/;

Uniqueness Validation

  • On Creation: Automatically checked during ID generation
  • On Edit: Manually validated by checking if ID exists
  • Retry Logic: Up to 100 attempts to find unique ID

CORS Policy Architecture

Storage Locations

  1. Backup Copy: gs://finmatch-shared/cors.json

    • Human-readable JSON
    • Version controlled
    • Used as source of truth
  2. Applied Policy: gs://finmatch-finance-marketing-assets bucket

    • Applied via bucket.setCorsConfiguration()
    • Active CORS rules

Update Process

Automatic (on merchant creation):

  1. Merchant created with domain
  2. updateCorsPolicy() called
  3. Domain added to CORS origins
  4. Policy applied to bucket

Manual (if needed):

  1. Edit gs://finmatch-shared/cors.json
  2. Apply: gsutil cors set cors.json gs://finmatch-finance-marketing-assets
  3. Verify: gsutil cors get gs://finmatch-finance-marketing-assets

CORS Configuration Structure

[
{
"maxAgeSeconds": 3600,
"method": ["GET", "HEAD"],
"origin": [
"https://example.com",
"https://www.example.com",
...
],
"responseHeader": ["Content-Type"]
}
]

Merchant Router Architecture

Purpose

Maps merchant domains to merchant IDs and environments for routing.

Structure

{
"M000101": {
"environment": "p",
"domain": "https://example.com"
},
"FM-0294-8617-5039": {
"environment": "s",
"domain": "https://staging.example.com"
}
}

Update Process

  • On Creation: Automatically added with merchant creation
  • On Edit: Updated if environment or domain changes
  • Location: gs://finmatch-shared/merchant-router.json

Environment Assignment

Automatic Derivation

Function: deriveEnvironment(domain)

Logic:

  • Contains "staging" or "stage" → 's'
  • Contains "test" or "dev" → 't'
  • Otherwise → 'p'

Manual Override

  • Form field: environment (p/s/t)
  • Takes precedence over automatic derivation
  • Saved to both merchants.json and merchant-router.json

Node Module Reference

Merchant API Dependencies

Location: cloud-run/merchant-api/node_modules/

Installed via: npm install (from package.json)

Key Modules:

  • express/ - Web framework
  • cors/ - CORS middleware
  • @google-cloud/storage/ - GCS client library

Installation:

cd cloud-run/merchant-api
npm install

Partner API Dependencies

Location: cloud-run/partner-api/node_modules/

Note: Partner API has its own package.json and dependencies

Installation:

cd cloud-run/partner-api
npm install

Summary

  • New Format: MXXXXXX (6 digits)
  • Legacy Support: FM-XXXX-XXXX-XXXX format still supported
  • Automatic CORS: Domains added to CORS policy on creation
  • Router Update: Merchant router updated with environment
  • Validation: Both formats validated throughout system