1. Usuario cancela
cancelAtPeriodEnd = true, cancelAt = "2026-05-15". El acceso sigue
activo hasta esa fecha.
Este endpoint permite a un usuario autenticado reactivar una suscripción que estaba pendiente de cancelación. Es la operación inversa a cancelSubscription: elimina la marca de cancelación diferida para que la suscripción continúe renovándose automáticamente.
/api/user-endpoints/reactivate-subscription
Auth JWT Requiere token en
header Authorization
Autenticación — Verifica que el usuario tenga un JWT válido.
Validación del body — Comprueba que origin, vertical y subscriptionID estén presentes y sean válidos.
Selección de Stripe — Usa selectStripeCrm(origin) para obtener la instancia de Stripe correcta según el CRM (Inspiria o Alebat).
Búsqueda del customer — Localiza al cliente en Stripe por su email.
Verificación de suscripción — Confirma que el usuario tiene una suscripción activa con cancelación pendiente.
Reactivación — Llama a stripe.subscriptions.update() con cancel_at_period_end: false para eliminar la cancelación programada.
Limpieza en Strapi — Resetea cancelAtPeriodEnd: false y cancelAt: null en la base de datos.
Respuesta — Retorna success: true. No incluye activeUntil porque la suscripción seguirá renovándose normalmente.
Archivo: src/api/user-endpoints/controllers/check-subscription.ts
async reactivateSubscription(ctx) { const user = ctx.state.user; if (!user) return ctx.unauthorized(ERROR_UNAUTHORIZED);
validateKeys(ctx.request.body, CANCEL_SUBSCRIPTION_KEYS, FIELD_ROOT); validateRequiredFields(ctx.request.body, CANCEL_SUBSCRIPTION_KEYS); const { origin, vertical, subscriptionID } = ctx.request.body;
const stripe = await selectStripeCrm(origin); const customer = await searchCustomer(stripe, user.email);
const isUserSubscribed = await strapi .service(USER_SERVICE) .isUserSubscribed(stripe, customer.data[0].id, user.email, origin, vertical);
if (!isUserSubscribed) { ... }
const reactivatedSubscription = await strapi .service(SUBSCRIPTION_STRIPE_SERVICES) .reactivateSubscription(stripe, user.email, origin, vertical, subscriptionID);
return ctx.send({ success: true, message: SUBSCRIPTION_REACTIVATED_MESSAGE, });}Archivo: src/api/subscription/services/stripe.ts
reactivateSubscription: async function (stripe, userEmail, origin, vertical, subscriptionID) { // Busca suscripción activa en Strapi const subscription = await findOne(SUBSCRIPTION, CANCEL_SUBSCRIPTION_FETCH_FIELDS, { user: { email: userEmail }, stripeCrm: origin, stateSubscription: { $in: ['active'] }, vertical, id: subscriptionID, }, null);
if (!subscription || !subscription.subscriptionID) throw new Error(ERROR_SUBSCRIPTION_NOT_FOUND_FOR_REACTIVATION);
// Verifica que tenga cancelación pendiente if (!subscription.cancelAtPeriodEnd && !subscription.cancelAt) throw new Error(ERROR_SUBSCRIPTION_NOT_PENDING_CANCELLATION);
// Remueve cancelación en Stripe const reactivatedSubscription = await stripe.subscriptions.update( subscription.subscriptionID, { cancel_at_period_end: false } );
// Limpia campos de cancelación en Strapi await update(SUBSCRIPTION, { documentId: subscription.documentId }, { cancelAtPeriodEnd: false, cancelAt: null, });
return reactivatedSubscription;}| Campo | Valor |
|---|---|
cancelAtPeriodEnd | false |
cancelAt | null |
| Error | Cuándo ocurre |
|---|---|
ERROR_SUBSCRIPTION_NOT_FOUND_FOR_REACTIVATION | No existe suscripción activa con ese ID y vertical |
ERROR_SUBSCRIPTION_NOT_PENDING_CANCELLATION | La suscripción no estaba marcada para cancelarse (nada que reactivar) |
cancelSubscription| Aspecto | cancelSubscription | reactivateSubscription |
|---|---|---|
| Acción en Stripe | cancel_at_period_end: true | cancel_at_period_end: false |
cancelAt en Strapi | Fecha de fin de período | null |
| Precondición | Suscripción activa sin cancel pendiente | Suscripción activa con cancel pendiente |
| Respuesta | Incluye activeUntil | No incluye activeUntil |
POST /api/user-endpoints/reactivate-subscriptionAuthorization: Bearer <JWT_TOKEN>Content-Type: application/json{ "origin": "Inspiria", "vertical": "farma", "subscriptionID": 42}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
origin | string | ✅ | CRM de Stripe. Valores: "Inspiria" o "Alebat" |
vertical | string | ✅ | Vertical del producto (ej: "cirugia", "farma") |
subscriptionID | number | ✅ | ID interno (Strapi) de la suscripción a reactivar |
{ "success": true, "message": "Subscription reactivated successfully. It will no longer be cancelled at the end of the billing period."}A diferencia de cancelSubscription, no incluye activeUntil porque la suscripción continuará renovándose indefinidamente.
{ "success": false, "message": "User does not have an active subscription to reactivate"}{ "success": false, "message": "Error reactivating user subscription: Subscription is not pending cancellation"}{ "error": { "status": 401, "message": "Unauthorized" }}fetchconst response = await fetch("/api/user-endpoints/reactivate-subscription", { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ origin: "Inspiria", vertical: "farma", subscriptionID: 42, }),});
const data = await response.json();
if (data.success) { console.log("Suscripción reactivada correctamente.");}POST /reactivate-subscription │ ▼¿Autenticado? ──No──▶ 401 Unauthorized │ Sí ▼Validar body (origin, vertical, subscriptionID) │ ▼selectStripeCrm(origin) → Stripe instance │ ▼searchCustomer(stripe, email) │ ▼isUserSubscribed() ──No──▶ 400 Sin suscripción │ Sí ▼reactivateSubscription() ├─ findOne() → suscripción activa ├─ ¿Tiene cancel pendiente? ─No─▶ Error ├─ stripe.subscriptions.update({ cancel_at_period_end: false }) └─ update Strapi (cancelAtPeriodEnd: false, cancelAt: null) │ ▼200 { success, message }1. Usuario cancela
cancelAtPeriodEnd = true, cancelAt = "2026-05-15". El acceso sigue
activo hasta esa fecha.
2. Se arrepiente
Antes del 15/05 llama a reactivateSubscription. Se elimina la cancelación
pendiente.
3. Suscripción continúa
cancelAtPeriodEnd = false, cancelAt = null. Se renueva normalmente el
15/05.
| Archivo | Rol |
|---|---|
src/api/user-endpoints/controllers/check-subscription.ts | Controlador — valida auth, body y orquesta el flujo |
src/api/user-endpoints/routes/user-endpoints.ts | Definición de la ruta |
src/api/subscription/services/stripe.ts | Servicio — lógica de Stripe y actualización en Strapi |
src/extensions/users-permissions/services/userServices.ts | Verifica si el usuario tiene suscripción activa |
src/utils/stripe-crm.ts | Selecciona la instancia de Stripe según origin |