Authentication & Security
This document details the authentication mechanisms, security measures, and best practices implemented in the Mercado Libre Payment API.
Table of Contents
- Overview
- Authentication
- Authorization
- Data Protection
- Secure Communication
- Input Validation
- Error Handling
- Logging & Auditing
- Security Best Practices
- Compliance
Overview
The API implements multiple security layers to protect sensitive payment data and ensure secure transactions:
| Security Layer | Implementation |
|---|---|
| Authentication | Bearer Token (Mercado Pago) |
| Transport Security | HTTPS/TLS |
| Data Protection | Tokenization, Encryption |
| Request Security | Idempotency Keys |
| Input Validation | Type checking, Sanitization |
Authentication
Mercado Pago Authentication
The API authenticates with Mercado Pago using OAuth 2.0 Bearer tokens.
Token Types
| Token Type | Prefix | Usage |
|---|---|---|
| Production | APP_USR- |
Live transactions |
| Sandbox | TEST- |
Testing only |
Authentication Flow
sequenceDiagram
participant Dev as Developer
participant MP as Mercado Pago
participant App as Application
participant Env as Environment
Dev->>MP: Create application in dashboard
MP-->>Dev: Generate Access Token
Dev->>Env: Store token in .env file
Env->>App: Load token at startup
loop Every Request
App->>App: Read token from environment
App->>App: Add to Authorization header
App->>MP: POST request with Bearer token
MP->>MP: Validate token
MP-->>App: Response (200/401)
end
Implementation
# services/mercadopago.py
from decouple import config
class MercadoPago:
def __init__(self):
self.__access_token = config('MP_ACCESS_TOKEN')
self.__headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.__access_token}',
}
Security Headers
POST /v1/payments HTTP/1.1
Host: api.mercadopago.com
Authorization: Bearer APP_USR-xxxxxxxxxxxxxxxx
Content-Type: application/json
X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Authorization
Access Control
The API implements the following access control measures:
| Control | Description |
|---|---|
| Token-based | All requests require valid access token |
| Scope-based | Token permissions limit available actions |
| Environment isolation | Sandbox and production tokens are separate |
Token Permissions
Mercado Pago tokens can have different scopes:
| Scope | Permission |
|---|---|
payment |
Create and manage payments |
refund |
Process refunds |
customer |
Manage customer data |
merchant_orders |
Access merchant orders |
Data Protection
Sensitive Data Handling
Data Classification
| Data Type | Sensitivity | Protection Method |
|---|---|---|
| Card Number | Critical | Tokenization |
| CVV/CVC | Critical | Never stored |
| CPF | High | Encrypted transmission |
| Medium | Access control | |
| Transaction Amount | Medium | Access control |
Card Tokenization
Credit card data is tokenized before processing:
flowchart LR
A[Raw Card Data] --> B[POST /v1/card_tokens]
B --> C[Mercado Pago API]
C --> D[Card Token]
D --> E[Use token in payment]
style A fill:#ffebee
style D fill:#e8f5e9
Implementation:
def __get_card_token(self, card_data: dict) -> str:
"""Tokenize card data - raw card number never stored."""
response = self.__post('/v1/card_tokens', card_data)
return response.get('id') # Returns token, not card data
def pay_with_card(self, amount: float, installments: int,
description: str, card_data: dict, payer: dict) -> dict:
"""Create payment using tokenized card."""
token = self.__get_card_token(card_data) # Get token first
payload = {
'transaction_amount': amount,
'token': token, # Use token, not raw card data
'description': description,
'installments': int(installments),
'payer': payer,
}
return self.__create_payment(payload)
Data Storage Rules
| Data | Store? | Reason |
|---|---|---|
| Card Number | ❌ No | PCI-DSS compliance |
| CVV | ❌ No | Never store security codes |
| Card Token | ✅ Yes | Safe to store |
| Last 4 digits | ✅ Yes | For display purposes |
| Card Brand | ✅ Yes | Non-sensitive |
Secure Communication
HTTPS/TLS
All communication must use HTTPS with TLS 1.2 or higher:
flowchart LR
Client[Client] -->|HTTPS TLS 1.3| Server[API Server]
Server -->|HTTPS TLS 1.3| MP[Mercado Pago]
style Client fill:#e3f2fd
style Server fill:#e8f5e9
style MP fill:#fff3e0
TLS Configuration
| Setting | Value |
|---|---|
| Minimum Version | TLS 1.2 |
| Recommended | TLS 1.3 |
| Cipher Suites | Strong ciphers only |
| Certificate | Valid SSL certificate required |
Idempotency
Prevent duplicate transactions with idempotency keys:
def __post(self, path: str, payload: dict) -> dict:
url = f'{self.BASE_URL}{path}'
# Generate unique idempotency key for each request
headers = {**self.__headers, 'X-Idempotency-Key': str(uuid.uuid4())}
response = requests.post(url=url, json=payload, headers=headers)
return response.json()
Benefits: - Prevents duplicate charges on network retries - Ensures payment safety on connection failures - Provides transaction traceability
Input Validation
Request Validation
All incoming requests are validated:
@app.post('/create_payment')
async def create_payment(request: Request):
data = await request.json()
# Validate payment method
method = data.get('payment_method')
if method not in ['card', 'pix', 'boleto']:
raise HTTPException(status_code=400,
detail='Invalid payment method')
# Validate amount
amount = float(data.get('transaction_amount', 0))
if amount <= 0:
raise HTTPException(status_code=400,
detail='Amount must be greater than zero')
# Validate required fields based on payment method
if method == 'card':
self._validate_card_data(data)
elif method == 'boleto':
self._validate_boleto_data(data)
Validation Rules
| Field | Validation | Error Message |
|---|---|---|
payment_method |
Enum: card, pix, boleto | "Invalid payment method" |
transaction_amount |
Number > 0 | "Amount must be greater than zero" |
email |
Valid email format | "Invalid email format" |
card_number |
Luhn algorithm | "Invalid card number" |
security_code |
3-4 digits | "Invalid security code" |
identification_number |
CPF validation | "Invalid CPF" |
expiration_month |
01-12 | "Invalid expiration month" |
expiration_year |
Current year or later | "Card expired" |
Sanitization
# String sanitization
def sanitize_string(value: str) -> str:
"""Remove potentially harmful characters."""
if not isinstance(value, str):
return value
# Remove null bytes and control characters
return ''.join(char for char in value if ord(char) >= 32)
Error Handling
Secure Error Responses
Error messages don't expose sensitive information:
# ✅ Good - Generic error message
try:
result = mp.pay_with_card(...)
except RuntimeError as err:
logger.error(f"Payment processing error: {err}")
raise HTTPException(status_code=502, detail="Payment processing failed")
# ❌ Bad - Exposes internal details
except RuntimeError as err:
raise HTTPException(status_code=502, detail=f"Error: {err}")
Error Code Mapping
| Internal Error | HTTP Status | User Message |
|---|---|---|
| Validation Error | 400 | "Invalid payment data" |
| Authentication Error | 401 | "Authentication failed" |
| Resource Not Found | 404 | "Payment not found" |
| Mercado Pago Error | 502 | "Payment service unavailable" |
| Internal Error | 500 | "Internal server error" |
Logging & Auditing
Secure Logging
Log transactions without sensitive data:
import logging
logger = logging.getLogger(__name__)
# ✅ Good - Safe logging
logger.info(f"Payment created: method={method}, amount={amount}, status={status}")
# ❌ Bad - Never log sensitive data
logger.info(f"Payment with card={card_number}, cvv={security_code}") # NEVER!
Audit Trail
| Event | Logged Information |
|---|---|
| Payment Created | Timestamp, method, amount, status |
| Payment Approved | Timestamp, payment ID, status |
| Payment Rejected | Timestamp, reason (generic) |
| API Error | Timestamp, endpoint, error code |
Log Retention
| Log Type | Retention Period |
|---|---|
| Transaction logs | 5 years (regulatory) |
| Access logs | 1 year |
| Error logs | 1 year |
| Audit logs | 5 years |
Security Best Practices
DO's ✅
- Use environment variables for all credentials
- Enable HTTPS in production
- Validate all inputs before processing
- Use idempotency keys for payment requests
- Log transactions without sensitive data
- Rotate access tokens periodically
- Use sandbox tokens for development
- Implement rate limiting
- Monitor for suspicious activity
- Keep dependencies updated
DON'Ts ❌
- Never commit
.envfiles to version control - Never log card numbers or CVV
- Never store raw card data
- Never use production tokens in development
- Never disable SSL/TLS verification
- Never expose tokens in client-side code
- Never share access tokens
- Never skip input validation
- Never ignore security warnings
- Never hardcode credentials
Security Checklist
Before deploying to production:
- [ ] HTTPS enabled with valid certificate
- [ ] Production access token configured
- [ ] Debug mode disabled (
DEBUG=False) - [ ] Environment variables secured
- [ ] Input validation implemented
- [ ] Error handling doesn't expose internals
- [ ] Logging excludes sensitive data
- [ ] Rate limiting configured
- [ ] Firewall rules in place
- [ ] Monitoring and alerting configured
Compliance
PCI-DSS Compliance
The API follows PCI-DSS guidelines by:
| Requirement | Implementation |
|---|---|
| No card storage | Card data tokenized by Mercado Pago |
| Secure transmission | HTTPS/TLS for all communication |
| Access control | Token-based authentication |
| Logging | Audit trail without sensitive data |
| Network security | Firewall and security groups |
LGPD (Brazilian GDPR)
Data protection compliance:
| Principle | Implementation |
|---|---|
| Data minimization | Only collect necessary data |
| Purpose limitation | Data used only for payment processing |
| Security | Encryption and access controls |
| Retention | Data kept only as long as required |
Security Standards Reference
| Standard | Description |
|---|---|
| PCI-DSS | Payment Card Industry Data Security Standard |
| LGPD | Brazilian General Data Protection Law |
| OWASP | Open Web Application Security Project |
Threat Mitigation
Common Threats
| Threat | Mitigation |
|---|---|
| Man-in-the-Middle | HTTPS/TLS encryption |
| Replay Attacks | Idempotency keys |
| SQL Injection | No direct SQL (using API) |
| XSS | Jinja2 auto-escaping |
| CSRF | Same-site cookies, CORS |
| Brute Force | Rate limiting |
| Data Leakage | Tokenization, logging controls |
Security Architecture
flowchart TB
subgraph Perimeter["Security Perimeter"]
Firewall[Firewall/WAF]
LB[Load Balancer with SSL]
end
subgraph Application["Application Security"]
Auth[Authentication Layer]
Validation[Input Validation]
RateLimit[Rate Limiting]
end
subgraph Data["Data Security"]
Tokenization[Card Tokenization]
Encryption[Data Encryption]
Masking[Response Masking]
end
User --> Firewall
Firewall --> LB
LB --> Auth
Auth --> Validation
Validation --> RateLimit
RateLimit --> Tokenization
Tokenization --> Encryption
Encryption --> Masking
style Perimeter fill:#ffebee
style Application fill:#fff3e0
style Data fill:#e8f5e9
Next Steps
- Development Guide - Secure development practices
- Testing Guide - Security testing procedures
- Deploy Guide - Secure deployment configuration
Last Updated: April 2026
Version: 1.0.0