PayPal Checkout

Refund a Transaction

There are two ways to refund transactions: on the PayPal website and with the Payments API /refund resource.

Refunds with the PayPal website

  1. Log in to paypal.com to search transactions.
  2. Go to Activities and view transaction details.
  3. Process the refund by clicking Issue a refund. You can return a partial amount or leave the amount unchanged to issue full refund.
  4. Click Continue to review the refund, then click Issue Refund.

Refunds with the Payments API

Process refunds by calling the Payments API /refund resource with the capture ID. The capture ID is returned from any client-side actions.order.capture() call or server-side captures call.

The following request processes a partial refund for $20. Issue a full refund by leaving the request body empty.

  1. Set up your server to make calls to PayPal.
  2. Get the capture ID from your database.
  3. Call PayPal to refund the transaction.
  4. Handle any errors from the call.
// Note: This code is intended as a *pseudocode* example. Each server platform and programming language has a different way of handling requests, making HTTP API calls, and serving responses to the browser.

// 1. Set up your server to make calls to PayPal

// 1a. Add your client ID and secret
PAYPAL_CLIENT = 'PAYPAL_SANDBOX_CLIENT';
PAYPAL_SECRET = 'PAYPAL_SANDBOX_SECRET';

// 1b. Point your server to the PayPal API
PAYPAL_OAUTH_API    = 'https://api.sandbox.paypal.com/v1/oauth2/token/';
PAYPAL_PAYMENTS_API = 'https://api.sandbox.paypal.com/v2/payments/captures/';

// 1c. Get an access token from the PayPal API
basicAuth = base64encode(`${ PAYPAL_CLIENT }:${ PAYPAL_SECRET }`);
auth = http.post(PAYPAL_OAUTH_API {
  headers: {
    Accept:        `application/json`,
    Authorization: `Basic ${ basicAuth }`
  },
  data: `grant_type=client_credentials`
});

// 2. Get the capture ID from your database
captureID = database.lookupCaptureID();

// 3. Call PayPal to refund the transaction
refund = http.post(PAYPAL_PAYMENTS_API + captureID + '/refund', {
  headers: {
    Accept:        `application/json`,
    Authorization: `Bearer ${ auth.access_token }`
  },
  body: JSON.stringify({
    amount: {
      currency_code: 'USD',
      value:         '20.00'
    }
  })
});

// 4. Handle any errors from the call
if (refund.error) {
  console.error(refund.error);
}
curl -v -X POST https://api.sandbox.paypal.com/v2/payments/captures/2GG279541U471931P/refund \
-H "Content-Type: application/json" \
-H "Authorization: Bearer Access-Token" \
-H "PayPal-Request-Id: 123e4567-e89b-12d3-a456-426655440020" \
-d '{
  "amount": {
    "value": "10.99",
    "currency_code": "USD"
  },
  "invoice_id": "INVOICE-123",
  "note_to_payer": "Defective product"
}'
// 1. Set up your server to make calls to PayPal

// 1a. Import the SDK package
const checkoutNodeJssdk = require('@paypal/checkout-server-sdk');

// 1b. Import the PayPal SDK client that was created in `Set up Server-Side SDK`.
/**
 *
 * PayPal HTTP client dependency
 */
const payPalClient = require('../Common/payPalClient');

module.exports = async function captureAuthorization() {

  // 2. Get the capture ID from your database
  const captureID = await database.lookupCaptureID();

  // 3. Call PayPal to refund the transaction
  const request = new checkoutNodeJssdk.payments.CapturesRefundRequest(captureId);
  request.requestBody({
    amount: {
      currency_code: 'USD',
      value:         '20.00'
    }
  });
  const refund;
  try {
      refund = await payPalClient.client().execute(request);
  } catch (err) {

    // 4. Handle any errors from the call
    console.error(err);
  }
  // 5. Return a successful response to the client with the order ID
  response.send(200, {
    refundID: refund.id
  });
}
<?php
namespace Sample;

require __DIR__ . '/vendor/autoload.php';
//1. Import the PayPal SDK client that was created in `Set up Server-Side SDK`.
use Sample\PayPalClient;
use PayPalCheckoutSdk\Payments\CapturesRefundRequest;

class RefundOrder
{

  // 2. Set up your server to receive a call from the client
  // Use this function to perform a refund on the capture.

  public static function refundOrder($captureId, $debug=false)
  {
    $request = new CapturesRefundRequest($captureId);
    $request->body = self::buildRequestBody();
    // 3. Call PayPal to refund a capture
    $client = PayPalClient::client();
    $response = $client->execute($request);

    if ($debug)
    {
      print "Status Code: {$response->statusCode}\n";
      print "Status: {$response->result->status}\n";
      print "Order ID: {$response->result->id}\n";
      print "Links:\n";
      foreach($response->result->links as $link)
      {
        print "\t{$link->rel}: {$link->href}\tCall Type: {$link->method}\n";
      }
      // To toggle printing the whole response body comment/uncomment
      // the following line
      echo json_encode($response->result, JSON_PRETTY_PRINT), "\n";
    }
    return $response;
  }

  // Function to create a partial refund capture request.
  // For full refund, pass the empty body.
  // For more details, refer to the Payments API refund captured payment reference.

  public static function buildRequestBody()
  {
    return array(
      'amount' =>
        array(
          'value' => '20.00',
          'currency_code' => 'USD'
        )
    );
  }
}

// This driver function invokes the refund capture function with
// Capture ID to perform refund on capture.
//
if (!count(debug_backtrace()))
{
  //Reference the capture ID from the database that stores the ID after creating the capture.
  RefundOrder::refundOrder(&apos REPLACE-WITH-VALID-CAPTURE-ID', true);
}
?>
# 1. Import the PayPal SDK client that was created in `Set up Server-Side SDK`.
from sample import PayPalClient
from paypalcheckoutsdk.payments import CapturesRefundRequest
import json

class RefundOrder(PayPalClient):

  #2. Set up your server to receive a call from the client
  """Use the following function to refund an capture.
     Pass a valid capture ID as an argument."""
  def refund_order(self, capture_id, debug=False):
    request = CapturesRefundRequest(capture_id)
    request.prefer("return=representation")
    request.request_body(self.build_request_body())
    #3. Call PayPal to refund an capture
    response = self.client.execute(request)
    if debug:
      print 'Status Code:', response.status_code
      print 'Status:', response.result.status
      print 'Order ID:', response.result.id
      print 'Links:'
      for link in response.result.links:
        print('\t{}: {}\tCall Type: {}'.format(link.rel, link.href, link.method))
      json_data = self.object_to_json(response.result)
      print "json_data: ", json.dumps(json_data,indent=4)    
    return response

  """Request body for building a partial refund request.
     For full refund, pass the empty body.
     For more details, refer to the Payments API refund captured payment reference."""
  @staticmethod
  def build_request_body():
    return \
      {
        "amount": {
          "value": "20.00",
          "currency_code": "USD"
        }
      }


"""This driver function invokes the refund capture function.
   Replace the Capture Id with a valid capture ID. """
if __name__ == "__main__":
  capture_id = &apos REPLACE-WITH-VALID-CAPTURE-ID'
  RefundOrder().refund_order(capture_id, debug=True)
# 1. Import the PayPal SDK client that was created in `Set up Server-Side SDK`.
require_relative './paypal_client'
include PayPalCheckoutSdk::Payments
module Samples
  class RefundCapture

    #2. Set up your server to receive a call from the client
    # This sample function performs capturing a refund.
    # Pass a valid capture ID as an argument to this function.
    def refund_capture (capture_id, debug=false)
      request = CapturesRefundRequest::new(capture_id)
      request.prefer("return=representation")
      # Populate the request body to do a partial refund.
      request.request_body({
        amount: {
          value: '20.00',
          currency_code: 'USD'
        }
      });
      begin
        #3. Call PayPal to refund an capture
        response = PayPalClient::client::execute(request)
      if debug
      puts "Status Code: " + response.status_code.to_s
      puts "Status: " + response.result.status
      puts "Order ID: " + response.result.id
      puts "Intent: " + response.result.intent
        puts "Links:"
        for link in response.result.links
          # This could also be called as link.rel or link.href, but
          # as method is a reserved keyword for Ruby. Avoid calling link.method
          puts "\t#{link["rel"]}: #{link["href"]}\tCall Type: #{link["method"]}"
        end
        puts PayPalClient::openstruct_to_hash(response.result).to_json
      end
      rescue BraintreeHttp::HttpError => ioe
        # Exception occurred while processing the refund.
        puts " Status Code: " + ioe.status_code.to_s
        puts " Debug Id: " + ioe.result.debug_id
        puts " Response: " + PayPalClient::openstruct_to_hash(ioe.result).to_json
      end
      return response
    end
  end
end
# This driver function invokes the refund capture function.
# Replace the Capture ID value with the capture ID.
if __FILE__ == $0
  Samples::RefundCapture::new::refund_capture(&apos REPLACE-WITH-VALID-CAPTURE-ID', true)
end
package com.paypal.AuthorizeIntentExamples;

import java.io.IOException;

import org.json.JSONObject;

import com.braintreepayments.http.HttpResponse;
import com.braintreepayments.http.serializer.Json;
import com.paypal.PayPalClient;
import com.paypal.orders.OrderRequest;
import com.paypal.payments.CapturesRefundRequest;
import com.paypal.payments.LinkDescription;
import com.paypal.payments.Refund;

/*
*
*1. Import the PayPal SDK client that was created in `Set up Server-Side SDK`.
*This step extends the SDK client. It's not mandatory to extend the client, you also can inject it.
*/
public class RefundOrder extends PayPalClient {

  //2. Set up your server to receive a call from the client
  // Method to refund the capture. Pass a valid capture ID.
  //
  // @param captureId Capture ID from authorizeOrder response
  // @param debug     true = print response data
  // @return HttpResponse<Capture> response received from API
  // @throws IOException Exceptions from API if any

  public HttpResponse<Refund> refundOrder(String captureId, boolean debug) throws IOException {
    CapturesRefundRequest request = new CapturesRefundRequest(captureId);
    request.prefer("return=representation");
    request.requestBody(buildRequestBody());
    #3. Call PayPal to refund an capture
    HttpResponse<Refund> response = client().execute(request);
    if (debug) {
      System.out.println("Status Code: " + response.statusCode());
      System.out.println("Status: " + response.result().status());
      System.out.println("Refund Id: " + response.result().id());
      System.out.println("Links: ");
      for (LinkDescription link : response.result().links()) {
        System.out.println("\t" + link.rel() + ": " + link.href() + "\tCall Type: " + link.method());
      }
      System.out.println("Full response body:");
      System.out.println(new JSONObject(new Json()
              .serialize(response.result())).toString(4));            
    }
    return response;
  }

  // Creating a body for partial refund request.
  // For full refund, pass the empty body.
  //
  // @return OrderRequest request with empty body

  public RefundRequest buildRequestBody() {
    RefundRequest refundRequest = new RefundRequest();
    Money money = new Money();
    money.currencyCode("USD");
    money.value("20.00");
    refundRequest.amount(money);

    return refundRequest;
  }

  // This function initiates capture refund.
  // Replace Capture ID with a valid capture ID.
  //
  // @param args

  public static void main(String[] args) {
    try {
      new RefundOrder().refundOrder(" REPLACE-WITH-VALID-CAPTURE-ID", true);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
//1. Import the PayPal SDK client that was created in `Set up Server-Side SDK`.
using PayPalCheckoutSdk.Core;
using PayPalCheckoutSdk.Payments;
using BraintreeHttp;

namespace Samples
{
  public class CapturesRefundSample
  {

    //2. Set up your server to receive a call from the client
    // Method for refund the capture.
    // Pass a valid capture ID as an argument to this method.

    public async static Task<HttpResponse> CapturesRefund(string CaptureId, bool debug = false)
    {
      var request = new CapturesRefundRequest(CaptureId);
      request.Prefer("return=representation");
      // You can populate the following request body to perform a partial refund.
      // For more details, refer to the Payments API refund captured payment reference.
      request.RequestBody(BuildRequestBody());
      #3. Call PayPal to refund a capture
      var response = await PayPalClient.client().Execute(request);

      if (debug)
      {
        var result = response.Result<Refund>();
        Console.WriteLine("Status: {0}", result.Status);
        Console.WriteLine("Refund Id: {0}", result.Id);
        Console.WriteLine("Links:");
        foreach (LinkDescription link in result.Links)
        {
          Console.WriteLine("\t{0}: {1}\tCall Type: {2}", link.Rel,
                                  link.Href,
                                  link.Method);
        }
        Console.WriteLine("Response JSON: \n {0}",
                    PayPalClient.ObjectToJSONString(result));
      }
      return response;
    }

    // Creating body for partial refund request.
    // Pass empty body for full refund.
    // For more details, refer to the Payments API refund captured payment reference.
    //
    // @return RefundRequest

    private static RefundRequest BuildRequestBody()
    {
      RefundRequest refundRequest = new RefundRequest()
      {
        Amount = new Money
        {
          CurrencyCode = "USD",
          Value = "20.00"
        },

        NoteToPayer = "Partial Refund"
      };

      return refundRequest;
    }

    // Driver function to perform refund on capture.
    // Replace Capture ID with the valid capture ID.

    static void Main(string[] args)
    {
      string CaptureId = " REPLACE-WITH-VALID-CAPTURE-ID";
      CapturesRefund(CaptureId, true).Wait();
    }
  }
}

The response creates a refund resource with amount and fee break-up details.

{
  "id": "28R62814C3999453N",
  "amount":
  {
    "currency_code": "USD",
    "value": "20.00"
  },
  "seller_payable_breakdown":
  {
    "gross_amount":
    {
      "currency_code": "USD",
      "value": "20.00"
    },
    "paypal_fee":
    {
      "currency_code": "USD",
      "value": "0.58"
    },
    "net_amount":
    {
      "currency_code": "USD",
      "value": "19.42"
    },
    "total_refunded_amount":
    {
      "currency_code": "USD",
      "value": "20.00"
    }
  },
  "invoice_id": "INV-HighFashions",
  "status": "COMPLETED",
  "create_time": "2018-05-31T20:08:19-07:00",
  "update_time": "2018-05-31T20:08:19-07:00",
  "links": [
  {
    "href": "https://api.sandbox.paypal.com/v2/payments/refunds/28R62814C3999453N",
    "rel": "self",
    "method": "GET"
  },
  {
    "href": "https://api.sandbox.paypal.com/v2/payments/captures/1HW32023TU4585620",
    "rel": "up",
    "method": "GET"
  }]
}

Read about Refunding a captured payment, including sample responses, in the Payments API reference.

Note: Remember to swap the credentials and API URL from sandbox to production when going live with your integration.

Feedback