Este módulo permite a los usuarios comprar espacios adicionales para registrar más expositores cuando hayan alcanzado su límite máximo. La integración utiliza PayPal como pasarela de pago.
- ✅ Botón de compra visible solo cuando se alcanza el límite
- ✅ Modal con selector de cantidad (1-50 espacios)
- ✅ Integración completa con PayPal
- ✅ Cálculo automático del precio total (300 MXN por espacio)
- ✅ Validación segura de pagos con PayPal Secret
- ✅ Manejo de pagos PENDING con webhooks
- ✅ Actualización automática del límite tras pago exitoso
- ✅ Registro de transacciones en base de datos
- ✅ Traducciones en 3 idiomas (ES, EN, IT)
- ✅ Notificaciones de éxito/error/pendiente
npm install @paypal/react-paypal-jsAgrega las credenciales de PayPal en tu archivo .env.local:
# PayPal Client ID (público)
NEXT_PUBLIC_PAYPAL_CLIENT_ID=tu_paypal_client_id_aqui
# PayPal Secret (privado - NO compartir)
PAYPAL_SECRET=tu_paypal_secret_aqui
# PayPal API URL
PAYPAL_API_URL=https://api-m.sandbox.paypal.com
# PayPal Webhook ID (para verificar webhooks)
PAYPAL_WEBHOOK_ID=tu_webhook_id_aquiPara pruebas (Sandbox):
- Ve a https://developer.paypal.com/dashboard
- Inicia sesión con tu cuenta PayPal
- Ve a "Apps & Credentials"
- En la pestaña "Sandbox", crea una nueva app o usa una existente
- Copia el Client ID y el Secret
- Para webhooks:
- Ve a "Webhooks" en el menú lateral
- Crea un webhook apuntando a:
https://tu-dominio.com/api/webhooks/paypal - Selecciona estos eventos:
PAYMENT.CAPTURE.COMPLETEDPAYMENT.CAPTURE.DENIEDPAYMENT.CAPTURE.DECLINEDPAYMENT.CAPTURE.REFUNDED
- Copia el Webhook ID
Para producción (Live):
- En la misma página, cambia a la pestaña "Live"
- Crea una app y obtén las credenciales de Live
- Cambia
PAYPAL_API_URLahttps://api-m.paypal.com - Configura los webhooks en modo Live
⚠️ IMPORTANTE: Solo usa Live cuando estés listo para producción
Si deseas llevar un historial de las transacciones, ejecuta este script SQL:
mysql -u tu_usuario -p tu_base_de_datos < sql/create_exhibitor_payments_table.sqlO ejecuta manualmente el contenido del archivo en tu base de datos.
El módulo se activa automáticamente cuando:
- El usuario tiene
maxexhibitors > 0 - El usuario ha alcanzado su límite (
currentTotal >= maxExhibitors)
- El usuario ve el botón "Comprar más espacios" cuando alcanza su límite
- Hace clic y se abre un modal con:
- Información del uso actual
- Selector de cantidad de espacios (1-50)
- Precio calculado automáticamente con formato de miles
- Botones de PayPal para pagar
- El usuario completa el pago con PayPal
- El sistema verifica el pago directamente con la API de PayPal:
- Si está COMPLETED: Aplica el límite inmediatamente y muestra éxito
- Si está PENDING: Registra el pago y notifica que está pendiente
- Para pagos PENDING:
- PayPal envía un webhook cuando el pago se complete
- El sistema aplica el límite automáticamente
- Se puede configurar notificación por email (opcional)
- Se muestra una notificación según el estado
- La página se recarga solo si el pago fue completado inmediatamente
- Precio por espacio: $300 MXN
- Cantidad mínima: 1 espacio
- Cantidad máxima: 50 espacios por compra
- Moneda: MXN (Pesos mexicanos)
- Formato: Con separadores de miles ($1,500.00, $7,200.00, etc.)
Para cambiar el precio, edita la constante en BuyExhibitors.tsx:
const pricePerExhibitor = 300 // Cambia este valorComponente principal que:
- Muestra el botón de compra cuando se alcanza el límite
- Maneja el modal con formulario
- Integra PayPal con locale dinámico (es_MX, en_US, it_IT)
- Procesa pagos y maneja estados COMPLETED y PENDING
Endpoint que:
- Verifica el pago con PayPal API usando el Secret
- Valida que el monto pagado sea correcto
- Maneja pagos COMPLETED (aplica límite inmediatamente)
- Maneja pagos PENDING (registra y espera webhook)
- Registra la transacción en la base de datos
- Retorna el estado y nuevo límite
Endpoint webhook que:
- Recibe notificaciones de PayPal cuando cambia el estado de un pago
- Verifica la firma del webhook para seguridad
- Maneja eventos:
PAYMENT.CAPTURE.COMPLETED- Aplica el límitePAYMENT.CAPTURE.DENIED/DECLINED- Marca como fallidoPAYMENT.CAPTURE.REFUNDED- Revierte el límite
- Actualiza automáticamente la base de datos
Almacena (estructura actualizada):
- ID de usuario y pago
- Cantidad de espacios y monto pagado
- Moneda (MXN)
- Límite anterior y nuevo
- Estado del pago (PENDING, COMPLETED, FAILED, REFUNDED)
- Flag
applied(si ya se aplicó el límite) - Timestamps de creación, actualización y completado
- Pago procesado exitosamente de inmediato
- El límite se aplica automáticamente
- Usuario recibe notificación de éxito
- Página se recarga mostrando nuevos espacios
- Pago en proceso de verificación por PayPal
- Común con eChecks o pagos internacionales
- Se registra en BD pero NO se aplica el límite aún
- Usuario recibe notificación de espera
- Cuando PayPal confirme, el webhook aplicará el límite automáticamente
- Pago rechazado por PayPal
- No se aplica ningún límite
- Se registra en BD como FAILED
- Usuario recibe notificación de error
- Pago fue reembolsado
- El webhook revierte el límite al estado anterior
- Se marca como REFUNDED en BD
Los webhooks permiten que PayPal notifique a tu servidor cuando cambia el estado de un pago.
- Ve a https://developer.paypal.com/dashboard
- Selecciona tu app
- Ve a "Webhooks"
- Agrega webhook:
https://tu-dominio.com/api/webhooks/paypal - Selecciona eventos a escuchar
- Copia el Webhook ID
PAYMENT.CAPTURE.COMPLETED- Pago completadoPAYMENT.CAPTURE.DENIED- Pago denegadoPAYMENT.CAPTURE.DECLINED- Pago rechazadoPAYMENT.CAPTURE.REFUNDED- Pago reembolsado
Para probar webhooks en desarrollo local:
# Instalar ngrok
npm install -g ngrok
# Exponer tu servidor local
ngrok http 3000
# Usar la URL de ngrok en la configuración del webhook
# Ejemplo: https://abc123.ngrok.io/api/webhooks/paypalLas traducciones están en /src/messages/{es,en,it}.json bajo la clave:
{
"ExhibitorsPage": {
"buy": {
"button": "...",
"title": "...",
"pending": "...",
"pendingMessage": "...",
...
}
}
}Para probar sin realizar pagos reales:
- Usa el Client ID de Sandbox
- Crea cuentas de prueba en PayPal Developer
- Usa las credenciales de prueba para "pagar"
- Los pagos no serán reales pero el flujo será idéntico
- Ve a https://developer.paypal.com/dashboard/accounts
- Crea cuentas de comprador (buyer) para probar pagos
- Usa esas credenciales en el flujo de pago
- ✅ Verificación del pago directamente con PayPal API usando Secret
- ✅ Validación del monto pagado vs monto esperado
- ✅ Verificación de firma de webhooks con certificado PayPal
- ✅ Protección contra replay attacks (payment_id único)
- ✅ Validación en backend del estado del pago
- ✅ Límites de cantidad (1-50)
- ✅ Validación de usuario existente
- ✅ Transacciones atómicas en base de datos
- ✅ Secreto de PayPal nunca expuesto al cliente
- Verifica que
NEXT_PUBLIC_PAYPAL_CLIENT_IDesté configurado - Asegúrate de que el Client ID sea correcto
- Verifica la consola del navegador para errores
- Comprueba que el dominio esté en la whitelist de PayPal (producción)
- Si el pago está PENDING, es normal que no se aplique inmediatamente
- Verifica que el webhook esté configurado correctamente
- Revisa los logs del servidor para errores
- Comprueba que la tabla
exhibitor_paymentsexista - Verifica que el usuario exista en la BD
- Asegúrate de que
PAYPAL_WEBHOOK_IDesté configurado correctamente - Verifica que la URL del webhook en PayPal coincida con tu endpoint
- Revisa que el certificado de PayPal sea accesible
- En desarrollo local, usa ngrok o similar para exponer el webhook
- Asegúrate de que tu cuenta PayPal soporte MXN
- Verifica que la configuración de moneda sea correcta en PayPal
- Si necesitas otra moneda, actualiza en
BuyExhibitors.tsxyincrease-limit/route.ts
- Los pagos PENDING pueden tardar días (especialmente eChecks)
- Verifica en PayPal Dashboard el estado real del pago
- Asegúrate de que los webhooks estén funcionando
- Puedes consultar la tabla
exhibitor_paymentspara ver el estado
Para más información o soporte:
- Documentación PayPal: https://developer.paypal.com/docs
- API de PayPal React: https://www.npmjs.com/package/@paypal/react-paypal-js
Desarrollado para IGECO Dashboard 🚀