Home
Documentation
Resources
Certifications
Community

Resources

Check for updates on our solutions and system performance, or request technical support.

Community

Get the latest news, ask others for help and share your knowledge.

PSE - Integration configuration - Mercado Pago Developers

Intelligent search powered by OpenAI 

PSE

With Mercado Pago Checkout API you can offer payments with PSE -Secure Online Payments-, the service that allows you to make purchases and payments over the Internet by debiting online resources directly from savings and checking accounts, or electronic deposits.

To offer PSE payments, please follow the steps below.

Important
Remember to configure your Credentials before starting your integration.

Get payment methods

Server-Side

To get a detailed list of all payment methods available for integration, send a GET with your Access Token to the endpoint /v1/payment_methods or, if you prefer, make the request using the SDKs below.

          
<?php
  use MercadoPago\Client\PaymentMethod\PaymentMethodClient;
  use MercadoPago\MercadoPagoConfig;

  MercadoPagoConfig::setAccessToken("YOUR_ACCESS_TOKEN");
  $client = new PaymentMethodClient();
  $payment_methods = $client->list();
?>

        
          
import MercadoPago, { PaymentMethod } from 'mercadopago';

const client = new MercadoPago({ accessToken: '<ACCESS_TOKEN>' });
const paymentMethod = new PaymentMethod(client);

paymentMethod.get()
  .then(paymentMethods => res.status(200).json(paymentMethods))
  .catch(console.log);

        
          
MercadoPagoConfig.setAccessToken("ENV_ACCESS_TOKEN");

PaymentMethodClient client = new PaymentMethodClient();
client.list();


        
          
require 'mercadopago'
sdk = Mercadopago::SDK.new('ENV_ACCESS_TOKEN')

payment_methods_response = sdk.payment_methods.get()
payment_methods = payment_methods_response[:response]


        
          
using MercadoPago.Client.PaymentMethod;
using MercadoPago.Config;
using MercadoPago.Resource;
using MercadoPago.Resource.PaymentMethod;

MercadoPagoConfig.AccessToken = "ENV_ACCESS_TOKEN";

var client = new PaymentMethodClient();
ResourcesList<PaymentMethod> paymentMethods = await client.ListAsync();


        
          
import mercadopago
sdk = mercadopago.SDK("ACCESS_TOKEN")

payment_methods_response = sdk.payment_methods().list_all()
payment_methods = payment_methods_response["response"]

        
          
curl -X GET \
    -H 'accept: application/json' \
    -H 'content-type: application/json' \
    -H 'Authorization: Bearer ENV_ACCESS_TOKEN' \
    'https://api.mercadopago.com/v1/payment_methods' \

        

Once the payment methods are obtained, you can list the available banks for payments with PSE through the financial_institutions field within the object with id=pse, as shown in the example response below. This list of banks will be necessary to continue with the integration during the List banks stage.

json

[
  {
       "id": "pse",
       "name": "PSE",
       "payment_type_id": "bank_transfer",
       "status": "active",
       "secure_thumbnail": "https://www.mercadopago.com/org-img/MP3/API/logos/pse.gif",
       "thumbnail": "https://www.mercadopago.com/org-img/MP3/API/logos/pse.gif",
       "deferred_capture": "does_not_apply",
       "settings": [],
       "additional_info_needed": [
           "entity_type"
       ],
       "min_allowed_amount": 1600,
       "max_allowed_amount": 340000000,
       "accreditation_time": 30,
       "financial_institutions": [
           {
               "id": "1040",
               "description": "Banco Agrario"
           },
           {
               "id": "1507",
               "description": "NEQUI"
           },
           {
               "id": "1052",
               "description": "Banco AV Villas"
           },
           {
               "id": "1032",
               "description": "Banco Caja Social"
           }
       ],
       "processing_modes": [
           "aggregator"
       ]
   }
]

For the list of payment methods to be consumed by the frontend in the following steps, you will need to create a new GET /payment_methods endpoint in your application.

Add payment form

Client-Side

In the frontend of your project, you must add the following payment form.

html

 <form id="form-checkout" action="/process_payment" method="post">
    <div>
      <div>
        <label for="zipCode">Zip Code</label>
        <input id="form-checkout__zipCode" name="zipCode" type="text">
      </div>
      <div>
        <label for="streetName">Street Name</label>
        <input id="form-checkout__streetName" name="streetName" type="text">
      </div>
      <div>
        <label for="streetNumber">Street Number</label>
        <input id="form-checkout__streetNumber" name="streetNumber" type="text">
      </div>
      <div>
        <label for="neighborhood">Neighborhood</label>
        <input id="form-checkout__neighborhood" name="neighborhood" type="text">
      </div>
      <div>
        <label for="city">Ciudad</label>
        <input id="form-checkout__city" name="city" type="text">
      </div>
      <div>
        <label for="federalUnit">Unidad Federal</label>
        <input id="form-checkout__federalUnit" name="federalUnit" type="text">
      </div>
      <div>
        <label for="phoneAreaCode">PhoneAreaCode</label>
        <input id="form-checkout__phoneAreaCode" name="phoneAreaCode" type="text">
      </div>
      <div>
        <label for="phoneNumber">PhoneNumber</label>
        <input id="form-checkout__phoneNumber" name="phoneNumber" type="text">
      </div>
      <div>
        <label for="email">E-mail</label>
        <input id="form-checkout__email" name="email" type="text">
      </div>
      <div>
        <label for="personType">Tipo de persona</label>
        <select id="form-checkout__personType" name="personType" type="text">
          <option value="natural">Natural</option>
          <option value="juridica">Jurídica</option>
        </select>
      </div>
      <div>
        <label for="identificationType">Tipo de documento</label>
        <select id="form-checkout__identificationType" name="identificationType" type="text"></select>
      </div>
      <div>
        <label for="identificationNumber">Número del documento</label>
        <input id="form-checkout__identificationNumber" name="identificationNumber" type="text">
      </div>
    </div>
    <div>
      <div>
        <label for="banksList">Banco</label>
        <div id="banksList"></div> 
      </div>
    </div>
    <div>
      <div>
        <input type="hidden" name="transactionAmount" id="transactionAmount" value="100">
        <input type="hidden" name="description" id="description" value="Nome do Produto">
        <br>
        <button type="submit">Pagar</button>
      </div>
    </div>
  </form>

Get document types

Client-Side

To create a payments with PSE, it is also necessary to obtain the user's document type and number. This information will depend on the type of person (natural or legal) selected while adding the payment form, and you can obtain them automatically using the following function:

javascript

document.getElementById('form-checkout__personType').addEventListener('change', e => {
	const personTypesElement = document.getElementById('form-checkout__personType');
	updateSelectOptions(personTypesElement.value);
});

function updateSelectOptions(selectedValue) {

	const naturalDocTypes = [
		new Option('C.C', 'CC'),
		new Option('C.E.', 'CE'),
		new Option('Pasaporte', 'PAS'),
		new Option('Tarjeta de Extranjería', 'TE'),
		new Option('Tarjeta de Identidad ', 'TI'),
		new Option('Registro Civil', 'RC'),
		new Option('Documento de Identificación', 'DI')
	];
	const juridicaDocTypes = [
		new Option('NIT', 'NIT')
	];
	const idDocTypes = document.getElementById('form-checkout__identificationType');

	if (selectedValue === 'natural') {
		idDocTypes.options.length = 0;
		naturalDocTypes.forEach(item => idDocTypes.options.add(item, undefined));
	} else {
		idDocTypes.options.length = 0;
		juridicaDocTypes.forEach(item => idDocTypes.options.add(item, undefined));
	}
}

List banks

Client-Side

When creating a payment with PSE, it is necessary to send the bank code that will be used to make the transfer. To do this, you must list the available banks and offer the options to the payer, so that they can choose the bank of their preference.

If you have not done so yet, obtain the list of payment methods, as indicated in the Get payment methods stage, and filter the list of available banks for PSE. Next, create a select element in javascript, and enrich it with the data returned in that call, as shown in the example below:

javascript

function setPse() {
    fetch('/payment_methods')
        .then(async function(response) {
            const paymentMethods = await response.json();
            const pse = paymentMethods.filter((method) => method.id === 'pse')[0];
            const banksList = pse.financial_institutions;
            const banksListElement = document.getElementById('banksList');
            const selectElement = document.createElement('select');
            selectElement.name = 'financialInstitution';

            banksList.forEach(bank => {
                const option = document.createElement('option');
                option.value = bank.id;
                option.textContent = bank.description;
                selectElement.appendChild(option);
            });

            banksListElement.appendChild(selectElement);

        }).catch(function(reason) {
            console.error('Failed to get payment methods', reason);
        });
}
Note
We recommend that, when displaying the list of banks, you do so in alphabetical ascending order; that is, from A to Z.

In order for the dynamic elements created with this javascript to load when the page finishes rendering, you must add the following code:

javascript

(function initCheckout() {
    try {
        const docTypeElement = document.getElementById('form-checkout__identificationType');
        setPse();
        updateSelectOptions('natural')
    }catch(e) {
        return console.error('Error getting identificationTypes: ', e);
    }
 })();

Send payment

Server-Side

When finalizing the inclusion of the payment form, obtaining the types of documents and configuring the banks list, it is necessary to forward the buyer's email, telephone number, address, type and document number, the payment method used and the details of the amount to be paid.

To do so, send a POST with the appropriate parameters to the endpoint /v1/payments and execute the request or, if you prefer, use one of our SDKs below.

Important
For this step, when making the request via API or SDKs, it is necessary to send your Private Key (access_token).
          
<?php
use MercadoPago\Client\Common\RequestOptions;
use MercadoPago\Client\Payment\PaymentClient;
use MercadoPago\MercadoPagoConfig;

MercadoPagoConfig::setAccessToken("YOUR_ACCESS_TOKEN");

$client = new PaymentClient();
$request_options = new RequestOptions();
$request_options->setCustomHeaders(["X-Idempotency-Key: <SOME_UNIQUE_VALUE>"]);

$client = new PaymentClient();
$createRequest = [
  "transaction_amount" => 5000,
  "description" => "Product description",
  "payment_method_id" => "pse",
  "callback_url" => "http://www.your-site.com",
  "notification_url" => "http://www.your-site.com",
  "additional_info" => [
    "ip_address" => "127.0.0.1"
  ],
  "transaction_details" => [
    "financial_institution" => $_POST['financialInstitution']
  ],
  "payer" => [
    "email" => $_POST['email'],
    "entity_type" => "individual",
    "first_name" => $_POST['firstName'],
    "last_name" => $_POST['lastName'],
    "identification" => [
        "type" => $_POST['identificationType'],
        "number" => $_POST['identificationNumber']
    ],
    "address" => [
        "zip_code" => $_POST['zipCode'],
        "street_name" => $_POST['streetName'],
        "street_number" => $_POST['streetNumber'],
        "neighborhood" => $_POST['neighborhood'],
        "city" => $_POST['city'],
        "federal_unit" => $_POST['federalUnit']
    ],
    "phone" => [
        "area_code" => $_POST['phoneAreaCode'],
        "number" => $_POST['phoneNumber']
    ],
  ],
];

$payment = $client->create($createRequest, $request_options);
print_r($payment);
?>

        
          
import { Payment, MercadoPagoConfig } from 'mercadopago';

const client = new MercadoPagoConfig({ accessToken: '<ACCESS_TOKEN>' });
const payment = new Payment(client);

const requestOptions = {
	idempotencyKey: '<IDEMPOTENCY_KEY>',
};

const body = {
  transaction_amount: 5000,
  description: 'Product description',
  payment_method_id: 'pse',
  callback_url: 'http://www.your-site.com',
  notification_url: 'http://www.your-site.com',
  payer: {
    entity_type: 'individual',
    email: req.body.email,
    first_name: req.body.firstName,
    last_name: req.body.lastName,
    identification: {
      type: req.body.identificationType,
      number: req.body.identificationNumber
    },
    address: {
      zip_code: req.body.zipCode,
      street_name: req.body.streetName,
      street_number: req.body.streetNumber,
      neighborhood: req.body.neighborhood,
      city: req.body.city,
      federal_unit: req.body.federalUnit
    },
    phone: {
      area_code: req.body.phoneAreaCode,
      number: req.body.phoneNumber
    }
  },
  additional_info: {
    ip_address: '127.0.0.1'
  },
  transaction_details: {
    financial_institution: req.body.financialInstitution
  }
};

payment.create({body, requestOptions})
  .then(function (response) {
    console.info(response)
  })
  .catch(function (error) {
    console.error(error);
  });

        
          
MercadoPagoConfig.setAccessToken("YOUR_ACCESS_TOKEN");

        Map<String, String> customHeaders = Map.of("X-Idempotency-Key", "...");
        MPRequestOptions requestOptions = MPRequestOptions.builder().customHeaders(customHeaders).build();

        PaymentClient client = new PaymentClient();

        IdentificationRequest identification = IdentificationRequest.builder()
                .type(request.getIdentificationType())
                .number(request.getIdentificationNumber())
                .build();

        PaymentPayerAddressRequest address = PaymentPayerAddressRequest.builder()
                .zipCode(request.getZipCode())
                .streetName(request.getStreetName())
                .streetNumber(request.getStretNumber())
                .neighborhood(request.getNeighborhood())
                .city(request.getCity())
                .federalUnit(request.getFederalUnit())
                .build();

        PaymentPayerPhoneRequest phone = PaymentPayerPhoneRequest.builder()
                .areaCode(request.getPhoneAreaCode())
                .number(request.getPhoneNumber())
                .build();

        PaymentPayerRequest payer = PaymentPayerRequest.builder()
                .email(request.getEmail())
                .entityType("individual")
                .firstName("firstName")
                .lastName("lastName")
                .identification(identification)
                .address(address)
                .phone(phone)
                .build();

        PaymentAdditionalInfoRequest additionalInfo = PaymentAdditionalInfoRequest.builder()
                .ipAddress("127.0.0.1")
                .build();

        PaymentTransactionDetailsRequest transactionDetails = PaymentTransactionDetailsRequest.builder()
                .financialInstitution(request.getFinancialInstitution())
                .build();

        PaymentCreateRequest paymentCreateRequest = PaymentCreateRequest.builder()
                .transactionAmount(new BigDecimal(5000))
                .description("Product description")
                .paymentMethodId("pse")
                .additionalInfo(additionalInfo)
                .transactionDetails(transactionDetails)
                .callbackUrl("https://your-site.com")
                .notificationUrl("https://your-site.com")
                .payer(payer)
                .build();

        client.create(paymentCreateRequest);

        
          
require 'mercadopago'
sdk = Mercadopago::SDK.new('ACCESS_TOKEN')

custom_headers = {
 'x-idempotency-key': '<SOME_UNIQUE_VALUE>'
}

request_options = Mercadopago::RequestOptions.new(custom_headers: custom_headers)

body = {
  transaction_amount: 5000,
  description: "Product description",
  payment_method_id: "pse",
  callback_url: "https://your-site.com",
  notification_url: "https://your-site.com",
  additional_info: {
    ip_address: "127.0.0.1"
  },
  transaction_details: {
    financial_institution: params[:financialInstitution]
  },
  payer: {
    email: params[:email],
    entity_type: "individual",
    first_name: params[:firstName],
    last_name: params[:lastName],
    identification: {
      type: params[:identificationType],
      number: params[:identificationNumber]
    }
    address: {
      zip_code: params[:zipCode],
      street_name: params[:streetName],
      street_number: params[:streetNumber],
      neighborhood: params[:neighborhood],
      city: params[:city],
      federal_unit: params[:federalUnit]
    }
    phone: {
      area_code: params[: phoneAreaCode],
      number: params[: phoneNumber]
    }
  }
}

payment_response = sdk.payment.create(body, request_options)
payment = payment_response[: response]

        
          
MercadoPagoConfig.AccessToken = "ACCESS_TOKEN";

var requestOptions = new RequestOptions();
requestOptions.CustomHeaders.Add(Headers.IDEMPOTENCY_KEY, "YOUR_IDEMPOTENCY_KEY");

var identification = new IdentificationRequest() {
  Type = request.IdentificationType,
    Number = request.IdentificationNumber
};

var address = new PaymentPayerAddressRequest() {
    ZipCode = request.ZipCode,
    StreetName = request.StreetName,
    StreetNumber = request.StreetNumber,
    Neighborhood = request.Neighborhood,
    City = request.City,
    FederalUnit = request.FederalUnit
};

var phone = new PaymentPayerPhoneRequest() {
    AreaCode = request.PhoneAreaCode,
    Number = request.PhoneNumber
};

var payer = new PaymentPayerRequest() {
    Email = request.Email,
    EntityType = "individual",
    FirstName = firstName,
    LastName = lastName,
    Identification = identification,
    Address = address,
    Phone = phone
};

var additionalInfo = new PaymentAdditionalInfoRequest() {
  IpAddress = "127.0.0.1"
};

var transactionDetails = new PaymentTransactionDetailsRequest() {
  FinancialInstitution = request.FinancialInstitution
};

var paymentCreateRequest = new PaymentCreateRequest() {
    TransactionAmount = 5000,
    Description = "Product description",
    PaymentMethodId = "pse",
    AdditionalInfo = additionalInfo,
    TransactionDetails = transactionDetails,
    CallbackUrl = "https://your-site.com",
    NotificationUrl = "https://your-site.com",
    Payer = payer
};

var client = new PaymentClient();
var payment = await client.CreateAsync(paymentCreateRequest, requestOptions);

        
          
 import mercadopago
sdk = mercadopago.SDK("ACCESS_TOKEN")

request_options = mercadopago.config.RequestOptions()
request_options.custom_headers = {
    'X-Idempotency-Key': '<SOME_UNIQUE_VALUE>'
}
 
body = {
    "transaction_amount": 5000,
    "description": "Product description",
    "payment_method_id": "pse",
    "callback_url": "https://your-site.com",
    "notification_url": "https://your-site.com",
    "additional_info": {
        "ip_address": "127.0.0.1"
    },
    "transaction_details": {
        "financial_institution": request.POST.get("financialInstitution")
    },
    "payer": {
        "email": request.POST.get("email"),
        "entity_type": "individual",
        "first_name": request.POST.get("firstName"),
        "last_name": request.POST.get("lastName"),
        "identification": {
            "type": request.POST.get("identificationType"), 
            "number": request.POST.get("identificationNumber")
        },
        "address": {
            "zip_code": request.POST.get("zipCode"),
            "street_name": request.POST.get("streetName"),
            "street_number": request.POST.get("streetNumber"),
            "neighborhood": request.POST.get("neighborhood"),
            "city": request.POST.get("city"),
            "federal_unit": request.POST.get("federalUnit")
        },
        "phone": {
            "area_code": request.POST.get("phoneAreaCode"),
            "number": request.POST.get("phoneNumber")
        }
    }
}
 
payment_response = sdk.payment().create(body, request_options)
payment = payment_response["response"]

        
          
curl --location --request POST 'https://api.mercadopago.com/v1/payments' \
-H 'Authorization: Bearer ENV_ACCESS_TOKEN' \
-H 'X-Idempotency-Key: SOME_UNIQUE_VALUE' \
-H 'Content-Type: application/json' \
--d '{
    "transaction_amount": 5000,
    "description": "Product description",
    "payment_method_id": "pse",
    "callback_url": "http://www.your-site.com",
    "notification_url": "http://www.your-site.com",
    "payer": {
        "email": "test_user_19549678@testuser.com",
        "entity_type": "individual",
        "first_name": "first name",
        "last_name": "last_name",
        "identification": {
            "type": "type",
            "number": "number"
        }, 
        "address": {
          "zip_code": "111",
          "street_name": "street name",
          "street_number": "street number",
          "neighborhood": "neighborhood",
          "city": "city",
          "federal_unit": "federal unit"
        },
        "phone": {
          "area_code": "area code",
          "number": "number"
        }
    },
    "additional_info": {
        "ip_address": "127.0.0.1"
    },
    "transaction_details": {
        "financial_institution": "1009"
    }
}'

        

The following are mandatory fields that must be completed when sending a payment according to the specifications below:

FieldDescriptionPosssible values/validaationsRequest to get the values
transaction_amountPayment amount.Must be greater than 0.-
transaction_details.financial_institutionBank informed in the POST to make the electronic transfer. You must show the list to the user and allow him to select. This list usually refreshes, so it’s recommended to consume the information every hour.-https://api.mercadopago.com/v1/payment_methods/search?site_id=MCO&id=pse&public_key=YOUR_PUBLIC_KEY
payer.entity_typeType of person, natural or legal.individual or association-
payer.identification.typeBuyer's document type.Accepted values:
- RC (Registro Civil de Nacimiento)
- TI (Tarjeta de Identidad)
- CC (Cedula de Ciudadania)
- TE (Tarjeta de Extranjeria)
- CE (Cedula de Extranjeria)
- PAS (Pasaporte)
- NIT
- DI (Documento de Identificación)
curl -X GET \
'https://api.mercadopago.com/v1/identification_types' \
-H 'Authorization: Bearer YOUR_PUBLIC_KEY'
payer.identification.numberBuyer's document number.String
Must have between 1 and 15 numeric positions. If it is of the 'passport' type, it will accept alphanumeric values.
-
payer.first_nameBuyer's first name.Must have between 1 and 32 positions.-
payer.last_nameBuyer's last name.Must have between 1 and 32 positions.-
payer.address.zip_codeZip code of the payer’s address.Must have exactly 5 positions.-
payer.address.street_nameStreet name of the payer’s address.Must have between 1 and 18 positions.-
payer.address.street_numberStreet number of the payer’s address.Must have between 1 and 5 positions.-
payer.address.neighborhoodNeighbourhood of the payer’s address.Must have between 1 and 18 positions.-
payer.address.cityPayer’s cityMust have between 1 and 18 positions.-
payer.phone.area_codeArea code of the payer’s phone number.Must have 3 positions.-
payer.phone.numberPayer’s phone number.String
Must have between 1 and 7 positions and only accepts numeric characters.
-
additional_info.ip_addressBuyer’s IP address, where the payment is made.--
callback_urlURL where the buyer is redirected by default after making the payment within the bank's page, when the buyer indicates that they want to return to the store.
You can check suggested messages to show the buyer under Examples for callback URLs.
Must not be null or empty and must have a maximum of 512 characters.-
notification_urlURL used to notify the application that the transfer has been completed.Must not be null or empty and must have a maximum of 512 characters.-

The response will show the pending status until the buyer completes the payment. Also, the external_resource_url parameter will return a URL where you should redirect the buyer to complete the payment flow.

You can see an example of this response below. Please note that information was omitted in order to show the most relevant fields.

json

{
    "id": 1312147735,
     … 
    "operation_type": "regular_payment",
    "payment_method_id": "pse",
    "payment_type_id": "bank_transfer",
    "payment_method": {
        "id": "pse",
        "type": "bank_transfer"
    },
    "status": "pending",
    "status_detail": "pending_waiting_transfer",
     … 
    "description": "Título del producto",
     … 
    "callback_url": "http://www.your-site.com",
    
    "transaction_details": {
     … 
        "total_paid_amount": 5000,
     … 
        "external_resource_url": "https://www.mercadopago.com.co/sandbox/payments/1312147735/bank_transfer?caller_id=1148920820&hash=f41dd14f-b3a6-4ac4-9b78-5cfeb5a35e77",
     … 
        "financial_institution": "1009",
     … 
        "bank_transfer_id": 129229,
        "transaction_id": "10022214"
    },
}
Important
In case an error is returned when generating a payment, you can check the list of possible errors in the API Reference section , or go to Errors with PSE payments .

Examples for callback URLs

Once the buyer makes the payment on the platform of the selected bank, they are redirected to a callback URL, in which they are informed of the status of their transaction.

Below, we show you examples of messages that you can offer, according to the three possible states in which a payment can be found.

Approved status

image of an approved transaction and the redirection

Pending status

Pending transaction image

Rejected status

Rejected transaction image

Expiration

The payment created with PSE automatically expires within 15 minutes of being generated and its status becomes rejected. If the user does not access the web and makes the payment within that time, a new one must be generated.