PayU LatAm (integrado en el commit anterior) resultó inviable: Rapyd
compró ese negocio completo en LatAm/África en 2023, y el registro real
del usuario quedó en la plataforma de Rapyd (board.rapyd.net,
credenciales access_key/secret_key incompatibles con el módulo ya
construido, API completamente distinta). Se evaluó reconstruir contra
Rapyd, pero el usuario comparó costos para su volumen real (~8
ventas/mes) y encontró que ePayco cobra menos en PSE. Se investigó
ePayco en vivo: es 100% de Davivienda desde 2024, acepta liquidación a
cualquier banco colombiano (no exige Bancolombia/Nequi como Wompi) y da
tarifa preferencial a cuentas Davivienda -- el usuario ya tiene una.
Mejor encaje de negocio y más barato que PayU/Rapyd. Confirmado como
elección definitiva.
- src/modules/epayco-pse/ reemplaza a src/modules/payu-pse/: mismo
contrato AbstractPaymentProvider (initiatePayment/authorizePayment
"pending_authorization"/getWebhookActionAndData con firma SHA256
verificada), pero envuelve el SDK oficial `epayco-sdk-node` en vez de
llamar la API cruda -- leyendo el código fuente del SDK (no la
documentación pública, que resultó desactualizada en más de un campo)
se confirmó que el protocolo real de ePayco para PSE es bastante más
particular de lo que sugieren los ejemplos: cada campo del payload se
cifra con AES usando la private key del comercio, más un login previo
que da un bearer token que también viaja en la misma request.
Reimplementar esto a mano tenía mucho más riesgo de error que delegar
en el SDK mantenido por el propio proveedor.
- Corregido en el camino: el método real para listar bancos es
`bank.getBanks()`, no `pseBank()` (así aparecía en un resumen de
búsqueda, pero no existe en el SDK real -- confirmado leyendo
node_modules/epayco-sdk-node/lib/resources/bank.js directamente). El
campo real para la URL de confirmación es `method_confirmation`
(con guión bajo en medio), no `metodoconfirmacion` como mostraba la
documentación pública -- confirmado contra tests/bank.js del propio
paquete.
- retrievePayment sí se implementó esta vez (endpoint de validación de
ePayco documentado); refundPayment sigue sin implementar -- ese
endpoint no quedó verificado en vivo, tira error explícito en vez de
fingir éxito.
- Frontend: el step de pago recupera el selector de banco + tipo de
persona + documento (a diferencia del diseño que se había planeado
para Rapyd, donde esos datos se recolectaban en la página del propio
Rapyd/SafetyPay) -- ePayco sí necesita esos campos de nuestro lado,
como PayU.
- Dos puntos quedan sin verificar contra un sandbox real (anotados en
el código con qué hacer si fallan): el nombre exacto del campo con la
URL del banco en la respuesta de bank.create() (initiatePayment tira
un error con el payload completo si no matchea ningún candidato
probado), y si p_cust_id_cliente/p_key del webhook son iguales a las
API keys o valores aparte del dashboard de ePayco.
Verificado: build real de Medusa con las 7 env vars EPAYCO_* dummy,
smoke test del wrapper del SDK cargando y la verificación de firma
dando el resultado esperado contra un caso conocido, typecheck+lint+
build completo del frontend.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01THZFwPzrwv6VAbuwmaf5vR
Instala @rokmohar/medusa-plugin-meilisearch@^1.4.1 (linea v1, la unica
compatible con nuestro Medusa 2.18.0 exacto -- confirmado contra
peerDependencies reales de npm: @medusajs/* ^2.15.2, satisfecho por
2.18.0). Conectado via un array `plugins:` nuevo en medusa-config.ts
(primer uso en este repo, no `modules:`), env vars leidos con `?? ""`
en vez de required() -- si Meilisearch esta caido o mal configurado,
el plugin se degrada solo en vez de tumbar el arranque de Medusa
entero.
No se escribe un subscriber propio: se verifico contra el codigo
fuente real del paquete (descargado el tarball exacto de npm,
inspeccionado en /app/node_modules dentro de una imagen Docker de
verdad) que trae sus propios subscribers de product/category/
collection/variant/etc, descubiertos por Medusa en runtime desde
dentro del propio paquete -- confirmado que sobreviven a
`npm prune --omit=dev` y llegan intactos a la imagen final. Tambien
trae un job de indexado inicial de una sola corrida al boot
(numberOfExecutions: 1, no un cron recurrente -- no viola la
restriccion del ADR de "nunca cron").
Nueva ruta Store API propia (src/api/store/search/route.ts): las
rutas /store/meilisearch/* que trae el plugin no devuelven
facetDistribution (verificado leyendo su codigo fuente), asi que esta
ruta pide facets explicitamente via additionalOptions.facets, y
despues re-hidrata los productos via query.graph (mismos campos que
ya usa el storefront) en vez de confiar en precios/inventario
indexados -- mismo patron "safe path" que ya usa la ruta oficial del
plugin para /store/meilisearch/products.
Transformer custom en medusa-config.ts que espeja mapProduct() del
storefront (category/collection/brand/ageRange desde metadata,
disponibilidad desde la primera variante) para que el documento
indexado sea exactamente lo que el storefront ya muestra.
Validado en vivo: build completo de Docker (target builder) corre
limpio -- npm ci, medusa build (typecheck + compile backend/frontend),
npm prune -- cero errores. package-lock.json regenerado con un
contenedor node:22.18.0-bookworm-slim real (mismo base image que el
Dockerfile), no a mano.
Dockerfile: dummy MEILISEARCH_HOST/MEILISEARCH_API_KEY en el stage de
build (insurance barata, no son required() asi que no eran
estrictamente necesarios para que el build pasara, y de hecho paso
sin ellos en la prueba -- se dejan de todas formas por consistencia
con el resto de dummies de esa capa).