From ca66bec45bdffb9ebbe90fea2c52b75c8c1b1322 Mon Sep 17 00:00:00 2001 From: julien vdb Date: Fri, 24 Jul 2026 04:14:52 +0200 Subject: [PATCH] 0.13.13 : fix Stripe entitlement loop (maxChoirs never credited) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - webhook : crédite maxChoirs selon le plan (pro=1, premium=5), écrit uniquement des champs existants du schéma (isPremium supprimé, subscriptionId -> stripeSubscriptionId) - schéma user : ajout stripeCustomerId / stripeSubscriptionId (private) - controller choral.create : contrôle du crédit côté serveur + décrément après création Co-Authored-By: Claude Fable 5 --- package.json | 2 +- src/api/choral/controllers/choral.ts | 19 +++++++++++++++++++ src/api/order/controllers/order.ts | 17 +++++++++++++---- .../content-types/user/schema.json | 10 +++++++++- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index bfcb459..182259d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "harmony-back", - "version": "0.13.12", + "version": "0.13.13", "private": true, "description": "A Strapi application", "scripts": { diff --git a/src/api/choral/controllers/choral.ts b/src/api/choral/controllers/choral.ts index a09fccd..ff7ee3c 100644 --- a/src/api/choral/controllers/choral.ts +++ b/src/api/choral/controllers/choral.ts @@ -8,6 +8,18 @@ export default factories.createCoreController( "api::choral.choral", ({ strapi }) => ({ async create(ctx) { + // Contrôle du crédit de création côté serveur (le front bloque aussi, + // mais seul ce contrôle fait foi). + const user = ctx.state.user; + if (!user) { + return ctx.unauthorized(); + } + if (!user.maxChoirs || user.maxChoirs <= 0) { + return ctx.forbidden( + "Plus de crédit de création de chorale. Mettez à jour votre abonnement.", + ); + } + const data = JSON.parse(ctx.request.body.data); if (ctx.request.files.coverImage) { @@ -31,6 +43,13 @@ export default factories.createCoreController( ctx.request.body = { data }; const result = await super.create(ctx); + + // Décrémente le crédit de création une fois la chorale créée. + await strapi.db.query("plugin::users-permissions.user").update({ + where: { id: user.id }, + data: { maxChoirs: user.maxChoirs - 1 }, + }); + return result; }, }), diff --git a/src/api/order/controllers/order.ts b/src/api/order/controllers/order.ts index eba76d9..6432e35 100644 --- a/src/api/order/controllers/order.ts +++ b/src/api/order/controllers/order.ts @@ -34,6 +34,14 @@ export default factories.createCoreController( | "pro" | "premium"; + // Capacité de création de chorales par plan (cf. offres du front, + // UserSubscriptionTab : pro = "1 chorale", premium = "5 chorales"). + const PLAN_MAX_CHOIRS: Record = { + free: 0, + pro: 1, + premium: 5, + }; + if (userId) { try { // Création de la commande @@ -46,19 +54,20 @@ export default factories.createCoreController( }, }); - // Mise à jour de l'utilisateur + // Mise à jour de l'utilisateur : plan + crédit de création de + // chorales + identifiants Stripe (résiliation/portail client). await strapi.documents("plugin::users-permissions.user").update({ documentId: userId, data: { - isPremium: true, subscriptionPlan: planType, + maxChoirs: PLAN_MAX_CHOIRS[planType] ?? 0, stripeCustomerId: session.customer as string, - subscriptionId: session.subscription as string, + stripeSubscriptionId: session.subscription as string, trialStartedAt: null, }, }); strapi.log.info( - `✅ Droits mis à jour pour l'utilisateur ${userId}`, + `✅ Droits mis à jour pour l'utilisateur ${userId} : plan ${planType}, maxChoirs ${PLAN_MAX_CHOIRS[planType] ?? 0}`, ); } catch (dbError) { strapi.log.error(`❌ Erreur base de données : ${dbError.message}`); diff --git a/src/extensions/users-permissions/content-types/user/schema.json b/src/extensions/users-permissions/content-types/user/schema.json index 954dfd7..2bedc27 100644 --- a/src/extensions/users-permissions/content-types/user/schema.json +++ b/src/extensions/users-permissions/content-types/user/schema.json @@ -267,6 +267,14 @@ }, "smartTags": { "type": "json" + }, + "stripeCustomerId": { + "type": "string", + "private": true + }, + "stripeSubscriptionId": { + "type": "string", + "private": true } } -} +} \ No newline at end of file