Skip to content

ADR-001: MPAC Matcher - Reconciliation Service

Status: Accepted
Date: 2026-02-01
Deciders: Platform Engineering Team
Technical Story: Design and implement an automated reconciliation service for the Payment Gateway (mpac-pgw)


Context and Problem Statement

The MPAC Payment Gateway (mpac-pgw) processes transactions across multiple payment providers (PayPay, LINE Pay, credit cards via SP-NET). Currently, reconciliation between internal transaction records and external provider reports is performed manually using spreadsheets, which is:

  1. Time-consuming - Staff spend hours matching transactions daily
  2. Error-prone - Manual comparison leads to missed discrepancies
  3. Non-compliant - No audit trail for financial reconciliation
  4. Not scalable - Cannot handle growing transaction volumes

We need an automated reconciliation system that can:

  • Compare internal PaymentIntents with external provider settlement reports
  • Detect and flag discrepancies with confidence scoring
  • Route exceptions to appropriate reviewers
  • Maintain compliance-ready audit logs

Decision Drivers

  • Accuracy - Must correctly match transactions with varying identifiers and timing
  • Scalability - Handle 10,000+ transactions per day per merchant
  • Auditability - Full audit trail for SOX/J-SOX compliance
  • Flexibility - Support multiple providers with different data formats
  • Integration - Work seamlessly with existing mpac-pgw infrastructure
  • Operability - Clear exception workflows and dashboards

Considered Options

Option 1: Extend Existing Settlement Domain

Enhance the current /settlements endpoint with reconciliation logic.

Pros:

  • No new service to deploy
  • Reuses existing database models

Cons:

  • Bloats the Payment Gateway codebase
  • Limited matching algorithms
  • Difficult to scale independently

Option 2: Adopt Lerian Matcher

Use the open-source Lerian Matcher product directly.

Pros:

  • Feature-rich, battle-tested
  • Active community
  • Native ledger integration

Cons:

  • Requires Midaz Ledger (not our architecture)
  • Additional infrastructure dependency
  • Learning curve for operations team

Option 3: Build MPAC Matcher (Custom Service)

Develop a dedicated reconciliation microservice inspired by Lerian Matcher concepts.

Pros:

  • Tailored to mpac-pgw data models
  • Full control over matching rules
  • Consistent with existing Go stack
  • Can start simple, add features incrementally

Cons:

  • Development effort required
  • Must build and maintain ourselves

Decision Outcome

Chosen option: Option 3 - Build MPAC Matcher

We will build a custom reconciliation service called MPAC Matcher, inspired by Lerian Matcher's architecture but tailored for our specific use cases and existing infrastructure.

Rationale

  1. Tailored Integration - Direct access to payment_intents and card_slips tables without translation layers
  2. Consistent Stack - Go + PostgreSQL + existing observability (mpac-obs)
  3. Incremental Delivery - Can launch with PayPay reconciliation first, then expand
  4. Operational Simplicity - Same deployment patterns as mpac-pgw

Architecture Decision

Deployment Model

MPAC Matcher will be deployed as a standalone microservice:

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   svc-portal    │     │    mpac-pgw      │     │  mps-matcher    │
│   (Frontend)    │────▶│ (Payment API)   │────▶│ (Reconciliation)│
└─────────────────┘     └─────────────────┘     └─────────────────┘
                                │                        │
                                ▼                        ▼
                        ┌─────────────────────────────────────┐
                        │        PostgreSQL Database          │
                        │   (Shared, schema isolation)        │
                        └─────────────────────────────────────┘

Justification:

  • Independent scaling based on reconciliation workload
  • Clear bounded context separation
  • Can be scheduled independently (batch processing)
  • Failure isolation from payment processing

Multi-Tenancy Strategy

Row-level tenant filtering with merchant_id in all tables:

sql
-- All queries include merchant context
SELECT * FROM match_runs 
WHERE merchant_id = :merchant_id
  AND id = :match_run_id;

Justification:

  • Simpler than schema-per-tenant for current scale
  • Consistent with mpac-pgw approach
  • Can migrate to schema isolation later if needed
  • Connection pooling efficiency

Technology Stack

ComponentTechnologyRationale
LanguageGo 1.22+Consistency with mpac-pgw
DatabasePostgreSQL 16Existing infrastructure
CacheRedisJob queues, rate limiting
APIRESTExternal/portal access
AuthHMAC-SHA256Same as mpac-pgw S2S
ObservabilityOTLP → mpac-obsExisting stack

Core Concepts

1. Reconciliation Context

A logical grouping that defines what is being reconciled:

go
type ReconciliationContext struct {
    ID          uuid.UUID      `json:"id"`
    Name        string         `json:"name"`
    Description string         `json:"description"`
    MerchantID  int64          `json:"merchant_id"`
    StoreID     *int64         `json:"store_id,omitempty"`
    Schedule    *string        `json:"schedule,omitempty"` // cron
    Status      ContextStatus  `json:"status"`
    CreatedAt   time.Time      `json:"created_at"`
    UpdatedAt   time.Time      `json:"updated_at"`
}

2. Match Rules

Configurable rules for comparing transactions:

Rule TypeDescriptionUse Case
EXACTAll fields must match exactlyTransaction ID matching
TOLERANCEAllow numeric varianceAmount differences (fees)
DATE_LAGAllow date windowSettlement timing differences
COMPOSITECombine multiple rulesComplex matching logic

3. Confidence Scoring

Each match receives a confidence score (0-100):

ScoreTierAction
90-100HighAuto-confirm
70-89MediumReview recommended
50-69LowManual review required
0-49PoorCreate exception

4. Exception Management

Unmatched or low-confidence matches create exceptions:

go
type Exception struct {
    ID            uuid.UUID        `json:"id"`
    MatchRunID    uuid.UUID        `json:"match_run_id"`
    SourceID      uuid.UUID        `json:"source_id"`
    TransactionID string           `json:"transaction_id"`
    Type          ExceptionType    `json:"type"`
    Severity      Severity         `json:"severity"`
    Status        ExceptionStatus  `json:"status"`
    AssignedTo    *string          `json:"assigned_to,omitempty"`
    DueDate       *time.Time       `json:"due_date,omitempty"`
    CreatedAt     time.Time        `json:"created_at"`
}

Data Model


API Design

Configuration Endpoints

MethodEndpointDescription
POST/v1/contextsCreate reconciliation context
GET/v1/contextsList contexts
GET/v1/contexts/{id}Get context details
PATCH/v1/contexts/{id}Update context
POST/v1/contexts/{id}/sourcesAdd data source
POST/v1/contexts/{id}/rulesAdd match rule

Matching Endpoints

MethodEndpointDescription
POST/v1/contexts/{id}/matchTrigger match run
POST/v1/contexts/{id}/match/previewDry-run (no commit)
GET/v1/match-runsList match runs
GET/v1/match-runs/{id}Get run details
POST/v1/match-groups/{id}/confirmConfirm match
POST/v1/match-groups/{id}/rejectReject match

Exception Endpoints

MethodEndpointDescription
GET/v1/exceptionsList exceptions
GET/v1/exceptions/{id}Get exception
PATCH/v1/exceptions/{id}Update (assign)
POST/v1/exceptions/{id}/resolveResolve exception

Integration Points

1. mpac-pgw Database (Read)

Direct read access to:

  • payment_intents - Internal transaction records
  • card_slips - Credit card transaction slips
  • settlements - Settlement summaries

2. External Provider APIs

ProviderMethodData
PayPayREST APISettlement reports
LINE PayREST APITransaction reports
SP-NETSFTPDaily batch files (CSV)

3. Observability (mpac-obs)

Emit OTLP telemetry:

  • Traces for match run execution
  • Metrics for match rates, exception counts
  • Structured logs with corr_id

Primary Use Cases

Use Case 1: PayPay Daily Reconciliation

Trigger: Daily scheduled job (02:00 JST)

Sources:

  1. payment_intents WHERE provider_name = 'paypay' AND status = 'succeeded'
  2. PayPay Settlement API (previous day)

Rules:

  1. EXACT: provider_transaction_id + amount (confidence: 100)
  2. DATE_LAG: amount with ±1 day (confidence: 75)

Expected: 95%+ auto-match rate

Use Case 2: SP-NET Card Slip Reconciliation

Trigger: After SFTP file arrival (daily)

Sources:

  1. card_slips table
  2. SP-NET batch CSV file

Rules:

  1. EXACT: slip_number + approval_code + amount (confidence: 100)
  2. TOLERANCE: Allow ¥1 variance (confidence: 90)

Use Case 3: Cash Drawer Verification

Trigger: Manual (end of day settlement)

Sources:

  1. payment_intents WHERE payment_method_type = 'cash'
  2. Manual cash count entry

Rules:

  1. EXACT: Total matches (confidence: 100)
  2. TOLERANCE: Within store threshold (confidence: 80)

Implementation Phases

PhaseDurationScope
Phase 13 weeksCore entities, Configuration APIs, Basic ingestion
Phase 23 weeksMatching engine, Rule types, Confidence scoring
Phase 32 weeksException workflow, Resolution APIs
Phase 42 weeksPayPay integration, SP-NET adapter
Phase 52 weeksReporting, Dashboard, Performance tuning

Total: 12 weeks (3 months)


Consequences

Positive

  • Automation - Reduces manual reconciliation effort by 90%+
  • Accuracy - Rule-based matching eliminates human error
  • Auditability - Complete audit trail for compliance
  • Visibility - Real-time exception dashboard
  • Scalability - Handles growing transaction volumes

Negative

  • Development Effort - 12 weeks of engineering work
  • Operational Overhead - New service to monitor and maintain
  • Learning Curve - Team must learn new domain concepts

Risks and Mitigations

RiskMitigation
Provider API changesAdapter pattern isolates changes
High exception rates initiallyGradual rule tuning, manual review period
Database performanceRead replicas, indexed queries, pagination
Late file arrivalsConfigurable retry and alerting

Alternatives Not Chosen

Commercial Reconciliation Platforms

Products like ReconArt, Trintech, BlackLine:

  • High cost ($50K+ annually)
  • Over-engineered for our scale
  • Integration complexity with custom PGW

Database Triggers / Stored Procedures

Real-time matching in PostgreSQL:

  • Limited flexibility for complex rules
  • Difficult to debug and maintain
  • No exception workflow support


Notes

This ADR is based on analysis of:

  • Lerian Matcher Documentation
  • Existing mpac-pgw Settlement domain
  • Payment Gateway entity models
  • Team operational experience with manual reconciliation

Changelog

DateAuthorChange
2026-02-01Design TeamInitial ADR created

MPAC — MP-Solution Advanced Cloud Service