The ID That Lost Its Last Digits
Casting a provider's string ID to int corrupts the charge
Published Oct 29, 2026
Executive Summary
A payment webhook handler casts the provider's charge ID to (int). The provider sends IDs as strings because they exceed what an int or float can hold exactly — and the cast silently truncates the value. The refund flag then lands on the wrong charge, with no error.
The Prompt
“Handle the payment webhook and mark the charge as refunded by its ID.”
The Flaw
The provider sends charge_id as a string because 19- to 20-digit IDs exceed what a 64-bit int — and certainly what a JSON float — can represent exactly. Casting to (int) truncates the value. The corrupted ID can still match a different charge row, so the UPDATE succeeds silently against the wrong customer's record. No exception, no warning — just wrong data.
Risk & Impact
[Correctness] A refund is recorded against the wrong charge. Reconciliation fails, and the mistake only surfaces later when a customer disputes a charge they already paid.
Cost: Silent data corruption in the payments path — the hardest kind to notice and the most expensive to unwind.
Code Comparison
❌ Vulnerable (AI generated)
❌ Vulnerable (AI generated)
✅ Hardened (production ready)
The Fix
Treat provider IDs as opaque strings — cast to (string), never (int) — and store and compare them as strings end-to-end.
Prevention
- [linter] A rule banning (int) casts of payload/ID fields in payment, identity, and webhook code paths.
- [ci] A fixture webhook with a 20-digit string ID must update exactly the intended fixture row — assert both the affected row count and the targeted ID.
- [manual] Tell the agent 'IDs are strings, do not cast them' (rule 3), and test with a real provider ID (rule 9).
Why This Happened
Root cause: the prompt omitted Rule 3 and Rule 9 — the guardrails that would have caught this. See the full guide →
