Beztack
Payments

Mercado Pago

Payment and subscription flows with Mercado Pago

Beztack supports Mercado Pago as a first-class payment provider, including plan sync, subscriptions, checkout preference, direct payment processing, and webhook/event handling.

For an overview of the provider-agnostic payment system, see Payments Core.

When to use Mercado Pago

Use Mercado Pago when you need:

  • Native Mercado Pago plans/subscriptions
  • Direct card payment processing
  • Mercado Pago webhook-driven state updates
  • Real-time payment events for frontend UX

See also: Polar

Required environment variables

API (apps/api/.env)

MERCADO_PAGO_ACCESS_TOKEN=APP_USR-...
MERCADO_PAGO_APPLICATION_ID=123456789
MERCADO_PAGO_WEBHOOK_SECRET=... # optional but recommended
MERCADO_PAGO_INTEGRATOR_ID=...  # optional
PAYMENT_PROVIDER=mercadopago
APP_URL=http://localhost:3000
DEFAULT_CURRENCY=UYU # your project's currency, e.g. the Mercado Pago site's
DEFAULT_LOCALE=es-UY

DEFAULT_CURRENCY and DEFAULT_LOCALE are required for every project (the create CLI asks for them). Mercado Pago plans are created in the price's own currency; DEFAULT_CURRENCY only fills in when a price carries none.

MERCADO_PAGO_APPLICATION_ID is required because it is the Mercado Pago-native Payment integration boundary. Beztack uses it to prevent plans, subscriptions, webhooks, and Membership cache from one Mercado Pago Application from being visible or manageable through another Mercado Pago Application.

Do not confuse it with MERCADO_PAGO_INTEGRATOR_ID: the integrator ID is optional partner tracking and is not an access-control boundary.

For existing installations, get the Application ID from the Mercado Pago developer application details or from an existing plan/subscription API response, then set MERCADO_PAGO_APPLICATION_ID before running Mercado Pago in Beztack.

UI (apps/ui/.env)

VITE_PAYMENT_PROVIDER=mercadopago
VITE_MERCADO_PAGO_PUBLIC_KEY=APP_USR-...
VITE_API_URL=http://localhost:3000
VITE_DEFAULT_CURRENCY=UYU
VITE_DEFAULT_LOCALE=es-UY

Implementation references:

  • Env schemas: packages/env/src/api.ts, packages/env/src/ui.ts
  • Provider factory: apps/api/lib/payments/index.ts
  • React provider wiring: apps/ui/src/app.tsx

API Routes

Unified routes (shared across all providers)

These routes are provider-agnostic and work with whichever provider is active:

  • GET /api/subscriptions/products — List products/plans from the active provider.
  • POST /api/subscriptions/checkout — Create a checkout session.
  • GET /api/subscriptions — List subscriptions for the current user.
  • GET /api/subscriptions/:id — Get a specific subscription.
  • PATCH /api/subscriptions/:id — Update a subscription.
  • DELETE /api/subscriptions/:id — Cancel a subscription.
  • POST /api/subscriptions/webhooks — Process webhook payloads (unified handler).

MercadoPago-specific route

  • POST /api/payments/mercado-pago/webhook — Validate and process MercadoPago-specific webhook payloads.

Admin plan management routes

  • GET /api/admin/plans — List all plans from the local DB.
  • POST /api/admin/plans — Create a new plan.
  • POST /api/admin/plans/import — Import plans from the provider.
  • POST /api/admin/plans/sync — Sync all plans from the provider into the local DB.
  • GET /api/admin/plans/sync-status — Check the current sync status.
  • PATCH /api/admin/plans/:id — Update a plan.
  • DELETE /api/admin/plans/:id — Delete a plan.

Plan sync flow

Beztack keeps a local plan mirror in the plan table (provider-agnostic, with a providerPlanId field linking to the remote product ID) to make plan selection and checkout flows predictable in the app.

  • GET /api/admin/plans reads local plans.
  • POST /api/admin/plans/sync performs an explicit admin-triggered sync from the provider.
  • POST /api/admin/plans/import imports plans from the provider into the local DB.
  • GET /api/admin/plans/sync-status reports the current sync state.

Client integration

In UI, Mercado Pago is enabled by environment flags and wrapped with MercadoPagoProvider.

<MercadoPagoProvider apiBaseUrl={env.VITE_API_URL} publicKey={mpPublicKey}>
  {children}
</MercadoPagoProvider>

The package @beztack/mercadopago/react exposes:

  • Provider + context
  • Hooks (usePlans, useSyncPlans, useSubscriptions, etc.)
  • Components for subscriptions, status badges, billing history, and bricks

Testing checklist

  1. Configure sandbox credentials and VITE_MERCADO_PAGO_PUBLIC_KEY.
  2. Configure MERCADO_PAGO_APPLICATION_ID for the Mercado Pago Application used by this integration.
  3. Validate plan sync via admin routes (/api/admin/plans/sync) and confirm only plans from that Application are visible.
  4. Test checkout via POST /api/subscriptions/checkout and confirm the created subscription belongs to the configured Application.
  5. Sign in with a Mercado Pago payer email that has a subscription in another Application and confirm it is not listed.
  6. Trigger/receive webhook events and verify mismatched Application events are skipped while matching events update local DB state.
  7. Verify SSE events in the UI (if using the events endpoint).

Troubleshooting

  • 401/403 on plan sync/create: verify admin session.
  • Mercado Pago fails to start: verify MERCADO_PAGO_APPLICATION_ID is set and matches the Application tied to the access token.
  • Plans or subscriptions are missing: confirm they belong to the configured Mercado Pago Application, not just the same Merchant account or payer email.
  • Missing webhook updates: verify MERCADO_PAGO_WEBHOOK_SECRET and public URL.
  • Provider not active in UI: check VITE_PAYMENT_PROVIDER=mercadopago and public key presence.
  • Wrong API base: confirm VITE_API_URL points to the running API.