
Mercadopago
- 11 installs
- 1 repo stars
- Updated March 21, 2026
- ansanabria/skills
mercadopago is a skill for integrating MercadoPago payment solutions, SDKs, checkout, subscriptions and webhooks across Latin America.
About
mercadopago is a skill for integrating MercadoPago payment solutions, the payment platform of Mercado Libre used across Latin America. A developer uses it when working with MercadoPago APIs, SDKs, checkout solutions, payment processing, subscriptions or webhooks, especially in Argentina, Brazil, Mexico, Colombia, Chile, Peru and Uruguay. It navigates to references for Checkout Pro, Checkout Bricks, Checkout API, payment methods, 3DS, subscriptions, refunds, webhooks and testing.
- Guide for integrating MercadoPago payments across Latin America
- Covers Checkout Pro, Checkout Bricks, Checkout API, subscriptions and webhooks
- Includes per-country payment methods and MercadoPago MCP tools
Mercadopago by the numbers
- 11 all-time installs (skills.sh)
- Ranked #3,574 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
mercadopago capabilities & compatibility
- Capabilities
- payment integration · checkout · webhooks · subscriptions
- Works with
- stripe
- Use cases
- api development
- Pricing
- Bring your own API key
What mercadopago says it does
Comprehensive guide for integrating MercadoPago payment solutions.
MercadoPago is the payment platform of Mercado Libre, operating across Latin America.
npx skills add https://github.com/ansanabria/skills --skill mercadopagoAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 11 |
|---|---|
| repo stars | ★ 1 |
| Last updated | March 21, 2026 |
| Repository | ansanabria/skills ↗ |
What it does
Integrate MercadoPago checkout, payments, subscriptions and webhooks for a Latin American app.
Who is it for?
Integrating MercadoPago checkout and payment processing for Latin American markets.
Skip if: Payment gateways outside the MercadoPago ecosystem.
When should I use this skill?
Working with MercadoPago APIs, checkout, payments, subscriptions or webhooks, or accepting payments online in Latin America.
What you get
A correct MercadoPago integration with the right checkout product, payment methods, webhooks and 3DS for the target country.
By the numbers
- 7 supported countries
- 3 checkout products
- 4 MercadoPago MCP tools
Files
MercadoPago Integration Skill
MercadoPago is the payment platform of Mercado Libre, operating across Latin America.
Quick Reference
| Country | Code | Currency |
|---|---|---|
| Argentina | MLA | ARS |
| Brazil | MLB | BRL |
| Mexico | MLM | MXN |
| Colombia | MCO | COP |
| Chile | MLC | CLP |
| Peru | MPE | PEN |
| Uruguay | MLU | UYU |
Navigation
Getting Started
- SDKs and Setup - Frontend/backend SDKs, initialization
- Authentication - Credentials, API keys
Integration Products
- Checkout Pro - Hosted redirect checkout
- Checkout Bricks - Modular embedded components
- Checkout API - Full API control
Core Topics
- Payment Methods - Available methods by country
- Payment Flow - Complete payment process
- Payment Status - Status codes reference
- Webhooks - Notifications setup
Features
- 3DS Authentication - 3D Secure integration
- Subscriptions - Recurring payments
- Refunds - Cancellations and refunds
Colombia (MCO)
- Colombia Guide - PSE, Efecty, specific methods
Utilities
- Testing - Test cards, test users
- Error Handling - Error codes and handling
- Security - Best practices, PCI compliance
- API Reference - Endpoint documentation
---
Country-Specific Payment Methods
| Country | Cards | Cash | Bank Transfer | Wallet |
|---|---|---|---|---|
| MCO | Visa, Mastercard | Efecty | PSE | Mercado Pago |
| MLA | Visa, Mastercard | Rapipago, Pago Fácil | - | Mercado Pago |
| MLB | Visa, Mastercard | Boleto | Pix | Mercado Pago |
| MLM | Visa, Mastercard | OXXO | SPEI | Mercado Pago |
| MLC | Visa, Mastercard | - | - | Mercado Pago |
| MPE | Visa, Mastercard | PagoEfectivo | - | Mercado Pago |
| MLU | Visa, Mastercard | Abitab, Redpagos | - | Mercado Pago |
---
Common Tasks
Process a payment: 1. Read Payment Flow 2. Use SDKs to tokenize card 3. Send to backend, create payment via API
Set up webhooks: 1. Read Webhooks 2. Configure notifications 3. Validate signatures
Add new payment method: 1. Check Payment Methods for your country 2. See Colombia for PSE/Efecty
---
MCP Tools Available
When using MercadoPago MCP, access these tools:
search_documentation- Search docscreate_test_user- Create test userssave_webhook- Configure webhooksquality_evaluation- Check integration quality
3DS 2.0 Authentication
3D Secure (3DS 2.0) validates cardholder identity during purchase, reducing fraud and increasing approval rates.
Benefits
- Higher approval rates - Authenticated transactions less likely to be declined
- Liability shift - Reduces chargeback risk for merchants
- Buyer protection - Reduces fraud risk for customers
How It Works
1. Cardholder enters card details at checkout 2. Issuer displays authentication challenge (iframe/modal) 3. Cardholder verifies identity (OTP, biometric, etc.) 4. Authentication result returned to merchant 5. Payment proceeds based on result
Integration Options
Checkout API (Orders API)
// Create order with 3DS
const response = await fetch('https://api.mercadopago.com/v1/orders', {
method: 'POST',
headers: {
'Authorization': 'Bearer {access_token}',
'Content-Type': 'application/json',
'X-Idempotency-Key': '{unique_key}'
},
body: JSON.stringify({
type: 'online',
external_reference: 'order_123',
total_amount: 150.00,
config: {
online: {
transaction_security: {
validation: 'on_fraud_risk',
liability_shift: 'required'
}
}
},
payer: { email: 'buyer@example.com' },
transactions: {
payments: [{
amount: 150.00,
payment_method: {
id: 'master',
type: 'credit_card',
token: '{card_token}',
installments: 1
}
}]
}
})
});Checkout API (Payments API)
// Create payment with 3DS
const payment = await mercadopago.payment.create({
transaction_amount: 150.00,
token: 'card_token',
payment_method_id: 'visa',
payer: { email: 'buyer@example.com' },
three_d_secure_mode: 'optional'
}, { idempotencyKey: 'unique_key' });Checkout Bricks
3DS is handled automatically when enabled in initialization:
const bricksBuilder = mp.bricks();
// With 3DS enabled (default for high-value transactions)
const cardPayment = bricksBuilder.create('cardPayment', 'cardPaymentBrick_container', {
initialization: { /* ... */ },
callbacks: { /* ... */ }
});Response Handling
No Challenge Required
Transaction proceeds automatically:
{
"status": "approved",
"status_detail": "accredited"
}Challenge Required
{
"status": "pending",
"status_detail": "pending_challenge",
"three_ds_info": {
"external_resource_url": "https://acs.bank.com/challenge",
"creq": "eyJ0aHJlZURTU2VydmVyVHJhbnNJRCI6..."
}
}Display Challenge (iframe)
function display3DSChallenge(payment) {
const { three_ds_info } = payment;
if (payment.status === 'pending' &&
payment.status_detail === 'pending_challenge') {
// Create iframe for challenge
const iframe = document.createElement('iframe');
iframe.id = '3ds-challenge';
iframe.style.cssText = 'width:500px;height:600px;border:none;';
document.body.appendChild(iframe);
// Create form to post to ACS
const form = iframe.contentWindow.document.createElement('form');
form.method = 'post';
form.action = three_ds_info.external_resource_url;
const creqField = iframe.contentWindow.document.createElement('input');
creqField.type = 'hidden';
creqField.name = 'creq';
creqField.value = three_ds_info.creq;
form.appendChild(creqField);
iframe.contentWindow.document.body.appendChild(form);
form.submit();
}
}
// Listen for challenge completion
window.addEventListener('message', (event) => {
if (event.data.status === 'COMPLETE') {
// Challenge finished - check payment status
checkPaymentStatus();
}
});3DS Status Codes
| 3DS Status | Meaning |
|---|---|
authenticated | Authentication successful |
attempted | Authentication attempted (partial liability) |
not_authenticated | Authentication failed |
challenge | Challenge displayed/required |
Payment Status After 3DS
| Final Status | Status Detail | Meaning |
|---|---|---|
approved | accredited | Payment successful |
rejected | cc_rejected_3ds_challenge | Challenge failed |
cancelled | expired | Challenge timeout (40 min) |
Testing 3DS
Test Cards (MCO)
| Card | Challenge Flow |
|---|---|
| Mastercard 5254 1336 7440 3564 | Challenge required |
| Visa 4013 5406 8274 6260 | No challenge |
Test Cardholder Names
| Name | Result |
|---|---|
APRO-AUTH | Approved, authenticated |
APRO-ATMT | Approved, attempted |
OTHE-NAUT | Rejected, not authenticated |
APRO-CHOK | Challenge, then approved |
OTHE-CHNO | Challenge, then rejected |
Test Card Numbers (MCO)
// Successful 3DS
const testCard = {
number: '5254133674403564', // Mastercard
securityCode: '123',
expirationDate: '11/30',
cardholderName: 'APRO-CHOK'
};Requirements
1. TLS 1.2+ - Required for production 2. HTTPS - Challenge URLs must be served over HTTPS 3. Merchant category - Some categories may have restrictions 4. Acquirer support - 3DS requires acquirer support
When 3DS is Recommended
| Scenario | Recommendation |
|---|---|
| High-value transactions | Enable |
| New customer cards | Enable |
| High fraud risk markets | Enable |
| Low-value transactions | Optional |
| Returning customer, known device | Optional |
Best Practices
1. Show loading state - While challenge is in progress 2. Handle timeout - Challenge expires after 40 minutes 3. Mobile-friendly - Responsive iframe for challenge 4. Clear messaging - Explain 3DS to customers if prompted 5. Fallback - Have alternative payment methods available
Next Steps
- Payment flow
- Reduce rejections
- Test integration
MercadoPago API Reference
Base URLs
- Production:
https://api.mercadopago.com - Sandbox:
https://api.mercadopago.com(test mode)
Authentication
Authorization: Bearer YOUR_ACCESS_TOKENCommon Headers
Content-Type: application/json
X-Idempotency-Key: UNIQUE_KEY---
Payments API
Create Payment
POST /v1/paymentsRequest Body:
{
"transaction_amount": 100.00,
"token": "CARD_TOKEN",
"payment_method_id": "visa",
"payer": {
"email": "buyer@email.com",
"identification": {
"type": "CC",
"number": "123456789"
}
},
"notification_url": "https://yoursite.com/webhook"
}Response:
{
"id": 123456789,
"status": "approved",
"status_detail": "accredited",
"transaction_amount": 100.00,
"payment_method_id": "visa",
"payer": { "email": "buyer@email.com" }
}Get Payment
GET /v1/payments/{PAYMENT_ID}Refund Payment
POST /v1/payments/{PAYMENT_ID}/refundsRequest Body (partial refund):
{
"amount": 50.00
}Cancel Payment
PUT /v1/payments/{PAYMENT_ID}{
"status": "cancelled"
}---
Payment Methods API
List Payment Methods
GET /v1/payment_methodsResponse includes:
{
"id": "visa",
"name": "Visa",
"payment_type_id": "credit_card",
"status": "active"
}---
Preferences API
Create Preference
POST /v1/checkout/preferences{
"items": [
{
"title": "Product Name",
"quantity": 1,
"price": 100.00,
"currency_id": "COP"
}
],
"payer": {
"email": "buyer@email.com"
},
"payment_methods": {
"excluded_payment_types": [
{ "id": "amex" }
]
},
"back_urls": {
"success": "https://yoursite.com/success",
"pending": "https://yoursite.com/pending",
"failure": "https://yoursite.com/failure"
}
}Get Preference
GET /v1/checkout/preferences/{PREFERENCE_ID}---
Orders API
Create Order
POST /v1/orders{
"type": "online",
"external_reference": "ORDER_123",
"total_amount": 100.00,
"currency_id": "COP",
"payer": {
"email": "buyer@email.com"
},
"transactions": {
"payments": [
{
"amount": 100.00,
"payment_method": {
"id": "visa",
"type": "credit_card"
}
}
]
}
}---
Subscriptions API
Create Preapproval Plan
POST /v1/preapproval_plans{
"description": "Monthly Subscription",
"auto_recurring": {
"frequency": 1,
"frequency_type": "months",
"transaction_amount": 100.00,
"currency_id": "COP"
}
}Create Subscription
POST /v1/preapprovals{
"preapproval_plan_id": "PLAN_ID",
"payer": {
"email": "subscriber@email.com"
}
}Update Subscription
PUT /v1/preapprovals/{SUBSCRIPTION_ID}---
Identification Types API
Get Identification Types
GET /v1/identification_typesResponse:
[
{ "id": "CC", "name": "Cédula de Ciudadanía" },
{ "id": "CE", "name": "Cédula de Extranjería" },
{ "id": "NIT", "name": "Número de Identificación Tributaria" }
]---
Installments API
Get Installments
GET /v1/payment_methods/installmentsParameters:
payment_method_id: Card issuer (e.g.,visa)amount: Transaction amountissuer_id: Card issuer ID (optional)
---
Important Endpoints Summary
| Endpoint | Method | Description |
|---|---|---|
/v1/payments | POST | Create payment |
/v1/payments/{id} | GET | Get payment |
/v1/payments/{id}/refunds | POST | Refund payment |
/v1/payment_methods | GET | List payment methods |
/v1/checkout/preferences | POST | Create preference |
/v1/orders | POST | Create order |
/v1/preapprovals | POST | Create subscription |
/v1/identification_types | GET | Get ID types |
Authentication and Credentials
Credential Types
Public Key
- Used in frontend code
- Identifies your application
- Safe to expose in browser
- Found in: Tus integraciones > Detalles de aplicación
Access Token
- Used in backend code
- Identifies your account
- Never expose in frontend
- Has test and production versions
---
Environments
| Environment | Use For | Credentials |
|---|---|---|
| Test | Development, testing | Test keys (prefix TEST_) |
| Production | Real transactions | Production keys |
---
Finding Credentials
1. Go to Tus integraciones 2. Select your application 3. View credentials under Credenciales de prueba or Credenciales de producción
---
Configuration
Frontend (Public Key)
const mp = new MercadoPago('APP_ID', {
locale: 'es-CO'
});Backend (Access Token)
// Node.js
MercadoPagoConfig.setAccessToken('ACCESS_TOKEN');
// Python
sdk = mercadopago.SDK("ACCESS_TOKEN")
// PHP
MercadoPagoConfig::setAccessToken("ACCESS_TOKEN");---
Security Best Practices
1. Never commit credentials to version control 2. Use environment variables for all secrets 3. Rotate credentials periodically 4. Use test mode during development 5. Validate webhook signatures to verify sender
---
Sharing Credentials
If building for another seller, use credential sharing:
- Access their credentials via app settings
- Never store credentials of other users
- Use OAuth for authorized access
---
Credential Validation
Test your credentials:
curl -X GET https://api.mercadopago.com/v1/payment_methods \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'Checkout API
Full API control for custom checkout experiences. Highest customization but requires more development effort. Handle card data directly or use tokenization.
When to Use
- Full control over UI/UX needed
- Custom checkout flow
- Mobile app integration
- Complex business logic
---
Integration Options
| Option | PCI Level | Complexity |
|---|---|---|
| Core Methods (Secure Fields) | SAQ A | High |
| Cardform | SAQ A | Medium |
---
Secure Fields (Core Methods)
Card data is captured in MercadoPago-hosted iframes - you never handle raw card data.
1. Add HTML Containers
<div id="cardNumber"></div>
<div id="expirationDate"></div>
<div id="securityCode"></div>
<div id="cardholderName"></div>
<select id="docType"></select>
<input id="docNumber"></input>2. Create Fields
const cardNumber = mp.fields.create('cardNumber', {
placeholder: '1234 1234 1234 1234'
}).mount('cardNumber');
const expirationDate = mp.fields.create('expirationDate', {
placeholder: 'MM/YY'
}).mount('expirationDate');
const securityCode = mp.fields.create('securityCode', {
placeholder: '123'
}).mount('securityCode');3. Listen for BIN Changes
cardNumber.on('binChange', async (data) => {
const { bin } = data;
if (bin.length === 6) {
const methods = await mp.getPaymentMethods({ bin });
const issuers = await mp.getIssuers({
paymentMethodId: methods.results[0].id,
bin
});
}
});4. Create Card Token
const token = await mp.fields.createCardToken({
cardholderName: document.getElementById('cardholderName').value,
identificationType: document.getElementById('docType').value,
identificationNumber: document.getElementById('docNumber').value,
});
// token.id is the card token to send to backend5. Send to Backend
fetch('/process_payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token: token.id,
transactionAmount: 100000,
paymentMethodId: 'visa',
payer: { email: 'buyer@email.com' }
})
});---
Cardform (Legacy)
Deprecated - use Secure Fields instead.
---
Create Payment (Backend)
const payment = await payments.create({
transaction_amount: 100000,
token: 'CARD_TOKEN_FROM_FRONTEND',
payment_method_id: 'visa',
payer: {
email: 'buyer@email.com',
identification: {
type: 'CC',
number: '123456789'
}
},
installments: 1,
description: 'Product description',
notification_url: 'https://yoursite.com/webhook'
});---
Payment Response
{
id: 1234567890,
status: 'approved', // or 'pending', 'rejected'
status_detail: 'accredited', // or 'pending_waiting_payment', etc.
transaction_amount: 100000,
payment_method_id: 'visa',
payer: { email: 'buyer@email.com' }
}---
Idempotency
Always use idempotency keys to prevent duplicate payments:
const requestOptions = {
idempotencyKey: crypto.randomUUID()
};
payments.create(paymentData, requestOptions);Checkout Bricks Integration Guide
Bricks Overview
Checkout Bricks are pre-built, modular UI components that provide a secure, PCI-compliant way to accept payments. Card data is tokenized in MercadoPago's iframes, so you never handle raw card numbers.
Available Bricks
| Brick | Purpose | Use Case |
|---|---|---|
| Payment Brick | All payment methods | Full checkout with cards, PSE, cash |
| Card Payment Brick | Cards only | Simple card-only checkout |
| Wallet Brick | MercadoPago account | Returning users with saved cards |
| Status Screen Brick | Payment status | Show payment result |
| Brand Brick | Card brands | Show accepted card logos |
---
Common Initialization Pattern
// 1. Load SDK
const mp = new MercadoPago('PUBLIC_KEY');
// 2. Create bricks builder
const bricksBuilder = mp.bricks();
// 3. Create and render brick
const brick = await bricksBuilder.create(
'brickType', // 'payment', 'cardPayment', 'wallet', 'statusScreen'
'containerId', // HTML element ID
{
initialization: { /* ... */ },
customization: { /* ... */ },
callbacks: { /* ... */ }
}
);---
Payment Brick (Colombia - MCO)
Basic Integration
<div id="paymentBrick_container"></div>const paymentBrick = await bricksBuilder.create(
'payment',
'paymentBrick_container',
{
initialization: {
amount: 100000, // COP
payer: {
email: 'buyer@email.com'
}
},
customization: {
paymentMethods: {
creditCard: ['visa', 'mastercard'],
debitCard: ['visa_debit', 'mastercard_debit'],
bankTransfer: ['pse'],
cash: ['efecty'],
wallet: ['mercadopago']
},
visual: {
style: {
theme: 'default', // or 'dark', 'bootstrap'
customVariables: {
primaryColor: '##FF0000'
}
}
}
},
callbacks: {
onReady: () => {
// Brick is ready
},
onSubmit: (formData) => {
// Send to your backend
// formData contains: token, paymentMethodId, issuer_id, cardholderName, etc.
return fetch('/process_payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
}).then(res => res.json());
},
onError: (error) => {
console.error('Error:', error);
},
onBrickReady: () => {},
onExit: () => {}
}
}
);FormData Returned by onSubmit
{
token: 'card_token_id',
paymentMethodId: 'visa',
issuer_id: '1234',
cardholderName: 'JUAN PEREZ',
cardholderEmail: 'buyer@email.com',
identificationType: 'CC',
identificationNumber: '123456789'
}---
Card Payment Brick
Integration
<div id="cardPaymentBrick_container"></div>const cardPaymentBrick = await bricksBuilder.create(
'cardPayment',
'cardPaymentBrick_container',
{
initialization: {
totalAmount: 100000,
paymentAmount: 100000,
},
customization: {
visual: {
style: {
theme: 'default',
customVariables: {
fontFamily: 'Roboto'
}
}
},
form: {
cardNumber: { placeholder: '1234 1234 1234 1234' },
expirationDate: { placeholder: 'MM/YY' },
securityCode: { placeholder: '123' },
cardholderName: { placeholder: 'Nombre como aparece en la tarjeta' },
cardholderEmail: { placeholder: 'email@email.com' }
}
},
callbacks: {
onSubmit: (formData) => {
// formData.token - card token
// formData.paymentMethodId - card issuer
return fetch('/process_payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
});
},
onError: (error) => console.error(error),
onReady: () => {}
}
}
);---
Wallet Brick
Integration
<div id="walletBrick_container"></div>const walletBrick = await bricksBuilder.create(
'wallet',
'walletBrick_container',
{
initialization: {
preferenceId: 'PREFERENCE_ID', // From your backend
redirectMode: 'self' // or 'parent'
},
callbacks: {
onReady: () => {},
onError: (error) => console.error(error)
}
}
);Backend - Create Preference
const preference = await preferences.create({
items: [{
title: 'Product',
quantity: 1,
price: 100000,
currency_id: 'COP'
}],
payer: {
email: 'buyer@email.com'
},
back_urls: {
success: 'https://yoursite.com/success',
pending: 'https://yoursite.com/pending',
failure: 'https://yoursite.com/failure'
}
});
// Return preference.id to frontend---
Status Screen Brick
Integration
<div id="statusScreenBrick_container"></div>const statusScreenBrick = await bricksBuilder.create(
'statusScreen',
'statusScreenBrick_container',
{
initialization: {
paymentId: 'PAYMENT_ID_FROM_URL_OR_STORAGE'
},
callbacks: {
onReady: () => {},
onError: (error) => console.error(error)
}
}
);3DS Challenge Integration
// For 3DS, pass additional info
const statusScreenBrick = await bricksBuilder.create(
'statusScreen',
'statusScreenBrick_container',
{
initialization: {
paymentId: paymentId,
additionalInfo: {
externalResourceURL: threeDsInfo.external_resource_url,
creq: threeDsInfo.creq
}
},
callbacks: {
onReady: () => {},
onError: (error) => console.error(error)
}
}
);---
Themes and Styling
Available Themes
| Theme | Description |
|---|---|
default | Standard MercadoPago theme |
dark | Dark theme |
bootstrap | Bootstrap-compatible |
Custom Variables
const customization = {
visual: {
style: {
theme: 'default',
customVariables: {
primaryColor: '##FF5733',
secondaryColor: '#333333',
backgroundColor: '#FFFFFF',
fontFamily: 'Roboto, sans-serif',
borderRadius: '8px',
buttonHeight: '48px'
}
}
}
};---
Languages
Bricks supports: Spanish (es), Portuguese (pt), English (en)
// Set via SDK initialization
const mp = new MercadoPago('PUBLIC_KEY', {
locale: 'es-CO' // Spanish Colombia
});---
Responsive Behavior
Bricks automatically adapt to container size. Set container CSS:
#paymentBrick_container {
width: 100%;
max-width: 400px;
min-height: 600px;
}---
Security Notes
1. Card data never touches your server - handled in MercadoPago iframe 2. Token generated is single-use and expires quickly 3. Use HTTPS on your site 4. Validate all webhook notifications 5. PCI SAQ A compliance when using Bricks
---
Error Handling
callbacks: {
onError: (error) => {
// Error types:
// - 'invalid_fields': Form validation error
// - 'card_error': Card was rejected
// - 'network_error': Connection error
// - 'back_error': User exited
console.error('Error:', error);
}
}---
Migration from CardForm
CardForm is deprecated. Migrate to Card Payment Brick:
1. Replace CardForm script with MercadoPago.js V2 2. Replace form inputs with Brick container div 3. Update submit handler to use onSubmit callback 4. Token generation is handled automatically by Brick
Checkout Pro
Checkout Pro is a hosted payment page where buyers are redirected to MercadoPago to complete payment. Easiest integration with minimal PCI compliance requirements.
When to Use
- Quick integration needed
- Minimal customization required
- Hosted experience is acceptable
- Lower PCI compliance burden
---
Integration Flow
1. Create preference in backend 2. Redirect buyer to MercadoPago 3. Buyer completes payment 4. Redirect back to your site 5. Receive webhook notification
---
1. Create Preference (Backend)
const preference = await preferences.create({
items: [{
title: 'Product Name',
quantity: 1,
unit_price: 100000,
currency_id: 'COP'
}],
payer: {
email: 'buyer@email.com'
},
back_urls: {
success: 'https://yoursite.com/success',
pending: 'https://yoursite.com/pending',
failure: 'https://yoursite.com/failure'
},
auto_return: 'approved'
});
// preference.init_point - URL to redirect---
2. Redirect Buyer (Frontend)
Option A: Redirect Link
<a href="${preference.init_point}">Pay with MercadoPago</a>Option B: Wallet Brick
const mp = new MercadoPago('PUBLIC_KEY');
const bricksBuilder = mp.bricks();
bricksBuilder.create('wallet', 'container', {
initialization: { preferenceId: preference.id }
});---
Preference Options
Items
items: [{
id: 'item-id',
title: 'Product',
description: 'Product description',
picture_url: 'https://example.com/img.jpg',
quantity: 1,
unit_price: 100.00,
currency_id: 'COP'
}]Payer
payer: {
name: 'Juan',
surname: 'Perez',
email: 'buyer@email.com',
phone: { area_code: '57', number: '3001234567' },
address: { zip_code: '110111', street_name: 'Calle 123' }
}Payment Methods
payment_methods: {
excluded_payment_types: [{ id: 'amex' }],
excluded_payment_methods: [{ id: 'visa' }],
installments: 12 // Max installments
}Shipments
shipments: {
receiver_address: {
zip_code: '110111',
street_name: 'Calle 123',
city_name: 'Bogota',
state_name: 'Cundinamarca'
}
}External Reference
external_reference: 'ORDER_12345'---
Return URLs
back_urls: {
success: 'https://yoursite.com/success',
pending: 'https://yoursite.com/pending',
failure: 'https://yoursite.com/failure'
}---
Notification_url
For webhook notifications:
notification_url: 'https://yoursite.com/webhook'---
Get Preference
const pref = await preferences.get('PREFERENCE_ID');Colombia (MCO) Specific Information
Document Types
| Type | ID | Description |
|---|---|---|
| CC | CC | Cédula de Ciudadanía |
| CE | CE | Cédula de Extranjería |
| NIT | NIT | Número de Identificación Tributaria |
| Otro | Otro | Other |
Payment Methods
Cards
| Method | ID | Type |
|---|---|---|
| Visa Credit | visa | Credit Card |
| Mastercard Credit | mastercard | Credit Card |
| Visa Debit | visa_debit | Debit Card |
| Mastercard Debit | mastercard_debit | Debit Card |
Cash/Offline Payments
| Method | ID | Instructions |
|---|---|---|
| Efecty | efecty | Pay at Efecty stores |
Bank Transfer
| Method | ID | Description |
|---|---|---|
| PSE | pse | Pagos Seguros en Línea - Bank transfer from savings/checking accounts |
Digital Wallet
| Method | ID | Description |
|---|---|---|
| Cuenta Mercado Pago | mercadopago | Mercado Pago balance/wallet |
---
PSE (Bank Transfer) Integration
Creating Payment with PSE
// Backend
const payment = await payments.create({
transaction_amount: 100000, // COP amount
payment_method_id: 'pse',
payer: {
email: 'buyer@email.com',
entity_type: 'individual', // or 'association'
identification: {
type: 'CC',
number: '123456789'
}
},
transaction_details: {
financial_institution: '1001' // Bank code
}
});
// Response includes external_resource_url for bank redirectPSE Banks (financial_institution codes)
const pseBanks = {
'1001': 'Banco de Bogotá',
'1002': 'Banco de Occidente',
'1003': 'Banco Popular',
'1004': 'Bancolombia',
'1005': 'BBVA Colombia',
'1006': 'Bank Davivienda',
'1007': 'Banco Caja Social',
'1008': 'Banco AV Villas',
'1009': 'Bancoomeva',
'1010': 'Credifinanciera',
'1012': 'Cotrafa',
'1013': 'Confiar',
'1014': 'Nequi',
'1015': 'Scotiabank'
};---
Test Cards (Colombia)
| Card Number | Result |
|---|---|
| 4242 4242 4242 4242 | Approved |
| 4000 0000 0000 0002 | Rejected (insufficient funds) |
| 4000 0000 0000 0010 | Rejected (bad filled) |
---
Currency
- Currency Code: COP
- Currency Symbol: $
- Decimal Places: 2
---
Common Rejection Reasons for Colombia
| Status Detail | Cause | Solution |
|---|---|---|
rejected_by_issuer | Card issuer declined | Try different card |
high_risk | Fraud detection | Review transaction |
invalid_card_token | Token expired/invalid | Regenerate token |
bad_filled_card_data | Incorrect card data | Verify card info |
---
Cash Payment Flow (Efecty)
1. Create payment with efecty 2. Response includes external_resource_url 3. Redirect user to URL to print payment slip 4. User pays at Efecty store 5. MercadoPago notifies via webhook when payment is confirmed
---
Specific API Parameters for Colombia
Payer Object (Colombian specific)
const payer = {
email: 'buyer@email.com',
identification: {
type: 'CC', // or 'CE', 'NIT', 'Otro'
number: '123456789'
},
// For PSE:
entity_type: 'individual', // or 'association'
fiscal_id: '123456789' // For business payments
};---
Webhook Topics for Colombia
| Topic | Events |
|---|---|
payment | Payment created, updated |
order | Order created, updated |
subscription_authorized_payment | Recurring payment |
subscription_preapproval | Subscription events |
mp-connect | OAuth connection |
wallet_connect | Wallet transactions |
stop_delivery_op_wh | Fraud alerts |
topic_claims_integration_wh | Claims/disputes |
topic_card_id_wh | Card updates |
topic_merchant_order_wh | Merchant orders |
topic_chargebacks_wh | Chargebacks |
---
Go to Production Checklist (Colombia)
1. Use production credentials (not test) 2. Set up webhook notifications 3. Implement proper error handling 4. Add SSL certificate to site 5. Configure all payment methods 6. Test with real cards (small amounts) 7. Review rate limits and best practices
Error Handling
Comprehensive error codes and handling strategies for Mercado Pago integrations.
Error Categories
| Category | Source | Example |
|---|---|---|
| Card Token Errors | Client-side | Invalid card number |
| Payment Errors | Server-side | Insufficient funds |
| API Errors | Request validation | Missing parameters |
| Webhook Errors | Notification processing | Invalid signature |
Card Token Errors
These occur when creating card tokens client-side.
| Code | status_detail | Description | Action |
|---|---|---|---|
| 205 | - | Card number required | Prompt card number |
| 208 | - | Expiration month required | Prompt month |
| 209 | - | Expiration year required | Prompt year |
| 212 | - | Document type required | Prompt ID type |
| 213 | - | Document subtype required | Prompt ID number |
| 214 | - | Document number required | Prompt ID |
| 220 | - | Bank issuer required | Prompt bank |
| 221 | - | Cardholder name required | Prompt name |
| 224 | - | Security code required | Prompt CVV |
| E203 | - | Invalid security code | Check CVV format |
| E301 | - | Invalid card number | Check card number |
| 316 | - | Invalid cardholder name | Check name format |
| 322 | - | Invalid document type | Use correct type |
| 323 | - | Invalid document subtype | Check document |
| 324 | - | Invalid document number | Check number format |
| 325 | - | Invalid expiration month | Use 01-12 |
| 326 | - | Invalid expiration year | Use 4-digit year |
Payment Creation Errors
These occur when creating payments via API.
| Code | status_detail | Description | Action |
|---|---|---|---|
| 106 | - | Cannot operate between countries | Check payer/receiver locations |
| 109 | - | Invalid installments | Use valid installment count |
| 126 | - | Invalid payment state | Check payment status |
| 129 | - | Amount below minimum | Increase amount |
| 145 | - | Invalid users | Test/live user mismatch |
| 150 | - | Payer cannot pay | Check payer status |
| 151 | - | Payer cannot use method | Try different method |
| 160 | - | Collector cannot operate | Check collector status |
| 204 | - | Payment method unavailable | Try different method |
| 801 | - | Duplicate request | Use idempotency key |
Payment Status Errors
Rejection Reasons
| status_detail | Description | Recommended Action |
|---|---|---|
cc_rejected_bad_filled_card_number | Invalid card number | Ask for correct number |
cc_rejected_bad_filled_date | Invalid expiry | Ask for correct date |
cc_rejected_bad_filled_security_code | Invalid CVV | Ask for correct CVV |
cc_rejected_insufficient_amount | Insufficient funds | Ask for different card |
cc_rejected_invalid_installments | Invalid installments | Try different count |
cc_rejected_card_disabled | Card disabled | Activate card with bank |
cc_rejected_call_for_authorize | Requires authorization | Call bank to authorize |
cc_rejected_max_attempts | Max attempts reached | Try tomorrow or different card |
cc_rejected_duplicated_payment | Duplicate attempt | Use different payment |
cc_rejected_high_risk | High risk flagged | Try different payment method |
cc_rejected_blacklist | Blacklisted card | Use different card |
cc_rejected_other_reason | Issuer rejected | Contact issuer |
bank_error | Bank processing error | Retry later |
rejected_by_regulations | Regulatory rejection | Cannot proceed |
Handling Errors in Code
Client-Side Error Handling
// Using MercadoPago.js
const cardToken = await mp.getCardToken({
cardNumber: '5254133674403564',
cardholderName: 'TEST USER',
expirationMonth: '11',
expirationYear: '2026',
securityCode: '123',
identificationType: 'CC',
identificationNumber: '123456789'
}).catch(error => {
if (error.cause) {
// Handle specific validation error
console.log(error.cause.code); // e.g., '205'
}
});Server-Side Error Handling
try {
const payment = await mercadopago.payment.create({
transaction_amount: 150.00,
token: cardToken,
payment_method_id: 'mastercard',
payer: { email: 'buyer@example.com' }
});
if (payment.status === 'rejected') {
handleRejection(payment.status_detail);
}
} catch (error) {
if (error.status === 400) {
// Bad request - check parameters
console.log(error.response.details);
}
}Payment Rejection Handler
function handleRejection(statusDetail) {
const messages = {
'cc_rejected_bad_filled_card_number': 'Please check your card number',
'cc_rejected_bad_filled_date': 'Please check your card expiry',
'cc_rejected_bad_filled_security_code': 'Please check your CVV',
'cc_rejected_insufficient_amount': 'Insufficient funds. Try another card.',
'cc_rejected_card_disabled': 'Card disabled. Call your bank.',
'cc_rejected_call_for_authorize': 'Please authorize with your bank.',
'cc_rejected_max_attempts': 'Max attempts reached. Try tomorrow.',
'cc_rejected_duplicated_payment': 'Payment already processed.',
'cc_rejected_high_risk': 'Please try a different payment method.',
'default': 'Payment declined. Please try again.'
};
return messages[statusDetail] || messages['default'];
}API Response Errors
HTTP Status Codes
| Status | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 500 | Server Error |
Error Response Format
{
"status": 400,
"error": "bad_request",
"message": "Invalid payment_method_id",
"cause": [
{
"code": "126",
"description": "The action is not valid for payment state"
}
]
}Handling API Errors
async function createPayment(paymentData) {
try {
const response = await fetch('https://api.mercadopago.com/v1/payments', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json'
},
body: JSON.stringify(paymentData)
});
if (!response.ok) {
const error = await response.json();
if (response.status === 401) {
throw new Error('Invalid credentials');
}
if (response.status === 400) {
const cause = error.cause[0];
throw new Error(`Payment error: ${cause.description}`);
}
throw new Error('Payment failed');
}
return await response.json();
} catch (err) {
console.error('Payment error:', err.message);
throw err;
}
}Idempotency
Prevent duplicate payments using idempotency keys:
const idempotencyKey = crypto.randomUUID();
const payment = await mercadopago.payment.create({
transaction_amount: 150.00,
token: cardToken,
payment_method_id: 'visa',
payer: { email: 'buyer@example.com' }
}, {
idempotencyKey: idempotencyKey
});Retry Logic
async function createPaymentWithRetry(data, maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await createPayment(data);
} catch (error) {
lastError = error;
// Only retry on transient errors
if (!isTransientError(error)) {
throw error;
}
// Exponential backoff
await sleep(Math.pow(2, i) * 100);
}
}
throw lastError;
}
function isTransientError(error) {
const transientCodes = ['500', '503', 'ETIMEDOUT'];
return transientCodes.includes(error.status);
}User-Facing Messages
Provide clear messages to users:
| Error | User Message |
|---|---|
| Invalid card | "Please check your card details and try again." |
| Insufficient funds | "Insufficient funds. Please try a different card." |
| Expired card | "Card has expired. Please use a different card." |
| Bank declined | "Payment declined by your bank. Please contact them or try another method." |
| 3DS failed | "Verification failed. Please try again or use a different card." |
| Duplicate | "This payment was already processed." |
Logging
Log errors for debugging:
function logError(context, error) {
console.error({
timestamp: new Date().toISOString(),
context: context,
error: {
message: error.message,
code: error.code,
status: error.status,
stack: error.stack
},
request: {
path: error.config?.url,
method: error.config?.method
}
});
}Next Steps
- Payment flow
- Webhooks
- Security
Payment Flow
Complete payment processing flow from start to finish.
---
Flow Diagram
Frontend Backend MercadoPago
| | |
|-- 1. Collect card data ->| |
| |-- 2. Create payment ------>|
| | |
| |<--- 3. Payment response ----|
| | |
|<-- 4. Show result | |
| | |
| |<--- 5. Webhook notification|---
Step 1: Collect Payment Data (Frontend)
Using Checkout Bricks
const bricksBuilder = mp.bricks();
const paymentBrick = await bricksBuilder.create('payment', 'container', {
initialization: { amount: 100000 },
callbacks: {
onSubmit: (formData) => {
// formData contains token, paymentMethodId, etc.
return fetch('/process_payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
});
}
}
});Using Secure Fields
const token = await mp.fields.createCardToken({
cardholderName: 'Juan Perez',
identificationType: 'CC',
identificationNumber: '123456789'
});---
Step 2: Send to Backend
// Frontend
fetch('/process_payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token: 'card_token_id',
transactionAmount: 100000,
paymentMethodId: 'visa',
issuerId: '1234',
installments: 1,
payer: {
email: 'buyer@email.com',
identification: { type: 'CC', number: '123456789' }
}
})
});---
Step 3: Process Payment (Backend)
// Node.js
const payment = await payments.create({
transaction_amount: 100000,
token: req.body.token,
payment_method_id: req.body.paymentMethodId,
installments: req.body.installments,
payer: {
email: req.body.payer.email,
identification: req.body.payer.identification
}
});
// Response:
{
id: 1234567890,
status: 'approved',
status_detail: 'accredited',
// ...
}---
Step 4: Handle Response
// Backend sends response to frontend
res.json({
status: payment.status,
statusDetail: payment.status_detail,
paymentId: payment.id
});
// Frontend shows result
.then(response => response.json())
.then(result => {
if (result.status === 'approved') {
// Show success
} else if (result.status === 'pending') {
// Show pending (e.g., cash payment)
} else {
// Show failure
}
});---
Step 5: Webhook Notification
Your server receives webhook:
// Express example
app.post('/webhook', (req, res) => {
const { type, data } = req.body;
if (type === 'payment') {
const paymentId = data.id;
// Update order status
}
res.sendStatus(200);
});---
Complete Example
Frontend (HTML + JS)
<div id="paymentBrick_container"></div>
<script src="https://sdk.mercadopago.com/js/v2"></script>
<script>
const mp = new MercadoPago('PUBLIC_KEY');
const brick = await mp.bricks().create('payment', 'paymentBrick_container', {
initialization: { amount: 100000 },
callbacks: {
onSubmit: (formData) => {
return fetch('/api/process_payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
}).then(r => r.json());
}
}
});
</script>Backend (Node.js/Express)
app.post('/api/process_payment', async (req, res) => {
try {
const { token, paymentMethodId, issuerId, installments } = req.body;
const payment = await payments.create({
transaction_amount: 100000,
token,
payment_method_id: paymentMethodId,
installments: parseInt(installments),
payer: { email: 'buyer@email.com' }
});
res.json({
status: payment.status,
paymentId: payment.id
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});---
Cash Payment Flow (Different)
For cash payments like PSE or Efecty:
1. Create payment → status is pending 2. Response includes external_resource_url 3. Redirect user to complete payment 4. User pays at store/bank 5. Webhook notifies when payment is confirmed
Payment Methods
Get Available Payment Methods
curl -X GET https://api.mercadopago.com/v1/payment_methods \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'const methods = await paymentMethods.get();---
Payment Methods by Country
Colombia (MCO)
| Method | ID | Type |
|---|---|---|
| Visa Credit | visa | Credit Card |
| Mastercard Credit | mastercard | Credit Card |
| Visa Debit | visa_debit | Debit Card |
| Mastercard Debit | mastercard_debit | Debit Card |
| PSE | pse | Bank Transfer |
| Efecty | efecty | Cash |
| Cuenta Mercado Pago | mercadopago | Wallet |
Argentina (MLA)
| Method | ID | Type |
|---|---|---|
| Visa | visa | Credit/Debit |
| Mastercard | mastercard | Credit/Debit |
| American Express | amex | Credit |
| Rapipago | rapipago | Cash |
| Pago Fácil | pagofacil | Cash |
| Cuenta Mercado Pago | mercadopago | Wallet |
Brazil (MLB)
| Method | ID | Type |
|---|---|---|
| Visa | visa | Credit/Debit |
| Mastercard | mastercard | Credit/Debit |
| Pix | pix | Instant Payment |
| Boleto | bolbradesco | Cash |
| Hipercard | hipercard | Credit |
| Elo | elo | Credit |
Mexico (MLM)
| Method | ID | Type |
|---|---|---|
| Visa | visa | Credit/Debit |
| Mastercard | mastercard | Credit/Debit |
| OXXO | oxxo | Cash |
| SPEI | spei | Bank Transfer |
| Cuenta Mercado Pago | mercadopago | Wallet |
Chile (MLC)
| Method | ID | Type |
|---|---|---|
| Visa | visa | Credit/Debit |
| Mastercard | mastercard | Credit/Debit |
| Cuenta Mercado Pago | mercadopago | Wallet |
Peru (MPE)
| Method | ID | Type |
|---|---|---|
| Visa | visa | Credit/Debit |
| Mastercard | mastercard | Credit/Debit |
| PagoEfectivo | pagoefectivo | Cash |
| Cuenta Mercado Pago | mercadopago | Wallet |
Uruguay (MLU)
| Method | ID | Type |
|---|---|---|
| Visa | visa | Credit/Debit |
| Mastercard | mastercard | Credit/Debit |
| Abitab | abitab | Cash |
| Redpagos | redpagos | Cash |
| Cuenta Mercado Pago | mercadopago | Wallet |
---
Document Types by Country
Colombia (MCO)
| ID | Name |
|---|---|
CC | Cédula de Ciudadanía |
CE | Cédula de Extranjería |
NIT | Número de Identificación Tributaria |
Otro | Otro |
Argentina (MLA)
| ID | Name |
|---|---|
DNI | Documento Nacional de Identidad |
CI | Cédula de Identidad |
LC | Libreta Civil |
LE | Libreta de Enrolamiento |
Brazil (MLB)
| ID | Name |
|---|---|
CPF | Cadastro de Pessoas Físicas |
CNPJ | Cadastro Nacional de Pessoa Jurídica |
---
Get Installments
const installments = await mp.getInstallments({
amount: 100000,
bin: '424242',
paymentTypeId: 'credit_card'
});
// Response:
{
payer_costs: [
{ installments: 1, installment_amount: 100000, total_amount: 100000 },
{ installments: 3, installment_amount: 34333, total_amount: 103000 },
// ...
]
}---
Get Issuers
const issuers = await mp.getIssuers({
paymentMethodId: 'visa',
bin: '424242'
});
// Response:
[
{ id: '1234', name: 'Banco de Bogotá', secure_thumbnail: '...', thumbnail: '...' },
// ...
]Payment Status Reference
Payment Status Codes
Every payment generates a status and status_detail that indicates its current condition.
Status Overview
| Status | Description |
|---|---|
approved | Payment was approved and credited |
pending | Payment is awaiting completion |
in_process | Payment is being processed |
rejected | Payment was rejected |
cancelled | Payment was cancelled |
refunded | Payment was refunded |
charged_back | Payment was charged back |
Status Detail Codes
Approved Payments
| status_detail | Description |
|---|---|
accredited | Payment credited successfully |
partially_refunded | Partial refund was processed |
Pending Payments
| status_detail | Description |
|---|---|
pending_waiting_payment | Awaiting payment from buyer |
pending_waiting_transfer | Awaiting bank transfer completion |
pending_challenge | Pending 3DS authentication |
pending_contingency | Processing offline, check email in 2 business days |
pending_review_manual | Manual review in progress |
Rejected Payments
| status_detail | Description |
|---|---|
cc_rejected_bad_filled_card_number | Check card number |
cc_rejected_bad_filled_date | Check expiration date |
cc_rejected_bad_filled_security_code | Check CVV |
cc_rejected_bad_filled_other | Check card data |
cc_rejected_insufficient_amount | Insufficient funds |
cc_rejected_invalid_installments | Invalid installments number |
cc_rejected_card_disabled | Card disabled, call issuer |
cc_rejected_call_for_authorize | Must authorize payment |
cc_rejected_max_attempts | Max attempts reached |
cc_rejected_duplicated_payment | Duplicate payment attempt |
cc_rejected_high_risk | High risk transaction |
cc_rejected_blacklist | Blacklisted card |
cc_rejected_other_reason | Issuer rejected |
cc_rejected_3ds_challenge | 3DS challenge failed |
cc_rejected_3ds_mandatory | 3DS required but not performed |
bank_error | Bank processing error |
rejected_by_bank | Bank rejected transaction |
rejected_by_regulations | Rejected by regulations |
rejected_insufficient_data | Missing required data |
cc_amount_rate_limit_exceeded | Rate limit exceeded |
Cancelled Payments
| status_detail | Description |
|---|---|
expired | Payment expired (30 days pending) |
by_collector | Cancelled by seller |
by_payer | Cancelled by buyer |
Charged Back Payments
| status_detail | Description |
|---|---|
in_process | Chargeback under review |
settled | Funds retained after chargeback |
reimbursed | Funds reimbursed to buyer |
Handling Payment Status
Checking Payment Status
curl -X GET https://api.mercadopago.com/v1/payments/{payment_id} \
-H "Authorization: Bearer {access_token}"Status Response Example
{
"id": 123456789,
"status": "approved",
"status_detail": "accredited",
"payment_method_id": "visa",
"payment_type_id": "credit_card",
"transaction_amount": 150.00,
"currency": "COP"
}Webhook Notification
When status changes, webhook sends:
{
"action": "payment.updated",
"api_version": "v1",
"data": {
"id": "123456789"
},
"date_created": "2024-01-15T10:00:00Z",
"id": 123456789,
"live_mode": true,
"type": "payment",
"user_id": "123456789"
}Next Steps
- Process refunds
- Set up webhooks
- Handle errors
Refunds
Refunds return money to buyers for completed payments. Can be full or partial.
Refund Types
| Type | Description |
|---|---|
| Full | Complete refund of transaction |
| Partial | Refund specific amount |
| Cancelled | Void pending payment before processing |
Full Refund
curl -X POST https://api.mercadopago.com/v1/payments/{payment_id}/refunds \
-H "Authorization: Bearer {access_token}"Response
{
"id": "refund_123456789",
"payment_id": "payment_123456789",
"amount": 150.00,
"status": "approved",
"date_created": "2024-01-20T10:30:00Z"
}Partial Refund
curl -X POST https://api.mercadopago.com/v1/payments/{payment_id}/refunds \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"amount": 50.00}'Multiple Partial Refunds
Can issue multiple partial refunds until total refunded equals payment amount.
# First partial refund
curl -X POST https://api.mercadopago.com/v1/payments/{payment_id}/refunds \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"amount": 25.00}'
# Second partial refund
curl -X POST https://api.mercadopago.com/v1/payments/{payment_id}/refunds \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"amount": 25.00}'Check Refunds on Payment
curl -X GET https://api.mercadopago.com/v1/payments/{payment_id}/refunds \
-H "Authorization: Bearer {access_token}"Order Refunds
For orders, refund individual payments:
# Get order payments
curl -X GET https://api.mercadopago.com/v1/orders/{order_id} \
-H "Authorization: Bearer {access_token}"
# Refund specific payment
curl -X POST https://api.mercadopago.com/v1/payments/{payment_id}/refunds \
-H "Authorization: Bearer {access_token}"Subscription Cancellations
Cancel Active Subscription
curl -X PUT https://api.mercadopago.com/v1/preapprovals/{subscription_id} \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"status": "cancelled"}'Note: Cancellation stops future billing but does not refund past payments.
Refund Last Subscription Payment
# Find the last payment
curl -X GET "https://api.mercadopago.com/v1/preapprovals/{id}/payments" \
-H "Authorization: Bearer {access_token}"
# Refund specific payment
curl -X POST https://api.mercadopago.com/v1/payments/{payment_id}/refunds \
-H "Authorization: Bearer {access_token}"Refund Status
| Status | Description |
|---|---|
pending | Refund request received |
approved | Refund processed |
rejected | Refund rejected |
error | Processing error |
Refund via SDK
Node.js
const mercadopago = require('mercadopago');
mercadopago.configurations.setAccessToken(accessToken);
// Full refund
const refund = await mercadopago.refund.create({ payment_id: '123456789' });
// Partial refund
const refund = await mercadopago.refund.create({
payment_id: '123456789',
amount: 50.00
});Python
import mercadopago
sdk = mercadopago.SDK(accessToken)
# Full refund
result = sdk.refund().create('123456789')
# Partial refund
result = sdk.refund().create('123456789', { 'amount': 50.00 })PHP
$client = new \MercadoPago\Client\Refund\RefundClient();
$client->create($paymentId);
// Partial refund
$client->create($paymentId, ['amount' => 50.00]);Refund Timing
| Payment Method | Refund Time |
|---|---|
| Credit/Debit Card | 2-10 business days |
| Bank Transfer (PSE) | 1-5 business days |
| Cash (Efecty) | 1-3 business days |
| Wallet | Instant |
Webhook Notifications
Subscribe to topic_claims_integration_wh for refund events:
app.post('/webhook', (req, res) => {
if (req.body.type === 'topic_claims_integration_wh') {
const { id, topic } = req.body;
if (topic === 'refund') {
// Handle refund notification
updateOrderStatus(req.body.data.id, 'refunded');
}
}
res.status(200).send('OK');
});Cancellation vs Refund
| Action | Use When |
|---|---|
| Cancellation | Payment still processing, not yet captured |
| Refund | Payment already completed/captured |
Cancel Pending Payment
curl -X PUT https://api.mercadopago.com/v1/payments/{payment_id} \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"status": "cancelled"}'Works only for payments with status authorized or pending.
PSE Payment Refunds
For PSE bank transfers in Colombia:
curl -X POST https://api.mercadopago.com/v1/payments/{payment_id}/refunds \
-H "Authorization: Bearer {access_token}"PSE refunds typically take 1-5 business days.
Error Codes
| Code | Description |
|---|---|
refund_not_possible | Payment cannot be refunded (captured amount exceeded) |
refund_already_processed | Refund already requested |
invalid_refund_amount | Amount exceeds available |
Best Practices
1. Process refunds promptly - Within 30 days recommended 2. Communicate with buyers - Email confirmation of refund 3. Handle partial refunds carefully - Track remaining refundable amount 4. Test refund flow - Use test credentials first 5. Log all refund requests - Audit trail for disputes
Next Steps
- Handle webhooks
- Payment status
- Error handling
SDKs and Setup
Frontend SDKs
MercadoPago.js V2 (Web)
<script src="https://sdk.mercadopago.com/js/v2"></script>npm install @mercadopago/sdk-jsconst mp = new MercadoPago('YOUR_PUBLIC_KEY');React SDK
npm install @mercadopago/sdk-reactimport { initMercadoPago } from '@mercadopago/sdk-react';
initMercadoPago('YOUR_PUBLIC_KEY');Mobile SDKs
iOS (Swift Package Manager):
https://github.com/mercadopago/sdk-iosAndroid (Maven):
https://artifacts.mercadolibre.com/repository/android-releases---
Backend SDKs
Node.js
npm install mercadopagoimport { MercadoPagoConfig, Payments } from 'mercadopago';
const client = new MercadoPagoConfig({
accessToken: 'YOUR_ACCESS_TOKEN',
});Python
pip install mercadopagoimport mercadopago
sdk = mercadopago.SDK("ACCESS_TOKEN")PHP (Composer)
composer require mercadopago/sdkuse MercadoPago\MercadoPagoConfig;
MercadoPagoConfig::setAccessToken("YOUR_ACCESS_TOKEN");Java
<dependency>
<groupId>com.mercadopago</groupId>
<artifactId>sdk-java</artifactId>
<version>2.1.0</version>
</dependency>Ruby
gem install mercadopagosdk = Mercadopago::SDK.new('ACCESS_TOKEN').NET
dotnet add package mercadopago-sdkMercadoPagoConfig.AccessToken = "ACCESS_TOKEN";Go
go get github.com/mercadopago/go-sdk---
SDK Initialization
Frontend (Public Key)
const mp = new MercadoPago('PUBLIC_KEY', {
locale: 'es-CO' // Optional: specify locale
});Backend (Access Token)
// Node.js
MercadoPagoConfig.setAccessToken('ACCESS_TOKEN');
// Python
sdk = mercadopago.SDK("ACCESS_TOKEN")
// PHP
MercadoPagoConfig::setAccessToken("ACCESS_TOKEN");---
MercadoPago.js Core Methods
Create Card Token (Secure Fields)
const cardToken = await mp.fields.createCardToken({
cardholderName: 'Juan Perez',
identificationType: 'CC',
identificationNumber: '123456789',
});
// Returns: { id: 'card_token_id', ... }Get Identification Types
const idTypes = await mp.getIdentificationTypes();
// Returns: [{ id: 'CC', name: 'Cédula de Ciudadanía' }, ...]Get Payment Methods
const methods = await mp.getPaymentMethods({ bin: '424242' });
// Returns: { results: [{ id: 'visa', name: 'Visa' }, ...] }Get Installments
const installments = await mp.getInstallments({
amount: 100000,
bin: '424242',
paymentTypeId: 'credit_card'
});---
React Components
Wallet Component
import { Wallet } from '@mercadopago/sdk-react';
<Wallet initialization={{ preferenceId: 'PREF_ID' }} />Status Screen Component
import { StatusScreen } from '@mercadopago/sdk-react';
<StatusScreen initialization={{ paymentId: 'PAYMENT_ID' }} />Payment Brick (React)
import { Payment } from '@mercadopago/sdk-react';
<Payment
initialization={{ amount: 100000 }}
customization={{ paymentMethods: { creditCard: ['visa'] } }}
onSubmit={handleSubmit}
/>Security
Mercado Pago implements industry-standard security measures. Follow these best practices to protect your integration.
Security Standards
OAuth 2.0
Authorization protocol for secure API access without sharing credentials.
// OAuth flow
const authUrl = 'https://auth.mercadopago.com/oauth/token';
const params = new URLSearchParams({
grant_type: 'authorization_code',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
code: authorizationCode,
redirect_uri: 'YOUR_REDIRECT_URI'
});
const response = await fetch(authUrl, {
method: 'POST',
body: params
});PCI DSS Compliance
Mercado Pago is PCI DSS compliant. Never store card numbers.
OWASP Guidelines
Follow OWASP security recommendations for web applications.
Credential Security
Never Expose Credentials
// BAD - Credentials in frontend code
const accessToken = 'APP_USR-123456789';
// GOOD - Environment variables
const accessToken = process.env.MP_ACCESS_TOKEN;
// GOOD - Server-side only
app.post('/create-payment', (req, res) => {
const accessToken = req.serverConfig.accessToken;
// Use for API calls
});Send Token via Header
# Correct - Header
curl -H "Authorization: Bearer ACCESS_TOKEN" \
https://api.mercadopago.com/v1/payments
# Wrong - Query parameter
curl "https://api.mercadopago.com/v1/payments?access_token=TOKEN"Rotate Credentials
// Schedule credential rotation every 6 months
// Use Dashboard to generate new credentials
// Update environment
// Deploy with new credentials
// Revoke old credentialsCard Data Security
Tokenization Required
// NEVER store card numbers
// ALWAYS tokenize first
const cardToken = await mp.getCardToken({
cardNumber: '5254133674403564',
cardholderName: 'TEST USER',
expirationMonth: '11',
expirationYear: '2026',
securityCode: '123',
identificationType: 'CC',
identificationNumber: '123456789'
});
// Use token for payment
const payment = await mercadopago.payment.create({
token: cardToken.id,
// ... other fields
});Frontend Card Handling
<!-- Use MercadoPago.js for card fields -->
<script src="https://sdk.mercadopago.com/js/v2"></script>
<div id="cardNumber"></div>
<div id="securityCode"></div>
<div id="cardExpiration"></div>
<div id="cardholderName"></div>
<script>
const mp = new MercadoPago('PUBLIC_KEY');
mp.cardNumber.create({
id: 'cardNumber',
placeholder: 'Card number'
});
// Get token when form submitted
const cardToken = await mp.getCardToken(formData);
</script>Webhook Security
Validate Signatures
const crypto = require('crypto');
function validateWebhookSignature(req) {
const signature = req.headers['x-signature'];
const timestamp = req.headers['x-signature-date'];
const webhookKey = process.env.MP_WEBHOOK_KEY;
// Check timestamp (within 5 minutes)
const fiveMinutesAgo = Date.now() - (5 * 60 * 1000);
if (parseInt(timestamp) < fiveMinutesAgo) {
return false;
}
// Verify signature
const data = timestamp + '.' + JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac('sha256', webhookKey)
.update(data)
.digest('hex');
return signature === expectedSignature;
}
app.post('/webhook', (req, res) => {
if (!validateWebhookSignature(req)) {
return res.status(401).send('Invalid signature');
}
// Process notification
res.status(200).send('OK');
});HTTPS Requirements
- All production URLs must use HTTPS
- TLS 1.2+ required
- Valid SSL certificate required
- No self-signed certificates
Input Validation
Validate All Inputs
function validatePaymentInput(data) {
const errors = [];
if (!data.transaction_amount || data.transaction_amount <= 0) {
errors.push('Invalid amount');
}
if (!data.token) {
errors.push('Card token required');
}
if (!data.payment_method_id) {
errors.push('Payment method required');
}
if (!data.payer?.email || !isValidEmail(data.payer.email)) {
errors.push('Valid email required');
}
return errors;
}Fraud Prevention
Send Device Data
// In frontend - SDK collects device data automatically
const deviceData = await mp.deviceProfiler.getDeviceData();
// Include in payment
const payment = await mercadopago.payment.create({
transaction_amount: 150.00,
token: cardToken,
device_id: deviceData.id, // If using custom device profiling
payer: { email: 'buyer@example.com' }
});Use 3DS for High-Value
// Enable 3DS for high-value transactions
if (transactionAmount > 500) {
paymentConfig.three_d_secure_mode = 'optional';
}Security Checklist
- [ ] Store credentials in environment variables
- [ ] Never expose Access Token in frontend
- [ ] Use HTTPS for all production URLs
- [ ] Validate all user inputs
- [ ] Implement webhook signature validation
- [ ] Use idempotency keys
- [ ] Log security events
- [ ] Rotate credentials periodically
- [ ] Tokenize all card data
- [ ] Enable 3DS for high-value transactions
- [ ] Implement rate limiting
- [ ] Sanitize log output
Rate Limiting
const rateLimit = require('express-rate-limit');
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per window
message: 'Too many requests'
});
app.use('/api/', apiLimiter);Secure Headers
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=31536000');
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
next();
});Common Security Mistakes
| Mistake | Risk | Fix |
|---|---|---|
| Hardcoded credentials | Exposed in code | Use env variables |
| Credentials in URLs | Logged in server logs | Use headers |
| Storing card numbers | PCI violation | Always tokenize |
| No webhook validation | Fake notifications | Validate signatures |
| No input validation | Injection attacks | Sanitize inputs |
| HTTP webhook URL | Intercepted data | Use HTTPS |
Reporting Security Issues
If you find a security vulnerability: 1. Do not disclose publicly 2. Contact Mercado Pago security team 3. Wait for acknowledgment 4. Follow responsible disclosure
Next Steps
- Authentication
- Webhooks
- Error handling
- 3DS
Subscriptions
Subscriptions enable recurring payments for products or services with automatic billing.
Subscription Types
With Associated Plan
Same subscription reused for multiple payers. Ideal for:
- Monthly/annual memberships
- Tiered pricing plans
- Product subscriptions
Without Associated Plan
Custom subscription per payer. Ideal for:
- Variable amounts
- Custom frequencies
- Donor-specific billing
Available Payment Methods by Country
| Country | Credit | Debit | Cash | Bank Transfer |
|---|---|---|---|---|
| MCO | Yes | Yes | Efecty | PSE |
| MLA | Yes | Yes | Rapipago, Pago Fácil | - |
| MLB | Yes | - | Boleto, Pix | Pix |
| MLM | Yes | Yes | OXXO, Paycash | SPEI |
| MLC | Yes | Yes | - | - |
| MPE | Yes | Yes | PagoEfectivo | - |
| MLU | Yes | Yes | Abitab, Redpagos | - |
Creating a Plan
curl -X POST https://api.mercadopago.com/v1/preapproval_plan \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"description": "Premium Plan",
"billing_frequency": "monthly",
"payment_types_enabled": ["credit_card"],
"accepted_payment_methods": ["visa", "mastercard"],
"auto_recurring": {
"frequency": 1,
"frequency_type": "months",
"transaction_amount": 29.99,
"currency_id": "COP"
},
"external_reference": "plan_001"
}'Plan Response
{
"id": "plan_123456789",
"description": "Premium Plan",
"billing_frequency": "monthly",
"auto_recurring": {
"transaction_amount": 29.99,
"currency_id": "COP"
},
"status": "active"
}Creating a Subscription
With Plan
curl -X POST https://api.mercadopago.com/v1/preapprovals \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "plan_123456789",
"payer": {
"email": "subscriber@example.com"
},
"external_reference": "subscription_001"
}'Without Plan (Custom Amount)
curl -X POST https://api.mercadopago.com/v1/preapprovals \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"auto_recurring": {
"frequency": 1,
"frequency_type": "months",
"transaction_amount": 50.00,
"currency_id": "COP"
},
"payer": {
"email": "subscriber@example.com"
},
"description": "Donation",
"external_reference": "donation_001"
}'Subscription Response
{
"id": "preapproval_123456789",
"plan_id": "plan_123456789",
"payer": {
"email": "subscriber@example.com"
},
"status": "authorized",
"auto_recurring": {
"transaction_amount": 29.99
},
"start_date": "2024-02-01T00:00:00Z",
"next_payment_date": "2024-03-01T00:00:00Z"
}Subscription Status
| Status | Description |
|---|---|
authorized | Active and processing |
paused | Subscription paused |
cancelled | Subscription cancelled |
expired | Subscription expired |
pending | Awaiting first payment |
Managing Subscriptions
Pause Subscription
curl -X PUT https://api.mercadopago.com/v1/preapprovals/{id} \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"status": "paused"}'Cancel Subscription
curl -X PUT https://api.mercadopago.com/v1/preapprovals/{id} \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"status": "cancelled"}'Update Amount
curl -X PUT https://api.mercadopago.com/v1/preapprovals/{id} \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"auto_recurring": {
"transaction_amount": 39.99,
"currency_id": "COP"
}
}'Change Payment Method
curl -X PUT https://api.mercadopago.com/v1/preapprovals/{id} \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type": application/json" \
-d '{
"card_token_id": "new_card_token"
}'Free Trial
curl -X POST https://api.mercadopago.com/v1/preapproval_plan/{plan_id} \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"free_trial": {
"frequency": 1,
"frequency_type": "months"
}
}'Webhook Notifications
Subscribe to subscription_authorized_payment topic:
// Handle subscription payment
app.post('/webhook', (req, res) => {
if (req.body.type === 'subscription_authorized_payment') {
const { id, status, preapproval_id } = req.body.data;
if (status === 'authorized') {
// Payment successful - fulfill service
activateMember(preapproval_id);
}
}
res.status(200).send('OK');
});Searching Subscriptions
curl -X GET "https://api.mercadopago.com/v1/preapprovals/search?status=authorized" \
-H "Authorization: Bearer {access_token}"Use Cases
Membership Sites
// Create plan for monthly membership
const plan = await mercadopago.preapprovalPlan.create({
description: "Gold Membership",
billing_frequency: "monthly",
auto_recurring: {
frequency: 1,
frequency_type: "months",
transaction_amount: 29.99,
currency_id: "COP"
}
});Donations
// Open-ended donation
const subscription = await mercadopago.preapproval.create({
auto_recurring: {
frequency: 1,
frequency_type: "months",
transaction_amount: 0, // Payer chooses
free_recurrence: true
},
payer: { email: "donor@example.com" },
description: "Monthly Donation"
});Usage-Based Billing
// Custom amount per billing period
const subscription = await mercadopago.preapproval.create({
auto_recurring: {
frequency: 1,
frequency_type: "months",
transaction_amount: 0,
currency_id: "COP"
},
payer: { email: "customer@example.com" },
description: "Usage-based Service"
});Best Practices
1. Clear cancellation policy - Easy to cancel subscriptions 2. Notification emails - Before renewal, after payment 3. Multiple payment methods - Offer alternatives for failures 4. Retry logic - Automatic retry on failed payments 5. Proration - Handle mid-cycle plan changes
Next Steps
- Set up webhooks
- Process refunds
- Test payments
Testing
Mercado Pago provides test credentials, test cards, and test users for integration testing.
Test Credentials
Getting Test Credentials
1. Go to Developer Dashboard 2. Select your application 3. Go to Testing > Test credentials 4. Copy Public Key and Access Token
Using Test Credentials
// Set test credentials
mercadopago.configure({
access_token: 'TEST_ACCESS_TOKEN',
client_id: 'TEST_CLIENT_ID',
client_secret: 'TEST_CLIENT_SECRET'
});Test Cards (MCO - Colombia)
Card Numbers
| Card Type | Flag | Number | CVV | Expiry |
|---|---|---|---|---|
| Credit | Mastercard | 5254 1336 7440 3564 | 123 | 11/30 |
| Credit | Visa | 4013 5406 8274 6260 | 123 | 11/30 |
| Credit | American Express | 3743 781877 55283 | 1234 | 11/30 |
| Debit | Visa | 4915 1120 5524 6507 | 123 | 11/30 |
Cardholder Names for Status Simulation
| Name | Result |
|---|---|
APRO | Approved payment |
OTHE | Declined - general error |
CONT | Pending payment |
CALL | Declined - call issuer |
FUND | Declined - insufficient funds |
SECU | Declined - invalid security code |
EXPI | Declined - expired card |
FORM | Declined - form error |
CARD | Rejected - missing card number |
INST | Rejected - invalid installments |
DUPL | Rejected - duplicate payment |
LOCK | Rejected - disabled card |
CTNA | Rejected - card type not allowed |
ATTE | Rejected - exceeded attempts |
BLAC | Rejected - blacklisted |
UNSU | Not supported |
TEST | Apply amount rules |
Test Document Numbers (MCO)
For Colombia, use any number like 123456789.
Testing Example
Create Test Payment
const payment = await mercadopago.payment.create({
transaction_amount: 150.00,
token: 'test_card_token',
payment_method_id: 'mastercard',
payer: {
email: 'test_buyer@example.com',
identification: {
type: 'CC',
number: '123456789'
}
},
external_reference: 'test_order_001'
});
console.log(payment.status); // 'approved' if cardholder name is 'APRO'Test Card Tokenization
// Using MercadoPago.js in browser
const cardData = {
cardNumber: '5254133674403564',
cardholderName: 'APRO',
expirationMonth: '11',
expirationYear: '2026',
securityCode: '123',
identificationType: 'CC',
identificationNumber: '123456789'
};
// Get card token
const cardToken = await mp.getCardToken(cardData);
// Use cardToken in payment creationTest Users
Create Test User (MCP Tool)
// Use create_test_user MCP tool
create_test_user({
site_id: 'MCO',
description: 'Test Seller',
profile: 'seller'
})Manual Creation
curl -X POST https://api.mercadopago.com/v1/test/users \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"site_id": "MCO",
"description": "Test Seller"
}'Test User Types
| Profile | Description |
|---|---|
seller | Receives payments |
buyer | Makes payments |
integrator | For testing integrations |
Testing Webhooks
Local Testing with ngrok
# Start local server
ngrok http 3000
# Configure webhook URL to: https://{ngrok-id}.ngrok.io/webhookSimulate Webhook (MCP)
// Use simulate_webhook MCP tool
simulate_webhook({
topic: 'payment',
resource_id: '123456789',
callback_env_production: false
})Sandbox Environment
Test environment URLs:
| Environment | Base URL |
|---|---|
| Sandbox | https://api.mercadopago.com |
| Production | https://api.mercadopago.com |
Test mode is enabled by using test credentials. Switch to production credentials before going live.
Testing Checklist
- [ ] Create payment with approved card
- [ ] Create payment with declined card (various reasons)
- [ ] Test webhook notifications
- [ ] Test refund flow (full and partial)
- [ ] Test subscription creation and cancellation
- [ ] Test 3DS flow if applicable
- [ ] Verify error handling
- [ ] Test mobile responsiveness
Testing Specific Scenarios
Test PSE (Bank Transfer)
PSE doesn't have sandbox test cards. Use cash methods for testing.
Test Cash Payments (Efecty)
const payment = await mercadopago.payment.create({
transaction_amount: 150.00,
payment_method_id: 'efecty',
payer: {
email: 'test@example.com',
identification: {
type: 'CC',
number: '123456789'
}
},
external_reference: 'test_order_001'
});
// Check for 'pending' status and collect URL
console.log(payment.status); // 'pending'
console.log(payment.transaction_details.external_resource_url);Test Installments
// Test different installment options
for (let i = 1; i <= 12; i++) {
const payment = await mercadopago.payment.create({
transaction_amount: 150.00,
token: cardToken,
payment_method_id: 'visa',
installments: i,
payer: { email: 'test@example.com' }
});
console.log(`Installments ${i}:`, payment.status);
}Going to Production
1. Replace credentials - Use production access token 2. Update URLs - Remove sandbox references 3. Enable 3DS - If applicable for your use case 4. Test with real cards - Small amounts first 5. Monitor transactions - Check webhook delivery
Production Credentials
1. Go to Developer Dashboard 2. Select application 3. Go to Production > Production credentials 4. Activate if needed (complete business profile) 5. Copy credentials
Common Testing Issues
| Issue | Solution |
|---|---|
| 404 on test endpoint | Check access token is correct |
| Webhook not received | Verify HTTPS URL, check firewall |
| Test card declined | Verify card number and CVV |
| Amount mismatch | Check currency_id matches |
Next Steps
- Authentication
- Payment flow
- Error handling
Webhooks
Webhooks notify your backend when payment events occur. Required for production integrations.
Webhook vs IPN
| Feature | Webhooks | IPN |
|---|---|---|
| Security | Signature validation | No validation |
| Reliability | Retry mechanism | Basic |
| Recommendation | Preferred | Legacy, deprecated |
Topic Types
| Topic | Events | Products |
|---|---|---|
payment | Payment created/updated | Checkout Pro, Checkout Bricks, Checkout API |
order | Order status changes | Checkout API, QR Code |
subscription_authorized_payment | Subscription recurring payment | Subscriptions |
subscription_preapproval | Subscription linking | Subscriptions |
subscription_preapproval_plan | Plan changes | Subscriptions |
mp-connect | OAuth linking/unlinking | All OAuth products |
wallet_connect | Wallet transactions | Wallet Connect |
stop_delivery_op_wh | Fraud alerts | Checkout Pro, Checkout API |
topic_claims_integration_wh | Refunds and claims | All products |
topic_card_id_wh | Card updates | Checkout Pro, Checkout API |
topic_merchant_order_wh | Commercial orders | Checkout Pro |
topic_chargebacks_wh | Chargebacks | Checkout Pro, Checkout API |
point_integration_wh | Point device events | Mercado Pago Point |
Configuration
Via Dashboard
1. Go to Developer Dashboard 2. Select your application 3. Go to Notifications section 4. Enter your callback URL (HTTPS required) 5. Select topics to subscribe
Via API (MCP Tool)
// Use save_webhook MCP tool
save_webhook({
callback: "https://yourdomain.com/webhook",
topics: ["payment"]
})Manual API Setup
curl -X POST https://api.mercadopago.com/v1/webhooks \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourdomain.com/webhook",
"webhook_events": ["payment.created", "payment.updated"]
}'Signature Validation
Validate that notifications come from Mercado Pago:
const crypto = require('crypto');
function validateSignature(req, webhookKey) {
const signature = req.get('x-signature');
const timestamp = req.get('x-signature-date');
const data = timestamp + '.' + JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac('sha256', webhookKey)
.update(data)
.digest('hex');
return signature === expectedSignature;
}Handling Notifications
Webhook Handler Example
app.post('/webhook', (req, res) => {
const { type, data, date_created } = req.body;
if (type === 'payment') {
const paymentId = data.id;
// Process payment status update
getPaymentStatus(paymentId).then(payment => {
if (payment.status === 'approved') {
// Fulfill order
fulfillOrder(payment.external_reference);
}
});
}
// Always respond 200 quickly
res.status(200).send('OK');
});Idempotency
Handle duplicate notifications gracefully:
const processedPayments = new Set();
async function handlePaymentNotification(payment) {
if (processedPayments.has(payment.id)) {
return; // Already processed
}
// Process payment...
processedPayments.add(payment.id);
}Testing Webhooks
Simulate Webhook (MCP)
// Use simulate_webhook MCP tool
simulate_webhook({
topic: "payment",
resource_id: "123456789",
callback_env_production: false
})Local Testing
Use tools like ngrok for local development:
ngrok http 3000
# Set webhook URL to https://{your-ngrok-id}.ngrok.io/webhookTroubleshooting
Webhook Not Received
1. Verify URL is HTTPS (HTTP not supported) 2. Check server responds with 200 within 30 seconds 3. Ensure firewall allows incoming from Mercado Pago IPs 4. Check application has correct topics enabled
Duplicate Notifications
Implement idempotency keys or deduplication logic.
Signature Validation Fails
- Ensure webhook key matches exactly
- Verify timestamp is still recent (within 5 minutes)
- Check JSON stringification matches signature calculation
Best Practices
1. Respond quickly - Return 200 within 30 seconds, process async 2. Use HTTPS - Required for webhook URLs 3. Implement idempotency - Handle duplicate notifications 4. Validate signatures - Verify webhook authenticity 5. Log all notifications - For debugging and auditing 6. Process async - Don't block the webhook handler
Next Steps
- Payment status codes
- Error handling
- Go to production checklist
Related skills
FAQ
Which countries does it cover?
The docs list Argentina, Brazil, Mexico, Colombia, Chile, Peru and Uruguay with their codes and currencies.
What checkout options are covered?
Checkout Pro (hosted redirect), Checkout Bricks (modular embedded components) and Checkout API (full control).