Avertissement : oui, j’ai utilisé Claude pour m’aider à résoudre ce problème :
Exécution du cœur de Discourse sur le commit 5ee8e24 (2026-08-19). La synchronisation Patreon se termine sans erreur, mais ne synchronise aucun utilisateur :
Patreon sync complete: 63 pledges, 0 users synced
Les réponses de l’API sont toutes status=200, donc les requêtes elles-mêmes sont correctes. En traçant le problème dans la console rails, la requête des membres construite par le plugin est correcte et demande bien l’adresse e-mail sur la ressource membre :
fields[member]=full_name,last_charge_date,last_charge_status,currently_entitled_amount_cents,patron_status,email
En appelant directement ce point de terminaison avec un jeton de créateur correctement scoppé (campaigns.members[email] accordé), l’adresse e-mail est présente sur l’objet member, et non sur l’objet user dans included. Une récupération d’un seul membre le confirme :
data.attributes.email => "patron@example.com" # présent
included[].attributes.email => (absent) # les objets user ne portent que full_name
Ainsi, dans la liste des membres de la campagne, member.attributes.email est renseigné (46 de mes 63 membres ; le reste a un partage restreint ou sont des abonnés gratuits), tandis que les entrées utilisateur included n’ont tout simplement pas de champ e-mail.
Le problème se trouve dans Patreon::ApiVersion::V2.extract (lib/api_version/v2.rb). Il construit la carte users à partir des objets utilisateur included :
ruby
(member_data["included"] || []).each do |entry|
if entry["type"] == "user" && entry["attributes"]["email"].present?
users[entry["id"]] = entry["attributes"]["email"].downcase
end
end
Comme ces objets utilisateur ne portent jamais d’adresse e-mail, la carte ressort vide et chaque membre échoue à la correspondance d’e-mail, d’où 0 synchronisés.
Lire l’adresse e-mail depuis l’entrée membre plutôt que d’ailleurs (clé par l’identifiant utilisateur déjà disponible dans member.relationships.user.data.id, qui est le même patron_id utilisé pour les engagements) a corrigé le problème sur mon installation : 0 est devenu 46 users synced. Le changement que j’ai appliqué :
diff
+ users[patron_id] = attrs["email"].downcase if attrs["email"].present?
pledges[patron_id] = attrs["currently_entitled_amount_cents"]
declines[patron_id] = attrs["last_charge_date"] if attrs["last_charge_status"] == "Declined"
end
-
- (member_data["included"] || []).each do |entry|
- if entry["type"] == "user" && entry["attributes"]["email"].present?
- users[entry["id"]] = entry["attributes"]["email"].downcase
- end
- end
Je ne suis pas certain que la suppression complète de la boucle included soit la bonne décision dans tous les cas — si certaines configurations reçoivent l’adresse e-mail sur l’objet utilisateur, vous voudrez peut-être la conserver en tant que solution de repli plutôt que de la remplacer. Je publie le comportement observé et le changement minimal qui a fonctionné ici, au cas où cela serait utile ou indiquerait le bon correctif.