High Wire Payments

Refunds

Refund a captured payment in full or in part, read the refund's own status, and know what a dispute does to refunds.

POST /v1/payments/{id}/refund

Refunds a captured payment. A refund may be full or partial, and several partial refunds are allowed on the same payment as long as their sum does not exceed the captured amount. Accounts limited to refunds and voids may call this route.

To cancel a payment that was not captured yet, void it instead.

Eligibility

  • The payment has a successful capture: it is CAPTURED, PARTIALLY_REFUNDED, or DISPUTED with a dispute that does not block (see Disputes). Anything else answers 404.
  • No other refund of the same payment is in progress.
  • amount, when given, is at least 1 and no more than the remaining refundable balance. Omit it to refund the whole remaining balance.
  • Your daily refund limit, if one is configured, is not exceeded. See Account limits.

Request

Idempotency-Key: honoured. The 409 for a disputed payment is not stored under the key.

FieldTypeRequiredRules
amountintegernoCents to refund, at least 1 and at most the remaining refundable balance. Omit for a full refund of the remaining balance.
reasonstringnoFree text recorded against the refund, at most 255 characters. It is returned on the payment's history. Never put card numbers, security codes or other sensitive data in it.
# Full refund of the remaining balance
curl -X POST https://api-payments-staging.highwirepayments.com/v1/payments/01928e3e-7b6c-7c8e-9b3a-5b9f8b9b9b9b/refund \
  -u "YOUR_API_KEY:YOUR_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0193c30a-1f2e-7b6c-8d9a-0e1f2a3b4c5d" \
  -d '{}'

# Partial refund with a reason
curl -X POST https://api-payments-staging.highwirepayments.com/v1/payments/01928e3e-7b6c-7c8e-9b3a-5b9f8b9b9b9b/refund \
  -u "YOUR_API_KEY:YOUR_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0193c30b-7a8b-7c9d-8e0f-1a2b3c4d5e6f" \
  -d '{
    "amount": 500,
    "reason": "customer request"
  }'

An empty body ({}) is a full refund; the Content-Type header is still required.

Response

201 Created:

{
  "refundId": "01928e55-3c4d-7c8e-8b1a-fedcba987654",
  "paymentId": "01928e3e-7b6c-7c8e-9b3a-5b9f8b9b9b9b",
  "status": "SUCCEEDED",
  "amount": 500,
  "remainingRefundableAmount": 6700,
  "cardNumber": "555555xxxxxxxx4444"
}
FieldTypeNotes
refundIdstringThe refund's own id. It appears in the payment's transactions with object: "refund".
paymentIdstringThe payment that was refunded.
statusenumThe refund's status, not the payment's. SUCCEEDED: the money is on its way back. PENDING: the processor accepted the refund and is still settling it; it resolves to SUCCEEDED, FAILED or CANCELED asynchronously, visible on the payment's history.
amountintegerCents refunded by this call.
remainingRefundableAmountintegerCents still available for further refunds, computed as if this refund succeeds.
cardNumberstring or nullMasked card number of the payment. null for wallet payments, which carry no card number.

The payment's own status moves only when a refund succeeds: to PARTIALLY_REFUNDED, or to REFUNDED once the refunded total reaches the captured amount. A DISPUTED payment keeps that status; the refund is recorded on its history. See Payment lifecycle.

StatusBodyWhen
201The response above.Refunded, or accepted and settling (PENDING).
400Validation erroramount is not a positive integer, or reason is too long.
400Plain errorA refund is already in progress for this payment, Payment has already been fully refunded, or amount exceeds remaining refundable balance (6700).
400Refund declineThe processor refused the refund, or Amount exceeds daily refund limit.
404Plain errorPayment not found or not refundable: <id>: unknown, another account's, or a payment without a successful capture (AUTHORIZED, DECLINED, VOIDED, PENDING). A malformed id answers 404 too.
409Reason-coded conflictreason: "PAYMENT_DISPUTED", see below; or (without reason) an Idempotency-Key in flight.
422Plain errorThe key was reused with a different body.
401Plain errorInvalid credentials, or an inactive account.
500Plain errorThe refund was accepted but could not be finalized yet: do not retry; poll the payment.

Disputes

A cardholder can dispute a payment with their bank. The payment becomes DISPUTED, permanently. While the dispute still holds the funds, a refund would pay the cardholder twice, so it is refused:

{
  "statusCode": 409,
  "message": "Payment has an open or lost dispute and cannot be refunded.",
  "reason": "PAYMENT_DISPUTED"
}
  • A chargeback, or a later stage, that is open or was lost blocks refunds.
  • An inquiry (the bank asking for information, no funds moved) does not block, and neither does a dispute that was won or closed.
  • The 409 is not stored under the Idempotency-Key: the same key can be sent again once the dispute is won.

On this page