ngx-mp-payments
v1.0.2
Published
Angular library for Mercado Pago integration — Checkout Bricks & Checkout Pro
Maintainers
Readme
📋 Descripción General
ngx-mp-payments es una librería Angular moderna y reutilizable que simplifica la integración con Mercado Pago en aplicaciones Angular 21+.
¿Qué hace esta librería?
| Funcionalidad | Descripción | |---|---| | 🧱 Checkout Bricks | Formularios de pago embebidos (tarjeta, efectivo, transferencia) directamente en tu app | | 🛒 Checkout Pro | Botón que redirige al checkout hospedado de Mercado Pago | | 📊 Status Screen | Pantalla de resultado de pago con el look oficial de MP | | 🔌 Servicios inyectables | API programática para control total del flujo de pago |
¿Por qué usar esta librería?
- ✅ Standalone Components — Sin
NgModule, tree-shakeable por defecto - ✅ Angular Signals — Estado reactivo con la API moderna de Angular 21
- ✅ Tipado completo — Interfaces TypeScript para todos los modelos de MP
- ✅ Carga lazy del SDK — El script de MP se carga solo cuando se necesita
- ✅ Cleanup automático — Los bricks se destruyen al desmontar el componente
- ✅ Zone-aware — Correcta detección de cambios con
NgZone - ✅ SSR-compatible — Usa
DOCUMENTtoken en vez dewindowdirectamente - ✅ Backend-agnóstica — Funciona con cualquier backend (Node, .NET, Java, etc.)
⚙️ Requisitos Previos
| Requisito | Versión mínima |
|---|---|
| Angular | ^21.0.0 |
| Node.js | 18.x o superior |
| npm | 9.x o superior |
| Cuenta en Mercado Pago | Crear cuenta |
También necesitas un backend que se comunique con la API de Mercado Pago usando el Access Token (secreto). La librería incluye un ejemplo completo de backend en Node.js.
📦 Instalación
npm install ngx-mp-paymentsNota: La librería carga el SDK de Mercado Pago automáticamente desde el CDN oficial (
https://sdk.mercadopago.com/js/v2). No necesitas instalar ningún otro paquete adicional en el frontend.
Peer Dependencies
La librería requiere Angular 21 como peer dependency:
{
"peerDependencies": {
"@angular/common": "^21.0.0",
"@angular/core": "^21.0.0"
}
}🔧 Configuración
Paso 1: Obtener Credenciales
- Ve al panel de desarrolladores de Mercado Pago
- Crea una aplicación o selecciona una existente
- Copia tu Public Key (para frontend) y Access Token (para backend)
⚠️ Importante sobre las credenciales:
| Credencial | Uso | ¿Dónde va? | |---|---|---| | Public Key | Identifica tu app ante MP | ✅ Frontend (segura) | | Access Token | Permite operar (crear cobros, reembolsos, etc.) | 🔒 SOLO Backend |
El Access Token es equivalente a una contraseña. NUNCA lo incluyas en código frontend.
Paso 2: Configurar la librería en Angular
Abre tu archivo app.config.ts y agrega el provider:
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { provideMercadoPago } from 'ngx-mp-payments';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(), // Requerido para el servicio de preferencias
// ── Configuración de Mercado Pago ──
provideMercadoPago({
publicKey: 'TEST-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
locale: 'es-AR',
advancedFraudPrevention: true,
}),
],
};Opciones de Configuración
| Propiedad | Tipo | Requerida | Default | Descripción |
|---|---|---|---|---|
| publicKey | string | ✅ | — | Public Key de tu cuenta de Mercado Pago |
| locale | MercadoPagoLocale | ❌ | 'es-AR' | Idioma y país del checkout |
| advancedFraudPrevention | boolean | ❌ | false | Activa device fingerprint para prevención de fraude |
Locales Soportados
| Locale | País |
|---|---|
| 'es-AR' | 🇦🇷 Argentina |
| 'es-MX' | 🇲🇽 México |
| 'es-CO' | 🇨🇴 Colombia |
| 'es-CL' | 🇨🇱 Chile |
| 'es-PE' | 🇵🇪 Perú |
| 'es-UY' | 🇺🇾 Uruguay |
| 'es-VE' | 🇻🇪 Venezuela |
| 'pt-BR' | 🇧🇷 Brasil |
| 'en-US' | 🇺🇸 Estados Unidos |
🚀 Uso Básico
Checkout Bricks (Pago embebido)
El Payment Brick renderiza un formulario de pago completo dentro de tu aplicación. Soporta tarjeta de crédito, débito, efectivo, transferencia y más — sin redirigir al usuario.
import { Component, signal } from '@angular/core';
import {
MpCheckoutBrickComponent,
PaymentResult,
BrickError,
} from 'ngx-mp-payments';
@Component({
selector: 'app-checkout',
standalone: true,
imports: [MpCheckoutBrickComponent],
template: `
<mp-checkout-brick
type="payment"
[amount]="1500"
processPaymentUrl="https://api.miapp.com/payments/process"
(paymentSuccess)="onSuccess($event)"
(paymentError)="onError($event)"
(brickReady)="onReady()"
/>
@if (result()) {
<p>Pago {{ result()?.status }} - ID: {{ result()?.id }}</p>
}
`,
})
export class CheckoutPage {
readonly result = signal<PaymentResult | null>(null);
onSuccess(result: PaymentResult): void {
console.log('Pago exitoso:', result);
this.result.set(result);
}
onError(error: BrickError): void {
console.error('Error:', error.message);
}
onReady(): void {
console.log('Brick listo para interacción');
}
}Resultado visual: El componente muestra un spinner de carga mientras se inicializa y luego renderiza el formulario oficial de Mercado Pago con todos los medios de pago habilitados para tu país.
Checkout Pro (Botón de pago con redirección)
El Checkout Pro muestra un botón oficial de Mercado Pago. Al hacer clic, redirige al usuario al checkout hospedado de MP donde puede pagar con cualquier medio disponible.
Flujo:
- Tu app crea una preferencia en el backend
- El backend retorna un
preferenceId - El componente renderiza el botón con ese ID
- Al hacer clic, el usuario va al checkout de MP
import { Component, inject, OnInit, signal } from '@angular/core';
import {
MpPaymentButtonComponent,
MercadoPagoPreferenceService,
PreferenceResponse,
BrickError,
} from 'ngx-mp-payments';
@Component({
selector: 'app-checkout-pro',
standalone: true,
imports: [MpPaymentButtonComponent],
template: `
@if (loading()) {
<p>Creando preferencia...</p>
}
@if (preferenceId()) {
<mp-payment-button
[preferenceId]="preferenceId()!"
(buttonReady)="onReady()"
(buttonError)="onError($event)"
/>
}
`,
})
export class CheckoutProPage implements OnInit {
private readonly preferenceService = inject(MercadoPagoPreferenceService);
readonly preferenceId = signal<string | null>(null);
readonly loading = signal(false);
ngOnInit(): void {
this.loading.set(true);
this.preferenceService
.createPreference('http://localhost:3000/api/payments/preference', {
title: 'Producto Premium',
quantity: 1,
unitPrice: 2500.00,
currencyId: 'ARS',
})
.subscribe({
next: (res: PreferenceResponse) => {
this.preferenceId.set(res.preferenceId);
this.loading.set(false);
},
error: (err) => {
console.error('Error creando preferencia:', err);
this.loading.set(false);
},
});
}
onReady(): void {
console.log('Botón de Checkout Pro listo');
}
onError(error: BrickError): void {
console.error('Error:', error.message);
}
}Status Screen (Resultado del pago)
El Status Screen Brick muestra el resultado de un pago con la interfaz oficial de Mercado Pago (aprobado, pendiente, rechazado).
import { Component, input } from '@angular/core';
import { MpStatusScreenComponent, BrickError } from 'ngx-mp-payments';
@Component({
selector: 'app-payment-result',
standalone: true,
imports: [MpStatusScreenComponent],
template: `
<h2>Resultado de tu pago</h2>
<mp-status-screen
[paymentId]="paymentId()"
(screenReady)="onReady()"
(screenError)="onError($event)"
/>
`,
})
export class PaymentResultPage {
readonly paymentId = input.required<string>();
onReady(): void {
console.log('Pantalla de estado lista');
}
onError(error: BrickError): void {
console.error('Error:', error.message);
}
}🔬 Uso Avanzado
Personalización visual de Bricks
Puedes personalizar la apariencia y los métodos de pago permitidos:
import { BrickCustomization } from 'ngx-mp-payments';
const customization: BrickCustomization = {
visual: {
style: {
theme: 'dark', // 'default' | 'dark' | 'flat' | 'bootstrap'
customVariables: {
formBackgroundColor: '#1a1a2e',
baseColor: '#00b4d8',
},
},
hideFormTitle: false,
hidePaymentButton: false,
},
paymentMethods: {
creditCard: 'all',
debitCard: 'all',
ticket: 'all', // Efectivo (Rapipago, Pago Fácil, etc.)
bankTransfer: 'all',
mercadoPago: 'all', // Saldo en cuenta MP
onboarding_credits: 'all', // Mercado Crédito
wallet_purchase: 'all', // Compra con dinero en cuenta MP
maxInstallments: 12,
minInstallments: 1,
},
};Pásalo al componente:
<mp-checkout-brick
type="payment"
[amount]="1500"
[customization]="customization"
processPaymentUrl="https://api.miapp.com/payments/process"
(paymentSuccess)="onSuccess($event)"
/>Uso directo de servicios (sin componentes)
Si necesitas control total, puedes usar los servicios directamente:
import { Component, inject, signal } from '@angular/core';
import {
MercadoPagoPaymentService,
MercadoPagoSdkService,
BrickConfig,
PaymentResult,
} from 'ngx-mp-payments';
@Component({
selector: 'app-custom-checkout',
standalone: true,
template: `
<div id="custom-brick-container"></div>
@if (paymentService.isLoading()) {
<p>Procesando...</p>
}
@if (paymentService.error()) {
<p class="error">{{ paymentService.error()?.message }}</p>
}
@if (paymentService.lastResult()) {
<p>Pago {{ paymentService.lastResult()?.status }}</p>
}
`,
})
export class CustomCheckoutPage {
readonly paymentService = inject(MercadoPagoPaymentService);
async ngOnInit(): Promise<void> {
// Renderizar Payment Brick manualmente
await this.paymentService.renderPaymentBrick(
{
type: 'payment',
amount: 3000,
containerId: 'custom-brick-container',
customization: {
paymentMethods: { maxInstallments: 6 },
},
callbacks: {
onReady: () => console.log('Brick montado'),
onError: (err) => console.error('Error en brick:', err),
},
},
'https://api.miapp.com/payments/process'
);
}
ngOnDestroy(): void {
// Limpiar brick al salir
this.paymentService.destroyBrick('custom-brick-container');
}
}Renderizar Wallet Brick manualmente
// Renderizar botón de Checkout Pro sin usar el componente
await this.paymentService.renderWalletBrick(
'wallet-container', // ID del div contenedor
'PREF-xxxx-xxxx', // preferenceId del backend
{
onReady: () => console.log('Wallet listo'),
onError: (err) => console.error(err),
}
);Múltiples Bricks en una página
Puedes renderizar varios bricks simultáneamente. Cada componente genera un ID único automáticamente:
<!-- Credit card only -->
<mp-checkout-brick
type="cardPayment"
[amount]="1500"
processPaymentUrl="/api/payments/process"
(paymentSuccess)="onCardPayment($event)"
/>
<!-- Wallet (Checkout Pro) -->
<mp-checkout-brick
type="wallet"
[amount]="1500"
[preferenceId]="'PREF-xxxx'"
(brickReady)="onWalletReady()"
/>Consultar estado de un pago
import { inject } from '@angular/core';
import { MercadoPagoPreferenceService } from 'ngx-mp-payments';
export class PaymentStatusPage {
private readonly preferenceService = inject(MercadoPagoPreferenceService);
checkPayment(paymentId: string): void {
this.preferenceService
.getPaymentStatus('http://localhost:3000/api/payments/status', paymentId)
.subscribe({
next: (data) => console.log('Estado del pago:', data),
error: (err) => console.error(err),
});
}
}Resetear el flujo de pago
// Limpiar errores y resultados previos
this.paymentService.resetState();
// Destruir todos los bricks activos
this.paymentService.destroyAllBricks();🖼️ Ejemplos Visuales
Flujo de Checkout Bricks
┌─────────────────────────────────────────────────────────────┐
│ Tu aplicación Angular │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ <mp-checkout-brick type="payment" /> │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 💳 Formulario de pago Mercado Pago │ │ │
│ │ │ │ │ │
│ │ │ Número de tarjeta: [________________] │ │ │
│ │ │ Vencimiento: [__/__] CVV: [___] │ │ │
│ │ │ Nombre: [____________________] │ │ │
│ │ │ Cuotas: [▼ 12 cuotas sin interés] │ │ │
│ │ │ │ │ │
│ │ │ [ 💙 Pagar $1,500.00 ] │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ También acepta: Efectivo · Transferencia · Saldo MP │
└─────────────────────────────────────────────────────────────┘Flujo de Checkout Pro
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Tu App │ │ Backend │ │ Mercado │
│ │ │ (Node.js) │ │ Pago API │
│ │ │ │ │ │
│ 1. Crear │──────▶│ 2. POST │──────▶│ 3. Crear │
│ preferencia │ │ /preference │ │ preferencia │
│ │◀──────│ │◀──────│ │
│ 4. Recibe │ │ preferenceId │ │ preferenceId │
│ prefId │ │ │ │ │
│ │ │ │ │ │
│ 5. Renderiza │ │ │ │ │
│ <mp-payment- │ │ │ │ │
│ button /> │ │ │ │ │
│ │ │ │ │ │
│ 6. Click ────│───────│──────────────│──────▶│ 7. Checkout │
│ │ │ │ │ hospedado │
│ │ │ │ │ │
│ 8. Redirect ◀│───────│──────────────│───────│ back_url │
└──────────────┘ └──────────────┘ └──────────────┘Arquitectura de la Librería
ngx-mp-payments
├── provideMercadoPago() ← Configuración (app.config.ts)
│
├── Componentes (standalone)
│ ├── <mp-checkout-brick /> ← Payment / Card / Wallet Brick
│ ├── <mp-payment-button /> ← Botón de Checkout Pro
│ └── <mp-status-screen /> ← Pantalla de resultado
│
├── Servicios (inyectables)
│ ├── MercadoPagoSdkService ← Carga el SDK desde CDN
│ ├── MercadoPagoPaymentService ← Facade para Bricks
│ └── MercadoPagoPreferenceService ← HTTP para preferencias
│
└── Modelos (TypeScript)
├── MercadoPagoConfig
├── BrickConfig / BrickType / BrickCustomization
├── PaymentResult / PaymentStatus
├── PreferenceRequest / PreferenceResponse
└── BrickError / BrickCallbacks / CheckoutState📖 API de la Librería
Función provideMercadoPago()
Provee la configuración de Mercado Pago a nivel de aplicación usando makeEnvironmentProviders.
function provideMercadoPago(config: MercadoPagoConfig): EnvironmentProviders;Componentes
<mp-checkout-brick />
Renderiza un Checkout Brick de Mercado Pago (Payment, Card o Wallet).
| Input | Tipo | Requerido | Descripción |
|---|---|---|---|
| type | BrickType | ❌ | Tipo de brick: 'payment' (default), 'cardPayment', 'wallet' |
| amount | number | ✅ | Monto total del pago |
| processPaymentUrl | string | ⚠️ | URL del backend para procesar pagos. Requerido para payment y cardPayment |
| preferenceId | string | ⚠️ | ID de la preferencia. Requerido para wallet |
| customization | BrickCustomization | ❌ | Personalización visual y de métodos de pago |
| Output | Tipo | Descripción |
|---|---|---|
| paymentSuccess | PaymentResult | Emitido cuando el pago se procesa exitosamente |
| paymentError | BrickError | Emitido cuando ocurre un error |
| brickReady | void | Emitido cuando el brick está listo para interacción |
| loadingChange | boolean | Emitido cuando cambia el estado de carga |
Selector: mp-checkout-brick
<mp-checkout-brick
type="payment"
[amount]="1500"
processPaymentUrl="/api/payments/process"
[customization]="customization"
(paymentSuccess)="onSuccess($event)"
(paymentError)="onError($event)"
(brickReady)="onReady()"
(loadingChange)="onLoading($event)"
/><mp-payment-button />
Renderiza el botón oficial de Checkout Pro (Wallet Brick).
| Input | Tipo | Requerido | Descripción |
|---|---|---|---|
| preferenceId | string | ✅ | ID de la preferencia creada en backend |
| Output | Tipo | Descripción |
|---|---|---|
| buttonReady | void | Emitido cuando el botón está renderizado |
| buttonError | BrickError | Emitido cuando ocurre un error |
| loadingChange | boolean | Emitido cuando cambia el estado de carga |
Selector: mp-payment-button
<mp-payment-button
[preferenceId]="preferenceId()"
(buttonReady)="onReady()"
(buttonError)="onError($event)"
/><mp-status-screen />
Muestra el resultado de un pago con la interfaz oficial de Mercado Pago.
| Input | Tipo | Requerido | Descripción |
|---|---|---|---|
| paymentId | string | ✅ | ID del pago a consultar |
| Output | Tipo | Descripción |
|---|---|---|
| screenReady | void | Emitido cuando la pantalla está lista |
| screenError | BrickError | Emitido cuando ocurre un error |
Selector: mp-status-screen
<mp-status-screen
[paymentId]="paymentId"
(screenReady)="onReady()"
(screenError)="onError($event)"
/>Servicios
MercadoPagoSdkService
Carga el SDK de Mercado Pago desde el CDN e inicializa la instancia.
| Método / Propiedad | Retorno | Descripción |
|---|---|---|
| load() | Promise<MercadoPagoInstance> | Carga el SDK y retorna la instancia. Singleton: solo carga una vez |
| getInstance() | MercadoPagoInstance \| null | Retorna la instancia si ya fue cargada |
| isLoaded | Signal<boolean> | Signal reactivo: true cuando el SDK está listo |
| loadError | Signal<string \| null> | Signal reactivo: mensaje de error si falló la carga |
const mp = await this.sdkService.load();
const bricks = mp.bricks();MercadoPagoPaymentService
Servicio facade para renderizar y gestionar Bricks.
| Método | Parámetros | Retorno | Descripción |
|---|---|---|---|
| renderPaymentBrick() | config: BrickConfig, processPaymentUrl: string | Promise<void> | Renderiza un Payment/Card Brick |
| renderWalletBrick() | containerId: string, preferenceId: string, callbacks?: {...} | Promise<void> | Renderiza un Wallet Brick |
| destroyBrick() | containerId: string | void | Destruye un brick específico |
| destroyAllBricks() | — | void | Destruye todos los bricks activos |
| resetState() | — | void | Resetea el estado interno |
| Signal | Tipo | Descripción |
|---|---|---|
| state | Signal<CheckoutState> | Estado completo del checkout |
| isLoading | Signal<boolean> | true durante la carga/procesamiento |
| error | Signal<BrickError \| null> | Error actual |
| lastResult | Signal<PaymentResult \| null> | Resultado del último pago |
MercadoPagoPreferenceService
Servicio HTTP para crear preferencias de pago en tu backend.
| Método | Parámetros | Retorno | Descripción |
|---|---|---|---|
| createPreference() | backendUrl: string, request: PreferenceRequest | Observable<PreferenceResponse> | Crea una preferencia de pago |
| getPaymentStatus() | backendUrl: string, paymentId: string | Observable<Record<string, unknown>> | Consulta el estado de un pago |
this.preferenceService
.createPreference('/api/payments/preference', {
title: 'Producto',
quantity: 1,
unitPrice: 999.99,
})
.subscribe((res) => console.log(res.preferenceId));Interfaces y Tipos
MercadoPagoConfig
interface MercadoPagoConfig {
publicKey: string; // Public Key de MP
locale?: MercadoPagoLocale; // 'es-AR' | 'pt-BR' | etc.
advancedFraudPrevention?: boolean; // Device fingerprint
}BrickConfig
interface BrickConfig {
type: BrickType; // 'payment' | 'cardPayment' | 'statusScreen' | 'wallet'
amount: number; // Monto total
containerId: string; // ID del div contenedor
customization?: BrickCustomization; // Visual y métodos de pago
callbacks?: BrickCallbacks; // Eventos del brick
}BrickCustomization
interface BrickCustomization {
visual?: {
style?: {
theme?: 'default' | 'dark' | 'flat' | 'bootstrap';
customVariables?: Record<string, string>;
};
hideFormTitle?: boolean;
hidePaymentButton?: boolean;
};
paymentMethods?: {
creditCard?: 'all' | string | string[];
debitCard?: 'all' | string | string[];
ticket?: 'all' | string | string[];
bankTransfer?: 'all' | string | string[];
mercadoPago?: 'all' | string | string[];
onboarding_credits?: 'all' | string | string[];
wallet_purchase?: 'all' | string | string[];
maxInstallments?: number;
minInstallments?: number;
};
}BrickCallbacks
interface BrickCallbacks {
onSubmit?: (formData: Record<string, unknown>) => Promise<void>;
onError?: (error: BrickError) => void;
onReady?: () => void;
onBinChange?: (bin: string) => void;
}PaymentResult
interface PaymentResult {
id: string; // ID del pago en MP
status: PaymentStatus; // 'approved' | 'pending' | 'rejected' | ...
statusDetail: string; // Detalle del estado
externalReference?: string; // Referencia de tu sistema
paymentMethodId?: string; // Tipo de pago
paymentTypeId?: string; // Medio de pago
raw?: Record<string, unknown>; // Datos crudos de MP
}PaymentStatus
type PaymentStatus =
| 'approved' // Pago aprobado
| 'pending' // Pago pendiente (efectivo, transferencia)
| 'authorized' // Autorizado (preautorización)
| 'in_process' // En proceso de revisión
| 'in_mediation' // En disputa/mediación
| 'rejected' // Rechazado
| 'cancelled' // Cancelado
| 'refunded' // Devuelto/reembolsado
| 'charged_back' // ContracargoPreferenceRequest
interface PreferenceRequest {
title: string; // Título del item
description?: string; // Descripción
quantity: number; // Cantidad
unitPrice: number; // Precio unitario
currencyId?: string; // 'ARS' | 'BRL' | 'MXN' | etc.
externalReference?: string; // Referencia de tu sistema
pictureUrl?: string; // URL de imagen
metadata?: Record<string, unknown>; // Datos adicionales
}PreferenceResponse
interface PreferenceResponse {
preferenceId: string; // ID de la preferencia
initPoint?: string; // URL del checkout
sandboxInitPoint?: string; // URL del sandbox
}BrickError
interface BrickError {
type: string; // Tipo de error
message: string; // Mensaje descriptivo
cause: string; // Causa/origen del error
stack?: string; // Stack trace (solo en dev)
}CheckoutState
interface CheckoutState {
sdkLoaded: boolean; // SDK cargado y listo
loading: boolean; // Procesando pago
error: BrickError | null; // Error actual
lastPaymentResult: PaymentResult | null; // Último resultado
}📁 Estructura del Proyecto
projects/ngx-mercadopago/
├── src/
│ ├── public-api.ts # API pública (exports)
│ └── lib/
│ ├── models/
│ │ ├── mercadopago.models.ts # Interfaces y tipos
│ │ └── index.ts # Barrel export
│ │
│ ├── config/
│ │ ├── mercadopago.token.ts # InjectionToken
│ │ ├── provide-mercadopago.ts # provideMercadoPago()
│ │ └── index.ts # Barrel export
│ │
│ ├── services/
│ │ ├── mercadopago-sdk.service.ts # Carga del SDK (CDN)
│ │ ├── mercadopago-payment.service.ts # Facade para Bricks
│ │ ├── mercadopago-preference.service.ts # HTTP para preferencias
│ │ └── index.ts # Barrel export
│ │
│ └── components/
│ ├── checkout-brick/
│ │ └── mp-checkout-brick.component.ts
│ ├── payment-button/
│ │ └── mp-payment-button.component.ts
│ ├── status-screen/
│ │ └── mp-status-screen.component.ts
│ └── index.ts # Barrel export
│
├── package.json # Metadata del paquete npm
├── ng-package.json # Configuración ng-packagr
├── tsconfig.lib.json # TypeScript config
└── README.md # Este archivo🖥️ Backend de Ejemplo
La librería incluye un backend de ejemplo en Node.js/Express listo para usar:
examples/backend/
├── server.js # Servidor Express completo
├── package.json # Dependencias
└── .env.example # Variables de entornoInstalación del Backend
cd examples/backend
npm installConfiguración
Crea un archivo .env:
MP_ACCESS_TOKEN=TEST-xxxx-your-access-token
FRONTEND_URL=http://localhost:4200
PORT=3000Ejecución
node server.js
# o con nodemon para desarrollo:
npx nodemon server.jsEndpoints disponibles
| Método | Endpoint | Descripción |
|---|---|---|
| POST | /api/payments/preference | Crea una preferencia de pago |
| POST | /api/payments/process | Procesa un pago con datos del brick |
| GET | /api/payments/status/:id | Consulta estado de un pago |
| POST | /api/webhooks/mercadopago | Recibe notificaciones de MP |
Ejemplo de request
curl -X POST http://localhost:3000/api/payments/preference \
-H "Content-Type: application/json" \
-d '{
"title": "Producto Demo",
"quantity": 1,
"unitPrice": 1500,
"currencyId": "ARS"
}'Respuesta:
{
"preferenceId": "1234567890-xxxx-xxxx",
"initPoint": "https://www.mercadopago.com.ar/checkout/v1/redirect?pref_id=...",
"sandboxInitPoint": "https://sandbox.mercadopago.com.ar/checkout/v1/redirect?pref_id=..."
}❗ Errores Comunes y Soluciones
1. "publicKey es requerida en la configuración"
Causa: No se configuró provideMercadoPago() en app.config.ts.
Solución:
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
provideMercadoPago({
publicKey: 'TU-PUBLIC-KEY-AQUÍ', // ← No olvidar
}),
],
};2. "No se pudo cargar el SDK de Mercado Pago desde el CDN"
Causa: Problema de red, bloqueador de anuncios, o proxy corporativo.
Soluciones:
- Verificar conectividad a
https://sdk.mercadopago.com/js/v2 - Desactivar bloqueadores de anuncios (algunos bloquean scripts de pago)
- Si estás detrás de un proxy, verificar que el dominio
sdk.mercadopago.comesté permitido - Verificar la consola del navegador para más detalles
3. "processPaymentUrl es requerido para bricks de tipo payment/cardPayment"
Causa: Usaste <mp-checkout-brick type="payment"> sin pasar processPaymentUrl.
Solución:
<mp-checkout-brick
type="payment"
[amount]="1500"
processPaymentUrl="/api/payments/process"
(paymentSuccess)="onSuccess($event)"
/>4. "preferenceId es requerido para bricks de tipo wallet"
Causa: Usaste type="wallet" o <mp-payment-button> sin un preferenceId.
Solución: Crear la preferencia en el backend primero y luego pasar el ID:
this.preferenceService
.createPreference('/api/payments/preference', { ... })
.subscribe((res) => this.preferenceId.set(res.preferenceId));5. El brick no se renderiza (div vacío)
Posibles causas y soluciones:
| Causa | Solución |
|---|---|
| Public Key inválida | Verificar en el panel de MP |
| Public Key de test con datos de producción | Usar credenciales del mismo entorno |
| CSP bloqueando scripts | Agregar sdk.mercadopago.com a script-src |
| Contenedor no visible | Verificar que el div tenga dimensiones |
6. Error CORS al crear preferencia
Causa: Tu backend no tiene CORS configurado para el dominio del frontend.
Solución en Express:
const cors = require('cors');
app.use(cors({
origin: 'http://localhost:4200', // Tu dominio frontend
}));7. El pago queda siempre en "pending"
Causa: Estás en modo sandbox. Los pagos de prueba siempre quedan pendientes a menos que uses las tarjetas de prueba de MP.
Solución: Usar las tarjetas de prueba oficiales:
- Aprobado:
5031 7557 3453 0604(Mastercard) - Rechazado:
5031 7557 3453 0604con CVV123y nombreAPRO/OTHE
✅ Buenas Prácticas
Seguridad
- 🔒 NUNCA incluyas el Access Token en código frontend
- 🔒 Valida montos y datos en el backend antes de crear preferencias
- 🔒 Implementa
externalReferencepara vincular pagos con órdenes de tu sistema - 🔒 Activa
advancedFraudPrevention: trueen producción - 🔒 Usa HTTPS en producción
Rendimiento
- ⚡ El SDK se carga lazy (solo cuando un componente lo necesita)
- ⚡ Usa
destroyBrick()odestroyAllBricks()enngOnDestroysi usas servicios directamente - ⚡ Los componentes hacen cleanup automático vía
DestroyRef - ⚡ El SDK se ejecuta fuera de
NgZonepara minimizar detección de cambios
UX
- 🎨 Los componentes incluyen spinner de carga por defecto
- 🎨 Escucha
loadingChangepara sincronizar el loading con tu UI - 🎨 Usa
paymentError/buttonError/screenErrorpara informar al usuario - 🎨 Considera mostrar el
PaymentResult.statuscon mensajes amigables
Testing
- 🧪 Todos los servicios son inyectables y fáciles de mockear
- 🧪 Los componentes usan
input()/output()de Angular 21, compatibles conComponentFixture - 🧪 La librería incluye tests unitarios escritos con Vitest 4.x
🧪 Testing
La librería usa Vitest como test runner (default en Angular 21).
Ejecutar tests
# Desde la raíz del workspace
npm run test
# O en modo CI (sin watch)
npm run test:ci
# O directamente con Angular CLI
npx ng test ngx-mp-paymentsCobertura de tests
| Suite | Tests |
|---|---|
| MercadoPagoSdkService | 11 |
| MercadoPagoPaymentService | 11 |
| MercadoPagoPreferenceService | 6 |
| MpCheckoutBrickComponent | 7 |
| MpPaymentButtonComponent | 6 |
| Total | 41 |
Mockear servicios en tus tests
import { vi, describe, it, expect } from 'vitest';
import { TestBed } from '@angular/core/testing';
import { MercadoPagoPaymentService } from 'ngx-mp-payments';
describe('MyComponent', () => {
const mockPaymentService = {
renderPaymentBrick: vi.fn().mockResolvedValue(undefined),
destroyBrick: vi.fn(),
isLoading: vi.fn().mockReturnValue(false),
error: vi.fn().mockReturnValue(null),
lastResult: vi.fn().mockReturnValue(null),
};
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{ provide: MercadoPagoPaymentService, useValue: mockPaymentService },
],
});
});
it('should render brick on init', async () => {
// ...
expect(mockPaymentService.renderPaymentBrick).toHaveBeenCalled();
});
});� Ejemplo Completo de Uso en Código
A continuación se muestra un ejemplo completo paso a paso de cómo integrar ngx-mp-payments en un proyecto Angular real, desde la configuración hasta el resultado del pago.
Paso 1: Configuración global (app.config.ts)
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { provideMercadoPago } from 'ngx-mp-payments';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideRouter(routes),
provideHttpClient(),
// ── Configuración de Mercado Pago ──
provideMercadoPago({
publicKey: 'TEST-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', // Tu Public Key
locale: 'es-AR',
advancedFraudPrevention: true,
}),
],
};Paso 2: Rutas con lazy loading (app.routes.ts)
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: '',
loadComponent: () =>
import('./pages/home/home.page').then((m) => m.HomePage),
},
{
path: 'checkout-bricks',
loadComponent: () =>
import('./pages/checkout-bricks/checkout-bricks.page').then(
(m) => m.CheckoutBricksPage
),
},
{
path: 'checkout-pro',
loadComponent: () =>
import('./pages/checkout-pro/checkout-pro.page').then(
(m) => m.CheckoutProPage
),
},
{
path: 'payment/success',
loadComponent: () =>
import('./pages/payment-result/payment-result.page').then(
(m) => m.PaymentResultPage
),
data: { status: 'success' },
},
{
path: 'payment/failure',
loadComponent: () =>
import('./pages/payment-result/payment-result.page').then(
(m) => m.PaymentResultPage
),
data: { status: 'failure' },
},
{
path: 'payment/pending',
loadComponent: () =>
import('./pages/payment-result/payment-result.page').then(
(m) => m.PaymentResultPage
),
data: { status: 'pending' },
},
{ path: '**', redirectTo: '' },
];Paso 3: Componente raíz con navegación (app.ts)
import { Component } from '@angular/core';
import { RouterLink, RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
imports: [RouterOutlet, RouterLink],
template: `
<nav class="navbar">
<a routerLink="/" class="logo">ngx-mp-payments Demo</a>
<div class="nav-links">
<a routerLink="/checkout-bricks">Checkout Bricks</a>
<a routerLink="/checkout-pro">Checkout Pro</a>
</div>
</nav>
<main class="container">
<router-outlet />
</main>
`,
styles: `
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 2rem;
background: #009ee3;
color: white;
}
.logo {
font-weight: 700;
font-size: 1.2rem;
color: white;
text-decoration: none;
}
.nav-links { display: flex; gap: 1.5rem; }
.nav-links a { color: white; text-decoration: none; font-weight: 500; }
.nav-links a:hover { text-decoration: underline; }
.container { max-width: 800px; margin: 2rem auto; padding: 0 1rem; }
`,
})
export class App {}Paso 4: Página de Checkout Bricks (pago embebido)
Este es el ejemplo más completo — un formulario de pago renderizado directamente en tu aplicación:
// pages/checkout-bricks/checkout-bricks.page.ts
import { Component, inject, signal } from '@angular/core';
import {
MpCheckoutBrickComponent,
MercadoPagoPaymentService,
BrickError,
PaymentResult,
} from 'ngx-mp-payments';
@Component({
selector: 'app-checkout-bricks',
standalone: true,
imports: [MpCheckoutBrickComponent],
template: `
<h1>Checkout Bricks</h1>
<p>Formulario de pago embebido con Payment Brick de Mercado Pago.</p>
<div class="product-card">
<h2>Producto Demo</h2>
<p class="price">$ {{ amount() }}</p>
<p>Producto de prueba para demostrar la integración.</p>
</div>
<!-- ── PAYMENT BRICK ── -->
<div class="brick-wrapper">
<mp-checkout-brick
type="payment"
[amount]="amount()"
[processPaymentUrl]="processUrl"
[customization]="brickCustomization"
(paymentSuccess)="onPaymentSuccess($event)"
(paymentError)="onPaymentError($event)"
(brickReady)="onBrickReady()"
(loadingChange)="onLoadingChange($event)"
/>
</div>
<!-- ── RESULTADO ── -->
@if (paymentResult()) {
<div class="result" [class.success]="paymentResult()?.status === 'approved'">
<h3>Resultado del pago</h3>
<p><strong>ID:</strong> {{ paymentResult()?.id }}</p>
<p><strong>Estado:</strong> {{ paymentResult()?.status }}</p>
<p><strong>Detalle:</strong> {{ paymentResult()?.statusDetail }}</p>
</div>
}
@if (errorMsg()) {
<div class="error">
<h3>Error</h3>
<p>{{ errorMsg() }}</p>
</div>
}
`,
styles: `
.product-card {
border: 1px solid #e0e0e0;
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 2rem;
}
.price { font-size: 2rem; font-weight: 700; color: #009ee3; }
.brick-wrapper { margin: 2rem 0; }
.result {
padding: 1rem 1.5rem;
border-radius: 8px;
background: #fff3cd;
border: 1px solid #ffc107;
margin-top: 1rem;
}
.result.success { background: #d4edda; border-color: #28a745; }
.error {
padding: 1rem 1.5rem;
border-radius: 8px;
background: #f8d7da;
border: 1px solid #dc3545;
margin-top: 1rem;
}
`,
})
export class CheckoutBricksPage {
private readonly paymentService = inject(MercadoPagoPaymentService);
/** Monto del producto demo */
readonly amount = signal(1500);
/** URL del backend para procesar pagos */
readonly processUrl = 'http://localhost:3000/api/payments/process';
/** Personalización del brick */
readonly brickCustomization = {
visual: {
style: { theme: 'default' as const },
},
paymentMethods: {
maxInstallments: 12,
},
};
/** Estado reactivo */
readonly paymentResult = signal<PaymentResult | null>(null);
readonly errorMsg = signal<string | null>(null);
onPaymentSuccess(result: PaymentResult): void {
console.log('Pago exitoso:', result);
this.paymentResult.set(result);
this.errorMsg.set(null);
}
onPaymentError(error: BrickError): void {
console.error('Error en pago:', error);
this.errorMsg.set(error.message);
}
onBrickReady(): void {
console.log('Brick listo para interacción');
}
onLoadingChange(loading: boolean): void {
console.log('Loading:', loading);
}
}Paso 5: Página de Checkout Pro (botón con redirección)
Muestra cómo crear una preferencia en el backend y renderizar el botón de pago:
// pages/checkout-pro/checkout-pro.page.ts
import { Component, inject, OnInit, signal } from '@angular/core';
import {
MpPaymentButtonComponent,
MercadoPagoPreferenceService,
BrickError,
PreferenceResponse,
} from 'ngx-mp-payments';
@Component({
selector: 'app-checkout-pro',
standalone: true,
imports: [MpPaymentButtonComponent],
template: `
<h1>Checkout Pro</h1>
<p>Botón de pago que redirige al checkout hospedado de Mercado Pago.</p>
<div class="product-card">
<h2>Producto Demo</h2>
<p class="price">$ 2500.00</p>
<p>Suscripción Premium - 1 mes</p>
</div>
@if (loading()) {
<p>Creando preferencia de pago...</p>
}
@if (errorMsg()) {
<div class="error">
<p>{{ errorMsg() }}</p>
</div>
}
<!-- ── WALLET BRICK (CHECKOUT PRO) ── -->
@if (preferenceId()) {
<div class="wallet-wrapper">
<mp-payment-button
[preferenceId]="preferenceId()!"
(buttonReady)="onButtonReady()"
(buttonError)="onButtonError($event)"
/>
</div>
}
<section class="info">
<h3>¿Cómo funciona?</h3>
<ol>
<li>La app crea una <strong>preferencia</strong> en el backend.</li>
<li>El backend usa el <strong>Access Token</strong> para comunicarse con la API de MP.</li>
<li>La API retorna un <strong>preferenceId</strong>.</li>
<li>El frontend renderiza el botón de pago con ese ID.</li>
<li>Al hacer clic, el usuario va al checkout seguro de MP.</li>
<li>Después del pago, MP redirige al usuario de vuelta a tu app.</li>
</ol>
</section>
`,
styles: `
.product-card {
border: 1px solid #e0e0e0;
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 2rem;
}
.price { font-size: 2rem; font-weight: 700; color: #009ee3; }
.wallet-wrapper { margin: 2rem 0; }
.error {
padding: 1rem 1.5rem;
border-radius: 8px;
background: #f8d7da;
border: 1px solid #dc3545;
margin: 1rem 0;
}
.info {
margin-top: 2rem;
padding: 1.5rem;
background: #f5f5f5;
border-radius: 8px;
}
`,
})
export class CheckoutProPage implements OnInit {
private readonly preferenceService = inject(MercadoPagoPreferenceService);
readonly preferenceId = signal<string | null>(null);
readonly loading = signal(false);
readonly errorMsg = signal<string | null>(null);
ngOnInit(): void {
this.createPreference();
}
private createPreference(): void {
this.loading.set(true);
this.errorMsg.set(null);
this.preferenceService
.createPreference('http://localhost:3000/api/payments/preference', {
title: 'Suscripción Premium',
description: 'Acceso premium por 1 mes',
quantity: 1,
unitPrice: 2500.0,
currencyId: 'ARS',
externalReference: `order-demo-${Date.now()}`,
})
.subscribe({
next: (response: PreferenceResponse) => {
console.log('Preferencia creada:', response);
this.preferenceId.set(response.preferenceId);
this.loading.set(false);
},
error: (err) => {
console.error('Error creando preferencia:', err);
this.errorMsg.set(
'No se pudo crear la preferencia. ¿Está corriendo el backend en localhost:3000?'
);
this.loading.set(false);
},
});
}
onButtonReady(): void {
console.log('Botón de Checkout Pro listo');
}
onButtonError(error: BrickError): void {
console.error('Error en Wallet Brick:', error);
this.errorMsg.set(error.message);
}
}Paso 6: Página de resultado de pago
Se muestra después de que Mercado Pago redirige al usuario de vuelta a tu app (Checkout Pro):
// pages/payment-result/payment-result.page.ts
import { Component, inject } from '@angular/core';
import { ActivatedRoute, RouterLink } from '@angular/router';
@Component({
selector: 'app-payment-result',
standalone: true,
imports: [RouterLink],
template: `
<div class="result-card" [class]="status">
@switch (status) {
@case ('success') {
<h1>✅ Pago Aprobado</h1>
<p>Tu pago fue procesado exitosamente.</p>
}
@case ('failure') {
<h1>❌ Pago Rechazado</h1>
<p>El pago no pudo ser procesado. Intenta nuevamente.</p>
}
@case ('pending') {
<h1>⏳ Pago Pendiente</h1>
<p>Tu pago está siendo procesado. Te notificaremos cuando se confirme.</p>
}
}
<a routerLink="/" class="btn">Volver al inicio</a>
</div>
`,
styles: `
.result-card {
text-align: center;
padding: 3rem 2rem;
border-radius: 12px;
margin: 2rem 0;
}
.result-card h1 { font-size: 2rem; }
.success { background: #d4edda; border: 1px solid #28a745; }
.failure { background: #f8d7da; border: 1px solid #dc3545; }
.pending { background: #fff3cd; border: 1px solid #ffc107; }
.btn {
display: inline-block;
margin-top: 1.5rem;
padding: 0.6rem 1.5rem;
background: #009ee3;
color: white;
border-radius: 8px;
text-decoration: none;
font-weight: 500;
}
`,
})
export class PaymentResultPage {
private readonly route = inject(ActivatedRoute);
readonly status: string = this.route.snapshot.data['status'] || 'pending';
}Paso 7: Backend en Node.js/Express
Este servidor procesa los pagos de forma segura usando el Access Token (que nunca llega al frontend):
// examples/backend/server.js
const express = require('express');
const cors = require('cors');
const { MercadoPagoConfig, Preference, Payment } = require('mercadopago');
const app = express();
const PORT = process.env.PORT || 3000;
// ── Configuración de MP (Access Token SOLO en backend) ──
const ACCESS_TOKEN = process.env.MP_ACCESS_TOKEN || 'TEST-xxxx-your-access-token-here';
const mpClient = new MercadoPagoConfig({ accessToken: ACCESS_TOKEN });
const preferenceClient = new Preference(mpClient);
const paymentClient = new Payment(mpClient);
app.use(express.json());
app.use(cors({
origin: process.env.FRONTEND_URL || 'http://localhost:4200',
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization'],
}));
// ── Crear preferencia (para Checkout Pro) ──
app.post('/api/payments/preference', async (req, res) => {
try {
const { title, description, quantity, unitPrice, currencyId, externalReference } = req.body;
if (!title || !quantity || !unitPrice) {
return res.status(400).json({ error: 'Campos requeridos: title, quantity, unitPrice' });
}
const preference = await preferenceClient.create({
body: {
items: [{
title,
description: description || '',
quantity: Number(quantity),
unit_price: Number(unitPrice),
currency_id: currencyId || 'ARS',
}],
back_urls: {
success: `${process.env.FRONTEND_URL || 'http://localhost:4200'}/payment/success`,
failure: `${process.env.FRONTEND_URL || 'http://localhost:4200'}/payment/failure`,
pending: `${process.env.FRONTEND_URL || 'http://localhost:4200'}/payment/pending`,
},
auto_return: 'approved',
external_reference: externalReference || `order-${Date.now()}`,
notification_url: `${process.env.BACKEND_URL || 'https://tu-backend.com'}/api/webhooks/mercadopago`,
},
});
res.json({
preferenceId: preference.id,
initPoint: preference.init_point,
sandboxInitPoint: preference.sandbox_init_point,
});
} catch (error) {
console.error('Error creando preferencia:', error);
res.status(500).json({ error: 'Error al crear la preferencia de pago' });
}
});
// ── Procesar pago (para Payment Brick) ──
app.post('/api/payments/process', async (req, res) => {
try {
const { token, issuer_id, payment_method_id, transaction_amount, installments, payer } = req.body;
if (!token || !payment_method_id || !transaction_amount) {
return res.status(400).json({ error: 'Datos de pago incompletos' });
}
const payment = await paymentClient.create({
body: {
token,
issuer_id: issuer_id ? String(issuer_id) : undefined,
payment_method_id,
transaction_amount: Number(transaction_amount),
installments: Number(installments) || 1,
payer: { email: payer?.email, identification: payer?.identification },
},
requestOptions: {
idempotencyKey: `payment-${Date.now()}`,
},
});
res.json({
id: String(payment.id),
status: payment.status,
statusDetail: payment.status_detail,
paymentMethodId: payment.payment_method_id,
paymentTypeId: payment.payment_type_id,
});
} catch (error) {
console.error('Error procesando pago:', error);
res.status(500).json({ error: 'Error al procesar el pago' });
}
});
// ── Consultar estado de un pago ──
app.get('/api/payments/status/:paymentId', async (req, res) => {
try {
const payment = await paymentClient.get({ id: req.params.paymentId });
res.json({
id: String(payment.id),
status: payment.status,
statusDetail: payment.status_detail,
externalReference: payment.external_reference,
});
} catch (error) {
res.status(500).json({ error: 'Error al consultar el estado del pago' });
}
});
// ── Webhook de Mercado Pago ──
app.post('/api/webhooks/mercadopago', async (req, res) => {
res.sendStatus(200); // Responder rápido
const { type, data } = req.body;
if (type === 'payment' && data?.id) {
const payment = await paymentClient.get({ id: data.id });
console.log(`Webhook: Pago ${data.id} → ${payment.status}`);
// TODO: Actualizar estado en tu DB, enviar email, etc.
}
});
app.listen(PORT, () => {
console.log(`✅ Backend corriendo en http://localhost:${PORT}`);
});Paso 8: Ejecutar todo
# Terminal 1 — Backend
cd examples/backend
npm install
MP_ACCESS_TOKEN=TEST-tu-access-token node server.js
# Terminal 2 — Frontend Angular
npm start
# Abrir http://localhost:4200Resumen del flujo completo
Tu Proyecto Angular
===================
app.config.ts ──► provideMercadoPago({ publicKey: '...' })
│
app.routes.ts ──► Lazy loading de páginas
│
├── /checkout-bricks
│ └── <mp-checkout-brick />
│ │
│ ├── Renderiza formulario de pago embebido
│ ├── Usuario completa datos de tarjeta/efectivo
│ ├── Brick envía datos al backend → POST /api/payments/process
│ └── Backend procesa pago con Access Token → retorna PaymentResult
│
├── /checkout-pro
│ ├── MercadoPagoPreferenceService.createPreference()
│ │ └── POST /api/payments/preference → { preferenceId }
│ └── <mp-payment-button [preferenceId]="..." />
│ └── Click → Redirige a checkout.mercadopago.com
│
└── /payment/success | /payment/failure | /payment/pending
└── PaymentResultPage muestra resultado�🔨 Desarrollo Local
Clonar y configurar
git clone https://github.com/JosemaCeballos/ngx-mp-payments.git
cd ngx-mp-payments
npm installCompilar la librería
npm run buildEjecutar la app demo
npm startBuild de producción
npm run build:prodPublicar en npm
npm run publish:lib🤝 Contribución
¡Las contribuciones son bienvenidas! Sigue estos pasos:
- Fork del repositorio
- Crea una rama para tu feature:
git checkout -b feature/mi-feature - Implementa los cambios siguiendo las convenciones del proyecto
- Agrega tests para cualquier funcionalidad nueva
- Verifica que todos los tests pasen:
npm run test - Compila la librería para verificar que no hay errores:
npm run build - Commit con mensajes descriptivos (preferiblemente Conventional Commits):
git commit -m "feat: agregar soporte para Card Payment Brick" - Push y abre un Pull Request
Lineamientos
- Usar standalone components (sin
NgModule) - Seguir las convenciones de Angular 21 (signals,
input(),output(),inject()) - Documentar funciones y clases públicas con JSDoc
- Mantener compatibilidad con tree-shaking (
sideEffects: false) - Escribir tests en Vitest
📄 Licencia
Este proyecto está bajo la licencia MIT. Ver LICENSE para más detalles.
🔗 Enlaces Útiles
| Recurso | URL | |---|---| | Documentación oficial de Mercado Pago | developers.mercadopago.com | | Panel de desarrolladores | Panel de apps | | Checkout Bricks (docs oficiales) | Checkout Bricks | | Checkout Pro (docs oficiales) | Checkout Pro | | Tarjetas de prueba | Test cards | | Angular 21 Signals | Angular Signals |
