docs
FeaturesTransactions

Refunds

Issue a refund from the dashboard, read refund state across the dashboard, and understand fees and timing.

A RefundAPI returns money you captured from a customer back to their original payment method. The dashboard does not have a separate "Refunds" page. Instead, every refund lives on the payment it came from, and the running record of every refund is in the All activity tab on Transactions.

This page covers how to issue a refund from the payment detail page, when the dashboard hides the refund button, what the customer experiences, and where the refund shows up across the rest of the dashboard.

What you can do

Refund any captured payment

A Refund button sits on the detail page of any successful payment. Issue a full refund or, when the processor supports it, a partial one.

Tag a reason

Optionally label the refund as Requested by customer, Duplicate, or Fraudulent so you can correlate later.

Track every refund

Refunds show up in the payment's timeline, on the payment's status, in the Payment Breakdown math, and as their own row in All activity.

See the impact on your balance

Each refund is a balance entry that reduces your available balance. Customer lifetime spend is also adjusted automatically.

Issue a refund

Open the payment

In Transactions, find the payment you want to refund and click it. The detail page opens.

Click Refund

In the top right of the header, click Refund. A side sheet opens, titled Refund payment.

If you don't see a Refund button, see When the button is hidden or disabled below.

Set the amount

The amount field is prefilled with the Available to refund value at the bottom of the sheet (the original captured amount minus anything you've already refunded).

  • For a full refund, leave the amount as is.
  • For a partial refund, lower the value. Partial amounts must be greater than zero and at most the available-to-refund value.

If the payment processor doesn't support partial refunds, the field is locked at the full amount and the sheet shows "Only full refunds are supported for this payment processor".

(Optional) Pick a reason

The Reason select offers three values: Requested by customer, Duplicate charge, Fraudulent. Leave it blank if none apply. Reasons help when you filter or report on refunds later, but they don't change how the refund is processed.

Submit

Click the Refund button at the bottom of the sheet (the label shows the exact amount you're about to refund). On success the sheet closes and a "Refund processed successfully" toast confirms. The payment's status flips to Partially Refunded or Refunded and a refund entry appears at the top of the timeline.

When the button is hidden or disabled

The dashboard hides or disables the Refund button when a refund can't go through.

Hidden when the payment isn't in a refundable state:

ReasonWhy
Status is not Successful or Partially RefundedFailed, canceled, expired, and pending payments don't have captured funds to refund.
The payment is not yet capturedAuthorized but not captured: there's no charge to reverse yet.
The payment is already fully refundedNothing left to refund.
The payment method doesn't support refundsSome processors and methods route refunds out of band. The button is hidden when XPay can't issue the refund directly.

Disabled with a tooltip when you have permission gaps or a hold:

TooltipWhy
"You don't have permission to perform this action"Your role lacks refund-write permission. Ask an account admin to grant it.
"This charge is currently under an active risk hold..."A risk hold is on the funds. Contact support to release the hold before refunding.

If you submit and hit a server-side block, the dashboard surfaces a specific error toast. Common ones:

  • "This charge cannot be refunded in its current state."
  • "Refund amount is invalid or exceeds the refundable amount."
  • "Insufficient merchant balance to process this refund."

What happens after

Timing. Refunds take 7 to 14 days to land on the customer's statement. The dashboard reflects the refund immediately; the customer's bank takes longer.

Fees. XPay's fees on the original payment are not returned with the refund, but XPay does not charge an additional fee on top of the refund itself.

Destination. Refunds always go back to the original payment method. There is no way to redirect a refund to a different card or account.

Customer record. The customer's lifetime spend on their profile updates: Total Refunded goes up, Net Spend goes down. Total Spent (gross) stays as the historical record of what they've ever been charged.

Where refund state shows up

Across the dashboard, a refund is visible in seven places:

SurfaceWhat it shows
Payments tab → Status columnThe badge on the payment row flips to Refunded or Partially Refunded. Hover for the refunded amount.
Payments tab → Refunded Date columnWhen the column is on, this shows when the most recent refund was issued.
Payment detail → Header status badgeSame status badge on the detail page.
Payment detail → TimelineOne entry per refund (succeeded, pending, or failed) with the amount and reason.
Payment detail → Payment BreakdownA Refunded amount line appears in the math, reducing Net amount.
Transactions → All activity tabThe refund is its own balance entry, alongside its parent charge, fees, and any adjustments.
Customer profile → Insights and Customer DetailsTotal Refunded updates; Net Spend is recalculated.

If you also subscribe to webhooks, the refund fires refund.created, and refund.failed if the bank rejects it. See Refunds for the developer-side companion (status fields, the Refund object, and webhook handling).

Refund reasons

The dashboard offers three reason codes when you issue a refund. They're optional and free-form text isn't supported.

ReasonWhen to pick it
Requested by customerThe customer asked for the refund (returned a product, changed their mind, billing dispute resolved in their favor).
Duplicate chargeThe same payment went through twice and you're reversing the duplicate.
FraudulentYou believe the original payment was unauthorized or fraudulent.

Reasons are surfaced on the timeline entry and on the Refund object in API responses and webhook payloads.

Where to next

On this page