Payments have a lifecycle, and a lifecycle with scattered update(status: ...) calls will eventually corrupt itself. I used AASM to turn our payment status handling into an explicit state machine, then added row locking, idempotency, and a transition log to make it hold up under retries and duplicate events. Here is the problem, the approach, and the code.
The problem: failures that kept coming back
I worked on a Rails platform that handled recurring payments for renewals across multiple regions. Customers paid through bank (ACH) transfers and cards, and every payment had to stay consistent with a payment provider and an external CRM.
The symptoms were familiar to anyone who has maintained a payment flow:
• Payments stuck in states that made no sense
• The same payment processed twice after a retry
• Payment summaries that didn't match what had actually happened
• Bugs that we patched, only to see a close cousin of them appear a few weeks later Each fix addressed one symptom.…