Mercadopago Elixir SDK

Copy Markdown View Source

Hex.pm Hex Docs License

Elixir client for the MercadoPago REST API, ported from the official Ruby SDK (feature parity with mercadopago-sdk 3.2.0).

Installation

Add to mix.exs:

def deps do
  [
    {:mercadopago_sdk_elixir, "~> 0.2.0"}
  ]
end

Usage

client = Mercadopago.new("YOUR_ACCESS_TOKEN")

# Create a payment
{:ok, %{status: 201, response: payment}} =
  Mercadopago.Payment.create(client, %{
    transaction_amount: 100.0,
    description: "Product",
    payment_method_id: "pix",
    payer: %{email: "buyer@example.com"}
  })

# Fetch a payment
{:ok, %{status: 200, response: payment}} =
  Mercadopago.Payment.get(client, payment["id"])

Checkout Pro orders

Mercadopago.Order.create_checkout_pro/3 wraps Order.create/3 and applies the Checkout Pro defaults type: "online" and processing_mode: "manual" when omitted (raising ArgumentError if incompatible values are given):

{:ok, %{status: 201, response: order}} =
  Mercadopago.Order.create_checkout_pro(client, %{
    external_reference: "order-0001",
    total_amount: "100.00",
    items: [
      %{title: "Product", unit_price: "100.00", quantity: 1}
    ]
  })

# order["type"] == "online", order["processing_mode"] == "manual"

Per-call options

Every resource call accepts options that override the client configuration for that single request:

Mercadopago.Payment.get(client, id, access_token: "OTHER_TOKEN")

# Pin the idempotency key of one POST (case-insensitive override of the
# generated x-idempotency-key header):
Mercadopago.Order.create_checkout_pro(client, order_data,
  custom_headers: %{"X-Idempotency-Key" => my_key}
)

Mercadopago.Payment.search(client, filters, timeout: 5_000, max_retries: 1)

Supported keys: :access_token, :custom_headers, :timeout (ms) and :max_retries (GET only).

Connection pooling

By default requests go through Req's shared Finch pool. For high-throughput applications, start a dedicated Finch pool in your supervision tree and point the client at it:

# In your application supervisor
{Finch, name: MyApp.MercadopagoFinch, pools: %{default: [size: 25]}}

# When building the client
client = Mercadopago.new(token, finch: MyApp.MercadopagoFinch)

Testing

Unit tests (no network, no token)

Use Req.Test stubs to intercept HTTP calls. The SDK exposes a :plug option on the client that routes requests through the stub instead of the network.

A helper is provided in test/support/stub_client.ex (compiled only in the :test env):

# In your test file
import Mercadopago.Test.StubClient, only: [new: 1]

test "creates a payment" do
  Req.Test.stub(:payment_stub, fn conn ->
    conn
    |> Plug.Conn.put_status(201)
    |> Req.Test.json(%{"id" => "pay_123", "status" => "approved"})
  end)

  client = new(:payment_stub)

  assert {:ok, %{status: 201, response: %{"id" => "pay_123"}}} =
           Mercadopago.Payment.create(client, %{transaction_amount: 100})
end

new/1 builds a client with access_token: "test_token" and plug: {Req.Test, stub_name}. The stub receives a %Plug.Conn{} and must return a response — use Req.Test.json/2 for JSON bodies.

Run unit tests:

mix test

Integration tests (real MercadoPago sandbox)

Tag integration tests with @moduletag :integration. They are excluded from the default mix test run and require a sandbox ACCESS_TOKEN.

defmodule Mercadopago.PaymentIntegrationTest do
  @moduletag :integration
  use ExUnit.Case

  setup do
    token = System.fetch_env!("ACCESS_TOKEN")
    {:ok, client: Mercadopago.new(token)}
  end

  test "search payments", %{client: client} do
    assert {:ok, %{status: 200, response: %{"results" => _}}} =
             Mercadopago.Payment.search(client)
  end
end

Run only integration tests:

ACCESS_TOKEN=APP_USR_xxx mix test --include integration

Run all tests (unit + integration):

ACCESS_TOKEN=APP_USR_xxx mix test --include integration --include test

Webhook validation

case Mercadopago.Webhook.Validator.validate(
       x_signature,    # "ts=...,v1=..." header from MercadoPago
       x_request_id,   # x-request-id header
       data_id,        # params["data"]["id"] from the webhook body
       secret          # your webhook secret from the MercadoPago dashboard
     ) do
  {:ok, _ts} -> :ok
  {:error, %Mercadopago.Webhook.Validator.InvalidSignatureError{} = e} -> handle_error(e)
end

Timestamp drift tolerance (default: no check):

Mercadopago.Webhook.Validator.validate(x_sig, x_req, data_id, secret,
  tolerance_seconds: 300
)

Raising variant (validate!/5) is also available — raises Mercadopago.Webhook.Validator.InvalidSignatureError on failure instead of returning {:error, _}.