@zaamx/medusa-payment-mercadopago-international
v0.2.9
Published
Mercado Pago payment provider plugin for MedusaJS
Readme
@zaamx/medusa-payment-mercadopago-international
Mercado Pago payment provider plugin for MedusaJS v2. Supports multiple Latin American countries in a single package, each running as an independent payment provider.
Medusa Website | Medusa Repository | Mercado Pago Developers
Supported countries
| Country | Provider ID | Supported methods | Route |
|---|---|---|---|
| Colombia (MCO) | pp_mercadopago-co_mercadopago-co | Visa, Mastercard, Amex, Efecty, PSE | POST /store/mercadopago-co/payment |
| Perú (MPE) | pp_mercadopago-pe_mercadopago-pe | Visa, Mastercard, PagoEfectivo (ATM) | POST /store/mercadopago-pe/payment |
Each country requires its own Mercado Pago application and credentials — credentials are not shared across countries.
Features
- Multi-country support from a single package
- Checkout Bricks (Payment Brick) integration via Payments API
- Asynchronous payments (Efecty, PSE, PagoEfectivo) handled via webhook
- Card payments automatically captured; offline methods remain pending until Mercado Pago confirms via webhook
- Customer and card saving (saved payment methods) for authenticated users
- Webhook signature validation (
x-signature) - Idempotency via
paymentSessionId— protects against double charges, provides free audit trail - Cart items auto-injected in
additional_info.itemsfrom Medusa (no frontend work needed) statement_descriptorinjected from plugin options- MP Quality Checklist compliant:
payer,issuer_id,statement_descriptor,additional_info.items,external_reference
Prerequisites
- Node.js v20 or greater
- Medusa backend v2.14+
- One Mercado Pago application per country:
- For local webhook testing: ngrok or equivalent
Installation
npm install @zaamx/medusa-payment-mercadopago-international
# The mercadopago SDK must also be installed in the Medusa host project:
npm install mercadopagoConfiguration
Environment variables
# Colombia
MERCADOPAGO_CO_ACCESS_TOKEN=your_colombia_access_token
MERCADOPAGO_CO_WEBHOOK_SECRET=your_colombia_webhook_secret
# Perú
MERCADOPAGO_PE_ACCESS_TOKEN=your_peru_access_token
MERCADOPAGO_PE_WEBHOOK_SECRET=your_peru_webhook_secretmedusa-config.ts
The plugin must be added to both plugins (to register API routes) and modules (to register the payment provider):
import { ContainerRegistrationKeys } from "@medusajs/framework/utils";
export default defineConfig({
plugins: [
// Required to load API routes, middlewares and admin extensions
{ resolve: "@zaamx/medusa-payment-mercadopago-international", options: {} },
// ... other plugins
],
modules: [
{
resolve: "@medusajs/medusa/payment",
options: {
providers: [
// ── Colombia ────────────────────────────────────────────────────
{
resolve: "@zaamx/medusa-payment-mercadopago-international/providers/co",
id: "mercadopago-co",
options: {
accessToken: process.env.MERCADOPAGO_CO_ACCESS_TOKEN,
webhookSecret: process.env.MERCADOPAGO_CO_WEBHOOK_SECRET,
statementDescriptor: "NOMBRE TIENDA",
},
dependencies: [ContainerRegistrationKeys.LOGGER],
},
// ── Perú ────────────────────────────────────────────────────────
{
resolve: "@zaamx/medusa-payment-mercadopago-international/providers/pe",
id: "mercadopago-pe",
options: {
accessToken: process.env.MERCADOPAGO_PE_ACCESS_TOKEN,
webhookSecret: process.env.MERCADOPAGO_PE_WEBHOOK_SECRET,
statementDescriptor: "NOMBRE TIENDA",
},
dependencies: [ContainerRegistrationKeys.LOGGER],
},
],
},
},
],
});Plugin options
| Option | Type | Required | Description |
|---|---|---|---|
| accessToken | string | Yes | Country-specific MP access token |
| webhookSecret | string | Yes | Secret to validate x-signature on webhook notifications |
| statementDescriptor | string | No | Text shown on buyer's card statement — reduces chargebacks |
Medusa regions
Create one region per country in the Medusa Admin:
| Region | Currency | Country code | Provider |
|---|---|---|---|
| Colombia | COP | co | pp_mercadopago-co_mercadopago-co |
| Perú | PEN | pe | pp_mercadopago-pe_mercadopago-pe |
API routes
Colombia
| Method | Route | Description |
|---|---|---|
| POST | /store/mercadopago-co/payment | Create a Mercado Pago payment |
| GET | /store/mercadopago-co/payment-methods | List customer's saved payment methods |
Perú
| Method | Route | Description |
|---|---|---|
| POST | /store/mercadopago-pe/payment | Create a Mercado Pago payment |
| GET | /store/mercadopago-pe/payment-methods | List customer's saved payment methods |
POST /store/mercadopago-{country}/payment — responses
| Case | HTTP | Body |
|---|---|---|
| Payment approved or pending | 201 | { "payment_id": 1234567, "status": "approved" } |
| MP rejects the payment | 422 | { "type": "payment_authorization_error", "message": "Saldo insuficiente." } |
| Unexpected error | 500 | Medusa generic error |
status in the 201 response:
"approved"— captured (card payments)"pending"/"in_process"— async flow (Efecty, PSE, PagoEfectivo). Order confirmed when MP webhookpayment.updated → approvedarrives.
The message in 422 responses is user-facing — display it directly in the checkout UI.
Webhooks
MP sends asynchronous notifications for offline payment methods (Efecty, PSE, PagoEfectivo). Configure each country's webhook in the respective MP developer dashboard.
| Country | Webhook URL |
|---|---|
| Colombia | https://your-backend.com/hooks/payment/mercadopago-co_mercadopago-co |
| Perú | https://your-backend.com/hooks/payment/mercadopago-pe_mercadopago-pe |
Under Eventos, select Pagos. Generate a webhook secret and set it in MERCADOPAGO_{COUNTRY}_WEBHOOK_SECRET.
For local testing with ngrok:
yarn dev # terminal 1
ngrok http 9000 # terminal 2
# Configure in MP dashboard:
# https://xxxx.ngrok-free.app/hooks/payment/mercadopago-co_mercadopago-coFrontend integration
Use the @mercadopago/sdk-react Payment Brick with one component per country. Each component encapsulates the country-specific brick config, route, and public key:
// Colombia: Efecty + PSE + cards
<MercadopagoCoPaymentSection
cart={cart}
paymentSessionId={sessionId}
regionCurrency="COP"
onPaymentSuccess={handleSuccess}
onPaymentError={handleError}
isCompletingOrder={loading}
/>
// Perú: PagoEfectivo ATM + cards
<MercadopagoPePaymentSection
cart={cart}
paymentSessionId={sessionId}
regionCurrency="PEN"
onPaymentSuccess={handleSuccess}
onPaymentError={handleError}
isCompletingOrder={loading}
/>
additional_info.itemsis auto-populated by the backend from the Medusa cart — do not send it from the frontend.
See the frontend integration document for full component implementation details.
Idempotency
Each payment creation uses paymentSessionId as the X-Idempotency-Key:
- MP only caches successful (2xx) responses — failed requests are always reprocessed with the same key
- Double-submit of the same session → MP returns the cached approval (no double charge)
- Audit trail is free:
external_reference = paymentSessionId = idempotencyKey
Adding a new country
- Create
src/providers/mercado-pago/<country>/service.tsextendingMercadopagoBaseProviderServicewithstatic identifier = "mercadopago-<country>". - Create
src/providers/mercado-pago/<country>/index.tsregistering the provider. - Add step files under
src/workflows/mercadopago/<country>/steps/using the shared factories. - Add the workflow under
src/workflows/mercadopago/<country>/workflows/. - Add API routes under
src/api/store/mercadopago-<country>/. - Export the new workflow in
src/workflows/index.ts. - Add the provider export in
package.jsonexports.
Package structure
src/
providers/mercado-pago/
base-service.ts ← shared logic for all countries
co/ ← Colombia provider
pe/ ← Perú provider
workflows/mercadopago/
shared/steps/ ← reusable step factories (create-payment, validate-amount)
workflows/ ← Colombia workflow
pe/ ← Perú workflow and steps
api/store/
mercadopago-co/ ← Colombia routes & validators
mercadopago-pe/ ← Perú routes & validators