---
title: "Refund through the provider with a key and · Agent Wranglers"
url: https://agentwranglers.se/playbook/payments/refunds-disputes-and-fraud/provider-refunds
updated: 2026-10-20
licence: "All rights reserved"
---

# Refund through the provider with a key and a reason

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Required
- Chapter: [Refunds, disputes and fraud](https://agentwranglers.se/playbook/payments/refunds-disputes-and-fraud)
- Rule: Issue every refund through the provider's API with an idempotency key, a reason and the person who asked. Track it until it succeeds or fails.
- How: Refund to the original payment method, unless the customer agrees to something else. Allow partial refunds only up to the amount not yet refunded, checked on the server inside a transaction. Refunds can fail, such as on a closed card, so handle the provider's refund failure event. Write a ledger row as in [payment ledger](https://agentwranglers.se/playbook/payments/payment-events-and-reconciliation/payment-ledger). Issue a credit note for every refund of an invoiced charge.
- Check: Send the same refund request twice at once. The provider shows one refund. A refund larger than the remaining amount is rejected before any provider call.

Example:

```js
async function refund(order, amountMinor, reason, staffId) {
  const remaining = order.paidMinor - order.refundedMinor
  if (amountMinor <= 0 || amountMinor > remaining) throw new Error(`Refund must be between 1 and ${remaining}`)
  return stripe.refunds.create(
    { payment_intent: order.paymentId, amount: amountMinor, metadata: { orderId: order.id, reason, staffId } },
    { idempotencyKey: `refund-${order.id}-${order.refundCount + 1}` },
  )
}
```

```python
def refund(order, amount_minor: int, reason: str, staff_id: str):
    remaining = order.paid_minor - order.refunded_minor
    if not 0 < amount_minor <= remaining:
        raise ValueError(f"Refund must be between 1 and {remaining}")
    return stripe.Refund.create(
        payment_intent=order.payment_id,
        amount=amount_minor,
        metadata={"order_id": order.id, "reason": reason, "staff_id": staff_id},
        idempotency_key=f"refund-{order.id}-{order.refund_count + 1}",
    )
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers payments playbook: https://agentwranglers.se/playbook/payments/refunds-disputes-and-fraud/provider-refunds`.

- Next: [Refund EU withdrawals within 14 days](https://agentwranglers.se/playbook/payments/refunds-disputes-and-fraud/withdrawal-refund-deadline)
- [Refunds, disputes and fraud](https://agentwranglers.se/playbook/payments/refunds-disputes-and-fraud)
