Compare commits
5 Commits
5a8a7d9f66
...
57faa37e70
| Author | SHA1 | Date | |
|---|---|---|---|
| 57faa37e70 | |||
| 87842c7585 | |||
| 3da128278d | |||
| 883b827b0d | |||
| 5be8873ad5 |
@@ -72,6 +72,24 @@ Vue par domaine (34 au total, liste exhaustive dans `src/api/`) :
|
||||
Toute nouvelle route/action consommée par le front DOIT être ajoutée à ce
|
||||
fichier (et jamais cochée uniquement dans l'admin, sinon elle sera signalée
|
||||
comme non déclarée).
|
||||
- ⚠️ **Les controllers `create` custom qui gèrent des fichiers n'utilisent PAS
|
||||
la convention `files.<attribut>` de Strapi.** `ad`, `post`, `group` et
|
||||
`chat-message` lisent directement `ctx.request.files.<attribut>` (donc un
|
||||
champ multipart nommé `medias`, `media`… et non `files.medias`), uploadent
|
||||
eux-mêmes puis rattachent les ids. Envoyer `files.<attribut>` à l'un d'eux
|
||||
ne produit **aucune erreur** : l'entité est créée, les fichiers sont
|
||||
ignorés en silence. Avant de brancher un formulaire avec upload, lire le
|
||||
controller du content-type visé. Symptôme : « le contenu s'enregistre mais
|
||||
pas l'image ».
|
||||
- ⚠️ **Une relation peuplée via l'API REST exige `find` sur le content-type
|
||||
cible.** Le sanitizer de l'API REST retire silencieusement les relations
|
||||
peuplées vers un content-type que le rôle n'a pas le droit de lire — pas
|
||||
d'erreur, juste un champ vide. C'est ce qui rendait les commentaires
|
||||
invisibles sur la page de détail d'une publication (corrigé en 0.13.16),
|
||||
alors qu'ils s'affichaient dans le fil : les controllers custom qui
|
||||
renvoient via `ctx.send()` ne passent pas par ce sanitizer, et masquent
|
||||
donc le problème. Symptôme à reconnaître : « ça marche dans le fil, pas
|
||||
dans le détail ».
|
||||
- Secrets uniquement en variables d'environnement. `.env` jamais commité ;
|
||||
`.env.example` maintenu à jour. Depuis le 26/07/2026, `config/plugins.ts`
|
||||
ne contient plus aucun secret (R2 et SMTP passent par `env()`).
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "harmony-back",
|
||||
"version": "0.13.16",
|
||||
"version": "0.13.19",
|
||||
"private": true,
|
||||
"description": "A Strapi application",
|
||||
"scripts": {
|
||||
|
||||
@@ -18,7 +18,24 @@ export default factories.createCoreController(
|
||||
process.env.STRIPE_WEBHOOK_SECRET!,
|
||||
);
|
||||
} catch (err: any) {
|
||||
strapi.log.error(`❌ Erreur de signature Webhook: ${err.message}`);
|
||||
// Deux causes très différentes se ressemblent ici. Corps brut absent
|
||||
// (middleware `strapi::body` sans `includeUnparsed`) : Stripe se
|
||||
// plaint du payload. Corps brut présent mais signature invalide :
|
||||
// c'est le secret qui ne correspond pas à l'endpoint appelant —
|
||||
// chaque endpoint Stripe a le sien.
|
||||
strapi.log.error(
|
||||
`❌ Erreur de signature Webhook: ${err.message} ` +
|
||||
`[corps brut: ${
|
||||
typeof unparsedBody === "string" || Buffer.isBuffer(unparsedBody)
|
||||
? `présent, ${unparsedBody.length} octets`
|
||||
: `ABSENT (${typeof unparsedBody})`
|
||||
}, en-tête stripe-signature: ${sig ? "présent" : "ABSENT"}, ` +
|
||||
`STRIPE_WEBHOOK_SECRET: ${
|
||||
process.env.STRIPE_WEBHOOK_SECRET
|
||||
? `défini (${process.env.STRIPE_WEBHOOK_SECRET.length} car.)`
|
||||
: "NON DÉFINI"
|
||||
}]`,
|
||||
);
|
||||
return ctx.badRequest(`Webhook Error: ${err.message}`);
|
||||
}
|
||||
|
||||
@@ -42,15 +59,37 @@ export default factories.createCoreController(
|
||||
premium: 5,
|
||||
};
|
||||
|
||||
if (userId) {
|
||||
if (!userId) {
|
||||
// Sans `userId` dans les métadonnées, l'ancien code sortait en
|
||||
// silence avec un 200 : le paiement aboutissait chez Stripe sans
|
||||
// qu'aucune commande ni aucun droit n'apparaisse, et sans trace.
|
||||
strapi.log.error(
|
||||
`❌ Webhook ${event.type} sans metadata.userId (session ${session.id}) : aucun droit crédité`,
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
// Les métadonnées portent un `documentId` (cf. le front,
|
||||
// UserSubscriptionTab). La relation `user` d'une commande attend
|
||||
// un id numérique : on résout l'utilisateur une fois pour les deux.
|
||||
const user = await strapi.db
|
||||
.query("plugin::users-permissions.user")
|
||||
.findOne({ where: { documentId: userId } });
|
||||
|
||||
if (!user) {
|
||||
strapi.log.error(
|
||||
`❌ Webhook : aucun utilisateur pour documentId ${userId} (session ${session.id})`,
|
||||
);
|
||||
ctx.send({ received: true });
|
||||
return;
|
||||
}
|
||||
|
||||
// Création de la commande
|
||||
await strapi.documents("api::order.order").create({
|
||||
data: {
|
||||
stripeId: session.id,
|
||||
amount: session.amount_total ? session.amount_total / 100 : 0,
|
||||
planType: planType,
|
||||
user: userId,
|
||||
user: user.id,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -74,6 +113,10 @@ export default factories.createCoreController(
|
||||
// On répond quand même 200 à Stripe pour éviter les retries infinis
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Permet de distinguer « Stripe n'appelle pas le webhook » de
|
||||
// « Stripe appelle mais n'envoie pas checkout.session.completed ».
|
||||
strapi.log.info(`ℹ️ Événement ${event.type} ignoré`);
|
||||
}
|
||||
|
||||
// INDISPENSABLE : Répondre 200 OK à Stripe pour TOUS les événements
|
||||
|
||||
@@ -250,8 +250,21 @@ module.exports = (plugin) => {
|
||||
|
||||
const stats = await getUserStats(ctx.state.user.id);
|
||||
|
||||
// `strapi.db.query` renvoie la ligne brute : sans cette omission, /users/me
|
||||
// exposait au navigateur le hash du mot de passe et les jetons de
|
||||
// réinitialisation et de confirmation.
|
||||
// On retire ces champs explicitement plutôt que de passer par
|
||||
// `sanitizeUser` : le sanitizer de l'API de contenu retirerait aussi les
|
||||
// relations peuplées que le front consomme ici.
|
||||
const {
|
||||
password: _password,
|
||||
resetPasswordToken: _resetPasswordToken,
|
||||
confirmationToken: _confirmationToken,
|
||||
...safeUser
|
||||
} = JSON.parse(JSON.stringify(fullUser));
|
||||
|
||||
const result = {
|
||||
...JSON.parse(JSON.stringify(fullUser)),
|
||||
...safeUser,
|
||||
stats,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user