← All provider guides

Stripe payment_records migration: four silent breaking changes in one pattern

stripe 4 adjudicated cases measured on 47 spec pairs

When a provider migrates a core resource, the individual spec diffs rarely look dramatic. Stripe's ongoing payment_records migration is a case study: across four separate API releases, fields inside payment_method_details were quietly made nullable, made optional, or removed outright. Each change is real reader-breaking behavior — code that reads those fields breaks — and each one is invisible to a default severity-gated diff tool. This page documents all four cases with independently adjudicated evidence from a 47-pair Stripe spec corridor.

The pattern

Stripe is consolidating payment detail schemas under the payment_records resource. During the migration, $ref anchors inside payment_method_details are re-pointed from legacy schemas to new payment_record variants — and the new variants relax the contract: properties that were required and non-nullable become optional and nullable, or disappear. The OpenAPI diff for each release is one small schema change. The blast radius is every consumer that reads those fields.

Case 1 — boleto tax_id (v2182 → v2183)

A single $ref re-anchor pointed payment_method_details.boleto at a new payment_record schema where tax_id is optional and nullable. One root cause — but a naive transitive diff fans it out to 1,162 findings across hundreds of embedding schemas. mendapi's specdiff reports the 12 canonical anchors and deliberately stops at transitive embeds: one change, one page of alerts, not 1,162. That stop rule is a design choice against alert fatigue, not a detection difference — both engines saw the change.

Case 2 — alma installments.count (v2248 → v2249)

Same re-anchor pattern: alma.installments.count went from required and non-nullable to nullable with no required list. specdiff completed in 15.4s and reported 9 real breaking units, each verified by walking both raw specs. The comparison engine we benchmark against did not complete at all on this pair: three independent runs hit a 900-second hard timeout with zero bytes of output. If your gate never finishes, its verdict for that release is silence.

Case 3 — card core fields relaxed (v2200 → v2210)

The largest batch: brand, exp_month, exp_year, funding and last4 on payment_records card details — the five fields nearly every reconciliation or receipt pipeline reads — all went optional and nullable, part of 96 adjudicated-real breaking units in this release (zero false positives across five root-cause families). This pair was also a timeout on the comparison engine: two independent 900-second runs, 32 CPU-minutes, no output. specdiff returned in 14.6s.

Case 4 — card intelligence fields removed (v2323 → v2324)

Four card-intelligence properties — description, iin, issuer, stored_credential_usage — were removed from payment_records card details. Risk and reconciliation readers that depend on iin or issuer break outright. The comparison engine detected the same removals but classified all of them as level-2 warnings among 587,122 total findings, so a default level≥3 breaking gate reports zero. To be precise: it did not miss the change — it filed it where a default gate does not look.

Why default gates stay silent

Two failure modes, both measured on this corridor of 47 real Stripe spec pairs:

  • Severity classification. Optional-property removal and nullability relaxation default to warning level in mature diff tools. That is a defensible general policy — and exactly wrong for reader code, which breaks on absent fields regardless of the optional flag. 58 of the 163 default-gate-invisible units on this corridor are of this kind.
  • Availability. On 3 of 47 pairs the comparison engine hit a 15-minute timeout with no output; 105 adjudicated-real breaking units sat inside two of those releases. A gate that times out fails open.

Full methodology, per-pair reports and the honest-citation rules (including where the comparison engine is simply the better tool) are on the when-to-use page.

Am I affected?

If your code reads payment_method_details from payment_records — reconciliation, receipts, risk scoring, exports — check locally in about 30 seconds, with no code leaving your machine:

npx mendapi scan
# -> findings: file, line, provider, confidence
mendapi deps --match
# -> which of these four changes actually hit your endpoints

Related

Pre-release. Every entry on this page is generated from the mendapi change database at build time and links its upstream source.