Model refund states explicitly in your database: PENDING, PROCESSING, SUCCEEDED, FAILED, and CANCELLED; the initial state on API submission is PENDING, not SUCCEEDED — most PSPs process refunds asynchronously.
Submit the refund via the PSP API with the original charge/payment ID and the refund amount in minor units; for partial refunds, submit an amount less than the original charge — confirm the PSP supports partial refunds on the original payment method (some wallets and BNPL methods do not).
Store the PSP-assigned refund ID alongside your internal refund record; use this ID for all subsequent status lookups and to correlate webhook events.
Listen for refund webhooks: Stripe emits 'charge.refund.updated', PayPal emits 'PAYMENT.CAPTURE.REFUNDED', etc.; update your local state on each event rather than assuming the refund succeeded immediately after the API call.
Handle FAILED refunds: failures occur when the original card has been cancelled, the account closed, or the refund window has expired; in these cases, issue the refund via alternative means (bank transfer, store credit) and update your refund record with 'FAILED' status and reason.
For partial refund tracking, store each partial refund as a separate record linked to the original payment; sum refunded amounts and compare to the original charge amount to determine remaining refundable balance before allowing further partial refunds.
Known gotchas
Treating a successful refund API response as immediate confirmation is wrong for most PSPs — the response confirms the refund was initiated, not completed; funds may not reach the customer for 5–10 business days and the PSP may still fail the refund after the initial success response.
Exceeding the original charge amount across multiple partial refunds is rejected by PSPs with a 4xx error; maintain a running total of refunded amounts in your database and validate before submitting each partial refund.
Some payment methods (certain prepaid cards, expired cards, closed accounts) cannot receive refunds electronically — PSPs return a specific error code for this; your refund workflow must handle this case with a manual fallback path rather than treating it as a transient error to retry.
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp