DEV Community

Payneteasy
Payneteasy

Posted on

Statement descriptors get cut to 22 characters and nobody warns you

Spent a week chasing a spike in 'unrecognized charge' disputes for a subscription product. Turned out the statement descriptor we sent — company name plus order reference — was 31 characters. Fine on our side, fine in the gateway's API response, fine in our own dashboard. But Visa truncates descriptors to 22 characters at the issuer level, and every bank does the truncation differently. Some cut from the end, some cut from the middle and keep a suffix, one issuer we tested just dropped vowels.

So a descriptor like "COMPANYNAME ORDER-88213" became "COMPANYNAME ORD" on one card, "COMPANYNAM-88213" on another. Customers didn't recognize either one, called their bank, bank filed it as fraud. Chargeback rate on that SKU was 3x the account average, and the reason code was almost always "cardholder does not recognize transaction," not actual fraud.

Fix ended up being: put the recognizable brand name in the first 12 characters, since that part survives truncation on basically every issuer we tested, and drop the order reference entirely (support already looks up orders by amount + timestamp anyway).

No gateway doc I've read mentions the 22-char limit or truncation behavior — you find out from dispute reason codes months later. Anyone tracking descriptor-related dispute rates as a metric on its own, separate from general chargeback rate?

Top comments (2)

Collapse
 
indiainfranotes profile image
IndiaInfraNotes •

this is the quiet version of a settlement-receipt problem.

if the descriptor that hits the statement cannot map cleanly back to the exact merchant + rail event, disputes turn into vibes. India UPI learned the hard way that rails need queryable finality, not just a pretty success toast.

curious: do you store the pre-truncation descriptor as an immutable field next to the processor id?

Collapse
 
payneteasy profile image
Payneteasy •

Good framing. "Disputes turn into vibes" is pretty much what the reason codes looked like before we worked out the cause.

Yes, we store the descriptor we send as an immutable field next to the processor transaction id. What bit us is that this only covers half the problem. The string the cardholder actually sees is whatever the issuer produced after truncation, and it never comes back to us. So the stored value tells us what we sent, not what the customer read. That's why we moved the brand name into the first 12 characters: it's the only part we can count on being the same on both sides.

The UPI comparison fits. The descriptor is the one piece of the transaction the cardholder sees, and card rails give you no way to check how it was rendered. Keeping the original next to the processor id at least lets us answer "which charge is this?" quickly when a dispute comes in, even if we can't see what the bank showed.