Skip to main content

Credential Management

Laravel MercadoPago uses a flexible credential resolution system that separates credential retrieval from SDK configuration. This enables support for environment-based credentials, database storage, secrets managers, and multi-tenant configurations.

Credential Types

Mercado Pago uses three types of credentials:

Access Token

Required - Authenticates API requests to Mercado Pago

Public Key

Optional - Used for client-side SDK initialization

Webhook Secret

Optional - Validates webhook signatures (HMAC-SHA256)

Access Token

Purpose: Server-side authentication for all API operations Required for: All services except WebhookService (when not validating signatures) Format: Long alphanumeric string (e.g., APP_USR-123456789-abcdef-...)
The access token provides full access to your Mercado Pago account. Never commit it to version control or expose it in client-side code.

Public Key

Purpose: Client-side SDK initialization for tokenizing cards Required for: Frontend integrations using Mercado Pago SDK.js Format: Public key string (e.g., APP_USR-abcdef123456-...)
The public key can be safely exposed in client-side code. It only allows card tokenization, not API operations.

Webhook Secret

Purpose: HMAC-SHA256 signature validation for incoming webhooks Required for: Secure webhook validation Format: Secret string provided in your Mercado Pago dashboard
Always configure webhook secrets in production to prevent unauthorized webhook requests.

Credential Resolution

MercadoPagoCredentials DTO

Credentials are encapsulated in a readonly Data Transfer Object: Location: src/DTO/MercadoPagoCredentials.php:7
final readonly class MercadoPagoCredentials
{
    public function __construct(
        public string $accessToken,
        public ?string $publicKey = null,
        public ?string $webhookSecret = null,
    ) {}

    public function toArray(): array
    {
        return [
            'access_token' => $this->accessToken,
            'public_key' => $this->publicKey,
            'webhook_secret' => $this->webhookSecret,
        ];
    }
}
Benefits of using a DTO:
  • Type Safety - Access token is guaranteed to be a string
  • Immutability - Credentials cannot be modified after creation
  • Clarity - Public and webhook secret are explicitly optional
  • Serialization - Easy conversion to array format

CredentialResolverInterface

The credential resolver contract defines how credentials are retrieved: Location: src/Contracts/CredentialResolverInterface.php:9
interface CredentialResolverInterface
{
    public function resolve(): MercadoPagoCredentials;
}
This interface allows you to implement custom credential resolution strategies while maintaining compatibility with the package.

EnvCredentialResolver

The default implementation reads credentials from Laravel’s configuration: Location: src/Support/EnvCredentialResolver.php:12
final readonly class EnvCredentialResolver implements CredentialResolverInterface
{
    public function __construct(
        private Repository $config,
    ) {}

    public function resolve(): MercadoPagoCredentials
    {
        $accessToken = (string) $this->config->get('mercadopago.access_token', '');

        if ($accessToken === '') {
            throw MercadoPagoConfigurationException::missingAccessToken();
        }

        $publicKey = $this->config->get('mercadopago.public_key');
        $webhookSecret = $this->config->get('mercadopago.webhook_secret');

        return new MercadoPagoCredentials(
            accessToken: $accessToken,
            publicKey: is_string($publicKey) && $publicKey !== '' ? $publicKey : null,
            webhookSecret: is_string($webhookSecret) && $webhookSecret !== '' ? $webhookSecret : null,
        );
    }
}
Key behaviors:
  1. Access Token Validation (src/Support/EnvCredentialResolver.php:22) - Throws MercadoPagoConfigurationException if missing
  2. Optional Credentials (src/Support/EnvCredentialResolver.php:26) - Public key and webhook secret default to null if empty
  3. Type Coercion (src/Support/EnvCredentialResolver.php:31) - Ensures only non-empty strings are passed

Configuration

Environment Variables

The default configuration reads from .env:
# Required: Access token for API authentication
MERCADOPAGO_ACCESS_TOKEN=APP_USR-123456789-abcdef-...

# Optional: Public key for client-side SDK
MERCADOPAGO_PUBLIC_KEY=APP_USR-abcdef123456-...

# Optional: Webhook secret for signature validation
MERCADOPAGO_WEBHOOK_SECRET=your_webhook_secret_here

# Optional: Route prefix for demo endpoints
MERCADOPAGO_ROUTE_PREFIX=api/mercadopago

# Optional: Enable/disable demo routes
MERCADOPAGO_ENABLE_DEMO_ROUTES=true

# Optional: Runtime environment (local, server)
MERCADOPAGO_RUNTIME_ENVIRONMENT=local

Configuration File

Location: config/mercadopago.php:5
return [
    'access_token' => env('MERCADOPAGO_ACCESS_TOKEN'),
    'public_key' => env('MERCADOPAGO_PUBLIC_KEY'),
    'webhook_secret' => env('MERCADOPAGO_WEBHOOK_SECRET'),
    'route_prefix' => env('MERCADOPAGO_ROUTE_PREFIX', 'api/mercadopago'),
    'enable_demo_routes' => (bool) env('MERCADOPAGO_ENABLE_DEMO_ROUTES', true),
    'runtime_environment' => env('MERCADOPAGO_RUNTIME_ENVIRONMENT'),
];
Publishing the configuration file allows you to:
  • Add custom configuration keys
  • Modify default values
  • Document configuration options for your team
  • Override configuration in tests
php artisan vendor:publish --tag=mercadopago-config
The published file will be located at config/mercadopago.php in your Laravel project.

SDK Configuration

The MercadoPagoClientFactory handles SDK configuration using resolved credentials: Location: src/Support/MercadoPagoClientFactory.php:45
private function configureSdk(): void
{
    $sdkConfigClass = 'MercadoPago\\MercadoPagoConfig';

    if (! class_exists($sdkConfigClass)) {
        throw MercadoPagoConfigurationException::sdkNotInstalled();
    }

    $credentials = $this->credentialResolver->resolve();

    $sdkConfigClass::setAccessToken($credentials->accessToken);

    $runtimeEnvironment = $this->config->get('mercadopago.runtime_environment');

    if (! is_string($runtimeEnvironment) || $runtimeEnvironment === '') {
        $runtimeEnvironment = app()->environment(['local', 'testing']) ? 'local' : 'server';
    }

    foreach (['setRuntimeEnvironment', 'setRuntimeEnviroment'] as $methodName) {
        if (method_exists($sdkConfigClass, $methodName)) {
            $sdkConfigClass::{$methodName}($runtimeEnvironment);
            break;
        }
    }
}
Configuration steps:
  1. SDK Validation - Verifies Mercado Pago SDK is installed
  2. Credential Resolution - Calls the credential resolver
  3. Access Token - Sets the access token globally on MercadoPagoConfig
  4. Runtime Environment - Configures SDK for local development or production
  5. Method Compatibility - Handles SDK version differences in method names
SDK configuration happens automatically on the first service call. You don’t need to manually configure the SDK.

Runtime Environment

The runtime environment affects how the Mercado Pago SDK reports telemetry:
  • local - Development/testing environment
  • server - Production environment
Auto-detection (src/Support/MercadoPagoClientFactory.php:60):
$runtimeEnvironment = app()->environment(['local', 'testing']) ? 'local' : 'server';
Manual override:
MERCADOPAGO_RUNTIME_ENVIRONMENT=server

Custom Credential Resolvers

Database-Backed Credentials

For multi-tenant applications or centralized credential management:
namespace App\Services;

use Fitodac\LaravelMercadoPago\Contracts\CredentialResolverInterface;
use Fitodac\LaravelMercadoPago\DTO\MercadoPagoCredentials;
use Fitodac\LaravelMercadoPago\Exceptions\MercadoPagoConfigurationException;
use App\Models\PaymentCredential;

class DatabaseCredentialResolver implements CredentialResolverInterface
{
    public function resolve(): MercadoPagoCredentials
    {
        $credential = PaymentCredential::where('provider', 'mercadopago')
            ->where('environment', app()->environment())
            ->first();

        if (! $credential) {
            throw MercadoPagoConfigurationException::missingAccessToken();
        }

        return new MercadoPagoCredentials(
            accessToken: decrypt($credential->access_token),
            publicKey: $credential->public_key ? decrypt($credential->public_key) : null,
            webhookSecret: $credential->webhook_secret ? decrypt($credential->webhook_secret) : null,
        );
    }
}
Register in AppServiceProvider:
use Fitodac\LaravelMercadoPago\Contracts\CredentialResolverInterface;
use App\Services\DatabaseCredentialResolver;

public function register()
{
    $this->app->bind(
        CredentialResolverInterface::class,
        DatabaseCredentialResolver::class
    );
}

Vault/Secrets Manager Integration

For AWS Secrets Manager, HashiCorp Vault, or similar:
namespace App\Services;

use Fitodac\LaravelMercadoPago\Contracts\CredentialResolverInterface;
use Fitodac\LaravelMercadoPago\DTO\MercadoPagoCredentials;
use Aws\SecretsManager\SecretsManagerClient;

class VaultCredentialResolver implements CredentialResolverInterface
{
    public function __construct(
        private SecretsManagerClient $secretsManager
    ) {}

    public function resolve(): MercadoPagoCredentials
    {
        $result = $this->secretsManager->getSecretValue([
            'SecretId' => 'mercadopago/credentials',
        ]);

        $credentials = json_decode($result['SecretString'], true);

        return new MercadoPagoCredentials(
            accessToken: $credentials['access_token'],
            publicKey: $credentials['public_key'] ?? null,
            webhookSecret: $credentials['webhook_secret'] ?? null,
        );
    }
}

Multi-Tenant Resolver

For applications serving multiple merchants:
namespace App\Services;

use Fitodac\LaravelMercadoPago\Contracts\CredentialResolverInterface;
use Fitodac\LaravelMercadoPago\DTO\MercadoPagoCredentials;

class TenantCredentialResolver implements CredentialResolverInterface
{
    public function resolve(): MercadoPagoCredentials
    {
        $tenant = tenant(); // Your tenant resolution logic

        return new MercadoPagoCredentials(
            accessToken: $tenant->mercadopago_access_token,
            publicKey: $tenant->mercadopago_public_key,
            webhookSecret: $tenant->mercadopago_webhook_secret,
        );
    }
}
In multi-tenant scenarios, ensure services are registered as scoped or transient, not singletons, to prevent credential leakage between tenants.

Security Considerations

Never Commit Credentials

Add to .gitignore:
.env
.env.production
config/mercadopago.php  # If you hardcode values

Encrypt Stored Credentials

When storing in database:
// Before storing
$credential->access_token = encrypt($accessToken);

// When resolving
$accessToken = decrypt($credential->access_token);

Use Environment-Specific Credentials

Never use production credentials in development:
# .env.local
MERCADOPAGO_ACCESS_TOKEN=TEST-123456789-...

# .env.production
MERCADOPAGO_ACCESS_TOKEN=APP_USR-123456789-...

Rotate Credentials Regularly

Implement credential rotation:
class RotateCredentialsCommand extends Command
{
    public function handle()
    {
        $newCredentials = $this->fetchFromMercadoPago();
        
        config([
            'mercadopago.access_token' => $newCredentials->accessToken,
            'mercadopago.public_key' => $newCredentials->publicKey,
        ]);
        
        // Clear SDK configuration to force re-initialization
        app()->forgetInstance(MercadoPagoClientFactory::class);
    }
}

Validate Webhook Signatures

Always configure webhook secrets in production:
// WebhookService automatically validates if secret is configured
$webhook = $webhookService->handle($request);

if (! $webhook['validated']) {
    \Log::warning('Webhook received without signature validation');
    // Consider rejecting in production
}
Validation implementation (src/Services/WebhookService.php:38):
private function assertValidSignature(
    Request $request,
    array $payload,
    string $secret,
    string $signatureHeader,
): void {
    $signatureParts = $this->parseSignatureHeader($signatureHeader);
    $resourceId = (string) ($request->query('data.id') ?? Arr::get($payload, 'data.id', ''));
    $requestId = (string) $request->header('x-request-id', '');

    $manifest = sprintf(
        'id:%s;request-id:%s;ts:%s;',
        $resourceId,
        $requestId,
        $signatureParts['ts'],
    );

    $computedHash = hash_hmac('sha256', $manifest, $secret);

    if (! hash_equals($computedHash, $signatureParts['v1'])) {
        throw InvalidWebhookSignatureException::signatureMismatch();
    }
}

Limit Credential Access

Use Laravel’s authorization:
Gate::define('view-mercadopago-credentials', function (User $user) {
    return $user->hasRole('admin');
});

if (Gate::allows('view-mercadopago-credentials')) {
    return config('mercadopago.access_token');
}

Testing

Mock Credentials in Tests

use Fitodac\LaravelMercadoPago\Contracts\CredentialResolverInterface;
use Fitodac\LaravelMercadoPago\DTO\MercadoPagoCredentials;

class PaymentServiceTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();

        $this->app->bind(CredentialResolverInterface::class, function () {
            return new class implements CredentialResolverInterface {
                public function resolve(): MercadoPagoCredentials
                {
                    return new MercadoPagoCredentials(
                        accessToken: 'TEST-ACCESS-TOKEN',
                        publicKey: 'TEST-PUBLIC-KEY',
                        webhookSecret: 'TEST-WEBHOOK-SECRET',
                    );
                }
            };
        });
    }
}

Override Configuration in Tests

public function test_payment_creation()
{
    config([
        'mercadopago.access_token' => 'TEST-TOKEN',
        'mercadopago.runtime_environment' => 'local'
    ]);

    $service = app(PaymentService::class);
    // Test payment creation
}

Troubleshooting

Missing Access Token

Error: MercadoPagoConfigurationException: Mercado Pago access token is not configured. Solution:
  1. Check .env file has MERCADOPAGO_ACCESS_TOKEN
  2. Verify .env is loaded: php artisan config:clear
  3. Ensure credential resolver is resolving correctly

SDK Not Installed

Error: MercadoPagoConfigurationException: Mercado Pago PHP SDK is not installed. Solution:
composer require mercadopago/dx-php:^3.8

Invalid Webhook Signature

Error: InvalidWebhookSignatureException: Mercado Pago webhook signature is invalid. Solution:
  1. Verify MERCADOPAGO_WEBHOOK_SECRET matches Mercado Pago dashboard
  2. Check webhook is coming from Mercado Pago (not a test tool)
  3. Ensure webhook secret hasn’t been rotated

Next Steps

Architecture

Understand how credentials flow through the package

Services

Learn how services use credentials

Security

Production security best practices

Webhooks

Implement secure webhook handling

Build docs developers (and LLMs) love