Refund API

Introduction

PagSeguro API offers an option to Merchant request the refund of a transaction.
A transaction needs to be in status Delivered/Complete in PagSeguro in order to be refunded. The payment method/country of the transaction must be one of which PagSeguro accepts refund. Appendix II has a list of payment methods that are eligible for refund.


How it Works

  1. Merchant requests refund to PagSeguro
  2. PagSeguro receives the data and returns the refund identifier to the Merchant
  3. PagSeguro processes the data transferred by the Merchant
  4. PagSeguro notifies the Merchant with the status of the transaction change
  5. Merchant receives notification and updates order status as described in the Notify API topic

Header Specification

For a new API request it is mandatory to format the header as described below:

ParameterTypeDescription
AcceptStringThis parameter informs the API version, data format (JSON) and encoding. The value will depend on which API you are calling.
Refund Create: application/vnd.boacompra.com.v2+json; charset=UTF-8
AuthorizationStringSHA256 generated with a secret key to guarantee request authenticity.
Format: {store-id}:{hash}
Content-TypeStringOnly application/json is accepted at the moment

📘

Store-id and secret-key are provided by your Account Manager.

POST / HTTP 1.1
Host: api.boacompra.com
Accept: application/vnd.boacompra.com.v2+json; charset=UTF-8
Authorization: 10:52e7178dea75ae0e71e0f8c2379e0278beb7e9d091763ee0a64e6719d2df0fc2
Content-Type: application/json
<?php

class header
{
    private $secretKey = 'ABCDE0987';
    private $storeId = 10;

    public function __construct($content, $url)
    {
        $this->setContentMd5($content);
        $this->setHttpVerb($url);
    }

    private function setContentMd5($content)
    {
        $this->contentMd5 = md5($content);
    }

    private function setHttpVerb($url)
    {
        if (parse_url($url, PHP_URL_QUERY)) {
            $this->httpVerb = parse_url($url, PHP_URL_PATH).'?'.parse_url($url, PHP_URL_QUERY);
        } else {
            $this->httpVerb = parse_url($url, PHP_URL_PATH);
        }
    }

    private function generateAuthorization()
    {
        return hash_hmac(
            'sha256',
            $this->httpVerb . $this->contentMd5,
            $this->secretKey
        );
    }




    public function generateHeader()
    {
$headers = array(
            'Accept' => 'application/vnd.boacompra.com.v2+json; charset=UTF-8',
            'Content-Type' => 'application/json',
            'Authorization' => $this->storeId . ':' . $this->generateAuthorization()
        );
        return $headers;
    }
}

echo 'POST EXAMPLE <br />';
$content = '{"transaction-id":123456789,"amount":10.57,"notify-url":"https://virtualstore.com/notifications","test-mode":0}';

$headerPost = new header($content, 'https://api.boacompra.com/refunds');
print_r($headerPost->generateHeader());
public class Header {
  private String secretKey = "ABCDE0987";
  private String storeId = "10";
  private String contentMD5;
  private String httpVerb;

  public Header(String content, String url) throws NoSuchAlgorithmException, MalformedURLException, UnsupportedEncodingException {
    this.setContentMd5(content);
    this.setHttpVerb(new URL(url));
  }

  private void setHttpVerb(URL url) {
    this.httpVerb = url.getPath() + (url.getQuery() != null ? '?' + url.getQuery() : "");
  }

  private void setContentMd5(String content) throws NoSuchAlgorithmException, UnsupportedEncodingException {
    MessageDigest md = MessageDigest.getInstance("MD5");
    byte messageDigest[] = md.digest(content.getBytes("UTF-8"));
    this.contentMD5 =  new BigInteger(1,messageDigest).toString(16);
  }

  private String generateAuthorization() throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
    final String data = this.httpVerb+this.contentMD5;
    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(this.secretKey.getBytes("UTF8"), "HmacSHA256"));

    return Hex.encodeHexString(mac.doFinal(data.getBytes("UTF-8")));
  }

  public HashMap<String,String> generateHeader() throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {
    HashMap<String, String> headers = new HashMap<>();
    headers.put("Accept", "application/vnd.boacompra.com.v2+json; charset=UTF-8");
    headers.put("Content-Type", "application/json");
    headers.put("Authorization", this.storeId+':'+this.generateAuthorization());

    return headers;

  }

}

String content = "{\"transaction-id\":123456789,\"amount\":10.57,\"notify-url\":\"https://virtualstore.com/notifications\",\"test-mode\":0}";

Header header = new Header(content, "https://api.boacompra.com/refunds");
HashMap<String, String> headerMap = header.generateHeader();

System.out.println(Arrays.asList(headerMap));
require 'uri'
require 'openssl'
require 'digest/md5'

class Header

   SECRET_KEY = 'ABCDE0987'
   STORE_ID = 10

   attr_reader :contentMD5, :httpVerb

   def initialize(content, url)
    setContentMD5(content)
    setHttpVerb(url)
   end

   private
   def setContentMD5(content)
      @contentMD5 = Digest::MD5.hexdigest(content).to_s
   end

   private
   def getQueryString(url)
    uri = URI(url)
    url.to_s.empty? || uri.query.nil? ? '' : '?' + uri.query
   end

   private
   def setHttpVerb(url)
    uri = URI::parse(url)
    @httpVerb = getQueryString(url).to_s.empty? ? uri.path : uri.path + "?" + getQueryString(url)
   end

   private
   def generateAuthorization()
    OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), SECRET_KEY, @httpVerb + @contentMD5)
   end

   public
   def generateHeader()
    headers = {
     "Accept"           => 'application/vnd.boacompra.com.v2+json; charset=UTF-8',
     "Content-Type"     => 'application/json',
     "Authorization"    => STORE_ID.to_s + ":" + generateAuthorization()
    }
    headers
   end
end

puts '<pre>POST EXAMPLE <br />'
content = '{"transaction-id":123456789,"amount":10.57,"notify-url":"https://virtualstore.com/notifications","test-mode":0}';
headerGet = Header.new(content, 'https://api.boacompra.com/refunds')
puts headerGet.generateHeader()
"""Python 2.7"""
from urlparse import urlparse
import hashlib
import hmac
import md5

class Header:

  __secretKey = '123'
  __storeId = 10

  def __init__(self, content, url):
    self.__setHttpVerb(url)
    self.__setContentMd5(content)

  def __setHttpVerb(self, url):
    urlParsed = urlparse(url)
    self.__httpVerb = urlParsed.path + urlParsed.query

  def __setContentMd5(self, content):
    self.__contentMd5 = md5.new(content).hexdigest()

  def __generateAuthorization(self):
    return hmac.new(
      self.__secretKey,
      self.__httpVerb + self.__contentMd5,
      hashlib.sha256
    ).hexdigest()

  def generateHeader(self):
    return {
      'Accept': 'application/vnd.boacompra.com.v2+json; charset=UTF-8',
      'Content-Type': 'application/json',
      'Authorization': str(self.__storeId) + ':' + self.__generateAuthorization()
    }

from Header import Header

content = '{"transaction-id":123456789,"amount":10.57,"notify-url":"https://virtualstore.com/notifications","test-mode":0}'
url = 'https://api.boacompra.com/refunds'

a = Header(content, url)
print a.generateHeader()


"""Python 3.x"""
from urllib.parse import urlparse
import hashlib
import hmac

class Header:

  __secretKey = '123'
  __storeId = 10

  def __init__(self, content, url):
    self.__setHttpVerb(url)
    self.__setContentMd5(content)

  def __setHttpVerb(self, url):
    urlParsed = urlparse(url)
    self.__httpVerb = urlParsed.path + urlParsed.query

  def __setContentMd5(self, content):
    self.__contentMd5 = hashlib.md5(content.encode()).hexdigest()

  def __generateAuthorization(self):
    authContent = self.__httpVerb + self.__contentMd5

    return hmac.new(
      self.__secretKey.encode(),
      authContent.encode(),
      hashlib.sha256
    ).hexdigest()

  def generateHeader(self):
    return {
      'Accept': 'application/vnd.boacompra.com.v2+json; charset=UTF-8',
      'Content-Type': 'application/json',
      'Authorization': str(self.__storeId) + ':' + self.__generateAuthorization()
    }

from Header import Header

content = '{"transaction-id":123456789,"amount":10.57,"notify-url":"https://virtualstore.com/notifications","test-mode":0}'
url = 'https://api.boacompra.com/refunds'

a = Header(content, url)
print(a.generateHeader())

Refund Request API

This request must be made in order to create a refund for a given transaction. This only creates the refund: the processing and finishing of this refund is made by PagSeguro directly through the payment provider.

📘

Refund API URL for Production

https://api.boacompra.com/refunds

Method: POST

Input parameters

ParameterTypeMandatory?Description
transaction-idIntegerMandatoryTransaction identifier in PagSeguro.
notify-urlStringMandatoryURL used to notify the Merchant. This URL must bind ports 80 or 443.
Format: http or https URL format
amountFloatOptionalAmount to be refunded. If not sent, the refund will be processed for the entire value of the transaction. For partial refunds, the amount parameter must be sent.
Format: 2000.00
test-modeIntegerOptionalParameter used to indicate that a transaction will be processed in test mode. Can be used the value 1 for sandbox or 0 for production environment. If not set assumes value 0.
Format: possible entries are 0 or 1
referenceStringOptionalMerchant refund identifier. Limit of 64 characters.
{
    "transaction-id": 123456789,
    "amount": 10.57,
    "notify-url": "https://virtualstore.com/notifications",
    "test-mode": 0,
    "reference": "BC-380465"
}

Success Response

HTTP Status code: 201

In case of success, the refund process will be started and the transaction will be set to processing refund status.
Also, a “Location” parameter will be informed in the header with the transaction URI location.

ParameterTypeDescription
refund-idStringRefund identifier code generated by PagSeguro
HTTP/1.1 201 Created
Content-type: application/vnd.boacompra.com.v2+json; charset=UTF-8
Location: /transactions/123456
{
    "refund-id":12345
}

Bank Deposit refund flow

For some payment methods, the refund flow is not automatic - an e-mail will be sent to the end user with URL so he can fulfill information on how they want to receive the refund. In this case, they can choose refund through bank transfer. This process is transparent to the merchant.

📘

This flow can also happen even for automatic payment methods, such as credit card. This is true if the refund is asked after the normal refund period has passed (usually it is 90 days).

When accessing the URL, a form similar to the following image will be displayed.

  1. Greeting screen where we share important information, such as the need to have a bank account.

  1. The actual form where the end user fills their data - personal info and bank account info.

After the refund is complete, a notification will be sent via notify API to inform the conclusion of the refund. Please refer to the notification section for further details.

🚧

The payer has 7 days to fulfill the form with their information, otherwise the refund will change its status to REJECTED.

Error response

In case of errors in the request, expect a HTTP Status Code different from 201. The body response also contains an error object with the description and an error code.

{
    "errors": [
        {
            "code": "20614",
            "description": "transaction_not_found "
        }
    ]
}

The full list of errors can be found here
It is possible that you encounter one or more errors in an error array, which will have a unique code number. In those cases, there is a problem with the Request Body: the need of a property or a constraint is not met.

{
  "errors": [
    {
      "property": "transaction-id",
      "constraint": "required",
      "code": 20698,
      "description": "The property transaction-id is required"
    },
    {
      "property": "amount",
      "constraint": "minimum",
      "minimum": 0.01,
      "code": 20698,
      "description": "Must have a minimum value of 0.01"
    }
  ]
}

Refund in Billing Partner

You can view refund requests in the https://billing-partner.boacompra.com. interface for both live transactions and test transactions. Also, there’s a field for filtering specifically by the unique Refund ID number.

In the transaction page a new section, called Refunds, was added. It contains a summary of refund requests.

Test Mode

To use the Refund in test mode you will need to have transactions delivered with the “test-mode” parameter set to true, after sending the request for Refund is it possible to simulate the notification of success and failure in the billing-partner interface.
In the Transaction Test menu it’s possible to find the transaction for which the refund request was made.

In the refund list you can simulate the notification for Success and Failure by clicking in the respective button and a notification will be sent to URL informed on the notify-url refund parameter.

Notification

When a Refund is finished successfully or is rejected for some reason, the merchant will receive a notification in the notification url first sent when creating the refund.
After receiving the notification, the merchant will have to consult the transaction (see section Transaction Search API) and could take an action accordingly its status.

Base URL: notify-url parameter sent when creating the refund

Method: POST

Notification Object

ParameterDescription
notification-typeAssumes the value “refund”
refund-idID of the refund in PagSeguro
transaction-idID of the transaction in PagSeguro

Body Example

{
 "notification-type": "refund",
 "refund-id": 12345,
 "transaction-id": 75040384
}

Refund Status

Refund status returned by PagSeguro:

NameDescription
REQUESTEDRefund requested by Customer.
PROCESSINGRefund in process.
PROCESSEDRefund successfully completed.
REJECTEDRefund was rejected.

Payment Methods Available for Refund

The list of payments and countries with refund available can be found on Available Payment List