PSE
With Checkout API from Mercado Pago, you can offer payments with PSE (Pagos Seguros en Línea). This electronic system allows customers to make online purchases and payments using funds directly from a savings account, checking account, or digital wallet, acting as a secure bridge between banks and stores, and thus eliminating the need for credit cards.
If you have already configured your development environment and want to offer payments via PSE, follow the steps below.
processing_mode parameter. For more information, access the Integration model section.To receive payments, you need to add a form to the frontend that allows you to capture the payer's data securely.
If you already have a development that includes a custom payment form, make sure to include PSE among the payment options you want to offer, as indicated below, and continue to the Get document types step.
If you don't yet have a payment form, add the model below to your project and include the PSE identifier as an option to be offered.
| Payment method | payment_method_id |
| PSE | pse |
html
<form id="form-checkout" action="/process_payment" method="post"> <div> <div> <label for="zipCode">Postal 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">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">City</label> <input id="form-checkout__city" name="city" type="text"> </div> <div> <label for="federalUnit">Department</label> <input id="form-checkout__federalUnit" name="federalUnit" type="text"> </div> <div> <label for="phoneAreaCode">Area Code</label> <input id="form-checkout__phoneAreaCode" name="phoneAreaCode" type="text"> </div> <div> <label for="phoneNumber">Phone</label> <input id="form-checkout__phoneNumber" name="phoneNumber" type="text"> </div> <div> <label for="email">Email</label> <input id="form-checkout__email" name="email" type="text"> </div> <div> <label for="personType">Person Type</label> <select id="form-checkout__personType" name="personType" type="text"> <option value="natural">Natural</option> <option value="juridica">Legal entity</option> </select> </div> <div> <label for="identificationType">Document Type</label> <select id="form-checkout__identificationType" name="identificationType" type="text"></select> </div> <div> <label for="identificationNumber">Document Number</label> <input id="form-checkout__identificationNumber" name="identificationNumber" type="text"> </div> </div> <div> <div> <label for="banksList">Bank</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="Product Name"> <br> <button type="submit">Pay</button> </div> </div> </form>
To facilitate the correct completion of the payment form, you need to get the document types that can be accepted.
The function below allows you to automatically fill in the available options. To do this, simply include in the form the select element with id=form-checkout__identificationType, used in the example from the previous step.
If you already have a development that handles getting document types, as indicated below, proceed to the Send payment step.
If you don't yet have this function, add the following code to your project.
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)); } }
When creating a payment with PSE, you need to send the code of the bank that will be used for the transfer. To do this, you need to list the available banks and present these options to the payer, allowing them to choose their preferred bank.
Send a GET with your test Access TokenPrivate key of the application created in Mercado Pago, used in the backend. You can access it through Your integrations > Integration details > Tests > Test credentials. to the endpoint /v1/payment_methodsAPI or, if you prefer, make the request using our SDKs below.
<?php
use MercadoPago\MercadoPagoConfig;
MercadoPagoConfig::setAccessToken("<YOUR_ACCESS_TOKEN>");
$client = new PaymentMethodClient();
$payment_method = $client->get();
?>
import { MercadoPagoConfig, PaymentMethods } from 'mercadopago';
const client = new MercadoPagoConfig({ accessToken: 'access_token' });
const paymentMethods = new PaymentMethods(client);
paymentMethods.get().then((result) => console.log(result))
.catch((error) => console.log(error));
MercadoPagoConfig.setAccessToken("<YOUR_ACCESS_TOKEN>");
PaymentMethodClient client = new PaymentMethodClient();
client.list();
require 'mercadopago'
sdk = Mercadopago::SDK.new('<YOUR_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 = "<YOUR_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 <YOUR_ACCESS_TOKEN>' \
'https://api.mercadopago.com/v1/payment_methods' \
The response will return the payment methods, where you can list the banks available for PSE payments through the financial_institutions field inside the object with id=pse, as shown in the example below.
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" ] } ]
Then, create a select element in JavaScript and populate it with the data returned in this request, 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); }); }
To ensure the dynamic elements created with this JavaScript are loaded when the page finishes rendering, 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); } })();
The payment submission must be performed by creating an order that contains the associated payment transaction.
in_process and without information. We recommend configuring Order topic notifications to receive updates on status changes, including updated order data. Alternatively, you can opt to send a GET to the endpoint /v1/orders/{id} to retrieve these updated data.To do this, send a POST with your test Access TokenPrivate key of the application created in Mercado Pago, used in the backend. You can access it through Your integrations > Integration details > Tests > Test credentials. and the required parameters listed below to the endpoint /v1/ordersAPI and execute the request.
curl
curl --location --request POST 'https://api.mercadopago.com/v1/orders' \ -H 'Authorization: Bearer <YOUR_ACCESS_TOKEN>' \ -H 'X-Idempotency-Key: <SOME_UNIQUE_VALUE>' \ -H 'Content-Type: application/json' \ -d '{ "type": "online", "total_amount": "5000", "external_reference": "ext_ref_1234", "processing_mode": "automatic", "expiration_time": "PT20M", "payer": { "email": "test_user_co@testuser.com", "entity_type": "individual", "identification": { "type": "NIT", "number": "76262349" }, "first_name": "John", "last_name": "Doe", "phone": { "area_code": "57", "number": "3001234567" }, "address": { "street_name": "Calle 10", "street_number": "100", "city": "Bogota", "zip_code": "110111", "neighborhood": "Centro" } }, "transactions": { "payments": [ { "amount": "5000", "payment_method": { "id": "pse", "type": "bank_transfer", "financial_institution": "1051" } } ] }, "additional_info": { "payer.ip_address": "200.100.50.25" }, "config": { "online": { "callback_url": "https://merchant.com/pse/return" } } }'
See in the table below the descriptions of the parameters that are required in the request and those that, although optional, have some important particularities worth highlighting.
| Attribute | Type | Description | Required |
Authorization | Header | Refers to your private key, the test Access TokenPrivate key of the application created in Mercado Pago, used in the backend. You can access it through Your integrations > Integration details > Tests > Test credentials.. | Required |
X-Idempotency-Key | Header | Idempotency key. This key ensures that each request is processed only once, avoiding duplicates. Use a unique value in the request header, such as a UUID V4 or a random string. | Required |
total_amount | Body. String | Total transaction amount. Must be greater than 0. | Required |
external_reference | Body. String | External order reference, which can be, for example, a hashcode functioning as a transaction source identifier. | Required |
processing_mode | Body. String | Order processing mode. Possible values are: - automatic: to create and process the order in automatic mode. - manual: to create the order and process it later. For more information, access the Integration model section. | Required |
expiration_time | Body. String | Allows you to set the payment expiration time using ISO 8601 duration format. The maximum time for the buyer to complete the PSE payment is 20 minutes ("PT20M"). After this period, the payment expires automatically and the transaction status changes to expired. | Optional |
transactions.payments.payment_method.id | Body. String | Payment method identifier. The value must be pse. | Required |
transactions.payments.payment_method.type | Body. String | Payment method type. The value must be bank_transfer. | Required |
transactions.payments.payment_method.financial_institution | Body. String | Code of the bank used for the transfer. Available banks must be obtained through the List banks step. | Required |
payer.email | Body. String | Buyer's email address. | Required |
payer.entity_type | Body. String | Person type (natural or legal). Possible values are: individual or association. | Required |
payer.identification.type | Body. String | Buyer'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 and DI (Documento de Identificación). | Required |
payer.identification.number | Body. String | Buyer's identification number. | Required |
payer.address.zip_code | Body. String | Payer's address postal code. | Required |
payer.address.street_name | Body. String | Payer's address street name. | Required |
payer.address.street_number | Body. String | Payer's address number. If there is no number, send "S/N". | Required |
payer.address.neighborhood | Body. String | Neighborhood where the payer's address is located. | Required |
payer.address.city | Body. String | City where the payer's address is located. | Required |
payer.phone.area_code | Body. String | Buyer's phone area code. Must have 3 digits. | Required |
payer.phone.number | Body. String | Buyer's phone number. Must have 1 to 5 digits and only accepts numeric characters. | Required |
additional_info.payer.ip_address | Body. String | IP address of the buyer where the payment is generated. | Required |
config.online.callback_url | Body. String | URL to which the buyer will be redirected after making the payment on the bank's page. Must not be null or empty and must have a maximum of 512 characters. | Required |
The response will return the redirect_url parameter, which contains the URL with the instructions for the buyer to complete the payment. You must redirect them to that page, following the instructions in the Make the payment available step. Additionally, it will show the action_required status with status_detail=waiting_transfer until the payment is made.
json
{ "id": "ORDOMG01KNSWP75YHQJ9EKKB13QCFF25", "total_amount": "5000", "total_paid_amount": "0", "status": "action_required", "status_detail": "waiting_transfer", "config": { "online": { "callback_url": "https://merchant.com/pse/return" } }, "transactions": { "payments": [ { "payment_method": { "id": "pse", "type": "bank_transfer", "financial_institution": "1051", "redirect_url": "https://www.mercadopago.com.co/sandbox/payments/154019160992/bank_transfer?caller_id=2676136474&hash=fd05e480-3f06-4f41-90dc-b6910946e196" } } ] } }
Among the returned parameters, we have those indicated in the table below.
| Attribute | Type | Description |
status | String | Returns the transaction status. In this case, it will return action_required to indicate the need for an action to complete the processing, that is, until the payment is made. |
status_detail | String | In cases of bank transfer, as is the case with PSE payments, the status_detail obtained is waiting_transfer, waiting for the user to complete the payment process at the bank. |
transactions.payments.payment_method.redirect_url | String | URL to which the buyer must be redirected to complete the payment flow on the chosen bank's platform. See more information in Make the payment available. |
After the payment is created, and for the client to be able to make the transfer, it is necessary to redirect them to the URL returned in the response to the order creation under the parameter redirect_url.
json
{ [...] "transactions": { "payments": [ { "payment_method": { "id": "pse", "type": "bank_transfer", "financial_institution": "1051", "redirect_url": "https://www.mercadopago.com.co/sandbox/payments/154019160992/bank_transfer?caller_id=2676136474&hash=fd05e480-3f06-4f41-90dc-b6910946e196" } } ] } }
To do this, you have two options:
- You can automatically redirect the user.
- You can provide that URL to the user through a clickable button, following the example below.
html
<a href="https://www.mercadopago.com.co/payments/154019160992/bank_transfer?caller_id=2676136474&hash=fd05e480-3f06-4f41-90dc-b6910946e196" target="_blank">Pay with PSE</a>
If you wish, you can cancel a payment created, as long as it is pending or in process; that is, with status=action_required.
Additionally, we recommend canceling payments that were not made by the established due date to avoid billing and reconciliation issues.
For more information, please consult the Refunds and cancellations section.