Librería completa para Mercado Libre con OAuth 2.0 PKCE, refresh tokens y gestión de aplicaciones. Implementa todas las mejores prácticas de seguridad y funcionalidades de la API oficial.
- ✅ OAuth 2.0 PKCE - Autenticación segura con Proof Key for Code Exchange
- ✅ Refresh Tokens - Renovación automática de tokens con offline_access
- ✅ Multi-Site Support - Soporte para todos los sitios de Mercado Libre
- ✅ App Management - Gestión completa de aplicaciones y permisos
- ✅ Security First - Validación robusta y protección CSRF
- ✅ PHP 8.3+ - Aprovecha las últimas funcionalidades del lenguaje
- ✅ Type Safety - Tipado estricto y union types
- ✅ Readonly Properties - Inmutabilidad donde sea apropiado
- ✅ Match Expressions - Lógica más clara y eficiente
- ✅ Named Arguments - Mejor legibilidad del código
- ✅ First-class Callable Syntax - Sintaxis moderna para callbacks
- ✅ Improved Error Handling - Manejo de errores más específico
- ✅ Backward Compatibility - Compatible con versiones anteriores
composer require tepuilabs/mini-meliAgrega en tu archivo de configuración:
GRANT_TYPE=authorization_code
CLIENT_ID=tu_client_id
CLIENT_SECRET=tu_client_secret
REDIRECT_URL=http://localhost:9000
SCOPES=read write offline_accessNote
Estos datos los debes configurar en Mercado Libre cuando crees una aplicación. Solo necesitas el client_id y client_secret.
<?php
use Tepuilabs\MeliServices\MeliConfig;
use Tepuilabs\MeliServices\MeliServices;
use Tepuilabs\MeliServices\MeliScopes;
require 'vendor/autoload.php';
// Generar PKCE y state
$pkce = MeliConfig::generatePkce();
$state = MeliConfig::generateState();
// Crear configuración
$config = MeliConfig::forAuthorization(
clientId: 'tu_client_id',
clientSecret: 'tu_client_secret',
redirectUri: 'http://localhost:9000',
codeVerifier: $pkce['code_verifier'],
codeChallenge: $pkce['code_challenge'],
state: $state,
scopes: MeliScopes::getOfflineAccess() // Incluye refresh tokens
);
$meli = new MeliServices($config);
// Generar URL de autorización
$authUrl = $meli->getAuthorizationUrl('MLA'); // Argentina
echo "Ve a: {$authUrl}";// En tu callback
$code = $_GET['code'] ?? '';
$state = $_GET['state'] ?? '';
// Verificar state para seguridad
if ($state !== $savedState) {
throw new Exception("State no coincide");
}
$config = new MeliConfig(
clientId: 'tu_client_id',
clientSecret: 'tu_client_secret',
code: $code,
redirectUri: 'http://localhost:9000',
codeVerifier: $savedCodeVerifier // De la sesión
);
$meli = new MeliServices($config);
$response = $meli->generateAccessToken();
// Guardar tokens
$accessToken = $response->getAccessToken();
$refreshToken = $response->getRefreshToken();
$userId = $response->getUserId();// Renovar token cuando expire
$response = $meli->refreshAccessToken($refreshToken);
// Actualizar tokens
$newAccessToken = $response->getAccessToken();
$newRefreshToken = $response->getRefreshToken(); // Siempre nuevo// Crear instancia para API calls
$config = MeliConfig::forAuthorization(
clientId: 'tu_client_id',
clientSecret: 'tu_client_secret',
redirectUri: 'http://localhost:9000'
);
$meli = new MeliServices($config);
// Hacer llamadas
$userProfile = $meli->get('/users/me', $accessToken);
$userItems = $meli->get("/users/{$userId}/items/search", $accessToken);
$categories = $meli->get('/sites/MLA/categories', $accessToken);// Obtener detalles de la aplicación
$appDetails = $meli->getApplicationDetails($accessToken, $appId);
// Obtener aplicaciones autorizadas por el usuario
$userApps = $meli->getUserApplications($accessToken, $userId);
// Obtener usuarios que dieron permisos a tu app
$appGrants = $meli->getApplicationGrants($accessToken, $appId);
// Revocar autorización de un usuario
$result = $meli->revokeUserAuthorization($accessToken, $userId, $appId);La librería soporta todos los sitios de Mercado Libre:
use Tepuilabs\MeliServices\MeliSites;
// Sitios disponibles
MeliSites::MLA; // Argentina
MeliSites::MLB; // Brasil
MeliSites::MLM; // México
MeliSites::MLC; // Chile
MeliSites::MCO; // Colombia
MeliSites::MPE; // Perú
MeliSites::MLU; // Uruguay
MeliSites::MLV; // Venezuela
// Obtener información del sitio
$siteInfo = MeliSites::getSite('MLA');
$domain = MeliSites::getDomain('MLA');
$name = MeliSites::getName('MLA');
$flag = MeliSites::getFlag('MLA');use Tepuilabs\MeliServices\MeliScopes;
// Scopes disponibles
MeliScopes::READ; // Solo lectura
MeliScopes::WRITE; // Lectura y escritura
MeliScopes::OFFLINE_ACCESS; // Incluye refresh tokens
// Obtener scopes predefinidos
$defaultScopes = MeliScopes::getDefault(); // read write
$offlineScopes = MeliScopes::getOfflineAccess(); // read write offline_access
// Validar scopes
$isValid = MeliScopes::isValid('read');
$hasOffline = MeliScopes::hasOfflineAccess($scopes);
$hasRead = MeliScopes::hasRead($scopes);
$hasWrite = MeliScopes::hasWrite($scopes);La librería implementa PKCE para prevenir ataques de interceptación de código:
// Generación automática
$pkce = MeliConfig::generatePkce();
// code_verifier: string aleatorio de 32 bytes
// code_challenge: hash SHA-256 del verifier, base64url encodedProtección CSRF con parámetros state aleatorios:
$state = MeliConfig::generateState(); // 16 bytes aleatorios// Validación automática de parámetros
$config = new MeliConfig(
clientId: $clientId,
clientSecret: $clientSecret,
code: $code,
redirectUri: $redirectUri,
codeVerifier: $codeVerifier
);
if (!$config->isValid()) {
throw new Exception("Configuración inválida");
}fromArray(array $params): self- Crear desde arrayfromEnvironment(): self- Crear desde variables de entorno
generateAccessToken(): MeliResponse- Generar tokenrefreshAccessToken(string $refreshToken): MeliResponse- Renovar tokengetAuthorizationUrl(string $site, array $params = []): string- Generar URL de autorizaciónget(string $endpoint, string $accessToken): array- GET requestpost(string $endpoint, string $accessToken, array $data = []): array- POST requestput(string $endpoint, string $accessToken, array $data = []): array- PUT requestdelete(string $endpoint, string $accessToken): array- DELETE requestgetApplicationDetails(string $accessToken, string $appId): array- Detalles de appgetUserApplications(string $accessToken, string $userId): array- Apps del usuariogetApplicationGrants(string $accessToken, string $appId): array- Usuarios conectadosrevokeUserAuthorization(string $accessToken, string $userId, string $appId): array- Revocar autorización
fromArray(array $params): self- Crear desde arrayfromEnvironment(): self- Crear desde variables de entornoforAuthorization(...): self- Crear para URLs de autorizacióngeneratePkce(): array- Generar PKCEgenerateState(): string- Generar state
toArray(): array- Convertir a arrayisValid(): bool- Verificar si es válidohasPkce(): bool- Verificar si tiene PKCEhasState(): bool- Verificar si tiene statehasRefreshToken(): bool- Verificar si tiene refresh tokenisForTokenExchange(): bool- Verificar si es para intercambiogetScopesString(): string- Obtener scopes como stringhasOfflineAccess(): bool- Verificar offline accesshasReadPermission(): bool- Verificar permiso de lecturahasWritePermission(): bool- Verificar permiso de escritura
data: array- Datos de la respuestastatusCode: int- Código de estado HTTP
getAccessToken(): ?string- Obtener access tokengetRefreshToken(): ?string- Obtener refresh tokengetTokenType(): ?string- Obtener tipo de tokengetExpiresIn(): ?int- Obtener tiempo de expiracióngetScope(): ?string- Obtener scopegetUserId(): ?int- Obtener ID de usuariohasAccessToken(): bool- Verificar si tiene access tokenhasRefreshToken(): bool- Verificar si tiene refresh tokentoArray(): array- Convertir a arraytoJson(): string- Convertir a JSONisSuccessful(): bool- Verificar si la respuesta es exitosagetErrorMessage(): ?string- Obtener mensaje de errorgetErrorDescription(): ?string- Obtener descripción del error
READ- Permiso de lecturaWRITE- Permiso de escrituraOFFLINE_ACCESS- Permiso offline (refresh tokens)
getAll(): array- Obtener todos los scopesgetDefault(): array- Obtener scopes por defectogetOfflineAccess(): array- Obtener scopes con offline accessisValid(string $scope): bool- Validar scopevalidateScopes(array $scopes): bool- Validar múltiples scopestoString(array $scopes): string- Convertir a stringtoArray(string $scopes): array- Convertir a arrayhasOfflineAccess(array|string $scopes): bool- Verificar offline accesshasRead(array|string $scopes): bool- Verificar lecturahasWrite(array|string $scopes): bool- Verificar escritura
MLA- ArgentinaMLB- BrasilMLM- MéxicoMLC- ChileMCO- ColombiaMPE- PerúMLU- UruguayMLV- Venezuela
getAll(): array- Obtener todos los sitiosgetSite(string $siteId): ?array- Obtener información del sitiogetDomain(string $siteId): string- Obtener dominiogetName(string $siteId): string- Obtener nombregetFlag(string $siteId): string- Obtener banderaisValid(string $siteId): bool- Validar sitiogetAuthorizationUrl(string $siteId): string- Obtener URL de autorizacióngetApiUrl(): string- Obtener URL base de la APIgetOAuthTokenEndpoint(): string- Obtener endpoint de tokens
La librería incluye manejo de errores específico:
try {
$response = $meli->generateAccessToken();
} catch (GenericException $e) {
echo "Error: " . $e->getMessage();
echo "Código: " . $e->getCode();
}- 400 - Solicitud inválida
- 401 - Credenciales inválidas
- 403 - Acceso denegado
- 404 - Endpoint no encontrado
- 429 - Demasiadas solicitudes
- 5xx - Error del servidor
- invalid_client - Client ID o Secret inválidos
- invalid_grant - Código o refresh token inválido/expirado
- invalid_scope - Scope inválido
- invalid_request - Parámetros faltantes o inválidos
- unsupported_grant_type - Grant type no soportado
- forbidden - Acceso denegado
- local_rate_limited - Demasiadas solicitudes
- unauthorized_client - Cliente no autorizado
- unauthorized_application - Aplicación bloqueada
# Ejecutar tests
composer test
# Ejecutar tests con coverage
composer test-coverage
# Formatear código
composer format<?php
use Tepuilabs\MeliServices\Exceptions\GenericException;
use Tepuilabs\MeliServices\MeliConfig;
use Tepuilabs\MeliServices\MeliServices;
use Tepuilabs\MeliServices\MeliScopes;
require 'vendor/autoload.php';
session_start();
// Configuración
$clientId = 'tu_client_id';
$clientSecret = 'tu_client_secret';
$redirectUri = 'http://localhost:9000';
// Paso 1: Generar URL de autorización
if (!isset($_GET['code'])) {
$pkce = MeliConfig::generatePkce();
$state = MeliConfig::generateState();
$_SESSION['code_verifier'] = $pkce['code_verifier'];
$_SESSION['state'] = $state;
$config = MeliConfig::forAuthorization(
clientId: $clientId,
clientSecret: $clientSecret,
redirectUri: $redirectUri,
codeVerifier: $pkce['code_verifier'],
codeChallenge: $pkce['code_challenge'],
state: $state,
scopes: MeliScopes::getOfflineAccess()
);
$meli = new MeliServices($config);
$authUrl = $meli->getAuthorizationUrl('MLA');
echo "Ve a: {$authUrl}";
exit;
}
// Paso 2: Intercambiar código por token
try {
$code = $_GET['code'];
$state = $_GET['state'];
// Verificar state
if ($state !== $_SESSION['state']) {
throw new Exception("State no coincide");
}
$config = new MeliConfig(
clientId: $clientId,
clientSecret: $clientSecret,
code: $code,
redirectUri: $redirectUri,
codeVerifier: $_SESSION['code_verifier']
);
$meli = new MeliServices($config);
$response = $meli->generateAccessToken();
// Guardar tokens
$_SESSION['access_token'] = $response->getAccessToken();
$_SESSION['refresh_token'] = $response->getRefreshToken();
$_SESSION['user_id'] = $response->getUserId();
echo "¡Autenticación exitosa!";
} catch (GenericException $e) {
echo "Error: " . $e->getMessage();
}- Fork el proyecto
- Crea una rama para tu feature (
git checkout -b feature/AmazingFeature) - Commit tus cambios (
git commit -m 'Add some AmazingFeature') - Push a la rama (
git push origin feature/AmazingFeature) - Abre un Pull Request
Este proyecto está bajo la Licencia MIT. Ver el archivo LICENSE.md para más detalles.
