Skip to content

Payment Code Your Agent Wrote

Payment code your agent wrote is AI-generated payment integration code that appears correct at the provider SDK call but is unsafe around the application handler. The important review target is the local trust boundary: authentication, authorisation, server-side amounts, account ownership, webhook verification, idempotency, and state transitions.

This matters because payment provider examples make the visible call look deceptively complete. An agent can create a checkout session, payment intent, transfer, or payout with plausible parameters, while leaving the route callable by the wrong user or fed by untrusted request data. The bug is often not that the provider rejects the call. It is that the application asked for the wrong thing and treated the result as authority.

The concrete audit starts at the handler, not the SDK line. For each route and webhook, ask who can invoke it, what identifiers and amounts come from the client, and what must be reloaded from the database. The server should derive the payable amount, order, customer, destination account, and allowed transition from local state. Webhooks must be signature-checked, matched to expected objects, and processed so repeated delivery does not duplicate fulfilment.

The trade-off is more state and more boring code. You cannot simply pass through a request body to the provider and mark an order paid after a redirect. You need durable records, explicit ownership checks, event processing, and careful retry behaviour. It depends on the product flow, but the general rule is stable: browser success pages are not proof of payment, and provider acceptance is not proof of business correctness.

Engineers meet this in generated pull requests, editor assistants, scaffolded routes, and snippets copied from provider documentation. The provider call may be idiomatic, so review diffs around middleware, database reads, webhook handlers, and fulfilment jobs. The common failure mode is broken access control hidden beside valid payment API usage: one user can affect another user's order, amount, connected account, payout, or fulfilment state.

Common questions

Why is the provider call not the main thing to audit?
The provider validates its own API contract, such as whether the request shape and credentials are acceptable. It does not know your product rules. It cannot infer that a user owns an order, that an amount was computed correctly, or that a destination account belongs to the right seller. Those checks belong in your application.
What should never be trusted from the client?
Treat client-supplied user identifiers, order identifiers, prices, discounts, account destinations, fulfilment flags, and payment status as untrusted. The client can name what it is trying to do, but the server must reload authoritative records, check ownership, compute amounts, and decide whether the requested payment or payout is allowed.
When should fulfilment or payout state change?
Usually after a verified provider event has been received and reconciled with local database state, not merely after a browser redirect or a client callback. The handler should confirm the event signature, check the referenced payment object, ensure the transition is expected, and make processing idempotent so retries do not repeat the business action.
What is the most common misunderstanding?
The common misunderstanding is that a successful payment object means the integration is safe. It only means the provider accepted a request. The application still has to prove that the right actor initiated it, the right amount was charged, the right account receives funds, and the resulting local state change is legitimate.