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
- Stripe API breaking changes: complete tracker
- When to use mendapi vs generic spec diff tools — the measured comparison behind this page.
- mendapi for fintech teams