Operaciones de facturación
Audience
Este artículo es para operadores de la plataforma GCM. Los administradores de iglesia deberían ver el módulo de Donaciones delimitado por organización para las donaciones de su congregación — esta página trata sobre los pagos de suscripción a GCM.
La pestaña Operaciones de facturación (/platform/billing-ops) es donde el personal resuelve todo lo relacionado con dinero: una tarjeta que fue declinada esta mañana, un cliente que quiere que le reembolsen su última factura, una renovación que necesita ser empujada una semana, un PDF de factura faltante. Todo aquí está restringido por superadmin o platform_admin y cada escritura aterriza en platform_audit_log con un motivo obligatorio.

El banner de salud del cron
Si alguna vez ves un banner rojo en la parte superior que dice "Recurring billing is not scheduled", detente y arréglalo antes de hacer cualquier otra cosa. Significa que el job charge_recurring no está registrado en pg_cron, así que no se dispararán renovaciones hasta que alguien vuelva a ejecutar la migración del cron. El banner solo se renderiza cuando el RPC del panel reporta cron_health.charge_recurring_scheduled = false — es la alerta más importante de la página.
Las cinco tarjetas de resumen
- MRR estimado — el mismo número que la franja de KPIs de Organizaciones. Formateado como USD.
- Vence hoy — organizaciones con
subscription_status = 'active'ynext_billing_date <= today. Estas serán cobradas en el siguiente tick del cron. - Vencido —
subscription_status = 'past_due'. El contador de reintentos en cada una es visible en la tabla. - Intentos fallidos 7d —
payment_sessionscon estado de error terminal en la última semana. Un pico aquí normalmente significa una interrupción del gateway; verifica el estado del proveedor del gateway. - Reembolsos 30d — conteo de filas de
payment_historyconstatus = 'refunded'en los últimos 30 días.
Las cuatro sub-pestañas
Suscripciones
La vista por defecto. Cada organización con datos de facturación, con su plan, estado, próxima fecha de facturación, postura de reintentos, tarjeta en archivo y último pago exitoso. Los cuatro botones de acción por fila son:
- Detalles — abre la hoja derecha con un desglose más completo, los últimos pagos y los intentos recientes del gateway para esa organización sola.
- Engrane de configuración — abre el diálogo Editar suscripción (cubierto a continuación).
- Ícono de refrescar — abre el diálogo Renovar / reintentar.
- Abrir — cierra esta pestaña y empuja la hoja estándar de detalle de organización desde la página Organizaciones, para que puedas saltar a suplantación o gestión del personal.
Pagos
El libro mayor de pagos. Cada fila en payment_history en toda la plataforma, más nuevas primero. Haz clic en el ícono de archivo para (re)generar el PDF de la factura — llama a adminGenerateInvoice que escribe una URL firmada en la fila. Haz clic en el ícono de rotar en sentido antihorario para abrir el Modal de reembolso estándar; es el mismo componente que el flujo de donaciones por organización usa, por lo que reembolsos parciales / totales / fuera de plataforma funcionan todos de la misma manera.
Un reembolso solo está disponible cuando el pago está en estado approved y el importe es mayor que cero. Pagos reembolsados y pendientes deshabilitan el botón.
Intentos
Una vista redactada de payment_sessions — cada intento de transacción, exitoso o no, con el código de respuesta del gateway y el mensaje. Este es el primer lugar para mirar cuando "el cliente dice que le cobraron dos veces": cada intento tiene un order_identifier que puedes correlacionar con el panel del gateway.
Línea de tiempo
Un feed de auditoría desplazable delimitado a acciones de facturación: ediciones de suscripción, renovaciones disparadas, reembolsos procesados, facturas generadas. Es una rebanada filtrada de platform_audit_log unida a nombres de organización — útil cuando un cliente pregunta "¿qué cambió en nuestra cuenta entre el martes y el viernes?".
Editando una suscripción
El diálogo Editar suscripción es el panel de sobrescritura. Cada campo es editable pero Motivo es obligatorio (mínimo 3 caracteres) y se anexa a la fila de auditoría junto a tu correo. Campos:
- Plan y Estado — mismos valores de enum que la pestaña Organizaciones.
- Fin de prueba / Próxima facturación / Inicio del ciclo de facturación — selectores de fecha. Usados para empujar o adelantar el próximo cobro.
- Día del ciclo de facturación — 1-31. Si el día de renovación difiere de la fecha de inicio (por ejemplo, se registró el 14 pero factura el 1).
- Límite de miembros — entero libre. Úsalo cuando una mejora de plan no sea apropiada pero el cliente necesite margen.
- Conteo de reintentos / Fecha de reintento — establece a cero y limpia la fecha para detener la tormenta de reintentos en una tarjeta declinada.
- Pausar hasta — establece la fecha de reanudación cuando un cliente pide suspender el servicio.
- Enviar correo al cliente — interruptor. Apagado por defecto; activa cuando el cambio sea visible al cliente (por ejemplo, degradación de plan).
Guardar llama a adminUpdateSubscription que escribe las nuevas columnas e inserta un evento de auditoría.
Renovando o reintentando
El diálogo Renovar / reintentar tiene dos modos:
- Solo vista previa (por defecto) — ejecuta la lógica de renovación en dry-run. El gateway no es llamado; la respuesta muestra lo que sucedería y se vuelca en un bloque
<pre>para inspección. Úsalo siempre que no estés seguro del estado de una tarjeta. - Cobrar ahora — realmente invoca el gateway con el token de pago almacenado. El botón se vuelve rojo para que no puedas pulsarlo por error. Se requiere un campo de motivo y se estampa en la fila de payment-history resultante.
El diálogo llama a adminTriggerRenewal con un requestId fresco para que un clic duplicado no pueda cobrar dos veces — la función deduplica del lado del servidor con esa clave.
Cobros fuera de ciclo
Cobrar fuera del día normal de facturación está bien, pero desplaza la cadencia de renovación: la próxima next_billing_date se calculará desde hoy, no desde la fecha programada previamente. Si quieres mantener el ciclo alineado, edita next_billing_date manualmente de vuelta en el diálogo Editar suscripción después de que el cobro tenga éxito.
Reembolsos
El Modal de reembolso se comparte con el flujo de donaciones por organización pero siempre opera en modo administrador de plataforma aquí. Puedes emitir:
- Reembolso por gateway — llama al endpoint de reembolso del gateway con el ID de transacción original. Los fondos regresan a la tarjeta del cliente.
- Reembolso fuera de plataforma — registra una fila de reembolso sin tocar el gateway. Úsalo cuando ya hayas reembolsado al cliente por transferencia / cheque y solo necesites que el libro mayor lo refleje. Los campos
manualMethodyreferenceNumberson obligatorios para que el auditor pueda rastrearlo.
Un reembolso exitoso escribe a payment_history (el estado se vuelve refunded), actualiza la fila relacionada de payment_sessions e inserta una entrada de platform_audit_log con el motivo.
Situaciones comunes
"La tarjeta del cliente fue declinada tres noches seguidas y quieren saber por qué." Abre Detalles en su fila, desplázate a los intentos recientes y lee el mensaje de respuesta del gateway. El 99% de las veces es INSUFFICIENT_FUNDS, EXPIRED_CARD o DO_NOT_HONOR. Abre la cuenta de la organización (suplanta) y haz que actualicen la tarjeta a través de la página de facturación dirigida al cliente.
"Necesitamos empujar la renovación una semana para que el cliente pueda hacer una transferencia." Editar suscripción -> establece next_billing_date a hoy + 7. Restablece billing_retry_count a 0 y limpia billing_retry_at para que la tormenta de reintentos pare. Motivo: "wire-transfer arrangement, T-7".
"El PDF de la factura falta." Pestaña Pagos -> encuentra la fila -> haz clic en el ícono de archivo. adminGenerateInvoice re-renderiza el PDF, lo sube al bucket de facturas y estampa la URL firmada en la fila.
"El cron de esta mañana no se ejecutó." Verifica primero el banner rojo. Si no se muestra pero sospechas, mira el número de Vence hoy una hora después del tiempo de ejecución habitual — si no ha bajado, el job no se disparó. La solución es volver a ejecutar la migración de pg_cron; consulta el runbook en docs/runbooks/.
