Aviso: Sí, usé Claude para ayudarme a solucionar este problema:
Ejecutando el núcleo de Discourse en el commit 5ee8e24 (2026-08-19). La sincronización con Patreon se completa sin errores, pero sincroniza cero usuarios:
Patreon sync complete: 63 pledges, 0 users synced
Las respuestas de la API son todas status=200, por lo que las solicitudes en sí están bien. Al rastrearlo en la consola de Rails, la solicitud de miembros que construye el plugin es correcta y sí solicita el correo electrónico en el recurso de miembro:
fields[member]=full_name,last_charge_date,last_charge_status,currently_entitled_amount_cents,patron_status,email
Al llamar directamente a ese punto de acceso con un token de creador correctamente limitado (con campaigns.members[email] concedido), el correo electrónico está presente en el objeto member, no en el objeto user dentro de included. Una búsqueda de un solo miembro lo confirma:
data.attributes.email => "patron@example.com" # presente
included[].attributes.email => (ausente) # los objetos user solo llevan full_name
Por lo tanto, en la lista de miembros de la campaña, member.attributes.email está poblado (46 de mis 63 miembros; el resto tiene el intercambio restringido o son seguidores gratuitos), mientras que las entradas de usuario en included no tienen ningún campo de correo electrónico en absoluto.
El problema está en Patreon::ApiVersion::V2.extract (lib/api_version/v2.rb). Construye el mapa users a partir de los objetos de usuario en 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
Como esos objetos de usuario nunca llevan el correo electrónico, el mapa sale vacío y cada miembro falla en el emparejamiento de correos, de ahí que sean 0 sincronizados.
Leer el correo electrónico desde la entrada del miembro en su lugar (usando como clave el id de usuario ya disponible en member.relationships.user.data.id, que es el mismo patron_id utilizado para las promesas) lo solucionó en mi instalación: 0 se convirtió en 46 users synced. El cambio que apliqué:
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
No estoy seguro de que eliminar por completo el bucle de included sea la decisión correcta para todos los casos: si algunas configuraciones sí reciben el correo electrónico en el objeto de usuario, es posible que deseen conservarlo como alternativa en lugar de reemplazarlo. Publico el comportamiento observado y el cambio mínimo que funcionó aquí, por si es útil o apunta a la solución correcta.