Skip to the content.
How U.S. Payments Really Work Part 10
How U.S. Payments Really Work Part 10

Why Credit Card Interchange Fees Differ at Authorization vs Settlement (and How to Reconcile Them)

Understanding why credit card interchange fees change between authorization and settlement is crucial for payment system design. This article explores the reconciliation challenges and provides practical solutions for tracking fee variances.

Suma Manjunath
Author: Suma Manjunath
Published on: September 01, 2025

Why Credit Card Interchange Fees Differ at Authorization vs Settlement (and How to Reconcile Them)

Audience Payment engineers, finance systems architects
Reading Time: 12 minutes
Prerequisites: Familiarity with card processing flows, Rails background job frameworks
Why now: Interchange downgrades are rising with new scheme rules—misreporting margins creates material financial risk.

TL;DR:

⚠️ Disclaimer: All scenarios, accounts, names, and data used in examples are not real. They are realistic scenarios provided only for educational and illustrative purposes.


Problem Definition

The challenge: Many payment systems treat authorization fees as final, leading to reconciliation mismatches when settlement occurs.

Who faces this: Merchants, PSPs, and finance teams handling high transaction volumes.

Cost of inaction: Reconciliation breaks, finance reporting errors, margin leakage of tens of thousands monthly.

Why current approaches fail: Most ledger systems don’t model fee variances explicitly, hiding downgrade causes from engineering.


The Misconception

Many assume authorization = final economics. In reality:

sequenceDiagram
    Shopper->>Merchant: Card swipe / online checkout
    Merchant->>PSP: Auth request
    PSP->>Network: Forward auth
    Network->>Issuer: Risk check
    Issuer-->>Network: Approve
    Network-->>PSP: Response (no final fees)
    PSP-->>Merchant: Approved

    rect rgba(200,200,200,0.15)
    Merchant->>PSP: Clearing file
    PSP->>Network: Full data
    Network->>Issuer: Funds + fee
    Issuer-->>Network: Final interchange
    Network-->>PSP: Settlement details
    PSP-->>Merchant: Payouts + fees
    end

Sequence diagram of authorization vs settlement fee flow


Why Auth ≠ Settlement

Final interchange differs due to:

graph TD
  A[Auth Estimate] -->|Mismatch Sources| B[Final Fee at Settlement]
  B --> D[Variance]

  A1["Data Downgrade (missing AVS, CVV, Level II/III)"]
  A2["Timing Downgrade (late clearing >24h)"]
  A3["Reclassification (MCC, recurring, small-ticket)"]
  A4["Amount/Metadata Mismatch (tips, amended amounts)"]
  A5["Network Rule Updates"]

  A1 --> B
  A2 --> B
  A3 --> B
  A4 --> B
  A5 --> B

Diagram of downgrade sources leading to variance


Failure Mode in Reconciliation

flowchart LR
  Auth[Auth Fee Estimate] -->|booked| Ledger
  Settlement[Final Fee] --> Ledger
  Ledger -->|Mismatch| Variance[Variance Account]

Flowchart of fee mismatch leading to variance account

Impact:


Real-World Impact

On a $100 txn:

Across 100,000 transactions/month, this becomes $25,000 in hidden costs.


Solution Implementation (Rails Example)

We’ll build a pipeline to:

  1. Estimate fees at auth.
  2. Ingest settlement files.
  3. Record variances + downgrade reasons.

Data Model

create_table :payments do |t|
  t.string  :psp_txn_id, null: false
  t.string  :card_brand, :mcc, :entry_mode, :recurring_flag, :currency
  t.bigint  :amount_minor, null: false
  t.datetime :authorized_at, :captured_at, :settled_at
end

create_table :interchange_estimates do |t|
  t.references :payment
  t.decimal :rate_bps
  t.decimal :per_txn_fee_minor
end

create_table :interchange_finalizations do |t|
  t.references :payment
  t.decimal :final_rate_bps, :final_per_txn_fee_minor, :final_amount_minor
  t.jsonb :downgrade_reasons, default: []
end

create_table :interchange_variances do |t|
  t.references :payment
  t.decimal :estimated_fee_minor, :final_fee_minor, :variance_minor
end

Estimate at Auth

module Interchange
  class Estimator
    def estimate(payment:)
      bucket = [payment.card_brand, payment.entry_mode, payment.recurring_flag, payment.mcc].join(":")
      rule   = @rate_table.fetch(bucket)
      pct    = ((payment.amount_minor * rule[:bps]) / 10_000.0).round
      total  = pct + rule[:per_txn_fee_minor]

      InterchangeEstimate.create!(payment:, rate_bps: rule[:bps], per_txn_fee_minor: rule[:per_txn_fee_minor])
      total
    end
  end
end

Finalize at Settlement

module Interchange
  class Finalizer
    def apply!(payment:, row:)
      final_fee = ((row.settlement_amount_minor * row.final_rate_bps) / 10_000.0).round + row.final_per_txn_fee_minor
      estimated = payment.interchange_estimates.last

      InterchangeFinalization.create!(payment:, final_rate_bps: row.final_rate_bps, downgrade_reasons: row.downgrade_reasons)
      InterchangeVariance.create!(payment:, estimated_fee_minor: estimated_total(estimated), final_fee_minor: final_fee, variance_minor: final_fee - estimated_total(estimated))
    end

    def estimated_total(est) = ((est.payment.amount_minor * est.rate_bps) / 10_000.0).round + est.per_txn_fee_minor
  end
end

Ops Playbook

flowchart TD
  Capture[Capture Txn] --> Estimate[Estimate Interchange]
  Estimate --> Accrual[Book Provisional Expense]
  Settlement[Settlement File] --> Finalize[Finalize Fees]
  Finalize --> Variance[Record Variance + Reasons]
  Variance --> Reporting[Ops/Finance Dashboards]
  Reporting --> Fixes[Engineering Fixes]

Ops playbook diagram for reconciliation loop

💡 Tip: Map downgrade reasons to specific engineering fixes:


Validation & Monitoring


Takeaways


Acronym


References

  1. Visa Interchange Fees - Visa USA Interchange Reimbursement Fees, 2024
  2. Mastercard Interchange Rates - Mastercard US Interchange Rates, 2024
  3. AmEx Merchant Pricing - American Express Merchant Pricing Overview, 2024
  4. NACHA ACH Rules - NACHA ACH Operations Rules & Advisories, 2024

Comments & Discussion

Share your thoughts, ask questions, or start a discussion about this article.