# A Processor-Neutral JSON Schema for Auditing Payment Processing Statements

> Source: <https://dev.to/daniel_wilsonkemp_62b88a/a-processor-neutral-json-schema-for-auditing-payment-processing-statements-2cg2>
> Published: 2026-08-02 16:32:52+00:00

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:

The maintained guide and downloads live at [liftedpayments.com/payment-processing-statement-audit](https://liftedpayments.com/payment-processing-statement-audit/). The versioned files are also public in the [Lifted Holdings payment-processing-resources repository](https://github.com/Lifted-Holdings/payment-processing-resources).

The first calculation is the effective rate:

```
effective rate = total processing fees / total card volume
```

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%
```

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 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": []
}
```

`pricing_model`

uses a bounded set:

```
interchange_plus
flat_rate
tiered
subscription
dual_pricing
unknown
```

`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.

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.

The repository publishes a Draft 2020-12 schema at:

[schema/payment-statement-audit.schema.json](https://github.com/Lifted-Holdings/payment-processing-resources/blob/main/schema/payment-statement-audit.schema.json)

With Python and `jsonschema`

, validation is straightforward:

``` python
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"]
```

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.

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:

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.

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

For a quick manual check, the [free effective-rate calculator](https://liftedpayments.com/effective-rate-calculator) performs the same core math in the browser without transmitting the values entered.

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](https://liftedholdings.com/apply).
