Saltar a contenido

Referencia — Pagos, aula virtual e IA

Billing, suscripciones, conexiones de pago, Zoom y los tres módulos de IA. Todas las rutas cuelgan de /api/v1.

Pago único (billing) — /modules/billing

Método Ruta Auth Qué hace
GET /modules/billing/offer/:courseId Bearer Oferta del curso (forSale, precio, descuento). Tolerante: sin Stripe configurado devuelve forSale: false, nunca 500.
POST /modules/billing/checkout/:courseId Bearer Crea la Checkout Session de Stripe y devuelve la URL. 409 si el curso no está publicado o ya tienes acceso.
GET · POST · PATCH · DELETE /modules/billing/products[/:id] admin Productos (curso ↔ precio de Stripe): alta con stripePriceId existente o amountCents (crea Product+Price), activación, borrado (no toca órdenes históricas).
GET /modules/billing/public/catalog · public/offer/:courseId Público (tenant por dominio) Catálogo público de venta · oferta pública. Degradan a vacío sin Stripe. La oferta no filtra existencia: curso inexistente o sin publicar responde 200 { forSale: false, options: [] }, nunca 404.
POST /modules/billing/public/checkout/:courseId Público Checkout anónimo: el visitante compra sin cuenta. Body { optionId?, email? } — el email solo precarga el formulario de Stripe; la cuenta se crea SIEMPRE con el email confirmado en el pago. 404 curso inexistente (o :courseId no UUID) · 409 no publicado · 503 sin pasarela.
POST /modules/billing/webhook Firma Stripe (stripe-signature) Recibe checkout.session.completed/expired, charge.refunded. Idempotente por stripe_event_id.

Suscripciones — /modules/subscriptions

Método Ruta Auth Qué hace
POST /modules/subscriptions/checkout/:courseId Bearer Checkout mode=subscription: { stripePriceId } (recurring).
GET /modules/subscriptions/me Bearer Mis suscripciones (incluye canceladas, con plan y curso). Devuelve [] si el módulo no está configurado.
GET /modules/subscriptions/me/:id/invoices Bearer (propietario) Facturas con URL hosted de Stripe.
POST /modules/subscriptions/me/:id/cancel Bearer (propietario) Cancela: { immediate? } (por defecto al final del periodo).
POST /modules/subscriptions/me/membership/pay-now Bearer Termina el trial de la membresía y cobra ya.
POST /modules/subscriptions/webhook Firma Stripe (secret propio) customer.subscription.*, invoice.*; fulfillment de membresía idempotente.
POST /modules/subscriptions/admin/grace-expiration/run-now super_admin Fuerza la expiración de periodos de gracia (QA) → 202.

Errores comunes: SUBSCRIPTIONS_PRICE_NOT_RECURRING 422 · SUBSCRIPTIONS_ALREADY_ACTIVE 409 · *_STRIPE_CONFIG_MISSING 503 · *_STRIPE_API_ERROR 502.

Conexiones de pago — /modules/payment-connections (super_admin)

Conexiones (solo lectura sobre las cuentas):

Método Ruta Qué hace
POST · GET …/connections Conecta cuenta (Stripe con clave restringida rk_/sk_, PayPal o WooCommerce; credenciales cifradas) · lista sin claves.
POST …/connections/:id/verify Re-valida credenciales.
GET …/connections/:id/reconcile Suscriptores activos separados en matched / unmatched contra los usuarios por email.
POST …/connections/:id/invite Invita en bloque a los no registrados (hasta 200 emails; resultado por email, nunca falla en bloque).
DELETE …/connections/:id Desconecta y borra la clave cifrada.

Tiers: GET/POST/PATCH/DELETE …/tiers/catalog[/:id] (catálogo) · GET …/user-tiers?userIds= (tier efectivo de hasta 500 usuarios) · PUT …/user-tiers/:userId (tier manual) · POST …/user-tiers/sync (reconcilia con los pagos; publica payment_connections.user_tier.changed).

Espejo de pedidos (WooCommerce): POST …/orders-mirror/sync?days= · GET …/orders-mirror/expiring?days= · GET/PUT …/orders-mirror/rules (clasificación por regex → LIFETIME|SUBSCRIPTION|TIMED|ONE_OFF|INFRA) · PUT …/orders-mirror/webhook-secret · GET …/orders-mirror/webhook-status.

Dashboard de suscripciones: POST …/subscriptions-dashboard/sync · GET …/subscriptions-dashboard[?filtros] · GET …/summary · GET …/subscribers/:id/renewal-url · POST …/subscribers/:id/renewal-email · GET/PUT …/renewal-template · GET/PUT …/cancel-portal-url · POST …/daily/run-now.

Autoservicio del usuario (Bearer): GET …/me/subscription (su suscripción externa, para /cuenta) · POST …/me/billing-portal (sesión del Customer Portal de Stripe).

Webhook entrante: POST /modules/payment-connections/woo-webhook?tenant=<slug> — público con firma HMAC-SHA256 (x-wc-webhook-signature); responde 200 a pings de verificación y a topics ignorados para que WooCommerce no desactive el webhook.

Aula virtual — /modules/zoom-live

Método Ruta Auth Qué hace
GET /modules/zoom-live/sessions[/:id] Bearer Lista (filtros curso/lección/estado/rango) · detalle. joinUrl y grabación solo para inscritos o staff.
POST /modules/zoom-live/sessions/:id/register · unregister Bearer Inscripción (idempotente) · cancelación.
POST /modules/zoom-live/sessions/:id/join Bearer Sella la entrada (proxy de asistencia) y devuelve el joinUrl.
POST · PUT · DELETE /modules/zoom-live/sessions[/:id] staff Crea (topic, startTime, durationMinutes 15-480, hostEmail, timezone, curso/lección opcional) · edita · cancela (soft).
GET /modules/zoom-live/sessions/:id/registrations staff Roster de inscritos.
GET · POST · PUT /modules/zoom-live/sessions/:id/attendance[…] staff Informe de asistencia · sincroniza contra la API de Zoom · override manual por miembro ({ present: bool\|null }).
POST /modules/zoom-live/test-credentials admin Smoke test de las credenciales S2S (real o stub).
GET /modules/zoom-live/webhook-events admin Log de webhooks de Zoom (QA).
GET /modules/zoom-live/sessions/:id/calendar.ics · calendar/google · outlook · office365 Público Evento de calendario (nunca incluye el joinUrl).
POST /webhooks/zoom Firma HMAC de Zoom meeting.started/ended, recording.completed + handshake de validación. Idempotente por event_id.

IA

Generación de contenido/modules/ai-content (formador+):

Método Ruta Qué hace
POST /modules/ai-content/generate Genera un borrador SUMMARY\|FLASHCARDS\|QUIZ de una lección: { lessonId, courseId, type }.
GET /modules/ai-content/drafts[/:id] Lista con filtros · detalle.
PATCH /modules/ai-content/drafts/:id/content · publish · reject Edita el JSON antes de publicar · publica · rechaza con razón.

Errores: AI_CONTENT_LESSON_TEXT_EMPTY 422 · AI_CONTENT_DRAFT_NOT_IN_DRAFT 409 · AI_CONTENT_PROVIDER_ERROR 503.

Corrección con rúbrica/modules/ai-grader (formador+):

Método Ruta Qué hace
GET · PUT · DELETE /modules/ai-grader/questions/:questionId/rubric Rúbrica por pregunta: instructions + 1-8 criterios con peso (la suma debe igualar los puntos de la pregunta).
POST /modules/ai-grader/attempts/:attemptId/suggest Genera sugerencias IA para las respuestas abiertas ({ force? } ignora la caché).
GET /modules/ai-grader/attempts/:attemptId/suggestions Sugerencias persistidas (sin llamar al modelo).
POST /modules/ai-grader/suggestions/:id/apply Marca la sugerencia como aplicada (auditoría). La nota real se pone en assessments.

Tutor/modules/ai-tutor y /admin/ai-tutor:

Método Ruta Auth Qué hace
POST /modules/ai-tutor/courses/:courseId/ask Bearer Pregunta al tutor (RAG): { question, conversationId?, lessonId?, topK? } → respuesta + citas. El staff puede probar cualquier curso sin matrícula ni cuota.
POST /admin/ai-tutor/courses/:courseId/index · reindex-all admin Re-indexa un curso · todos los publicados.

Errores del tutor: AI_TUTOR_DAILY_QUESTION_QUOTA / AI_TUTOR_TOKEN_QUOTA_EXCEEDED 429 · AI_TUTOR_COURSE_NOT_INDEXED 404 · AI_TUTOR_COURSE_ACCESS_DENIED 403 · AI_PROVIDER_NOT_CONFIGURED 424 · proveedor caído 502.

El panel de revisión del tutor (respuestas, conocimiento validado, informe mensual) está en Administración → IA.