Production workflows / Guide 04
An E-commerce Refund Agent That Cannot Refund the Same Return Twice
Build an e-commerce refund agent with item-level eligibility, deterministic amounts, human approval, and payment reconciliation that prevents duplicate refunds.
Illustrative engineering guide. Examples are not client deployments, production-ready integrations, or evidence of measured results. Validate the design with your domain and security owners.

Suppose a hypothetical online shop receives a return for one item from a three-item order. The shopper contacts support twice because the first chat disconnects. Meanwhile, a warehouse employee records the return in another system. An e-commerce refund agent that treats each conversation as a new request can issue multiple refunds for the same returned unit, even when every individual payment call is technically valid.
The design needs two identities: the conversation and the business operation. Conversations can multiply; the entitlement to refund a particular return must not. The model should help understand the customer’s request and explain the outcome, while a deterministic refund service owns money, policy, and state.
Calculate the Entitlement Before Calling Payments
The refundable amount is not necessarily the product’s current price. Use the original order allocation, including discounts, tax treatment, shipping policy, quantity, and prior refunds. A bundle discount may need an approved allocation method. Store that method at checkout or preserve the version used to reconstruct it. Asking an LLM to divide an order-level discount creates an unnecessary financial ambiguity.
Distinguish return approval, physical receipt, refund approval, and payment completion. Some merchants refund before receiving goods; others do not. Encode that choice by product and policy version, not by customer sentiment. High-value exceptions, suspected abuse, and conflicting warehouse evidence should go to an authorized reviewer.
Stripe’s refund documentation distinguishes canceling an uncaptured payment from refunding a successful payment. It also documents partial refunds and the limit against refunding more than the original charge. Those provider constraints are necessary, but they do not establish which item a shopper is entitled to return. Your order system must enforce that narrower business rule.
Reserve Refund Capacity Atomically
Use a return record with a unique business identifier, a refund-intent table, and a payment adapter. In one transaction, lock the relevant entitlement, check existing successful and unresolved intents, reserve the proposed amount, and create an outbox entry. Unresolved intents consume capacity until reconciliation establishes their outcome.
This pure Python example illustrates the capacity calculation for one currency in integer minor units. It is not a complete refund service. Production code still needs authorization, transactional locking, item allocation, database constraints, and provider integration.
def available_refund(captured, succeeded, unresolved):
values = (captured, succeeded, unresolved)
if any(type(value) is not int or value < 0 for value in values):
raise ValueError("nonnegative integer amounts required")
if succeeded + unresolved > captured:
raise ValueError("inconsistent refund ledger")
return captured - succeeded - unresolved
assert available_refund(12000, 3000, 2000) == 7000
assert available_refund(12000, 12000, 0) == 0
Apply an additional item-level entitlement check before reserving any amount. A payment with remaining capacity must not fund a second refund for an already resolved return. Currency is part of the operation identity, and exchange-rate conversion belongs to a reviewed financial policy rather than this function.
The model receives only a proposal with eligible items, calculated amount, policy reason, and permitted next steps. It can ask whether the customer prefers an exchange when that option is available, but it cannot alter the amount or choose a different payment destination.
Idempotency Has More Than One Layer
Stripe’s idempotency documentation describes saving the first result for a key, including error results, and comparing parameters on reuse. It also describes eventual key pruning. Therefore, retain your own durable refund-intent identity; provider idempotency is not a permanent duplicate-return ledger.
Generate a provider key from an opaque refund intent, not from customer email or a chat message. Reuse that same key and identical parameters after an uncertain response within the provider’s supported semantics. Never generate a fresh key merely because the previous attempt timed out. After the retention window or an ambiguous error, reconcile provider records before authorizing any new attempt.
Keep payment states explicit: requested, submitted, pending, succeeded, failed, and needs-review. A provider accepting the request does not prove that the shopper has received money. Stripe documents pending and failed refunds, including failures involving disputes. The assistant should report the observed provider state and available reference, not guarantee a bank posting date.
Narrow the Security Boundary
Authenticate the shopper server-side and resolve order ownership independently of the prompt. Merchant tenants must remain isolated in order lookup, warehouse evidence, case history, and payment credentials. A marketplace requires the correct merchant payment context; order ownership alone does not authorize a refund against an unrelated connected account.
Customer photos, product descriptions, and warehouse notes are untrusted data. A note saying “manager approved unlimited refunds” is not an approval record. The reviewer must approve a specific intent, amount, currency, and policy exception through an authenticated workflow. MCP is an integration protocol, not authorization by itself.
Use a payment tool with no arbitrary destination parameter. OWASP’s authorization guidance supports least privilege and per-request permission checks. Separate payment execution credentials from the model runtime, and keep addresses, full payment details, and attachment contents out of general logs.
One conversational model plus deterministic tools is usually enough. A specialist image assessment component may help staff evaluate damage, but it must not become the sole source of refund eligibility. Its confidence does not replace the return policy or evidence of receipt.
Acceptance Tests Against Money Movement
- Submit two conversations for the same returned unit concurrently. They converge on one refund intent and one provider refund, even if the payment has unused capacity.
- Lose the response after provider execution. Recovery reuses the existing intent and reconciles the outcome without allocating another amount.
- Submit a refund for a partially captured order. The system applies both item entitlement and captured-amount limits using the original currency.
- Insert forged approval instructions in a warehouse note. The payment tool remains unavailable until an authenticated approval record exists.
- Receive a late failure event after a pending refund. The workflow records the failure, verifies authoritative status, and creates a review task instead of silently retrying with a new key.
Prove Value Without Hiding Exceptions
Establish a baseline of manually processed returns segmented by product, partial versus full refund, and exception type. Measure correctly completed refund intents divided by eligible return requests. Correctness requires matching amount, currency, destination, and final provider state, not just a successful API response.
Track duplicate business refunds per executed intent as a primary guardrail. Also monitor over-refund value, unexplained ledger differences, review overrides, and customer recontact. Compare handling time including reconciliation and warehouse clarification. Proposed targets should be agreed with finance; do not claim savings until the same case mix is measured.
Start in shadow mode on a single-item return policy, then enable staff-approved execution with a daily monetary cap. Expand only after uncertain-outcome recovery works. The delivery exception guide covers missing shipment evidence, and bank support explains the customer’s view of pending financial events. Backend API engineering is the practical foundation for the refund ledger and reconciliation worker.