Skip to content

Repository files navigation

Common Knowledge Join Flow

Overview

This is a monorepo containing packages that together provide a full membership and donation management system, delivered as a WordPress plugin.

  • packages/join-flow — A React application implementing the join form frontend.
  • packages/join-block — A WordPress Gutenberg plugin containing the join form block(s) and the backend logic that processes memberships, payments, and CRM integrations.
  • packages/join-e2e — End-to-end tests (Puppeteer).

Current version: 1.4.9


How does the Join Flow work?

  1. A visitor lands on a WordPress page with the Join Form block. They enter their email address and are directed to the join form itself.
  2. The Join Form Fullscreen Takeover block hosts the React application. It takes over the full page and guides the visitor through a multi-step form: personal details, membership plan selection, optional donation, and payment.
  3. On submission the React app POSTs to a WordPress REST API endpoint provided by the plugin. The backend orchestrates all downstream services: it creates the payment, sets up the subscription or one-off charge, and syncs the new member to any configured integrations (Auth0, Zetkin, Action Network, Mailchimp).
  4. On success the visitor is redirected to a configurable success page.

WordPress Blocks

BlockPurpose
Join FormLightweight entry point — captures an email address and links to the full form page.
Join Form Fullscreen TakeoverHosts the full multi-step React join flow. Add this to one page to enable membership sign-up.

All copy within both blocks is fully configurable — nothing is hardcoded.


Form Flows

The join flow supports three distinct modes, controlled by environment variables:

Standard join flow

The default. Steps:

  1. Details — name, email, phone, address (with optional postcode lookup), date of birth, nationality, county, "how did you hear about us?", contact consent, and any configured custom fields.
  2. Membership plan — choose a tier from the configured plans.
  3. Donation (optional) — offer an additional one-off or recurring donation on top of membership.
  4. Payment — select a payment method and complete payment.
  5. Confirmation — review and submit.

Supporter / donation-focused mode (REACT_APP_DONATION_SUPPORTER_MODE)

Reorders and simplifies the flow to: Donation → Details → Payment. The membership amount equals the donation amount; no separate plan selection step is shown.

Update flow mode (REACT_APP_IS_UPDATE_FLOW)

For existing members updating their membership or payment details. The email is pre-filled via a URL parameter and the details step is skipped.


Payment Providers

The plugin supports three payment providers. They can be enabled independently and combined (e.g. Stripe for cards and GoCardless for Direct Debit).

Stripe

  • Recurring subscriptions (card and Direct Debit)
  • One-off donations via PaymentIntent (£0.01–£10,000)
  • Customer upsert (creates or looks up existing customers)
  • Subscription metadata (plan, organisation)
  • Test and Live mode
  • Environment variable: REACT_APP_USE_STRIPE=true

GoCardless

  • Recurring Direct Debit (UK/EU bank payments)
  • Supports both Hosted Pages mode and API mode (requires GoCardless Pro account — REACT_APP_USE_GOCARDLESS_API=true)
  • Automatic customer and mandate creation
  • Sandbox and Live environments
  • Environment variable: REACT_APP_USE_GOCARDLESS=true

Chargebee

  • Subscription and billing management (multi-currency)
  • Hosted Pages mode for PCI compliance
  • API mode for direct customer/subscription creation
  • Environment variable: REACT_APP_USE_CHARGEBEE=true

Integrations

All integrations are optional. Enable and configure them via environment variables or the WordPress settings page.

IntegrationPurpose
Auth0Creates user accounts with email, name, and role assignment on successful join. Requires an M2M application — see Auth0 Setup.
ZetkinAdds members to campaigns, applies plan-specific tags, syncs custom fields (DOB, "hear about us", contact preferences).
Action NetworkAdds or updates people in Action Network; applies and removes tags; syncs custom fields.
MailchimpAdds subscribers to mailing lists with plan-specific tags. Manages "lapsed" tag on payment failure/recovery. Supporter-mode forms: the flow does not collect a postal address, so the Mailchimp audience must have the ADDRESS merge field marked optional (not required); otherwise Mailchimp will reject new-member requests with a 400.
SentryReal-time error tracking for both frontend and backend.
Google Cloud LoggingCentralised log aggregation.
LogglyCentralised log aggregation. When a customer token is set, logs are sent to Loggly and are not written to disk.
Microsoft TeamsError alert notifications via incoming webhook.
getAddress.io / ideal-postcodesUK postcode address lookup.

Membership Lapsing

The plugin handles member lapsing automatically in response to payment provider webhooks.

  • A Stripe subscription entering unpaid or incomplete_expired triggers a lapse.
  • A Stripe subscription returning to active triggers an unlapse.
  • Lapsed/unlapsed status is reflected in all configured integrations (e.g. "lapsed" tag in Action Network, Mailchimp, Zetkin).

Developer Hooks

The plugin exposes filters and actions for customisation without modifying plugin code.

Filters

ck_join_flow_should_lapse_member

Controls whether a member should be lapsed when a lapsing event is detected.

ArgumentTypeDescription
$defaultbooltrue by default
$emailstringThe member's email address
$contextarrayprovider (e.g. stripe), trigger (e.g. invoice_payment_failed), event (raw payload)
add_filter('ck_join_flow_should_lapse_member', function ($default, $email, $context) {
if (($context['provider'] ?? '') === 'gocardless') {
returnfalse;
}
return$default;
}, 10, 3);

ck_join_flow_should_unlapse_member

Controls whether a member should be unlapsed when a reactivation event is detected.

ArgumentTypeDescription
$defaultbooltrue by default
$emailstringThe member's email address
$contextarraySame shape as above

ck_join_flow_add_tags

Customise tags applied across all integrations.

ArgumentTypeDescription
$tagsarrayTags to apply
$dataarrayForm submission data
$servicestringService name (e.g. action_network, mailchimp)

ck_join_flow_action_network_add_tags / ck_join_flow_action_network_remove_tags

Action Network-specific tag overrides.

Actions

ck_join_flow_success

Fired after a successful join.

ArgumentTypeDescription
$dataarraySubmitted form data
$customermixedCustomer object from the payment provider

ck_join_flow_error

Fired when the join process encounters an error.

ArgumentTypeDescription
$dataarraySubmitted form data
$exception\ThrowableThe exception that was thrown

ck_join_flow_member_lapsed

Fired after a member has been successfully marked as lapsed.

ArgumentTypeDescription
$emailstringThe member's email address
$contextarrayTrigger context (see above)
add_action('ck_join_flow_member_lapsed', function ($email, $context) {
// e.g. send an internal notification or update a CRM
}, 10, 2);

ck_join_flow_member_unlapsed

Fired after a member has been successfully unmarked as lapsed.

ArgumentTypeDescription
$emailstringThe member's email address
$contextarrayTrigger context (see above)

Auth0 Setup

Create an Auth0 machine-to-machine application and authorise it for the Auth0 Management API.

  1. Go to Applications > APIs > Auth0 Management API > Machine to Machine Applications.
  2. Authorise your application.
  3. Expand the authorisation and add the following scopes: read:users, update:users, create:users, delete:users.

Configuration Reference

Configuration is available through a WordPress settings page (Settings > CK Join Flow) or via environment variables. The .env.example files in each package list all available variables.

Key frontend environment variables

VariableDescription
REACT_APP_ORGANISATION_NAMEDisplayed throughout the form
REACT_APP_ORGANISATION_BANK_NAMEShown on Direct Debit pages
REACT_APP_ORGANISATION_EMAIL_ADDRESSContact email shown in the form
REACT_APP_MEMBERSHIP_PLANSJSON array of membership plan objects
REACT_APP_USE_STRIPEEnable Stripe
REACT_APP_STRIPE_PUBLISHABLE_KEYStripe public key
REACT_APP_STRIPE_DIRECT_DEBIT_ONLYRestrict Stripe to Direct Debit only
REACT_APP_USE_GOCARDLESSEnable GoCardless
REACT_APP_USE_GOCARDLESS_APIUse GoCardless API mode (requires Pro account)
REACT_APP_USE_CHARGEBEEEnable Chargebee
REACT_APP_CHARGEBEE_SITE_NAMEChargebee site name
REACT_APP_CHARGEBEE_API_PUBLISHABLE_KEYChargebee public key
REACT_APP_USE_MAILCHIMPEnable Mailchimp integration
REACT_APP_CREATE_AUTH0_ACCOUNTEnable Auth0 user creation
REACT_APP_COLLECT_DATE_OF_BIRTHCollect date of birth
REACT_APP_COLLECT_PHONE_AND_EMAIL_CONTACT_CONSENTCollect contact consent
REACT_APP_ASK_FOR_ADDITIONAL_DONATIONShow donation page
REACT_APP_DONATION_SUPPORTER_MODEEnable supporter/donation-focused flow
REACT_APP_IS_UPDATE_FLOWEnable existing-member update flow
REACT_APP_USE_POSTCODE_LOOKUPEnable UK postcode address lookup
REACT_APP_INCLUDE_SKIP_PAYMENT_BUTTONAllow skipping payment
REACT_APP_HIDE_ZERO_PRICE_DISPLAYHide £0 price labels
REACT_APP_USE_TEST_DATAPre-fill form with test data
REACT_APP_SENTRY_DSNSentry DSN for frontend error tracking

Membership plans format

[
{
"value": "standard",
"label": "Standard",
"amount": "10",
"frequency": "monthly",
"currency": "GBP",
"allowCustomAmount": false,
"add_tags": ["member"],
"remove_tags": []
}
]

Key backend environment variables

VariableDescription
STRIPE_SECRET_KEYStripe secret key
GC_ACCESS_TOKENGoCardless API token
GC_ENVIRONMENTsandbox or live
CHARGEBEE_SITE_NAMEChargebee site name
CHARGEBEE_API_KEYChargebee API key
AUTH0_DOMAINAuth0 tenant domain
AUTH0_CLIENT_IDAuth0 M2M app client ID
AUTH0_CLIENT_SECRETAuth0 M2M app client secret
AUTH0_MANAGEMENT_AUDIENCEAuth0 Management API identifier
MICROSOFT_TEAMS_INCOMING_WEBHOOKTeams webhook URL for error alerts
SENTRY_DSNSentry DSN for backend error tracking
GOOGLE_CLOUD_PROJECT_IDGoogle Cloud project ID
GOOGLE_CLOUD_KEY_FILE_CONTENTSGoogle Cloud service account credentials (JSON)
LOGGLY_TOKENLoggly customer token. When set, logs go to Loggly instead of disk
LOGGLY_TAGSComma-separated Loggly tags (default join-block)
DEBUG_JOIN_FLOWSet to true to load frontend from localhost:3000

Tech Stack

Frontend

  • React 16, React Bootstrap
  • React Hook Form, Yup (form state and validation)
  • Stripe.js / @stripe/react-stripe-js
  • Chargebee.js
  • Webpack 5, Babel 7, SASS
  • Jest, Testing Library

Backend

  • PHP 8+, WordPress
  • Carbon Fields (admin UI)
  • Stripe PHP SDK, GoCardless PHP SDK, Chargebee PHP SDK
  • Auth0 PHP SDK
  • Mailchimp Marketing SDK
  • GuzzleHTTP, Monolog, Google Cloud Logging
  • PHPUnit, Brain Monkey, Mockery

Infrastructure

  • Lerna (monorepo)
  • Docker Compose (local WordPress environment)
  • GitHub Actions (CI/CD, WordPress.org releases)

Build and Deployment

Build

yarn
yarn composer
yarn build

This produces a deployable WordPress plugin in packages/join-block.

Deploying to WordPress.org (automated)

Releases are automated via GitHub Actions (.github/workflows/release-plugin.yml).

  1. Bump the version:
./scripts/bump-version.sh # auto-increments patch
./scripts/bump-version.sh 1.4.0 # or specify a version
  1. Commit and tag:
git add -A
git commit -m "Bump version to 1.4.0"
git tag 1.4.0
git push && git push --tags

The GitHub Action will build, package, and deploy to the WordPress.org plugin repository automatically. Monitor progress in the Actions tab.

Manual deployment

yarn && yarn composer && yarn build
sh scripts/package.sh

Upload the resulting zip to a WordPress site and activate the plugin.

Note: The packaging script removes vendor/giggsey/libphonenumber-for-php/src/geocoding to stay under the 10 MB WordPress.org plugin size limit.


Developer Quickstart

Full stack (WordPress + React)

Prerequisites: Node.js >= 18, Yarn, Composer, Docker

# Install dependencies
yarn
yarn composer
# Configure the frontendcd packages/join-flow
cp .env.example .env
# Edit .env — add API keys and enable desired features# Start the Docker stackcd ../..
yarn start
  • WordPress admin: http://localhost:8082/wp-admin
  • React dev server (live reload): http://localhost:3000

WordPress setup:

  1. Go to http://localhost:8082/wp-admin/plugins.php and activate the Join plugin.
  2. Add the Join Form Fullscreen Takeover block to a page — this is where the join flow lives.
  3. Optionally add the Join Form block elsewhere to pre-fill the email and link through.
  4. Configure credentials and feature flags at Settings > CK Join Flow.

Frontend only (no backend)

yarn
cd packages/join-flow
cp .env.example .env
yarn run frontend
# Open http://localhost:3000

Use REACT_APP_USE_TEST_DATA=true in .env to pre-fill the form with example data.


Testing

Frontend unit tests

cd packages/join-flow
yarn test

Backend unit tests

cd packages/join-block
composer test

End-to-end tests

cd packages/join-e2e
yarn test

About

A WordPress plugin for joining organisations

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages