# Patreon v2の同期レポートで「N件のpledge、同期済みユーザー0件」と表示される — extract内で誤ったリソースからメンバーのメールアドレスが読み取られている

**URL:** https://meta.discourse.org/t/patreon-v2-sync-reports-n-pledges-0-users-synced-member-email-read-from-wrong-resource-in-extract/411874
**Category:** Bug
**Tags:** patreon
**Created:** [2026 年 9 月 8 日午前 8:17 UTC](https://meta.discourse.org/t/patreon-v2-sync-reports-n-pledges-0-users-synced-member-email-read-from-wrong-resource-in-extract/411874 "2026-09-08T08:17:12Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![WaitroseCarpark](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/waitrosecarpark/32/198599_2.png) [@WaitroseCarpark](https://meta.discourse.org/u/WaitroseCarpark)
#### Post date: [2026 年 9 月 8 日午前 8:17 UTC](https://meta.discourse.org/t/patreon-v2-sync-reports-n-pledges-0-users-synced-member-email-read-from-wrong-resource-in-extract/411874/1 "2026-09-08T08:17:13Z")

</div>

免責事項: はい、この問題の修正にClaudeの助けを借りました。

Discourse core はコミット `5ee8e24` (2026-08-19) で実行しています。Patreon の同期はエラーなく完了しますが、同期されるユーザーはゼロです:

```plaintext
Patreon sync complete: 63 pledges, 0 users synced

```

API のレスポンスはすべて `status=200` なので、リクエスト自体には問題ありません。Rails コンソールでトレースすると、プラグインが構築しているメンバーリクエストは正しく、member リソースに対して email の取得を要求しています:

```plaintext
fields[member]=full_name,last_charge_date,last_charge_status,currently_entitled_amount_cents,patron_status,email

```

適切にスコープされたクリエイタートークン (`campaigns.members[email]` が付与されている) を直接使用してそのエンドポイントを呼び出すと、email は `included` 内の `user` オブジェクトではなく、`member` オブジェクト上に存在します。単一のメンバー取得でも確認できます:

```plaintext
data.attributes.email => "patron@example.com" # 存在
included[].attributes.email => (なし) # user オブジェクトは full_name のみ

```

つまり、キャンペーンメンバーリストでは、`member.attributes.email` が設定されています（私の63人のメンバーのうち46人。残りは共有が制限されているか、無料フォロワーです）。一方、`included` の user エントリには email フィールドが全くありません。

問題は `Patreon::ApiVersion::V2.extract` (`lib/api_version/v2.rb`) にあります。これは `included` の user オブジェクトから `users` マップを構築しています:

ruby

```plaintext
(member_data["included"] || []).each do |entry|
  if entry["type"] == "user" && entry["attributes"]["email"].present?
    users[entry["id"]] = entry["attributes"]["email"].downcase
  end
end

```

これらの user オブジェクトには email が含まれないため、マップは空になり、すべてのメンバーが email マッチングに失敗し、結果として同期数が 0 になっています。

代わりに member エントリから email を読み取ること（pledges で使用されているのと同じ `patron_id` である `member.relationships.user.data.id` をキーとして利用可能）で、私の環境では修正されました: `0` が `46 users synced` に変わりました。適用した変更は以下の通りです:

diff

```plaintext
+ 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

```

`included` ループを完全に削除することが、すべてのケースにおいて正しい判断であるとは確信できません。一部の環境では user オブジェクトに email が返される可能性があるため、置き換えではなくフォールバックとして残すことを検討してもよいでしょう。参考になるか、あるいは適切な修正のヒントになるかと思い、観察された挙動と、ここで機能した最小限の変更を投稿します。
