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:
- Time-consuming - Staff spend hours matching transactions daily
- Error-prone - Manual comparison leads to missed discrepancies
- Non-compliant - No audit trail for financial reconciliation
- 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
- Tailored Integration - Direct access to
payment_intentsandcard_slipstables without translation layers - Consistent Stack - Go + PostgreSQL + existing observability (mpac-obs)
- Incremental Delivery - Can launch with PayPay reconciliation first, then expand
- 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:
-- 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
| Component | Technology | Rationale |
|---|---|---|
| Language | Go 1.22+ | Consistency with mpac-pgw |
| Database | PostgreSQL 16 | Existing infrastructure |
| Cache | Redis | Job queues, rate limiting |
| API | REST | External/portal access |
| Auth | HMAC-SHA256 | Same as mpac-pgw S2S |
| Observability | OTLP → mpac-obs | Existing stack |
Core Concepts
1. Reconciliation Context
A logical grouping that defines what is being reconciled:
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 Type | Description | Use Case |
|---|---|---|
EXACT | All fields must match exactly | Transaction ID matching |
TOLERANCE | Allow numeric variance | Amount differences (fees) |
DATE_LAG | Allow date window | Settlement timing differences |
COMPOSITE | Combine multiple rules | Complex matching logic |
3. Confidence Scoring
Each match receives a confidence score (0-100):
| Score | Tier | Action |
|---|---|---|
| 90-100 | High | Auto-confirm |
| 70-89 | Medium | Review recommended |
| 50-69 | Low | Manual review required |
| 0-49 | Poor | Create exception |
4. Exception Management
Unmatched or low-confidence matches create exceptions:
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
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/contexts | Create reconciliation context |
GET | /v1/contexts | List contexts |
GET | /v1/contexts/{id} | Get context details |
PATCH | /v1/contexts/{id} | Update context |
POST | /v1/contexts/{id}/sources | Add data source |
POST | /v1/contexts/{id}/rules | Add match rule |
Matching Endpoints
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/contexts/{id}/match | Trigger match run |
POST | /v1/contexts/{id}/match/preview | Dry-run (no commit) |
GET | /v1/match-runs | List match runs |
GET | /v1/match-runs/{id} | Get run details |
POST | /v1/match-groups/{id}/confirm | Confirm match |
POST | /v1/match-groups/{id}/reject | Reject match |
Exception Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /v1/exceptions | List exceptions |
GET | /v1/exceptions/{id} | Get exception |
PATCH | /v1/exceptions/{id} | Update (assign) |
POST | /v1/exceptions/{id}/resolve | Resolve exception |
Integration Points
1. mpac-pgw Database (Read)
Direct read access to:
payment_intents- Internal transaction recordscard_slips- Credit card transaction slipssettlements- Settlement summaries
2. External Provider APIs
| Provider | Method | Data |
|---|---|---|
| PayPay | REST API | Settlement reports |
| LINE Pay | REST API | Transaction reports |
| SP-NET | SFTP | Daily 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:
payment_intents WHERE provider_name = 'paypay' AND status = 'succeeded'- PayPay Settlement API (previous day)
Rules:
- EXACT:
provider_transaction_id+amount(confidence: 100) - DATE_LAG:
amountwith ±1 day (confidence: 75)
Expected: 95%+ auto-match rate
Use Case 2: SP-NET Card Slip Reconciliation
Trigger: After SFTP file arrival (daily)
Sources:
card_slipstable- SP-NET batch CSV file
Rules:
- EXACT:
slip_number+approval_code+amount(confidence: 100) - TOLERANCE: Allow ¥1 variance (confidence: 90)
Use Case 3: Cash Drawer Verification
Trigger: Manual (end of day settlement)
Sources:
payment_intents WHERE payment_method_type = 'cash'- Manual cash count entry
Rules:
- EXACT: Total matches (confidence: 100)
- TOLERANCE: Within store threshold (confidence: 80)
Implementation Phases
| Phase | Duration | Scope |
|---|---|---|
| Phase 1 | 3 weeks | Core entities, Configuration APIs, Basic ingestion |
| Phase 2 | 3 weeks | Matching engine, Rule types, Confidence scoring |
| Phase 3 | 2 weeks | Exception workflow, Resolution APIs |
| Phase 4 | 2 weeks | PayPay integration, SP-NET adapter |
| Phase 5 | 2 weeks | Reporting, 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
| Risk | Mitigation |
|---|---|
| Provider API changes | Adapter pattern isolates changes |
| High exception rates initially | Gradual rule tuning, manual review period |
| Database performance | Read replicas, indexed queries, pagination |
| Late file arrivals | Configurable 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
Related Decisions
- ADR-000: Payment Gateway Architecture - Foundation for transaction data
- ADR-000: Settlement Domain - Current reconciliation (to be superseded)
- ADR-000: Observability Stack - Telemetry infrastructure
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
| Date | Author | Change |
|---|---|---|
| 2026-02-01 | Design Team | Initial ADR created |