fof / open-collective
Open Collective integration for your Flarum forum
Package info
github.com/FriendsOfFlarum/open-collective
Type:flarum-extension
pkg:composer/fof/open-collective
Fund package maintenance!
Requires
- flarum/core: ^2.0.0
- guzzlehttp/guzzle: ^7.1
Requires (Dev)
- flarum/audit: ^2.0
- flarum/gdpr: ^2.0
- flarum/phpstan: ^2.0.0
- flarum/testing: ^2.0.0
Suggests
- flarum/audit: Records every backer status and group change the sync makes in the audit log.
- flarum/gdpr: Includes members' backer status in their data export, and erases it with their account.
Provides
None
Conflicts
None
Replaces
None
README
A Flarum extension that automatically synchronizes your Open Collective backers with Flarum user groups.
Features
- π Automatic Synchronization: Hourly checks for new and removed backers
- π₯ Smart User Matching: Matches backers via email addresses
- π― Dual Group Support: Separate groups for recurring and one-time backers
- π Status-Based Categorization: Automatically moves past recurring backers to one-time group
- π Safe Group Management: Only removes memberships it owns and no other supporter source still wants; a supporter is never in both groups
- π©Ί Sync Status and Connection Test: See the last sync's result on the settings page and test a token before saving it
- π Audit Log: With Audit enabled, every status and group change the sync makes is recorded
- π§ͺ Dry-Run Mode: Preview changes before applying them
- π Verbose Output: Detailed information about the synchronization process
How It Works
This extension connects your Open Collective account with your Flarum forum by:
- Fetching Backers: Queries Open Collective's GraphQL API to retrieve your current backers (active recurring, cancelled recurring, and one-time contributors)
- Categorizing Backers: Separates backers into two groups:
- Recurring backers: Users with active monthly or yearly subscriptions
- One-time backers: Users who made one-time contributions or whose recurring subscription has ended
- Matching Users: Identifies Flarum users by matching email addresses from Open Collective backers with Flarum user emails
- Managing Groups: Automatically adds backers to designated Flarum groups and removes users who are no longer backers
- Handling Transitions: When a recurring backer's subscription ends, they are automatically moved to the one-time backers group
- Tracking Changes: Remembers each backer's status, fires an event when it changes, and records every change in the audit log when Audit is enabled
Installation
Install with composer:
composer require fof/open-collective:"*"
php flarum migrate
php flarum cache:clear
Required: Flarum Scheduler
This extension requires Flarum's scheduler to be set up and running. The extension will automatically check for backer updates every hour.
Add this cron job to your server:
* * * * * cd /path-to-your-project && php flarum schedule:run >> /dev/null 2>&1
Configuration
After installation, configure the extension in your Flarum admin panel:
1. Create an Open Collective Personal Token
- Go to Open Collective Applications
- Create a new Personal Token with the
ordersandemailscopes - Copy the token (you won't be able to see it again)
The token can only see what its owner can see, so create it from an account that is an admin of the collective. Without the orders scope the sync fails; without the email scope Open Collective hides every backer's email and nobody can be matched.
Legacy API Keys: If you have an existing legacy API key, you can continue using it by enabling "Use Legacy Application API Key" in the settings. However, it's recommended to migrate to a Personal Token.
2. Configure the Extension
In your Flarum admin panel, navigate to the Open Collective extension settings:
- Personal Token (or API Key if using legacy): Paste your Open Collective token
- Collective Slug: Enter your collective slug (e.g., if your collective is at
opencollective.com/your-collective, enteryour-collective) - Recurring Backers Group: Select which Flarum group to assign to recurring backers (required)
- One-Time Backers Group: Optionally select a group for one-time backers and past recurring backers (optional)
Note: If you only configure the recurring backers group, all backers (both recurring and one-time) will be added to that single group. If you configure both groups, the extension will automatically categorize backers based on their subscription status.
Save the settings, and the extension will start syncing on the next scheduled run.
Usage
Once configured, the extension runs automatically every hour. You can also manually trigger an update:
php flarum fof:open-collective:update
Command Options
Dry-Run Mode
Preview what changes would be made without actually applying them:
php flarum fof:open-collective:update --dry-run
This is useful for testing your configuration before letting the extension make real changes to your groups.
Force Mode
If Open Collective reports no backers at all while the extension still manages group members, the sync stops without changing anything, because removing everyone is far more likely to be a bad API response than a real change. If your collective really has no backers left, confirm it with:
php flarum fof:open-collective:update --force
Verbose Output
Get detailed information about the synchronization process:
php flarum fof:open-collective:update -v
Verbose mode shows:
- Configuration details (API type, collective slug, target groups)
- Total number of backers and breakdown by type (recurring vs one-time)
- All backers from Open Collective with their emails and type
- Matched Flarum users for each group
- Unmatched backers who couldn't be linked to Flarum accounts
- Summary of users staying, being added, removed, and moved between groups
Combine both options for detailed preview:
php flarum fof:open-collective:update --dry-run -v
Monitoring the Sync
- Status: the top of the extension's settings page shows when the sync last ran, and why it failed if it did. It also warns when the last run was more than two hours ago, which usually means Flarum's scheduler is not running.
- Test connection: the button on the same page checks the token and slug currently in the form, saved or not: that the token is accepted, that it can read orders, and that it can see backer emails. It also shows which Open Collective account the token belongs to.
- Changes: with the Audit extension enabled, every change the sync makes is recorded in the audit log: a backer starting or stopping a recurring backing, becoming a one-time backer, no longer being a backer, or being restored to a group they were missing from. Search the log with
action:fof_open_collective.*, oruser:<username>for one person. - Errors: failed syncs (a rejected token, a missing scope, an unreachable API) are written to Flarum's log in
storage/logs/. - Manually: run the command yourself, with
--dry-run -vto see what it would change without changing anything.
The first sync after installing or upgrading records every backer's current status without announcing them as new backers. Only group memberships it restores on that run are recorded.
Privacy (GDPR)
With the GDPR extension enabled, the extension's record of a member's backing (their status, when it last changed, and which groups the sync manages for them) is included in their data export, and erased when their account is anonymized or deleted. Erasing it means the sync simply forgets the member: it does not report their backing as ended, so no event fires and no audit entry is written for an erased account.
Backer emails are only read from Open Collective while a sync runs; they are never stored.
How User Matching Works
The extension matches Open Collective backers to Flarum users by comparing email addresses. For a backer to be matched:
- The backer must have a public email address on their Open Collective profile
- A Flarum user must exist with that same email address
- The Flarum user's email must be confirmed
How Group Membership Works
- The sync owns a membership it added, or one it found already in place (for example added by hand) when the user's backing justifies it. Taking over such a membership is reported in the command output and recorded in the audit log.
- It only removes memberships it owns, and only when no other supporter source still wants the user in that group. With GitHub Sponsors managing the same groups, someone who supports through both never loses a group because one of them ended.
- A supporter is never in both groups. The recurring group wins: a recurring backer is taken out of the one-time group, even if they were added to it by hand, and anyone already in the recurring group is not also put in the one-time group.
- Members nobody wants are left alone. Hand-added members who are not backers are never touched.
- Sources never fight. A membership is only added when some source wants it and only removed when none does, so the syncs settle on one result. Give every source the same recurring and one-time groups: if they are mapped the other way round, a supporter can end up in both groups, but nothing flips back and forth.
Important Notes
- Email Matching: Users must have the same email address as their Open Collective account.
- Backer Categorization:
- Users with active monthly or yearly subscriptions are placed in the recurring backers group
- Users who made one-time contributions or whose subscription has ended are placed in the one-time backers group
- When a recurring subscription ends, the user is automatically moved to the one-time group (if configured)
- Rate Limits: The Open Collective API has rate limits. The extension checks once per hour to stay well within these limits.
- Email Privacy: Some backers may have private email addresses on Open Collective. These backers cannot be matched to Flarum users automatically.
For Developers: Event System
The extension remembers each matched user's backer status (none, one-time or recurring) and fires an event whenever it changes. Events describe the backing, not the groups: they fire for every matched user, including members an admin added to a group by hand, and whether or not a one-time group is configured.
Status events
| Event | Fired when |
|---|---|
FoF\OpenCollective\Event\RecurringBackingStarted |
A user becomes a recurring backer, new or upgrading from a one-time backing |
FoF\OpenCollective\Event\RecurringBackingStopped |
A recurring backer's subscription is cancelled or paused; they remain a past, one-time backer |
FoF\OpenCollective\Event\OneTimeBackingStarted |
A user who was not a backer makes a one-time contribution |
FoF\OpenCollective\Event\BackingEnded |
A user is no longer a backer at all, for example after a refund |
All four extend AbstractBackerStatusChanged and implement BackerStatusChanged, so you can listen to the interface to react to every change. Each carries:
$user(Flarum\User\User)$previous,$current(FoF\OpenCollective\BackerStatus|null): the status before and after;nullmeans not a backer$account(object|null): the Open Collective account (id,email);nullwhen the backing ended$oldGroupIds,$newGroupIds(int[]): the user's groups before and after this sync
Group repairs
FoF\OpenCollective\Event\BackerGroupsChanged fires when the sync changes a user's groups without their status changing, such as re-adding a backer who was missing from their group. It carries $user, $oldGroupIds and $newGroupIds.
Example listener
use Flarum\Extend; use FoF\OpenCollective\Event\RecurringBackingStopped; return [ (new Extend\Event()) ->listen(RecurringBackingStopped::class, function (RecurringBackingStopped $event) { // e.g. thank $event->user for their support so far }), ];
Notes on Events
- Events fire after the database changes have been made, and never in
--dry-runmode. - The first sync after installing or upgrading records statuses without status events, so existing backers are not announced as new. Group repairs on that run still fire
BackerGroupsChanged. - Group memberships are only removed where the extension owns them (it added or adopted them) and no other supporter source still wants them. A hand-added member is adopted once their backing justifies the group.
Backer counts on the API
A supporters page can only list backers who match a forum account. To show the rest, the forum API resource (/api, app.forum.attribute(...)) carries four public counts, refreshed by each sync:
| Attribute | Counts |
|---|---|
openCollectivePrivateRecurring |
Incognito backers with an active recurring contribution |
openCollectivePrivateOnetime |
Incognito backers who contributed once or in the past |
openCollectiveUnmatchedRecurring |
Other recurring backers with no matching forum account |
openCollectiveUnmatchedOnetime |
Other one-time or past backers with no matching forum account |
Nothing about an incognito backer is stored.
Sharing groups with other supporter sources
Supporter sources that manage the same groups ask each other before removing anyone. Each registers an invokable under the fof.supporter-groups container tag that takes user IDs and a group ID and returns the subset of those users it wants in that group:
$container->tag([MySourceClaims::class], 'fof.supporter-groups'); class MySourceClaims { /** @param array<int> $userIds @return array<int> */ public function __invoke(array $userIds, int $groupId): array { // the users your source currently puts in $groupId } }
The contract is a plain string tag, so sources need no shared package. Answer with what your source would actually do (for example, do not claim a one-time supporter who is already in the recurring group), and never call other sources from it. This extension's own answer comes from FoF\OpenCollective\Services\BackerGroupClaims. Adopting a membership fires FoF\OpenCollective\Event\BackerMembershipAdopted ($user, $groupIds).
Upgrading from 1.x
BackerAdded and BackerRemoved were removed in 2.0. BackerAdded maps to RecurringBackingStarted or OneTimeBackingStarted; BackerRemoved maps to RecurringBackingStopped or BackingEnded.
Troubleshooting
No backers are being synced
- Verify your token is correct and has proper permissions
- Check that the collective slug matches your Open Collective account exactly
- Ensure the cron job is running (
schedule:run) - Check Flarum's log in
storage/logs/for errors, and the audit log for changes
Some backers aren't being matched
- Ensure backers have public email addresses on their Open Collective profiles
- Verify that corresponding Flarum users exist with the same email addresses
- Check that Flarum user emails are confirmed
Authentication errors
- "Open Collective rejected the token": the token has expired or been revoked. Create a new one.
- "The Open Collective token is missing a required scope": the token lacks the
ordersoremailscope. Edit the token's scopes on Open Collective, or create a new one. - "The Open Collective token cannot read backer emails": the token lacks the
emailscope. - If using a legacy API Key, consider migrating to a Personal Token
Updating
composer update fof/open-collective php flarum migrate php flarum cache:clear
Links
An extension by FriendsOfFlarum.