All notable changes to this project are documented here.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.3.1
Dependency migration. No change to this SDK's own API — Mercadopago.new/2
still takes finch: MyPool as a bare name.
Changed
reqmoved from the0.6line to~> 0.7.4. The old bound meant< 0.7.0, so any application already on Req 0.7 could not resolve this SDK at all. Verified by running the fullmix ciagainst 0.7.4: everything the bound was guarding survives untouched —%Req.TransportError{},Req.Response.get_header/2,Req.Test.transport_error/2, and the:plug,:params,:receive_timeout,:retry,:jsonand:form_multipartoptions.This is the breaking part for consumers: an application pinned to Req 0.6 can no longer use this version. Hex resolves that by falling back to 0.3.0 rather than failing, so the practical effect is being held at the older release until Req is upgraded.
The bound stays inside a single Req minor deliberately. Req 0.8 is not a bump away: it drops Jason in favour of
JSON.encode!/2, replaces theretrystep withReq.Retry, and requires Elixir 1.18 — three minors above this SDK's floor.jasonraised to~> 1.4. Nothing inlib/calls it; Req encodes:jsonbodies and the tests decode with it. It cannot be scoped toonly: :test, because Req depends on it unconditionally and Mix rejects an:onlynarrower than a transitive dependency's.telemetrydeliberately left at~> 1.0. Only:telemetry.span/3and:telemetry.execute/3are used, both present since 1.0, and telemetry is shared with Phoenix, Ecto, Finch and Plug — a higher floor would buy nothing and could conflict in a consumer's tree.
Fixed
The
:finchoption warned on every request under Req 0.7. Req deprecatedfinch: namein favour offinch: [name: name]; the SDK passed the bare name, so any consumer using a dedicated Finch pool would have seensetting :finch to a Finch pool name is deprecatedlogged on every single call. The SDK's ownfinch:option is unchanged — the new shape is applied internally.This slipped through because no test exercised the
:finchpath::plugand:finchare mutually exclusive adapters and all 125 existing tests use:plug.test/mercadopago/finch_test.exsnow covers it, with a positive control asserting the warning is still detectable.
0.3.0
Synced with the official Ruby SDK 3.4.0 (this SDK was at 3.2.1). See DIVERGENCES.md for every intentional departure from it.
No public function was removed or changed arity.
Fixed
- Webhook replay tolerance rejected every notification.
Mercadopago.Webhook.Validatorcompared thetsfrom thex-signatureheader, which MercadoPago sends in seconds, against a clock in milliseconds. The computed drift was therefore around 55 years, so passingtolerance_seconds:rejected every webhook with:timestamp_out_of_tolerance— the option meant to harden replay protection was the one thing that broke validation. Callers who left it unset were unaffected, which is why this went unnoticed. The reference Ruby SDK carried the same defect until 3.3.0. The test fixture used a millisecondtsand a clock derived from it, so it agreed with the bug; it now uses a realistic ten-digittsand a real millisecond clock.Every version published before this one is affected — 0.1.0, 0.2.0 and 0.2.1. The defect dates from the first commit. Two things to check before deploying the upgrade, both covered in Upgrading from 0.2.x:- Your webhook endpoint has been rejecting everything, so MercadoPago has been retrying. Those retries will start succeeding — the handler must be idempotent.
- If you worked around the bug by passing
:nowin seconds, that inverts: it used to widen the window 1000×, and now rejects everything. Drop the option or return:os.system_time(:millisecond). The SDK warns when it is handed a clock in the wrong unit.
Added
Mercadopago.Pagination.stream/3and asearch_stream/3on every resource with asearch/3—AdvancedPayment,Chargeback,Customer,Invoice,MerchantOrder,Order,Payment,Preapproval,PreapprovalPlan,PreferenceandSubscription. Lazily walks thelimit/offsetpages and yields the records. Reads theresults,data(Orders v2) andelementsshapes, and acceptspaging.totalas an integer or a string. A failure mid-walk raises rather than truncating the stream silently.:kindonMercadopago.Error— the class of failure as an atom (:not_found,:rate_limit,:server, …), from the same status mapping the Ruby SDK gives its twelve exception subclasses. One struct with a matchable field, rather than twelve modules.:request_idand:retry_afteronMercadopago.Error, lifted from thex-request-idandRetry-Afterresponse headers.Mercadopago.Card.update/5,Mercadopago.Payment.capture/4,Mercadopago.Preference.search/3andMercadopago.Refund.get/4.Mercadopago.Subscription, mirroring the Ruby SDK'ssdk.subscription. The same/preapprovalendpointsMercadopago.Preapprovalalready exposed; every function delegates to it.:retry_on, on the client and per call, to choose which HTTP statuses make a GET worth retrying (default[429, 500, 502, 503, 504]). Retryable transport failures are retried regardless.- A warning when
Mercadopago.Webhook.Validatoris given a:nowfunction returning what looks like seconds rather than milliseconds — the shape of the pre-0.3.0 workaround for the bug above, which stops working once the unit is correct. [:mercadopago, :request, :retry]telemetry event, fired just before each backoff sleep with%{delay: milliseconds}and the failed attempt's metadata. This is the SDK's equivalent of the Ruby SDK'son_retrycallback.- Examples for Automatic Payments through Orders
(
examples/order/create_automatic_payment.exs, the two-step CIT → MIT flow) and for CREDENTIAL_ON_FILE payments (examples/payment/credential_on_file.exs). Note the field isprevious_transaction_reference; the Ruby SDK renamed it fromprev_transaction_refin 3.4.0.
Changed
- The response map carries two more keys:
:request_idand:retry_after, eachnilwhen the server sent no such header. So a completed request is now{:ok, %{status: _, response: _, request_id: _, retry_after: _}}. Pattern matches on maps are partial, so%{status: status, response: body}keeps working — the one thing that breaks is comparing the whole map for equality, e.g.result == {:ok, %{status: 200, response: body}}. Match instead of comparing.
0.2.1
Audited against MercadoPago's official MCP server
(https://mcp.mercadopago.com/mcp), walking the SDK's routes against the live
API documentation. Every finding was re-verified against the code before being
acted on; those that could not be confirmed were not acted on. See
DIVERGENCES.md for every intentional departure from the
reference Ruby SDK.
No public function was removed or changed arity in this release.
Fixed
- Webhook signatures for Orders notifications.
Mercadopago.Webhook.Validatornow lowercasesdata.idbefore building the HMAC manifest, as MercadoPago specifies:id:[data.id_lowercase];request-id:[x-request-id];ts:[ts];. Numeric payment ids have no case and were unaffected, which is why this went unnoticed — but the Orders API sends ULID-style ids such asORD01JQ4S4KY8HWQ6NA5PXB65B3D3, and every one of those notifications was being rejected as an invalid signature. Callers passdata.idthrough untouched; the lowercasing is internal to the manifest. Anyone who worked around this by lowercasing the id themselves is unaffected. The reference Ruby SDK 3.2.1 has the same defect. - The
mix cialias could never fail on formatting. It ranformatbeforeformat --check-formatted, so the first task rewrote the files and the check always passed. It also mutated the working tree. Only the check remains. Mercadopago.Order.create_online/3no longer mixes atom and string keys when injecting defaults into a string-keyed payload.
Added
Mercadopago.Order.create_online/3, replacing the misnamedcreate_checkout_pro/3(see Deprecated).Mercadopago.HTTP.patch/4, unblocking endpoints that take a partial update.- Multipart request bodies via
{:multipart, parts}onpost/4,put/4andpatch/4, for uploads such as chargeback documentation. Part content may be a stream, so large files need not be read into memory. Mercadopago.OAuth.generate_code_verifier/0andMercadopago.OAuth.code_challenge/1(PKCE, RFC 7636 S256), plus:code_challenge/:code_challenge_methodoptions onget_authorization_url/4.- Tokenless clients:
Mercadopago.new(nil)omits theAuthorizationheader instead of sending an emptyBearer. Previously the only way to bootstrap the OAuth authorization-code flow wasMercadopago.new(""). Mercadopago.OAuth.create/3andrefresh/3default their owngrant_type; an explicitly supplied one is never overwritten.Mercadopago.Errorand the opt-inMercadopago.HTTP.unwrap/1, for callers who prefer{:ok, body}/{:error, exception}over inspecting status codes. The default contract is unchanged: resource functions still return{:ok, %{status: _, response: _}}for every completed request.
Deprecated
Mercadopago.Order.create_checkout_pro/3— usecreate_online/3. The name is a misnomer inherited from the Ruby SDK:POST /v1/orderswithtype: "online"is the Checkout API via Orders, not Checkout Pro. Checkout Pro is the hosted flow built onPOST /checkout/preferences, available asMercadopago.Preference. The old name still works and delegates to the new one.
Security
- Updated all dependencies to close five transport-layer CVEs.
- Bounded
reqto the0.6line: it is pre-1.0 and breaks API across minors, and the SDK matches on%Req.TransportError{}and callsReq.Response.get_header/2andReq.Test.transport_error/2.
Changed
- Hardened the HTTP transport and restructured the integration suite into one
module per resource, so
async: trueactually parallelises the network round-trips.
0.2.0
- Synced with the official Ruby SDK 3.2.0.
0.1.0
- Initial release.