> For the complete documentation index, see [llms.txt](https://docs.verifone.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.verifone.com/psdk-latest/psdk-user-guide-and-training/psdk-user-guide/payment_functions/semi-integrated-payments/refund.md).

# Refund

A refund returns the funds from the merchant to the customer. There are two important types of refunds, linked and unlinked. Only one refund is performed at a time, unlike voids (see perform-void), though multiple refunds may be performed within the same session.

{% @plantuml/diagram content="            @startuml

```
        hide footbox

        participant POS order 20
        participant POI order 30
        actor Customer order 40
        |||
        POS ->> POI: Start session
        POS <<- POI: Session started
        loop While there are refunds for this order
        alt Linked Refund
            POS -> POS: Link refund payment to original payment
            POS -> POS: Optionally set amount to be refunded
        else Unlinked Refund
            POS -> POS: Create new refund payment
            POS -> POS: Set amount to be refunded
        end
        POS ->> POI: Process refund
        alt Linked Refund
            POI <-> Customer: Possibly request card presentation.
        else Unlinked Refund
            POI <-> Customer: Always request card presentation.
        end
        POS <<- POI: PaymentCompletedEvent
        POS -> POS: Handle refund event
        end
        POS ->> POI: End session
        POS <<- POI: Session ended
        |||
        @enduml" %}
```

### Creating a Linked Refund <a href="#creating-a-linked-refund" id="creating-a-linked-refund"></a>

A linked refund has reference information to the original payment, providing this information to the host and sometimes even allowing the refund to be processed without the customer presenting their card. Linked refunds are much less likely to be identified as possible fraud, and are universally supported by the payment hosts.

To link a refund, either set the App Specific Data from the original payment on a new Payment (see linking-payments), or less commonly, send the original Payment directly.

Setting the requested amount totals is optional for linked refunds. Leaving the requested amounts unset will refund the entire amount. The request totals should never be set higher than the original amount for a linked refund, this will generally cause the refund to be declined

{% hint style="info" %}
**Note:** It is mandatory to set the requested amount totals for linked refunds for the Verifone SCA regional solution. The Transaction Amount is a mandatory field for refunds in both SSI and UGP gateway protocol specifications.
{% endhint %}

### Creating an Unlinked Refund <a href="#creating-an-unlinked-refund" id="creating-an-unlinked-refund"></a>

An unlinked refund is a simple amount that is pushed from the merchant account into the customer’s account. Unlinked refunds are generally supported by the different hosts, but are not supported universally. Care should be taken when using unlinked refunds to make sure that it is supported by the merchant’s host. Simply create a new Payment for this type of refund and set the requested amount totals (Payment.setRequestedAmounts(totals)).

### Partial Refunds <a href="#partial-refunds" id="partial-refunds"></a>

Partial refunds may be performed by setting the requested amount totals (Payment.setRequestedAmountTotals(totals)) to a value below the original amount.

### Performing the Refund <a href="#performing-the-refund" id="performing-the-refund"></a>

Use the Payment for either the linked or unlinked refund, calling [transactionManager.processRefund(payment)](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#processrefund-payment). The [CommerceListener](https://docs.verifone.com/psdk-api-latest/android-java/index/commercelistener2) receives a [PaymentCompletedEvent](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentcompletedevent) with type [TransactionEvent.TRANSACTION\_PAYMENT\_COMPLETED](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionevent) once the refund is complete. Similarly to other payments, if the StatusCode returned from [PaymentCompletedEvent.getStatus()](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentcompletedevent#getstatus) is non-zero (non-SUCCESS), either [StatusCode.FAILED,](https://docs.verifone.com/psdk-api-latest/android-java/index/statuscode) or [StatusCode.CANCELLED,](https://docs.verifone.com/psdk-api-latest/android-java/index/statuscode) then an error occurred and the event will not necessarily contain more information besides the StatusCode and [PaymentCompletedEvent.getMessage()](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentcompletedevent#getmessage). If the StatusCode is [StatusCode.SUCCESS](https://docs.verifone.com/psdk-api-latest/android-java/index/statuscode), the refund [Payment](https://docs.verifone.com/psdk-api-latest/android-java/index/payment) is retrieved using `:event.getTransaction().getPayments().get(0)`.

{% tabs fullWidth="false" %}
{% tab title="Java" %}
{% code lineNumbers="true" fullWidth="true" %}

```java
@Override
public TransactionEventResponse handlePaymentCompletedEvent(PaymentCompletedEvent event) {
    if (TransactionEvent.TRANSACTION_ENDED.equals(event.getType())) {
    if (event.getStatus() == StatusCode.SUCCESS) {
        // The refund was successful.
        Payment payment = event.getTransaction().getPayments().get(0);
        // Information, such as the auth code, are available as well.
    } else if (event.getStatus() == StatusCode.CANCELLED) {
        // The refund was cancelled, either by the user or externally.
    } else {
        // The refund failed in some way.
        if (event.getTransaction() != null
                && event.getTransaction().getPayments() != null
                && !event.getTransaction().getPayments().isEmpty()) {
                Payment payment = event.getTransaction().getPayments().get(0);
                // The auth response text may contain information from the payment host with
                // more information regarding why it was not successful.
                payment.getAuthResponseText();
            }
        }
    }
    return event.generateTransactionEventResponse();
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
 fun handlePaymentCompletedEvent(event: PaymentCompletedEvent) : TransactionEventResponse {
    if (TransactionEvent.TRANSACTION_ENDED.equals(event.getType())) {
        if (event.getStatus() == StatusCode.SUCCESS) {
            // The refund was successful.
            val payment = event.getTransaction().getPayments().get(0)
            // Information, such as the auth code, are available as well.
        } else if (event.getStatus() == StatusCode.CANCELLED) {
            // The refund was cancelled, either by the user or externally.
        } else {
            // The refund failed in some way.
            if (event.getTransaction() != null
                && event.getTransaction().getPayments() != null
                && !event.getTransaction().getPayments().isEmpty()) {
                    val payment = event.getTransaction().getPayments().get(0)
                    // The auth response text may contain information from the payment host with
                    // more information regarding why it was not successful.
                    payment.getAuthResponseText()
                }
            }
        }
    }
    return event.generateTransactionEventResponse()
}
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
// Overridden from the VFICommerceListenerAdapter
override func handle(_ event: VFIPaymentCompletedEvent?) {
    if (event?.getType() == VFITransactionEventTRANSACTIONENDED) {
        if (event?.getStatus() == VFIStatusCodeSuccess) {
            let payment = event?.getTransaction()?.getPayments().first
        } else if (event?.getStatus() == VFIStatusCodeCancelled) {
            // the refund was cancelled, either by the user or externally.
        } else {
            // the refund failed in some way
            if let txn = event?.getTransaction(),
                let payment = txn.getPayments().first {
                // more information regarding why it was not successful.
                payment.getAuthResponseText()
            }
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="C#" %}

<pre class="language-csharp" data-line-numbers data-full-width="true"><code class="lang-csharp"><strong> private void HandlePaymentCompletedEvent(PaymentCompletedEvent event)
</strong>{
    if (event.Type.Value == TransactionEvent.TRANSACTION_ENDED)
    {
        if (event.Status == 0)
        {
            // The refund was successful.
            Payment payment = event.GetTransaction.Payments[0];
            // Information, such as the auth code, are available as well.
        }
        else if (event.Status == -11)
        {
            // The refund was cancelled, either by the user or externally.
        }
        else
        {
            // The refund failed in some way.
            if (event.GetTransaction != null
                &#x26;&#x26; event.GetTransaction.Payments != null
                &#x26;&#x26; event.GetTransaction.Payments.Count > 0)
            {
                Payment payment = event.GetTransaction.Payments[0];
                // The auth response text may contain information from the payment host with
                // more information regarding why it was not successful.
                payment.AuthResponseText;
            }
        }
    }
    return event.GenerateTransactionEventResponse();
}
</code></pre>

{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
class Listener: public verifone_sdk::CommerceListener {
public:
std::string getUniqueListenerId() override {
    return "ListenerId";
}

std::shared_ptr<verifone_sdk::PaymentCompletedEventResponse> handlePaymentCompletedEvent(const std::shared_ptr<verifone_sdk::PaymentCompletedEvent>& event) override {
    if (event->getType() == verifone_sdk::TransactionEvent::TRANSACTION_ENDED) {
    if (event->getStatus() == StatusCode::SUCCESS) {
        // Refund processed successfully
    } else if (event->getStatus() == StatusCode::CANCELLED) {
        // The refund was cancelled, either by the user or externally.
    } else {
        // Handle failure by examining the status code and message.
    }
    }
    return nullptr;
}

...
}

...
auto payment = verifone_sdk::Payment::create();
payment->setRequestedAmountTotals(amount_totals_to_refund);
payment->setPaymentId("payment-id-to-refund");
payment->setLocalPaymentId("localPaymentId");
//set app specific data for linking if necessary
payment->setAppSpecificData(app_specific_data)
psdk->getPaymentManager->processRefund(payment);
```

{% endcode %}
{% endtab %}

{% tab title=".NET" %}
{% code lineNumbers="true" %}

```aspnet
public void HandlePaymentCompletedEvent(PaymentCompletedEvent sdk_event)
{
    if (sdk_event.Type == TransactionEvent.TRANSACTION_ENDED)
    {
        if (sdk_event.Status == 0)
        {
            // The refund was successful.
            var payment = sdk_event.Transaction.Payments[0];
            // Information, such as the auth code, are available as well.
        }
        else if (sdk_event.Status == -11)
        {
            // The refund was cancelled, either by the user or externally.
        }
        else
        {
            // The refund failed in some way.
            if (sdk_event.Transaction != null
                && sdk_event.Transaction.Payments != null
                && sdk_event.Transaction.Payments.Count > 0)
            {
                Payment payment = sdk_event.Transaction.Payments[0];
                // The auth response text may contain information from the payment host with
                // more information regarding why it was not successful.
                String auth_response = payment.AuthResponseText;
            }
        }
    }
    sdk_event.GenerateTransactionEventResponse();
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

* [TransactionManager.processRefund(Payment)](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#processrefund-payment)
* [TransactionEvent.TRANSACTION\_ENDED](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionevent)
* [PaymentCompletedEvent](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentcompletedevent)
* [PaymentCompletedEvent.getStatus()](https://docs.verifone.com/psdk-api-latest/android-java/index/paymentcompletedevent#getstatus)
* [StatusCode](https://docs.verifone.com/psdk-api-latest/android-java/index/statuscode)
* [Transaction.getPayments()](https://docs.verifone.com/psdk-api-latest/android-java/index/transaction#getpayments)
* [Payment.getAuthCode()](https://docs.verifone.com/psdk-api-latest/android-java/index/payment#getauthcode)
* [Payment.getAuthResponseText()](https://docs.verifone.com/psdk-api-latest/android-java/index/payment#getauthresponsetext)
* [Create a Commerce Listener](/psdk-latest/psdk-user-guide-and-training/psdk-user-guide/pos_guide.md#create-a-commerce-listener)
* [start-session](https://docs.verifone.com/psdk-api-latest/android-java/index/transactionmanager#startsession2-transaction)

***

{% columns %}
{% column width="25%" %}

<figure><img src="/files/9ezXRridnCaI1awLr3X9" alt="" width="150"><figcaption></figcaption></figure>
{% endcolumn %}

{% column width="25%" %}

{% endcolumn %}

{% column width="16.66666666666666%" %}
**Version:**\
**Date:**
{% endcolumn %}

{% column width="33.333333333333336%" %} <code class="expression">space.vars.psdk\_version</code>\ <code class="expression">space.vars.psdk\_date</code>
{% endcolumn %}
{% endcolumns %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.verifone.com/psdk-latest/psdk-user-guide-and-training/psdk-user-guide/payment_functions/semi-integrated-payments/refund.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
