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.
javascriptdocument.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 request with your test Access TokenPrivate key linked to an application, used in the backend to authenticate operations in the test environment. You can find it in the application's Credentials section, within the test environment. It cannot process real payments, must never be exposed in the frontend, and starts with the `APP_USR` prefix. to the endpoint /v1/payment_methodsGET 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.
javascriptfunction 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 request to the endpoint /v1/orders/{id}GET to retrieve these updated data.To do this, send a request with your test Access TokenPrivate key linked to an application, used in the backend to authenticate operations in the test environment. You can find it in the application's Credentials section, within the test environment. It cannot process real payments, must never be exposed in the frontend, and starts with the `APP_USR` prefix. and the required parameters listed below to the endpoint /v1/ordersPOST.
processing_mode=manual) can have an expiration period defined in the expiration_time field. If the field is not sent, the default period is P1D (one day). When the period expires before the order reaches a final status, it is automatically closed, the associated pending payments are canceled, and the integrator is notified.curlcurl --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", "payer": { "email": "test_payer@example.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", "expiration_time": "PT20M", "payment_method": { "id": "pse", "type": "bank_transfer", "financial_institution": "1051" } } ] }, "additional_info": { "payer.ip_address": "200.100.50.25" }, "config": { "location": { "state_id": "CO-DC" }, "online": { "callback_url": "https://merchant.com/pse/return" } } }'
429 Too Many Requests error, wait the time indicated in the Retry-After response header before retrying. See Possible errors for more details.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.
| Parameter | Type | Description | Required |
Authorization | Header | Refers to your private key, the test Access TokenPrivate key linked to an application, used in the backend to authenticate operations in the test environment. You can find it in the application's Credentials section, within the test environment. It cannot process real payments, must never be exposed in the frontend, and starts with the `APP_USR` prefix.. | 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 | When an order is created in manual mode (processing_mode=manual), this parameter lets you define its expiration time using the ISO 8601 duration format. The period starts at created_date and the maximum limit is 2920 hours. If not sent, the default value will be P1D (one day). This parameter must not be sent when creating an order in automatic mode (processing_mode=automatic). | Optional |
config.location | Body. Object | Information about the payer's location. | Optional |
config.location.state_id | Body. String | Code of the payer's state, province, or administrative region, formed by the two-letter country code, followed by a hyphen and the location code. See the accepted values.Amazonas (CO-AMA); Antioquia (CO-ANT); Arauca (CO-ARA); Atlántico (CO-ATL); Bogotá D.C. (CO-DCO); Bolívar (CO-BOL); Boyaca (CO-BOY); Caldas (CO-CAL); Caqueta (CO-CAQ); Casanare (CO-CAS); Cauca (CO-CAU); Cesar (CO-CES); Choco (CO-CHO); Córdoba (CO-COR); Cundinamarca (CO-CUN); Guainía (CO-GUA); Guajira (CO-LAG); Guaviare (CO-GUV); Huila (CO-HUI); Magdalena (CO-MAG); Meta (CO-MET); Nariño (CO-NAR); Norte De Santander (CO-NSA); Putumayo (CO-PUT); Quindio (CO-QUI); Risaralda (CO-RIS); San Andres y Providencia (CO-SAP); Santander (CO-SAN); Sucre (CO-SUC); Tolima (CO-TOL); Valle Del Cauca (CO-VAC); Vaupes (CO-VAU); Vichada (CO-VID). | Optional |
transactions.payments.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.
| Parameter | 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. |
expiration_time | String | Expiration period (in ISO 8601 format) returned for an order created in manual mode (processing_mode=manual), counted from created_date. When omitted from the request, the returned default will be P1D (one day). This parameter will not be returned for an order created in automatic mode (processing_mode=automatic). |
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.