Skip to main content

POST /api/payments/refund

Creates a refund for a previously successful charge. You can refund the full amount or a partial amount. Refunds are processed immediately and the funds are returned to the customer’s original payment method.
Refunds require a charge ID (starting with ch_), not a payment intent ID. You can get the charge ID from the latest_charge field of a payment intent or from the charges.data array.

Request Body

chargeId
string
required
The Stripe charge ID to refund. Must be a valid Stripe charge ID starting with ch_.Example: ch_3OJxRe2eZvKYlo2C0ABC5678
amount
number
The amount to refund in the original currency unit (e.g., euros, not cents). If not provided, the full charge amount will be refunded.The API will multiply this by 100 internally to convert to cents.Example: 50.00 to refund €50.00
reason
string
The reason for the refund. Must be one of:
  • duplicate - The charge was a duplicate
  • fraudulent - The charge was fraudulent
  • requested_by_customer - The customer requested a refund
If not provided, Stripe will not store a specific reason.

Response

status
boolean
Indicates if the request was successful
message
string
Human-readable message describing the resultExample: Reembolso creado correctamente
data
object
The Stripe Refund object

Success Response

{
  "status": true,
  "message": "Reembolso creado correctamente",
  "data": {
    "id": "re_3OJxRe2eZvKYlo2C0XYZ9999",
    "object": "refund",
    "amount": 100000,
    "charge": "ch_3OJxRe2eZvKYlo2C0ABC5678",
    "currency": "eur",
    "payment_intent": "pi_3OJxRe2eZvKYlo2C0XYZ1234",
    "reason": null,
    "status": "succeeded",
    "created": 1672617600
  }
}

Error Responses

{
  "status": false,
  "message": "chargeId es requerido",
  "code": null
}

Code Examples

curl -X POST https://api.example.com/api/payments/refund \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "chargeId": "ch_3OJxRe2eZvKYlo2C0ABC5678",
    "reason": "requested_by_customer"
  }'

Refund Types

Full Refund

Omit the amount parameter to refund the entire charge amount:
{
  "chargeId": "ch_3OJxRe2eZvKYlo2C0ABC5678",
  "reason": "duplicate"
}

Partial Refund

Specify an amount less than the original charge to refund only part of it:
{
  "chargeId": "ch_3OJxRe2eZvKYlo2C0ABC5678",
  "amount": 25.50,  // Refund €25.50 out of €100.00 charge
  "reason": "requested_by_customer"
}
The amount field is in the original currency unit (euros), not cents. The API converts it to cents internally. For a €50.00 refund, pass 50.00, not 5000.

Refund Reasons

Use these reason codes to help track why refunds are issued:
  • duplicate - Use when the same charge was processed multiple times
  • fraudulent - Use when the charge is suspected or confirmed as fraudulent
  • requested_by_customer - Use when the customer asks for a refund
const REFUND_REASONS = {
  DUPLICATE: 'duplicate',
  FRAUDULENT: 'fraudulent',
  CUSTOMER_REQUEST: 'requested_by_customer'
};

// Usage
await refundPayment(chargeId, REFUND_REASONS.CUSTOMER_REQUEST);

Getting the Charge ID

If you only have a payment intent ID, retrieve the charge ID first:
const getChargeIdFromPaymentIntent = async (paymentIntentId) => {
  const response = await fetch(`/api/payments/${paymentIntentId}`);
  const { data } = await response.json();
  
  // Option 1: Use latest_charge
  return data.latest_charge;
  
  // Option 2: Use first charge from charges array
  // return data.charges.data[0].id;
};

// Then refund
const chargeId = await getChargeIdFromPaymentIntent('pi_3OJxRe2eZvKYlo2C0XYZ1234');
await refundPayment(chargeId);
You can issue multiple partial refunds against the same charge, up to the total charge amount. To view all refunds for a payment intent, use the Get Refunds endpoint.

Build docs developers (and LLMs) love