Mercadopago.HTTP (mercadopago_sdk_elixir v0.2.1)

Copy Markdown View Source

Req-based HTTP transport. Retries GET on transient errors; no retry for mutating verbs.

All functions accept per-call opts overriding the client configuration:

  • :access_token - token for this request only
  • :custom_headers - headers merged over the client's custom_headers, overriding generated headers case-insensitively
  • :timeout - receive timeout in milliseconds for this request only
  • :max_retries - GET retry budget for this request only
  • :retry_delay - base backoff in milliseconds for this request only
  • :max_retry_delay - backoff ceiling in milliseconds for this request only

Retry policy

A GET is retried while the response status is one of [429, 500, 502, 503, 504] or the request fails with a retryable transport error (a connection closed under the pool, a refused or unreachable host). A :timeout is never retried: the receive timeout has already spent the latency budget.

Backoff is exponential from :retry_delay, doubled per attempt, capped by :max_retry_delay, plus jitter so that concurrent callers do not come back in lockstep after an incident. A Retry-After header (delta-seconds form) takes precedence, but is capped by :max_retry_delay all the same.

The wait blocks the calling process, so the worst-case latency of a GET is roughly max_retries * timeout plus the accumulated backoff.

Telemetry

Every attempt — including each retry — is wrapped in a :telemetry.span/3:

  • [:mercadopago, :request, :start] - measurements %{system_time: _}
  • [:mercadopago, :request, :stop] - measurements %{duration: _}
  • [:mercadopago, :request, :exception] - when the request raises

Metadata carries :method, :path and :attempt (zero-based), plus :status on a completed request or :error on a transport failure. Neither the access token nor the request body is ever included.

Summary

Types

Request body: a JSON-encodable map, a multipart body, or none.

A MercadoPago resource identifier. The API returns some ids as integers.

A multipart/form-data body. Each part is {name, value}, where name is an atom (Req's requirement) and value is either a plain binary field or {content, filename: name, content_type: type} for a file part. content may be a binary or a stream, so large uploads need not be read into memory.

Result of an API call. {:ok, ...} carries the HTTP status and the response body: a map or a list for JSON, nil for an empty body, and a raw binary when the response is not JSON (an HTML error page from a proxy, for instance). {:error, reason} is returned for transport-level failures (timeouts, connection errors).

Functions

GET request with optional query params and per-call opts.

PATCH request, for endpoints that accept a partial update.

POST request. Omit body or pass nil to send no body.

Converts a response into idiomatic {:ok, body} / {:error, exception}.

Types

body()

@type body() :: map() | multipart_body() | nil

Request body: a JSON-encodable map, a multipart body, or none.

id()

@type id() :: String.t() | non_neg_integer()

A MercadoPago resource identifier. The API returns some ids as integers.

multipart_body()

@type multipart_body() :: {:multipart, [{atom(), term()}]}

A multipart/form-data body. Each part is {name, value}, where name is an atom (Req's requirement) and value is either a plain binary field or {content, filename: name, content_type: type} for a file part. content may be a binary or a stream, so large uploads need not be read into memory.

Required by endpoints such as chargeback documentation upload:

{:multipart, [
  {:kind, "invoice"},
  {:file, {File.stream!("proof.pdf", 2048), filename: "proof.pdf",
           content_type: "application/pdf"}}
]}

response()

@type response() ::
  {:ok, %{status: non_neg_integer(), response: map() | list() | binary() | nil}}
  | {:error, term()}

Result of an API call. {:ok, ...} carries the HTTP status and the response body: a map or a list for JSON, nil for an empty body, and a raw binary when the response is not JSON (an HTML error page from a proxy, for instance). {:error, reason} is returned for transport-level failures (timeouts, connection errors).

Functions

delete(client, uri, opts \\ [])

@spec delete(Mercadopago.Client.t(), String.t(), keyword()) :: response()

DELETE request.

get(client, uri, params \\ nil, opts \\ [])

@spec get(Mercadopago.Client.t(), String.t(), map() | nil, keyword()) :: response()

GET request with optional query params and per-call opts.

patch(client, uri, body, opts \\ [])

@spec patch(Mercadopago.Client.t(), String.t(), body(), keyword()) :: response()

PATCH request, for endpoints that accept a partial update.

post(client, uri, body, opts \\ [])

@spec post(Mercadopago.Client.t(), String.t(), body(), keyword()) :: response()

POST request. Omit body or pass nil to send no body.

Pass {:multipart, parts} as the body to upload files as multipart/form-data instead of JSON — see multipart_body/0.

put(client, uri, body, opts \\ [])

@spec put(Mercadopago.Client.t(), String.t(), body(), keyword()) :: response()

PUT request.

unwrap(error)

@spec unwrap(response()) :: {:ok, map() | list() | binary() | nil} | {:error, term()}

Converts a response into idiomatic {:ok, body} / {:error, exception}.

A completed request with a status of 400 or above becomes {:error, %Mercadopago.Error{}}; a success is unwrapped to its body alone. Transport failures pass through unchanged, since they are already {:error, _}.

client
|> Mercadopago.Payment.get(payment_id)
|> Mercadopago.HTTP.unwrap()

This is a pure function over an already-returned response, so it is opt-in: the resource functions themselves keep returning {:ok, %{status: _, response: _}}. Reach for it when the status code carries no information you act on.