Cover image for A Processor-Neutral JSON Schema for Auditing Payment Processing Statements

Daniel Wilson Kemp

Merchant processing statements are built for billing, not comparison. Two statements can describe the same monthly card volume with completely different labels, subtotals, and pricing structures. That makes the most basic question—“what did accepting cards actually cost?”—harder to answer than it should be.

We wanted a small data model that works for a spreadsheet, a software workflow, or an AI-assisted review without collecting cardholder data. The result is an open payment processing statement audit kit with:

  • a CSV template for side-by-side statement comparison;
  • a JSON Schema Draft 2020-12 contract;
  • a complete synthetic example;
  • a processor-neutral fee taxonomy; and
  • citation metadata for downstream reuse.

The maintained guide and downloads live at liftedpayments.com/payment-processing-statement-audit. The versioned files are also public in the Lifted Holdings payment-processing-resources repository.

Start with one defensible number

The first calculation is the effective rate:

effective rate = total processing fees / total card volume

Enter fullscreen mode Exit fullscreen mode

If a fictional merchant processed $125,000.00 and paid $2,864.75 in total processing-related fees:

2864.75 / 125000 = 0.022918 = 2.2918%

Enter fullscreen mode Exit fullscreen mode

The data model stores 0.022918, not 2.2918. Keeping the value as a decimal avoids ambiguity in APIs and calculations; multiply by 100 only for percentage display.

The hard part is not division. It is making sure the numerator contains every fee from the same period as the volume denominator. Monthly charges, authorization fees, compliance-program fees, gateway items, equipment charges, and adjustments can all disappear from a comparison if someone copies only the most visible subtotal.

The minimum useful schema

The required top-level fields are deliberately small:

{
  "statement_period": {
    "start": "2026-06-01",
    "end": "2026-06-30"
  },
  "card_volume": 125000.00,
  "transaction_count": 1860,
  "total_processing_fees": 2864.75,
  "effective_rate": 0.022918,
  "pricing_model": "interchange_plus",
  "fee_groups": []
}

Enter fullscreen mode Exit fullscreen mode

pricing_model uses a bounded set:

interchange_plus
flat_rate
tiered
subscription
dual_pricing
unknown

Enter fullscreen mode Exit fullscreen mode

unknown matters. A statement parser should not guess a pricing model from one line item. It is better to preserve uncertainty than manufacture a confident but wrong classification.

Normalize labels into stable fee groups

Statement labels vary. The audit model groups them by economic role:

  • interchange — card- and transaction-specific wholesale costs;
  • assessments — network assessments and access charges;
  • processor_markup — percentage and per-transaction provider markup;
  • authorization — approval, decline, AVS, gateway, and batch items;
  • monthly — account, statement, minimum, and recurring platform charges;
  • pci — compliance-program or noncompliance fees;
  • equipment — terminal purchase, rental, or lease costs;
  • chargebacks — chargeback and retrieval-related items; and
  • other — adjustments that cannot be classified honestly.

That taxonomy does not pretend every fee is avoidable. It makes the statement explainable. Wholesale cost, provider markup, fixed overhead, and operational exceptions stop being one opaque total.

Validate records before comparing them

The repository publishes a Draft 2020-12 schema at:

schema/payment-statement-audit.schema.json

With Python and jsonschema, validation is straightforward:

import json
from pathlib import Path

from jsonschema import Draft202012Validator

schema = json.loads(
    Path("schema/payment-statement-audit.schema.json").read_text()
)
record = json.loads(
    Path("examples/payment-statement-audit-example.json").read_text()
)

Draft202012Validator(schema).validate(record)

calculated = round(
    record["total_processing_fees"] / record["card_volume"],
    6,
)
assert calculated == record["effective_rate"]

Enter fullscreen mode Exit fullscreen mode

Schema validation proves structure, not business truth. A human or statement-specific parser still has to confirm that the extracted totals match the source document and cover the same dates.

Keep the public-data boundary strict

An audit needs monthly totals. It does not need payment credentials or personal identity data.

Never put these into a public repository, issue, prompt, or shared audit file:

  • full or partial card numbers;
  • card security codes or PIN data;
  • bank account or routing numbers;
  • passwords, API keys, or authentication values;
  • tax IDs or owner Social Security numbers; or
  • unredacted merchant statements.

The example in the repository is entirely synthetic. It does not describe a real merchant, and its fee-group amounts exist only to demonstrate the file structure and calculation.

Comparing two offers without lying to yourself

Once statements are normalized, a useful comparison follows five rules:

  1. Model the same volume, transaction count, average ticket, and card mix.
  2. Separate wholesale costs from provider markup.
  3. Include every fixed and per-item fee.
  4. Model card-present, keyed, e-commerce, and commercial-card traffic separately when they are material.
  5. Compare the operating workflow—refunds, reconciliation, tokenization, POS integration, and support—not only the headline rate.

For a quick manual check, the free effective-rate calculator performs the same core math in the browser without transmitting the values entered.

Why publish the model openly?

Payment pricing becomes easier to discuss when the underlying terms are explicit. A versioned schema also gives developers and agents a stable output contract instead of another unstructured summary.

The kit is licensed under CC BY 4.0. Reuse it, adapt it, or build a parser around it. The requested attribution is:

Lifted Payments payment statement audit model — https://liftedpayments.com/payment-processing-statement-audit/

If you would rather have a payments team review a real statement and design the processing technology around how the business operates, start the Lifted Payments merchant application.