AI-written code that touches charges needs financial correctness controls: environment separation, money-path tests, idempotency and daily reconciliation.
Key takeaways
- Security asks who got in. Financial correctness asks whether the amount, the currency and the number of times were right. Two questions, two sets of controls.
- The common failure in payment code is not a breach, it is a double charge, and it almost always comes from a retry sent without an idempotency key.
- A test environment holding a production key is not a test environment. Separation is enforced in configuration, not by discipline.
- Daily reconciliation is the only control that catches silence. A transaction that failed quietly raises no error, only a gap.
- An audit trail with no id linking request, provider transaction and accounting record cannot answer a question from an accountant.
Code written with AI that touches charges, refunds or transfers needs five controls that have nothing to do with security: real environment separation, a test suite covering the money path, idempotency on every financial action, daily reconciliation against the provider, and a recorded human approval before it ships.
Why is financial correctness a different problem from security?
Because it breaks even when nobody is attacking. A double charge, a currency that switched, a rounding rule that always falls one way, a refund recorded twice: these are business-logic bugs occurring in entirely legitimate traffic. A good security review will not catch any of them, which is why the two reviews complement rather than replace each other. The other side, the one that does ask who got in, is covered in security risks in AI-generated code.
There is also a behavioural difference in the generated code. Code produced quickly gravitates to the successful path: a payment that clears, one customer, a stable network. The real money path is made mostly of the other cases, and those are precisely what does not get written unless it is asked for explicitly.
Environment separation: what test mode has to mean in practice
- Production keys exist neither on the development machine nor in any file tracked in git.
- The environment is selected from one environment variable, not a flag in code somebody can forget.
- The test database is entirely separate, including its backups and its queues.
- Real email sending and real charging are blocked in the test environment, not merely unused.
- Copying production data into test passes through a masking step for customer detail.
- The startup log states plainly which environment it is running in, on the first line.
The simple rule: if a scenario exists in which a local run can charge a real card, separation does not exist. General background on the split is in staging versus production environments.
What does a money-path test suite have to cover?
The important test here is not "the payment succeeded" but "what happened when it did not". The case table below is the minimum, and every row in it represents a bug that has already happened to somebody.
| Case | What is tested | What must happen |
|---|---|---|
| Successful charge | Amount, currency, record | Exactly one record, identical amount |
| Provider-side failure | Error handling | No success record, an attempt record |
| Response lost in transit | Retry behaviour | Same key, not a second charge |
| Two fast clicks | Duplicate prevention | One charge from two requests |
| Partial refund | Balance arithmetic | Balance matches the refunded amount |
| Different currency | Conversion and rounding | No implicit conversion, no double rounding |
| Status callback delivered twice | Provider webhook handling | One update, not two |
| Transaction left pending | Cleanup process | Explicit closure or an alert |
The middle three rows are the ones fast-written code almost always misses, because each of them requires thinking about what happens between the request and the response.
Idempotency and the double charge
Idempotency means a request sent twice produces one result. In payment terms: the customer is charged once even if the network swallowed the response and the system tried again.
- Generate a unique key for the financial action before the first call, not during the retry.
- Store the key on the order record, so it survives a server restart.
- Send that same key on every retry of that exact same action.
- Never reuse a key for a different action, however similar it looks.
- Treat an "already processed" response as success rather than as an error.
- Cover this path with an automated test that simulates a lost response.
Payment providers document the mechanism explicitly, and it is still absent from quickly generated code, because the successful flow never needs it. That is the definition of a control: something redundant right up to the moment it is the only thing preventing damage.
Daily reconciliation: the control that catches what stayed quiet
Reconciliation is a daily comparison across three sources: what your system recorded, what the provider reports, and what the bookkeeping holds. A gap counts as a finding even when no log contains an error.
- Count transactions, not only totals. Two errors can cancel out into a correct sum.
- Match on transaction id, never on amount plus date.
- Separate a timing gap, such as a business-day offset, from a real one.
- Run the comparison every day, including days on which nothing happened.
- Alert on a gap rather than producing a report somebody is meant to open.
- Store the reconciliation result, so it can be revisited six months later.
A transaction that failed quietly produces no error and no alert. It produces only a gap, which makes reconciliation the only control that will find it. The comparison methods themselves are the same ones described in card transaction reconciliation.
Human approval and the audit trail
- Any change touching amounts, currency or rounding gets read by a person, even when the tests pass.
- The release is staged, for example a small share of traffic, before it is opened fully.
- Every financial action stores a request id, the provider's transaction id and the accounting record id.
- The log states who approved, which version, and when it went live.
- A written rollback procedure exists, including what to do about charges already made.
The third item is the one that decides matters when an accountant asks a question. Those three ids are what turns "what happened in this transaction" into a single query instead of an investigation, and that holds whether the code was written by a person or by a model.
Sources
Frequently asked questions
Are automated tests enough for code that handles money?
They are necessary and not sufficient. Tests cover the cases somebody thought of, and daily reconciliation catches the ones nobody thought of. The two controls operate at different times: tests before release, reconciliation every day after it, and each finds a different class of gap.
What do you do when a double charge appears in production?
Stop the path that produced it, identify every affected transaction by its key, and refund through the provider rather than by editing records by hand. Afterwards add a test case holding the exact scenario, so the same failure cannot recur quietly.
Does the payment provider not prevent double charges by itself?
The provider supplies the mechanism but only applies it when the code sends a unique key. Without a key, two identical requests are two legitimate transactions as far as the provider is concerned. Creating and storing the key is the calling system's responsibility, not the provider's.
What do you check first in AI-written payment code?
Check whether an idempotency key exists, whether the currency is set explicitly rather than assumed, and whether there is handling for a response that never returned. Those three items cover most financial failures in quickly written code, and all three are visible in a few minutes of reading.
Keep reading
Related service
Business Automation
I build custom automations that remove repetitive work end to end.
About the author
Yehonatan Saadia
Freelance automation, web & MVP developer
I'm Yehonatan Saadia, a senior developer who builds business automation, custom websites, and MVPs for small and mid-sized companies across the US, Europe, and Israel. These guides come from real client work, not theory.
Work with meHave a project like this?
Tell me what you're trying to automate or build. I reply within 24 business hours with a few targeted questions, then we walk through it on a free 30-minute call, with no commitment. You come away with a scope, a timeline and a fixed price - or a straight answer that it isn't worth building.
