428ad83e5b61222a730725f2c80020932e2406ed
7
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
76b7d827c5 |
fix(ecommerce): corrige origen de bodega Bogotá→Cali y activa redes/Cambios/FAQ
Build and Push Medusa / Escaneo de Secretos (Gitleaks) (push) Successful in 30s
Build and Push Frontend / Escaneo de Secretos (Gitleaks) (push) Successful in 21s
Build and Push Medusa / Construir y publicar Medusa (push) Successful in 19m40s
Build and Push Frontend / Construir y Subir Imagen (push) Successful in 9m12s
- ENVIOCLICK_ORIGIN_DANE_CODE en configmap.yaml estaba cargado con el código DANE de Bogotá (11001000) por error; corregido a Santiago de Cali, Valle del Cauca (76001000). Toda cotización real de envío hecha antes de esta fecha se calculó desde el origen equivocado. - Corregido también el fallback stale en medusa-config.ts (originDaneCode) que seguía apuntando a Bogotá. - Actualizado el texto de /envios y /contacto (misma dirección física, Cra 100 #42-65, solo cambia la ciudad). - Footer: Instagram y Facebook reales enlazados (verificados en vivo contra los perfiles antes de linkear), y "Preguntas frecuentes" / "Cambios" ya apuntan a contenido real en vez de /catalog. - Nuevas páginas /cambios (5 días hábiles, envío de devolución a cargo de ARI Shopping) y /preguntas-frecuentes. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012Y4df3Z7odr9Cg8F2nw6uV |
||
|
|
7a95957945 |
chore(envioclick): actualizar datos reales de contacto/dirección de origen
Placeholders reemplazados por los datos reales que dio el usuario -- dirección de la bodega (Cra 100 #42-65), email y teléfono de contacto. Estos valores aparecen como remitente en las guías reales de EnvíoClick. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01THZFwPzrwv6VAbuwmaf5vR |
||
|
|
c507da8abc |
feat(fulfillment): integrar EnvíoClick para cálculo real de envío
ADR 0009 §2 ya había anticipado esto: la shipping option actual es un placeholder de negocio ($15.000 COP fijo), no un cálculo real por peso/volumen -- y el modelo de direcciones del checkout (IDs DIVIPOLA en vez de texto libre en lib/data/colombia.ts) ya se había construido pensando en esta integración. Este commit la implementa. Investigación en vivo (no solo documentación -- misma disciplina que ya costó varios rehechos con las pasarelas de pago): la SPA de docs públicas (apidoc.envioclickpro.com.co) no renderiza sin sesión, pero está generada con apiDoc, así que se bajó y leyó directo su api_data.json/api_project.json (spec completa real), y se probó /quotation en vivo con la API key real del usuario -- 200 OK con tarifas reales de PIBOX/COORDINADORA/ENVIA/RAPPI/TCC. Esto corrigió varios datos mal asumidos de un plugin de WordPress desactualizado usado como referencia inicial (API v1): la URL base real es api.envioclickpro.com.co/api/v2 (no api.envioclickpro.com a secas), y el header de auth real es "Authorization" a secas, no "AuthorizationKey". Hallazgo crítico evitado leyendo el código fuente de @medusajs/utils/dist/common/define-config.js: defineConfig() ya registra por defecto un módulo de fulfillment con el provider manual_manual, aunque medusa-config.ts no lo mencione explícitamente -- pero transformModules() indexa por serviceName y el último que llega pisa al anterior (no mezcla providers). Agregar un bloque propio de fulfillment sin incluir también manual_manual ahí habría roto en silencio el envío manual que ya usa la shipping option existente. Verificado cargando la config compilada real: ambos providers quedan registrados juntos. - src/modules/envioclick-fulfillment/: AbstractFulfillmentProviderService. calculatePrice() cotiza en vivo durante el checkout -- Medusa advierte explícitamente que un error acá bloquea el checkout completo, así que NUNCA se deja propagar una excepción: cualquier falla (red, dirección sin id DIVIPOLA, EnvíoClick caído) cae al precio fijo de $15.000 que ya se usaba, para no romper la compra. createFulfillment() vuelve a cotizar en el momento de generar la guía real en vez de reusar el idRate mostrado en checkout (más correcto: CalculatedShippingOptionPrice no tiene forma de persistir ese id entre llamadas, confirmado leyendo el tipo real, y un idRate viejo podría estar vencido si el fulfillment pasa horas/días después). cancelFulfillment() sí cancela de verdad (cancellation/batch/order existe y está confirmado, a diferencia del refund de ePayco que quedó sin implementar). - Solo 1 paquete por cotización/guía (límite real de la API, confirmado: "min 1, max 1") -- el carrito se agrega a un solo paquete. Como hoy NINGÚN producto tiene peso/dimensiones cargados (confirmado en vivo: 18 productos activos, 0 con weight), se usa un paquete de fallback conservador completo en vez de mezclar datos reales con inventados a medias -- la cotización no va a ser precisa hasta que se carguen esos datos (tarea de datos, no de código, fuera de alcance de este commit). - Frontend: cambio chico pero necesario en checkout-client.tsx/ medusa-cart.ts -- el checkout mandaba el nombre de la ciudad a Medusa, nunca el id DIVIPOLA (aunque el form sí lo tenía disponible). Hay municipios con el mismo nombre en distintos departamentos de Colombia, así que el nombre solo no alcanza para identificar la ciudad sin ambigüedad -- se guarda el id real en la metadata de la dirección (Medusa sí tiene ese campo), que el fulfillment provider lee para derivar el código DANE real que pide EnvíoClick (5 dígitos + "000" de relleno, confirmado contra su propio ejemplo: "11001000" = Bogotá). Pendiente de verificar con una llamada real (no se quiso generar una guía de prueba en la cuenta real del usuario sin avisar): /shipment_sandbox y cancellation/batch/order, verificados contra la spec oficial real pero no probados en vivo todavía. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01THZFwPzrwv6VAbuwmaf5vR |
||
|
|
12c4afa09c |
fix(payments): reemplazar PayU LatAm por ePayco como pasarela PSE
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 |
||
|
|
2ceea23f40 |
feat(payments): integrar PayU LatAm (PSE) como pasarela de pagos
ADR 0009 ya había decidido PayU LatAm como proveedor (única opción que no exige cuenta Bancolombia/Nequi) y confirmado que no existe ningún plugin real de Medusa para su API -- los paquetes que aparecen buscando "PayU" son para PayU Polonia/CEE, API completamente distinta (clientId/posId/secondKey vs. apiLogin/apiKey/merchantId/accountId). Este commit implementa esa decisión: módulo de pago custom (AbstractPaymentProvider) para PSE (transferencia bancaria), primer alcance del método de pago -- Nequi/Daviplata quedan fuera por ahora. Backend (commerce-backend): - src/modules/payu-pse/: initiatePayment arma y postea SUBMIT_TRANSACTION contra la API real de PayU LatAm; authorizePayment devuelve "pending_authorization" (PSE es async, la confirmación real llega por webhook, no en el momento de completar el carrito); getWebhookActionAndData valida la firma MD5 del webhook antes de confiar en cualquier confirmación de pago -- falla cerrado si no matchea. refundPayment/ retrievePayment tiran error explícito en vez de simular éxito: esos endpoints de PayU no quedaron verificados en vivo y hay dinero real de por medio. - No hace falta un subscriber propio para completar la orden -- el subscriber nativo de Medusa (payment-webhook + process-payment-workflow) ya hace ese trabajo con los métodos de arriba bien implementados (confirmado leyendo el código fuente instalado de @medusajs/medusa 2.18.0, no la documentación). - Correlación webhook↔sesión: Medusa inyecta su propio session_id en `data` antes de initiatePayment; se usa ese mismo valor como referenceCode de PayU, así el reference_sale que vuelve en el webhook ya es directamente el session_id de Medusa. - src/lib/payu.ts: cliente HTTP + firmas (orden y webhook, fórmulas distintas, confirmadas contra el SDK PHP oficial de PayU). - src/api/store/payments/pse-banks/route.ts: lista de bancos PSE, cacheada en memoria. - medusa-config.ts, Dockerfile, configmap/secrets.template: 7 env vars nuevas (PAYU_API_LOGIN/API_KEY/MERCHANT_ID/ACCOUNT_ID/ENV/RESPONSE_URL/ NOTIFY_URL), dummies de build agregados, secrets con placeholder "CAMBIAR". Frontend (ecommerce): - Nuevo step "payment" en components/checkout/checkout-client.tsx (reemplaza al step "review" que solo ofrecía WhatsApp): selector de banco, tipo de persona (N/J), tipo/número de documento, botón "Pagar con PSE" que crea la sesión de pago, completa el carrito y redirige al banco (navegación completa, no iframe -- así lo exige PayU). WhatsApp queda como respaldo manual si falla la iniciación del pago. - lib/headless/providers/medusa-payment.ts + 3 rutas proxy nuevas bajo app/api/cart/, mismo patrón que el resto del carrito (medusaCartFetch, ahora exportado desde medusa-cart.ts). - app/checkout/pse/return/: página de vuelta del banco -- nunca afirma éxito, la confirmación real es el webhook async. Arranca contra las credenciales de sandbox genéricas y públicas de PayU (developers.payulatam.com) mientras el usuario termina su propio registro de comercio -- swap de las 4 credenciales via secret cuando estén listas, sin cambios de código. Verificado: build real de Medusa con las env vars dummy (no solo tsc), tipos de AbstractPaymentProvider/WebhookActionResult/PaymentActions confirmados contra @medusajs/utils y @medusajs/types instalados (node_modules limpiado después), typecheck + lint + build completo del frontend, páginas nuevas renderizando sin error. Pendiente: prueba end-to-end contra el sandbox real de PayU (necesita el backend desplegado) y habilitar el provider en la región Colombia desde el Admin. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01THZFwPzrwv6VAbuwmaf5vR |
||
|
|
751817ed61 |
Fase 1 ADR 0006 Etapa 2: infra de Meilisearch (StatefulSet+Service)
Motor de busqueda self-hosted, sin que Medusa lo conozca todavia (eso es Fase 2). Mismo patron que redis/postgres/minio en este mismo namespace: StatefulSet + PVC via volumeClaimTemplates en local-path, nunca Deployment ni emptyDir -- el indice de Meilisearch necesita sobrevivir a un reinicio del pod para no reindexar todo desde cero. Imagen getmeili/meilisearch:v1.53.1 (ultimo release estable no-RC, verificado contra la API de GitHub, no memoria de entrenamiento). Recursos exactos del ADR (request 100m/256Mi, limit 500m/768Mi). Sin securityContext explicito -- verificado en vivo que la imagen no trae un usuario no-root (Alpine minimo, /etc/passwd sin usuario dedicado), misma convencion que redis.yaml/minio.yaml en este repo, que tampoco fuerzan runAsNonRoot. MEILISEARCH_HOST (no secreto) va al ConfigMap commerce-config. MEILI_MASTER_KEY/MEILISEARCH_API_KEY (mismo valor, dos nombres de env var -- uno para el servidor, otro para el futuro plugin de Medusa en Fase 2) documentados en secrets.template.yaml; el valor real ya se aplico al Secret commerce-secrets en el cluster antes de este merge (patch puntual, no via create-commerce-secrets.sh completo -- ese script regenera TODAS las credenciales en cada corrida y hubiera rotado postgres/redis/minio ya en uso). |
||
|
|
aa857bc2ae | fase 1 |