> ## Documentation Index
> Fetch the complete documentation index at: https://primer.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Refund payments

export const TxnTag = ({status}) => {
  const getStyles = status => {
    const baseStyle = {
      display: 'inline-block',
      padding: '4px 8px',
      borderRadius: '4px',
      fontSize: '13px',
      fontWeight: '400'
    };
    switch (status) {
      case "PENDING":
        return {
          ...baseStyle,
          backgroundColor: '#ececec',
          color: '#9f9f9f'
        };
      case "AUTHORIZED":
        return {
          ...baseStyle,
          backgroundColor: '#ecfdf5',
          color: '#047857'
        };
      case "SETTLED":
      case "PARTIALLY_SETTLED":
        return {
          ...baseStyle,
          backgroundColor: '#ecfdf5',
          color: '#047857'
        };
      case "DECLINED":
      case "FAILED":
        return {
          ...baseStyle,
          backgroundColor: '#fef2f2',
          color: '#dc2626'
        };
      case "CANCELLED":
        return {
          ...baseStyle,
          backgroundColor: '#fefce8',
          color: '#d78203'
        };
      case "SETTLING":
        return {
          ...baseStyle,
          backgroundColor: '#e0f2fe',
          color: '#0c4a6e'
        };
      default:
        return {
          ...baseStyle,
          backgroundColor: '#f3f4f6',
          color: '#374151'
        };
    }
  };
  return <span style={getStyles(status)}>{status}</span>;
};

export const ApiEndpoint = ({method, path}) => {
  const getMethodColor = method => {
    switch (method?.toUpperCase()) {
      case "GET":
        return "#0285c7";
      case "POST":
        return "#008d71";
      case "PUT":
        return "#16a34a";
      case "PATCH":
        return "#808080";
      case "DELETE":
        return "#dc2626";
      default:
        return "#6b7280";
    }
  };
  const getEndpointUrl = (method, path) => {
    const endpoints = {
      "POST /client-session": "/api-reference/v2.4/api-reference/client-session-api/create-a-client-session",
      "PATCH /client-session": "/api-reference/v2.4/api-reference/client-session-api/update-client-session",
      "POST /payments": "/api-reference/v2.4/api-reference/payments-api/create-a-payment",
      "POST /payments/<YOUR-PAYMENT-ID>/resume": "/api-reference/v2.4/api-reference/payments-api/resume-payment",
      "POST /payments/<YOUR-PAYMENT-ID>/adjust-authorization": "/api-reference/v2.4/api-reference/payments-api/adjust-authorized-amount",
      "POST /payments/<PAYMENT_ID>/cancel": "/api-reference/v2.4/api-reference/payments-api/cancel-payment",
      "POST /payments/<PAYMENT_ID>/refund": "/api-reference/v2.4/api-reference/payments-api/refund-payment",
      "GET /payments/<PAYMENT_ID>": "/api-reference/v2.4/api-reference/payments-api/get-a-payment",
      "GET /payment-instruments": "/api-reference/v2.4/api-reference/payment-methods-api/list-saved-payment-methods",
      "DELETE /payment-instruments/<PAYMENT_METHOD_TOKEN>": "/api-reference/v2.4/api-reference/payment-methods-api/delete-payment-method-payment-methods-token-delete"
    };
    const key = `${method?.toUpperCase()} ${path}`;
    return endpoints[key] || "#";
  };
  const getPrettyPath = (method, path) => {
    const pathMapping = {
      "POST /payments/<YOUR-PAYMENT-ID>/resume": "/payments/{paymentId}/resume",
      "POST /payments/<YOUR-PAYMENT-ID>/adjust-authorization": "/payments/{paymentId}/adjust-authorization",
      "POST /payments/<PAYMENT_ID>/cancel": "/payments/{paymentId}/cancel",
      "POST /payments/<PAYMENT_ID>/refund": "/payments/{paymentId}/refund",
      "GET /payments/<PAYMENT_ID>": "/payments/{paymentId}",
      "DELETE /payment-instruments/<PAYMENT_METHOD_TOKEN>": "/payment-instruments/{paymentMethodToken}"
    };
    const key = `${method?.toUpperCase()} ${path}`;
    return pathMapping[key] || path;
  };
  const methodColor = getMethodColor(method);
  const url = getEndpointUrl(method, path);
  const displayPath = getPrettyPath(method, path);
  return <div style={{
    display: "inline-flex",
    alignItems: "center",
    cursor: "pointer",
    margin: "2px 0",
    padding: "4px",
    paddingRight: "6px",
    background: "rgb(245, 245, 245)",
    borderRadius: "6px",
    whiteSpace: "nowrap",
    textDecoration: "none"
  }}>
      <a href={url} target="_blank" rel="noopener noreferrer" style={{
    textDecoration: "none",
    border: "none"
  }}>
        <span style={{
    display: "inline-block",
    whiteSpace: "nowrap",
    padding: "0 8px",
    fontSize: "13px",
    lineHeight: "23px",
    borderRadius: "6px",
    fontWeight: 400,
    backgroundColor: methodColor,
    color: "white",
    border: "none"
  }}>
          {method.toUpperCase()}
        </span>
        <code style={{
    padding: "0",
    marginLeft: "4px",
    fontFamily: "monospace",
    color: methodColor,
    border: "none",
    textDecorationColor: methodColor
  }}>
          {displayPath}
        </code>
      </a>
    </div>;
};

## How to refund a payment

Primer enables you to fully or partially refund a settled payment across all your processors in a unified way.

In order to refund a payment, its status must be <TxnTag status="SETTLED" />. Once refunded, the payment will continue to have:

* `status` = <TxnTag status="SETTLED" />
* `amount` = original settled amount

but the payment object will be updated to show the refund details. Specifically:

* the processor refund transaction will be part of the `transactions` list
* `processor.amountRefunded` will be set to the total refunded amount

Below is an example payment object that has been refunded:

```json JSON theme={"dark"}
{
  "id": "ehFTWmh3",
  "date": "2022-12-05T12:35:46.204690",
  "amount": 11000,
  "currencyCode": "GBP",
  "orderId": "39526",
  "status": "SETTLED",
  "paymentMethod": {
    "paymentMethodToken": "Q-FcqfYCSa2hh2eMqZBjfHwxNjcwMjQzNzQ1",
    "isVaulted": false,
    "analyticsId": "fm1_-IOlVoK9eY09EP6-YV8w",
    "paymentMethodType": "PAYMENT_CARD",
    "paymentMethodData": {
      "last4Digits": "1111",
      "first6Digits": "411111",
      "expirationMonth": "12",
      "expirationYear": "2025",
      "cardholderName": "Joe Bloggs",
      "network": "Visa",
      "isNetworkTokenized": false,
      "binData": {
        "network": "VISA",
        "issuerCountryCode": "US",
        "issuerName": "JPMORGAN CHASE BANK, N.A.",
        "regionalRestriction": "UNKNOWN",
        "accountNumberType": "UNKNOWN",
        "accountFundingType": "UNKNOWN",
        "prepaidReloadableIndicator": "NOT_APPLICABLE",
        "productUsageType": "UNKNOWN",
        "productCode": "UNKNOWN",
        "productName": "UNKNOWN"
      }
    },
    "threeDSecureAuthentication": {
      "responseCode": "NOT_PERFORMED"
    }
  },
  "processor": {
    "name": "STRIPE",
    "processorMerchantId": "acct_1MBdnMJdRxA3z8D5",
    "amountCaptured": 11000,
    "amountRefunded": 11000
  },
  "transactions": [
    {
      "date": "2022-12-05T12:35:47.078285",
      "amount": 11000,
      "currencyCode": "GBP",
      "transactionType": "SALE",
      "processorTransactionId": "pi_3MBdtgJdRxA3z8D50HwTkJ7A",
      "processorName": "STRIPE",
      "processorMerchantId": "acct_1MBdnMJdRxA3z8D5",
      "processorStatus": "SETTLED"
    },
    {
      "date": "2022-12-06T21:55:02.830051",
      "amount": 11000,
      "currencyCode": "GBP",
      "transactionType": "REFUND",
      "processorTransactionId": "re_3MBdtgJdRxA3z8D50WBvqRLy",
      "processorName": "STRIPE",
      "processorMerchantId": "acct_1MBdnMJdRxA3z8D5",
      "processorStatus": "SETTLED"
    }
  ]
}
```

All refund calls are sent to the processor used in the authorization and capture request.

To check the refund status and details of a payment, you can use:

* the [Payment timeline](https://dashboard.primer.io/payments) in the Primer Dashboard
* payment webhooks to receive the refund complete event - see the [API reference](/api-reference/endpoints/v2.4/payment-webhooks/payment-refund) for more.
* the <ApiEndpoint method="GET" path="/payments/<PAYMENT_ID>" /> API call

Refund a payment with Primer in three ways:

<CardGroup cols={3}>
  <Card title="Payments timeline" icon="hourglass" href="#refund-a-payment-via-the-payments-timeline">
    Manually refund a payment from an easy-to-use dashboard.
  </Card>

  <Card title="Workflows" icon="desktop" href="#refund-a-payment-via-workflows">
    Automate sophisticated payment flows to refund your payments.
  </Card>

  <Card title="Payments API" icon="user" href="#refund-a-payment-via-payments-api">
    Programmatically refund your payments via API based on your business logic.
  </Card>
</CardGroup>

### Refund a payment via the Payments Timeline

<Note>
  This is useful to enable your customer support to provide refunds directly from within the dashboard.
</Note>

Manually refund a specific payment from the Payments Timeline in the Primer Dashboard.

Select the payment from the list view and navigate to the specific payment details page. If the payment has <TxnTag status="SETTLED" />, the **Refund** button in the top right corner will be enabled.

<Frame caption="Refund button enabled">![Refund button enabled](https://goat-assets.production.core.primer.io/marketing/payment-operations/external-docs/refund/can-refund-eg.png)</Frame>

Once the **Refund** button is selected, specify the amount you want to refund in the modal.

<Frame caption="Specify refund amount">![Specify refund amount](https://goat-assets.production.core.primer.io/marketing/payment-operations/external-docs/refund/refund-modal.png)</Frame>

After the refund is processed and complete, the refunded amount will appear next to the payment amount at the top of the payment details page.

<Frame caption="Refunded example">![Refunded example](https://goat-assets.production.core.primer.io/marketing/payment-operations/external-docs/refund/payment-refunded.png)</Frame>

Once the total settled amount is refunded, you will no longer be able to refund the payment and the **Refund** button will become disabled.

### Refund a payment via Workflows

Use Workflows to set up business logic on when and how to refund a payment - without writing a single line of code. Simply add the ["Refund payment" action](/workflows/apps/native-apps/primer-payments-app/refund-payment-action) from the Primer Payments app to a workflow.

Learn more [here](/workflows/apps/native-apps/primer-payments-app/refund-payment-action) about the Payments app and the "Refund payment" action.

<Note>
  Asynchronous refund considerations still apply to the "Refund payment" action
  in Workflows. It's recommended to use Primer's payment webhooks to confirm
  when the payment is refunded.
</Note>

### Refund a payment via Payments API

Refund a payment using the Payments API - see the [API reference](/api-reference/v2.4/api-reference/payments-api/refund-a-payment) for the details.

```bash BASH theme={"dark"}
curl --request POST \
     --url https://api.sandbox.primer.io/payments/<YOUR_PAYMENT_ID>/refund \
     --header 'X-API-KEY: <YOUR_API_KEY>' \
     --header 'X-API-VERSION: 2.2' \
     --header 'X-Idempotency-Key: <YOUR_IDEMPOTENCY_KEY>'
```

Include the Primer payment ID in the request, which you will have received as part of the payment creation and authorization.

The payload sent in the refund request is completely optional and can be used to partially refund the payment. See the [section](#partial-refund) below to learn more about partial refunds. You can also provide a `reason` input that can be used for your own records.

## Handle asynchronous refund

Refunds are usually processed asynchronously. Once the refund request has been made, the refund, and movement of funds back to the customer, will occur and could take days.

As a result, it’s recommended to set up the refund webhook notification event. Primer notifies you with a `PAYMENT.REFUND` webhook event when a refund request has been fully processed by a payment processor and the refund has reached a final state.

To determine whether the refund was successful, check the most recent refund transaction in the `transactions` list. Below is an example of a processor refund transaction:

```json JSON theme={"dark"}
{
  "date": "2022-12-06T21:55:02.830051",
  "amount": 11000,
  "currencyCode": "GBP",
  "transactionType": "REFUND",
  "processorTransactionId": "re_3MBdtgJdRxA3z8D50WBvqRLy",
  "processorName": "STRIPE",
  "processorMerchantId": "acct_1MBdnMJdRxA3z8D5",
  "processorStatus": "SETTLED"
}
```

* If `transactionType` = `REFUND` and `processorStatus` = <TxnTag status="SETTLED" />, the refund was successful and the funds have been returned to the customer
* If `transactionType` = `REFUND` and `processorStatus` = <TxnTag status="FAILED" />, the refund was unsuccessful

Read more [here](/api-reference/get-started/configure-webhooks) on how to handle webhooks.

## Partial refund

<Note>
  Not all processors and payment methods support partial refunds. Please see the details of the specific processor or payment method to learn more about the capabilities supported.
</Note>

You can partially refund a payment by setting the optional `amount` input to less than the full settled amount.

For subsequent refund requests, the default amount is then calculated by `<amount settled> - <amount already refunded>`.

You will not be able to have partial refunds that sum to a greater amount than the settled amount.
