We solved this by introducing a silent block. If the system notices unusual behavior (too many payment attempts per user, for example), it no longer sends the payment attempt to the provider. Instead, it idles for a second or two and then just fails with a generic “payment declined.” Most attackers don’t notice they’re being blocked and just assume all credit cards are bad.
Sounds like any per-user detection wouldn't have worked in this case.