# Sync WooCommerce Memberships with Discourse groups

**URL:** https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485
**Category:** Extras
**Tags:** pavilion
**Created:** [May 23, 2019, 7:13am UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485 "2019-05-23T07:13:06Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![angus](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/angus/32/341715_2.png) [@angus](https://meta.discourse.org/u/angus)
#### Post date: [May 23, 2019, 7:13am UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/1 "2019-05-23T07:13:06Z")

</div>

This Plugin allows you to Sync WooCommerce Memberships with Discourse Groups.

### Pre-requisites

For this to work you have to have [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) enabled, with either Wordpress or Discourse as the [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) provider.

### Steps

1. Install this Wordpress plugin [GitHub - paviliondev/discourse-woocommerce · GitHub](https://github.com/paviliondev/discourse-woocommerce).

2. Use the Wordpress theme editor to update these numbers to the WooCommerce plan\_id and Discourse group id and group\_name you want to sync

#### Optional Step

1. Install [WP Crontrol – WordPress plugin | WordPress.org](https://wordpress.org/plugins/wp-crontrol/) and schedule `run_full_wc_membership_sync` to run every 24 hours. This will do a full sync to ensure consistency.

### Technical Notes

The only reliable way to capture both membership creation and status change in WooCommerce is using the `wc_memberships_user_membership_saved` admin hook. You can use this in combination with the `wc_memberships_user_membership_status_changed` hook, but it should not be necessary.

This code uses the WooCommerce logger to log info to `/wp-content/uploads/wc-logs/{log_file}`. If it is not working check the output there. You will see a logs that look like this, showing you the stages of the logic:

```plaintext
2019-05-23T07:01:57+00:00 INFO Running handle_wc_membership_saved 1, 92, 1
2019-05-23T07:01:57+00:00 INFO angus@email.com membership of VIP Membership changed to wcm-active
2019-05-23T07:01:57+00:00 INFO Sending PUT request to http://localhost:3000/groups/41/members with angus@email.com
2019-05-23T07:01:57+00:00 INFO Response from Discourse: 200 OK

```

### Usage Notes

> 👉 The Sync will not work if you click “Delete User Membership” in the Wordpress admin panel. There is no hook in WooCommerce that fires when that is clicked. Change the status of the membership instead.

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [May 23, 2019, 12:40pm UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/2 "2019-05-23T12:40:01Z")

</div>

Thanks, @angus. Funny that it seems we were both doing this at the same time.

You managed to figure out what was in those `$arg`s. I couldn’t find the `user_id` in it, I think.

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [May 24, 2019, 12:09am UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/3 "2019-05-24T00:09:36Z")

</div>

> [@angus](#):
>
> @Simon\_Cossar if you could quickly cast your eyes over the above it would be much appreciated!

It looks correct to me. Since the site is using WordPress as the SSO provider, you could use the static `add_user_to_discourse_group` and `remove_user_from_discourse_group` functions to handle changing the group status on Discourse. That would let you do something like:

```php
	if ( in_array( $status, ACTIVE_STATUSES ) ) {
		DiscourseUtilities::add_user_to_discourse_group( $user_id, 'your_discourse_group_name' );
	} else {
		DiscourseUtilities::remove_user_from_discourse_group( $user_id, 'your_discourse_group_name' );
	}

```

Both of those functions take the user’s WordPress ID as the first argument, and a comma separated list of group names (with no spaces between the names) as the second argument. Those functions will take care of all the API credentials for you, so using them would simplify the code a bit. There are more details about those functions here: [Manage group membership in Discourse with WP Discourse SSO](https://meta.discourse.org/t/managing-discourse-group-membership-with-wp-discourse/74724).

---

<div class="post-metadata">

### Author: ![angus](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/angus/32/341715_2.png) [@angus](https://meta.discourse.org/u/angus)
#### Post date: [May 24, 2019, 4:48am UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/4 "2019-05-24T04:48:51Z")

</div>

> [@simon](#):
>
> Since the site is using WordPress as the SSO provider

In this case Discourse is the SSO Provider 🙂

> [@angus](#):
>
> I was asked by a client to sync WooCommerce Memberships with Discourse Groups when Discourse is the SSO Provider.

---

<div class="post-metadata">

### Author: ![angus](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/angus/32/341715_2.png) [@angus](https://meta.discourse.org/u/angus)
#### Post date: [June 13, 2019, 12:42am UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/5 "2019-06-13T00:42:46Z")

</div>

Just an update here.

The updated version of this integration has the following structure:

1. Sync on every WooCommerce Membership status change

2. Batch sync every relevant WooCommerce Member every 24 hours to ensure consistency.

3. In terms of matching wordpress users with discourse users, the code first checks if the wordpress user has a discourse account associated with it. If so, it uses the discourse user id. If not it attempts the sync using the wordpress user’s email. The sync can fail if

We’re successfully running this in production on a client’s site. So far we’ve had only 2 users who have failed to sync, i.e. where both 3.1 and 3.2 were true.

### Implementation

The updated implementation is

1. Functions.php methods (update MEMBERSHIP\_PLAN\_ID and DISCOURSE\_GROUP\_ID)

> **Functions.php**
>
> use WPDiscourse\Utilities\Utilities as DiscourseUtilities;
> 
> const MEMBERSHIP\_PLAN\_ID = 61128;  
> const DISCOURSE\_GROUP\_ID = 62;  
> const ACTIVE\_STATUSES = array(‘wcm-active’);
> 
> function update\_discourse\_group\_access($user\_id, $membership\_plan\_id, $membership\_plan\_name, $status) {  
> $options = DiscourseUtilities::get\_options();  
> $base\_url = $options[‘url’];  
> $api\_key = $options[‘api-key’];  
> $api\_username = $options[‘publish-username’];
> 
> ```
> if ( empty( $base_url ) || empty( $api_key ) || empty( $api_username ) ) {
> return new \WP_Error( 'discourse_configuration_error', 'The WP Discourse plugin has not been properly configured.' );
> }
> 
> $discourse_user_id = get_user_meta($user_id, 'discourse_sso_user_id', true);
> $user_info = get_userdata($user_id);
> $user_email = $user_info->user_email;
> $logger = wc_get_logger();
> 
> $logger->info( sprintf('%s membership of %s is %s' , $user_email, $membership_plan_name, $status ) );
> 
> if (in_array($status, ACTIVE_STATUSES)) {
> $action = 'PUT';
> } else {
> $action = 'DELETE';
> }
> 
> $external_url = esc_url_raw( $base_url . "/groups/". DISCOURSE_GROUP_ID ."/members" );
> 
> ```
> 
> $args = array(  
> ‘api\_key’ =\> $api\_key,  
> ‘api\_username’ =\> $api\_username  
> );
> 
> if ($discourse\_user\_id) {  
> $args[‘user\_id’] = $discourse\_user\_id;  
> } else {  
> $args[‘user\_emails’] = $user\_email;  
> }
> 
> $logger-\>info( sprintf(‘Sending %s request to %s with %s’, $action, $external\_url, http\_build\_query($args)) );
> 
> ```
> $external_url = add_query_arg($args, $external_url);
> 
> $response = wp_remote_request($external_url,
> array(
> 'method' => $action
> )
> );
> 
> $logger->info( sprintf( 'Response from Discourse: %s %s' ,
> wp_remote_retrieve_response_code($response),
> wp_remote_retrieve_response_message($response) ) );
> 
> if ( ! DiscourseUtilities::validate( $response ) ) {
> 
> return new \WP_Error( 'discourse_response_error', 'There has been an error in retrieving the user data from Discourse.' );
> }
> 
> ```
> 
> };
> 
> function handle\_wc\_membership\_saved($membership\_plan, $args) {  
> $logger = wc\_get\_logger();
> 
> ```
> $logger->info( sprintf('Running handle_wc_membership_saved %s, %s, %s', $args['user_id'], $args['user_membership_id'], $args['is_update'] ) );
> 
> $user_id = $args['user_id'];
> $membership = wc_memberships_get_user_membership($args['user_membership_id']);
> $membership_plan_id = $membership->plan->id;
> 
> if ($membership && $membership_plan_id == MEMBERSHIP_PLAN_ID) {
> $membership_plan_name = $membership_plan->name;
> $status = $membership->status;
> update_discourse_group_access($user_id, $membership_plan_id, $membership_plan_name, $status);
> }
> 
> ```
> 
> };
> 
> add\_action(‘wc\_memberships\_user\_membership\_saved’, ‘handle\_wc\_membership\_saved’, 10, 2);
> 
> function full\_wc\_membership\_sync() {  
> $allusers = get\_users();  
> $logger = wc\_get\_logger();
> 
> ```
> $logger->info( sprintf('Running full_wc_membership_sync') );
> 
> foreach ( $allusers as $user ) {
> $user_id = $user->id;
> $membership = wc_memberships_get_user_membership($user_id, MEMBERSHIP_PLAN_ID);
> $membership_plan_id = $membership->plan->id;
> 
> $logger->info( sprintf('Checking membership of %s', $user->user_login) );
> 
> if ($membership && $membership_plan_id === MEMBERSHIP_PLAN_ID) {
> $membership_plan_name = $membership->plan->name;
> $status = $membership->status;
> $logger->info( sprintf('Updating group access of %s', $user->user_login) );
> 
> update_discourse_group_access($user_id, $membership_plan_id, $membership_plan_name, $status);
> 
> $logger->info( sprintf('Sleeping for 5 seconds') );
> sleep(5);
> }
> }
> 
> ```
> 
> }
> 
> add\_action(‘run\_full\_wc\_membership\_sync’, ‘full\_wc\_membership\_sync’);

1. Install [WP Crontrol – WordPress plugin | WordPress.org](https://wordpress.org/plugins/wp-crontrol/) and schedule “run\_full\_wc\_membership\_sync” to run every 24 hours.

You can review the logs of the sync in WooCommerce \> Status \> Logs.

@DNSTARS @pfaffman

---

<div class="post-metadata">

### Author: ![DNSTARS](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/dnstars/32/100957_2.png) [@DNSTARS](https://meta.discourse.org/u/DNSTARS)
#### Post date: [June 13, 2019, 10:25am UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/6 "2019-06-13T10:25:32Z")

</div>

What a baller, sending some magical internet money your way in thanks. 👍

---

<div class="post-metadata">

### Author: ![angus](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/angus/32/341715_2.png) [@angus](https://meta.discourse.org/u/angus)
#### Post date: [June 14, 2019, 4:56am UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/7 "2019-06-14T04:56:17Z")

</div>

> [@DNSTARS](#):
>
> sending some magical internet money your way in thanks

Much appreciated! Thank you @DNSTARS

---

<div class="post-metadata">

### Author: ![Ed\_Bobkov](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ed_bobkov/32/200014_2.png) [@Ed\_Bobkov](https://meta.discourse.org/u/Ed_Bobkov)
#### Post date: [May 11, 2020, 4:45pm UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/8 "2020-05-11T16:45:55Z")

</div>

What changes should I make in my case? I have a little bit different situation. Here are the differencies:

1. I have Wordpress as a SSO Client, users login via Discourse.
2. I installed a WP plugin ‘WooCommerce Groups’: [Groups WooCommerce Documentation - WooCommerce](https://docs.woocommerce.com/documentation/plugins/woocommerce/woocommerce-extensions/groups-woocommerce/) . It’s similar to WooCommerce Memberships but with less functions. I think all core function are the same.
3. I have multiple groups in Discourse (more than 3) and that’s why 3 groups of access in WP.

Can you provide me with corrections for this code or some advice? Thanks!  
May be, you can help? @pfaffman @simon

Ah, I didn’t write a question there…  
So I need to activate this integration between WP and Discourse.

What is working:

1. Wp is working as a SSO client.
2. Memberships are selling through WP Groups plugin and expiring =\> users are added to necessary groups.
3. Users are deleted from groups on expiration.

I need:

- when user is added to a group in WP I need to add this user to specific group in Discourse
- when is deleted from that group in WP - delete in Discourse  
Multiple groups in WP and Discourse

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [May 13, 2020, 5:01pm UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/9 "2020-05-13T17:01:29Z")

</div>

> [@angus](#):
>
> @Simon\_Cossar if you could quickly cast your eyes over the above it would be much appreciated!

I noticed that the `update_discourse_group_access` function is adding the API authentication credentials to the URL. This will not work on any up to date version of Discourse. Do you have an updated version of the code that is using header based authentication? If not, I can edit the code, but I will not be able to test my edits, so it’s a little risky.

---

<div class="post-metadata">

### Author: ![Ed\_Bobkov](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ed_bobkov/32/200014_2.png) [@Ed\_Bobkov](https://meta.discourse.org/u/Ed_Bobkov)
#### Post date: [May 13, 2020, 5:43pm UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/10 "2020-05-13T17:43:56Z")

</div>

I have a clean Discourse and Wordpress sites, can test your code without any risk.

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [May 14, 2020, 12:06am UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/11 "2020-05-14T00:06:55Z")

</div>

Here is Angus’s code, updated to use header based authentication. I have not made any other changes to it. I have also not tested the code. I won’t update the OP until someone gets a chance to test it.

```php
use WPDiscourse\Utilities\Utilities as DiscourseUtilities;

const MEMBERSHIP_PLAN_ID = 35;
const DISCOURSE_GROUP_ID = 41;
const ACTIVE_STATUSES = array( 'wcm-active' );

function update_discourse_group_access( $user_id, $membership_plan_id, $membership_plan_name, $status ) {
	$options = DiscourseUtilities::get_options();
	$base_url = $options['url'];
	$api_key = $options['api-key'];
	$api_username = $options['publish-username'];

	if ( empty( $base_url ) || empty( $api_key ) || empty( $api_username ) ) {
		return new \WP_Error( 'discourse_configuration_error', 'The WP Discourse plugin has not been properly configured.' );
	}

	$user_info = get_userdata( $user_id );
	$user_email = $user_info->user_email;
	$logger = wc_get_logger();

	$logger->info( sprintf( '%s membership of %s changed to %s', $user_email, $membership_plan_name, $status ) );

	if ( in_array( $status, ACTIVE_STATUSES ) ) {
		$action = 'PUT';
	} else {
		$action = 'DELETE';
	}

	$external_url = esc_url_raw( $base_url . "/groups/" . DISCOURSE_GROUP_ID . "/members" );

	$logger->info( sprintf( 'Sending %s request to %s with %s', $action, $external_url, $user_email ) );

	$response = wp_remote_request( $external_url,
		array(
			'method' => $action,
			'headers' => array(
				'Api-Key' => sanitize_key( $api_key ),
				'Api-Username' => sanitize_text_field( $api_username ),
			),
			'body' => array( 'user_emails' => $user_email ),
		)
	);

	$logger->info( sprintf( 'Response from Discourse: %s %s',
		wp_remote_retrieve_response_code( $response ),
		wp_remote_retrieve_response_message( $response ) ) );

	if ( ! DiscourseUtilities::validate( $response ) ) {

		return new \WP_Error( 'discourse_response_error', 'There has been an error in retrieving the user data from Discourse.' );
	}
}

function handle_wc_membership_saved( $membership_plan, $args ) {
	$logger = wc_get_logger();

	$logger->info( sprintf( 'Running handle_wc_membership_saved %s, %s, %s', $args['user_id'], $args['user_membership_id'], $args['is_update'] ) );

	$user_id = $args['user_id'];
	$membership = wc_memberships_get_user_membership( $args['user_membership_id'] );
	$membership_plan_id = $membership->plan->id;

	if ( $membership && $membership_plan_id == MEMBERSHIP_PLAN_ID ) {
		$membership_plan_name = $membership_plan->name;
		$status = $membership->status;
		update_discourse_group_access( $user_id, $membership_plan_id, $membership_plan_name, $status );
	}
}
add_action( 'wc_memberships_user_membership_saved', 'handle_wc_membership_saved', 10, 2 );

```

The code is setting a `$discourse_user_id` variable, but that variable is not being used anywhere. It could probably be removed from the code.

> [@Ed\_Bobkov](#):
>
> I installed a WP plugin ‘WooCommerce Groups’: [Groups WooCommerce Archives - WooCommerce Docs](https://docs.woocommerce.com/documentation/plugins/woocommerce/woocommerce-extensions/groups-woocommerce/) . It’s similar to WooCommerce Memberships but with less functions. I think all core function are the same.

I’m not so sure about that. The code hooks into the `wc_memberships_user_membership_saved` action hook. It seems likely that that hook is added by the WooCommerce Memberships plugin. There is probably a similar hook available in the WooCommerce Groups plugin, but it is unlikely to have the same name and parameters.

---

<div class="post-metadata">

### Author: ![Ed\_Bobkov](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ed_bobkov/32/200014_2.png) [@Ed\_Bobkov](https://meta.discourse.org/u/Ed_Bobkov)
#### Post date: [May 14, 2020, 8:29am UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/12 "2020-05-14T08:29:15Z")

</div>

> [@simon](#):
>
> I’m not so sure about that. The code hooks into the `wc_memberships_user_membership_saved` action hook. It seems likely that that hook is added by the WooCommerce Memberships plugin. There is probably a similar hook available in the WooCommerce Groups plugin, but it is unlikely to have the same name and parameters.

‘Groups for Woocommerce’ plugin uses ‘WP Group’ plugin to operate groups functions, here is it’s API: [http://docs.itthinx.com/document/groups/api/actions/](http://docs.itthinx.com/document/groups/api/actions/)

As for [Groups WooCommerce](https://docs.woocommerce.com/documentation/plugins/woocommerce/woocommerce-extensions/groups-woocommerce/)

> # Actions
> 
> The extension does not invoke any specific actions at current.

---

<div class="post-metadata">

### Author: ![Ed\_Bobkov](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ed_bobkov/32/200014_2.png) [@Ed\_Bobkov](https://meta.discourse.org/u/Ed_Bobkov)
#### Post date: [May 14, 2020, 1:26pm UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/13 "2020-05-14T13:26:45Z")

</div>

To simplify I switched to WooCommerce Memberships and use original code + changes made by @simon.

I changed IDs to my data:

```
const MEMBERSHIP_PLAN_ID = 347;
const DISCOURSE_GROUP_ID = 42;

```

At this point I can’t sinc but get some info in log:

```plaintext
> 2020-05-14T13:12:34+00:00 INFO test@gmail.com membership of PAID1 changed to wcm-active
> 2020-05-14T13:12:34+00:00 INFO Sending PUT request to https://site/groups/42/members with test@gmail.com
> 2020-05-14T13:12:34+00:00 INFO Response from Discourse: 400 Bad Request

```

ID for the Discourse group I found here  
site/groups/PAID1.json

```plaintext
> {"group":{"id":42,"automatic":false,"name":"PAID1","user_count":0,"mentionable_level":0,"messageable_level":0,"visibility_level":0,"automatic_membership_email_domains":"","primary_group":false,"title":"","grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":"","flair_bg_color":"","flair_color":"","bio_raw":"","bio_cooked":null,"bio_excerpt":null,"public_admission":false,"public_exit":false,"allow_membership_requests":false,"full_name":"France-PAID1","default_notification_level":3,"membership_request_template":"","is_group_user":false,"is_group_owner":true,"members_visibility_level":0,"can_see_members":true,"publish_read_state":false,"is_group_owner_display":false,"mentionable":true,"messageable":false},"extras":{"visible_group_names":["admins","moderators","PAID1","PAID2","PAID3","staff","trust_level_0","trust_level_1","trust_level_2","trust_level_3","trust_level_4"]}}
```

---

<div class="post-metadata">

### Author: ![Ed\_Bobkov](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ed_bobkov/32/200014_2.png) [@Ed\_Bobkov](https://meta.discourse.org/u/Ed_Bobkov)
#### Post date: [May 14, 2020, 1:36pm UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/14 "2020-05-14T13:36:54Z")

</div>

So 2 questions are open:

1. How to sync without error?
2. What if I have more than 3 membership types in WP and groups in Discourse? this code is for 1 type only.

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [May 14, 2020, 4:09pm UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/15 "2020-05-14T16:09:51Z")

</div>

> [@Ed\_Bobkov](#):
>
> To simplify I switched to WooCommerce Memberships and use original code + changes made by @Simon\_Cossar.

> [@Ed\_Bobkov](#):
>
> 2020-05-14T13:12:34+00:00 INFO Response from Discourse: 400 Bad Request

That looks like it comes from a bug in my update to the code. I’ll take a look at it later today.

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [May 14, 2020, 9:45pm UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/16 "2020-05-14T21:45:21Z")

</div>

I have updated the code example here: [Sync WooCommerce Memberships with Discourse groups - #11 by simon](https://meta.discourse.org/t/syncing-woocommerce-memberships-with-discourse-groups-when-discourse-is-the-sso-provider/118485/11). There was an encoding error in the code I posted yesterday. I’ve tested that the API call in the code is working correctly, but have not tested the code with the Woocommerce Memberships plugin.

> [@Ed\_Bobkov](#):
>
> What if I have more than 3 membership types in WP and groups in Discourse? this code is for 1 type only

It should be possible to do that. It is possible that someone can help you here, but you might have better luck by hiring someone to do the work for you. You could try creating a topic in our #Marketplace category to find a developer to do the work.

---

<div class="post-metadata">

### Author: ![Ed\_Bobkov](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ed_bobkov/32/200014_2.png) [@Ed\_Bobkov](https://meta.discourse.org/u/Ed_Bobkov)
#### Post date: [May 15, 2020, 3:49am UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/17 "2020-05-15T03:49:22Z")

</div>

@simon, got this working! Thank you, team! 🖖

---

<div class="post-metadata">

### Author: ![Ed\_Bobkov](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ed_bobkov/32/200014_2.png) [@Ed\_Bobkov](https://meta.discourse.org/u/Ed_Bobkov)
#### Post date: [May 15, 2020, 5:21am UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/18 "2020-05-15T05:21:42Z")

</div>

> [@angus](#):
>
> Install [WP Crontrol – WordPress plugin | WordPress.org](https://wordpress.org/plugins/wp-crontrol/) and schedule “run\_full\_wc\_membership\_sync” to run every 24 hours.

@simon, in your code I didn’t find a part used for batch syncing that @angus used.

```
/*run_full_wc_membership_sync*/
function full_wc_membership_sync() {
$allusers = get_users();
$logger = wc_get_logger();

$logger->info( sprintf('Running full_wc_membership_sync') );

foreach ( $allusers as $user ) {
	$user_id = $user->id;
	$membership = wc_memberships_get_user_membership($user_id, MEMBERSHIP_PLAN_ID);
	$membership_plan_id = $membership->plan->id;

	$logger->info( sprintf('Checking membership of %s', $user->user_login) );

	if ($membership && $membership_plan_id === MEMBERSHIP_PLAN_ID) {
		$membership_plan_name = $membership->plan->name;
		$status = $membership->status;
		$logger->info( sprintf('Updating group access of %s', $user->user_login) );

		update_discourse_group_access($user_id, $membership_plan_id, $membership_plan_name, $status);

		$logger->info( sprintf('Sleeping for 5 seconds') );
		sleep(5);
	}
}

}

add_action('run_full_wc_membership_sync', 'full_wc_membership_sync');  

```

Should I add this code to the end of your code if I want to activate an everyday syncing?

When I try to add this cron event to WP in Wordpress Crontrol plugin with a hook name:

> ```
> run_full_wc_membership_sync
> 
> ```

 ![image](https://global.discourse-cdn.com/meta/original/3X/7/3/7382a2e7e1ab68f4eeae447b3a5c1fd4c3027dc5.png)

I got an error:

 ![image](https://global.discourse-cdn.com/meta/original/3X/7/f/7f6e0771493bde80cd7f56bfd114152a97a54134.png)

What may cause it?

---

<div class="post-metadata">

### Author: ![Ed\_Bobkov](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ed_bobkov/32/200014_2.png) [@Ed\_Bobkov](https://meta.discourse.org/u/Ed_Bobkov)
#### Post date: [May 15, 2020, 8:41am UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/19 "2020-05-15T08:41:35Z")

</div>

> [@Ed\_Bobkov](#):
>
> What may cause it?

Solved. May help smbd. I needed to choose ‘At’ in the ‘Next Run’ field and put there a date like 2020-05-10 10:00:00.

Tested syncing - now it is working.

Here is a working solution if you have multiple WooCommerce membership plans and groups in Discourse. You need to change your membership IDs and Discourse Group IDs to yours.

1. WooCommerce membership plans IDs can be found from the url while editing the plan in Wordpress: [https://site/wp-admin/post.php?post=\*\*441\*\*&action=edit](https://site/wp-admin/post.php?post= **441** &action=edit)
2. Discourse Groups IDs can be found here: [https://site/groups/group\_name.json](https://site/groups/group_name.json)

Working code to include in functions.php:

> **Summary**
>
> //wp+discourse  
> use WPDiscourse\Utilities\Utilities as DiscourseUtilities;
> 
> const MEMBERSHIP\_PLAN\_DISCOURSE\_GROUP = [  
> “347” =\> “42”,  
> “357” =\> “43”,  
> “441” =\> “44”  
> ];
> 
> //const ACTIVE\_STATUSES = array( ‘wcm-active’ );  
> const ACTIVE\_STATUSES = array( ‘wcm-active’, ‘wcm-free\_trial’ );
> 
> function update\_discourse\_group\_access( $user\_id, $membership\_plan\_id, $membership\_plan\_name, $status ) {  
> $options = DiscourseUtilities::get\_options();  
> $base\_url = $options[‘url’];  
> $api\_key = $options[‘api-key’];  
> $api\_username = $options[‘publish-username’];
> 
> ```
> if ( empty( $base_url ) || empty( $api_key ) || empty( $api_username ) ) {
> return new \WP_Error( 'discourse_configuration_error', 'The WP Discourse plugin has not been properly configured.' );
> }
> 
> $user_info = get_userdata( $user_id );
> $user_email = $user_info->user_email;
> $logger = wc_get_logger();
> 
> $logger->info( sprintf( '%s membership of %s changed to %s', $user_email, $membership_plan_name, $status ) );
> 
> if ( in_array( $status, ACTIVE_STATUSES ) ) {
> $action = 'PUT';
> } else {
> $action = 'DELETE';
> }
> 
> $external_url = esc_url_raw( $base_url . "/groups/" . MEMBERSHIP_PLAN_DISCOURSE_GROUP[$membership_plan_id] . "/members" );
> 
> $logger->info( sprintf( 'Sending %s request to %s with %s', $action, $external_url, $user_email ) );
> 
> $response = wp_remote_request( $external_url,
> array(
> 'method' => $action,
> 'headers' => array(
> 'Api-Key' => sanitize_key( $api_key ),
> 'Api-Username' => sanitize_text_field( $api_username ),
> ),
> 'body' => array( 'user_emails' => $user_email ),
> )
> );
> 
> $logger->info( sprintf( 'Response from Discourse: %s %s',
> wp_remote_retrieve_response_code( $response ),
> wp_remote_retrieve_response_message( $response ) ) );
> 
> if ( ! DiscourseUtilities::validate( $response ) ) {
> 
> return new \WP_Error( 'discourse_response_error', 'There has been an error in retrieving the user data from Discourse.' );
> }
> 
> ```
> 
> }
> 
> function handle\_wc\_membership\_saved( $membership\_plan, $args ) {  
> $logger = wc\_get\_logger();
> 
> ```
> $logger->info( sprintf( 'Running handle_wc_membership_saved %s, %s, %s', $args['user_id'], $args['user_membership_id'], $args['is_update'] ) );
> 
> $user_id = $args['user_id'];
> 
> $membership = wc_memberships_get_user_membership( $args['user_membership_id'] );
> 
> $membership_plan_id = $membership->plan->id;
> 
> if ( $membership && isset(MEMBERSHIP_PLAN_DISCOURSE_GROUP[$membership_plan_id])) {
> $membership_plan_name = $membership_plan->name;
> $status = $membership->status;
> update_discourse_group_access( $user_id, $membership_plan_id, $membership_plan_name, $status );
> }
> 
> ```
> 
> }  
> add\_action( ‘wc\_memberships\_user\_membership\_saved’, ‘handle\_wc\_membership\_saved’, 10, 2 );
> 
> /_run\_full\_wc\_membership\_sync_/  
> function full\_wc\_membership\_sync() {  
> $allusers = get\_users();  
> $logger = wc\_get\_logger();
> 
> ```
> $logger->info( sprintf('Running full_wc_membership_sync') );
> 
> foreach ( $allusers as $user ) {
> 
> $user_id = $user->id;
> 
> $membership = wc_memberships_get_user_membership($user_id);
> 
> $membership_plan_id = $membership->plan->id;
> 
> $logger->info( sprintf('Checking membership of %s', $user->user_login) );
> 
> if ($membership && isset(MEMBERSHIP_PLAN_DISCOURSE_GROUP[$membership_plan_id])) {
>        
> $membership_plan_name = $membership->plan->name;
>       
> $status = $membership->status;
>       
> $logger->info( sprintf('Updating group access of %s', $user->user_login) );
> 
> update_discourse_group_access($user_id, $membership_plan_id, $membership_plan_name, $status);
> 
> $logger->info( sprintf('Sleeping for 5 seconds') );
>       
> sleep(5);
> }
> }
> 
> ```
> 
> }
> 
> add\_action(‘run\_full\_wc\_membership\_sync’, ‘full\_wc\_membership\_sync’);

You need to change in the first column - Membership plans ids, 2nd column - Discourse groups ids.

> **Summary**
>
> ```
> "347" => "42",
> "357" => "43",
> "441" => "44"
> 
> ```

---

<div class="post-metadata">

### Author: ![jord8on](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jord8on/32/166809_2.png) [@jord8on](https://meta.discourse.org/u/jord8on)
#### Post date: [June 2, 2020, 10:07pm UTC](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485/20 "2020-06-02T22:07:53Z")

</div>

> [@Ed\_Bobkov](#):
>
> What is working:
> 
> 1.Wp is working as a SSO client.  
> ~~2.Memberships are selling through WP Groups plugin and expiring =\> users are added to necessary groups.~~  
> ~~3.Users are deleted from groups on expiration.~~
> 
> I need:
> 
> - when user is added to a group in WP I need to add this user to specific group in Discourse
> - when is deleted from that group in WP - delete in Discourse
> - Multiple groups in WP and Discourse

Hey @Ed_Bobkov, I was hoping you might be able to help me on this. Here’s a few statements about where I am right now…

1. I have Wordpress as a SSO Client, users login via Discourse or Wordpress.
2. I currently don’t have a “memberships” plugin that I’m using on Wordpress
3. I will have dozens of groups in Discourse for each different type of member
4. I was planning on manually creating a new membership group in WP, and a corresponding group in Discourse, whenever we have a new membership group available to our users.
5. As you stated, “I need to activate this integration between WP and Discourse.”
6. I’m guessing I need to get “Woocommerce Memberships” plugin?

What are my next steps?

[Next page](https://meta.discourse.org/t/sync-woocommerce-memberships-with-discourse-groups/118485.md?page=2)
