0.13.13 : fix Stripe entitlement loop (maxChoirs never credited)
Build release Docker image / Build Docker Images (push) Successful in 7m32s

- 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 <noreply@anthropic.com>
This commit is contained in:
2026-07-24 04:14:52 +02:00
parent 51947d49e7
commit ca66bec45b
4 changed files with 42 additions and 6 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "harmony-back", "name": "harmony-back",
"version": "0.13.12", "version": "0.13.13",
"private": true, "private": true,
"description": "A Strapi application", "description": "A Strapi application",
"scripts": { "scripts": {
+19
View File
@@ -8,6 +8,18 @@ export default factories.createCoreController(
"api::choral.choral", "api::choral.choral",
({ strapi }) => ({ ({ strapi }) => ({
async create(ctx) { 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); const data = JSON.parse(ctx.request.body.data);
if (ctx.request.files.coverImage) { if (ctx.request.files.coverImage) {
@@ -31,6 +43,13 @@ export default factories.createCoreController(
ctx.request.body = { data }; ctx.request.body = { data };
const result = await super.create(ctx); 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; return result;
}, },
}), }),
+13 -4
View File
@@ -34,6 +34,14 @@ export default factories.createCoreController(
| "pro" | "pro"
| "premium"; | "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<string, number> = {
free: 0,
pro: 1,
premium: 5,
};
if (userId) { if (userId) {
try { try {
// Création de la commande // 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({ await strapi.documents("plugin::users-permissions.user").update({
documentId: userId, documentId: userId,
data: { data: {
isPremium: true,
subscriptionPlan: planType, subscriptionPlan: planType,
maxChoirs: PLAN_MAX_CHOIRS[planType] ?? 0,
stripeCustomerId: session.customer as string, stripeCustomerId: session.customer as string,
subscriptionId: session.subscription as string, stripeSubscriptionId: session.subscription as string,
trialStartedAt: null, trialStartedAt: null,
}, },
}); });
strapi.log.info( 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) { } catch (dbError) {
strapi.log.error(`❌ Erreur base de données : ${dbError.message}`); strapi.log.error(`❌ Erreur base de données : ${dbError.message}`);
@@ -267,6 +267,14 @@
}, },
"smartTags": { "smartTags": {
"type": "json" "type": "json"
},
"stripeCustomerId": {
"type": "string",
"private": true
},
"stripeSubscriptionId": {
"type": "string",
"private": true
} }
} }
} }