WP Manifestindependent plugin directory
manifest / users / fx-memberships

FX Memberships

Simple membership plugin with Stripe and PayPal integration using hosted checkout portals.

by Ciprian Popescu · github.com/wolffe/fx-memberships · website

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/wolffe/fx-memberships/archive/refs/heads/master.zip

Readme

FX Memberships Plugin

Overview

Name: FX Memberships
Version: 1.0.1
Minimum ClassicPress: 2.5.0
Minimum PHP: 8.5
Architecture: Functional programming (no classes), PHP-first approach

Technical Requirements

  • Functional programming only (no classes)
  • Vanilla JavaScript only (no React, TypeScript, build processes)
  • No Composer - Stripe PHP library included manually
  • Full <?php tags (no short tags)
  • Curly braces for control structures (no endif, endforeach)
  • Strict comparison (=== and !==)
  • DRY principles
  • ClassicPress 2.5+ compatible (uses ClassicPress-compatible core APIs)

Architecture

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│   Your Site     │────▶│  Stripe/PayPal   │────▶│  Their Portal   │
│  (verify only)  │◀────│    (webhooks)    │     │ (manage billing)│
└─────────────────┘     └──────────────────┘     └─────────────────┘

Key Principle: Offload billing management to Stripe/PayPal hosted portals. We only:

  • Generate checkout links
  • Receive webhooks for status updates
  • Store subscription status in user meta
  • Restrict content based on status

File Structure

fx-memberships/
├── fx-memberships.php              # Main plugin file
├── uninstall.php                   # Clean uninstall handler
├── readme.txt                      # Plugin readme
│
├── includes/
│   ├── core.php                    # Core functions, activation/deactivation
│   ├── user-meta.php               # User subscription meta functions
│   ├── content-restriction.php     # Content restriction logic & shortcodes
│   ├── helpers.php                 # Utility/helper functions
│   │
│   ├── stripe/
│   │   ├── stripe-checkout.php     # Stripe Checkout link generation
│   │   ├── stripe-webhooks.php     # Stripe webhook handler
│   │   ├── stripe-api.php          # Stripe API calls (status checks)
│   │   └── stripe-portal.php       # Customer portal redirect
│   │
│   └── paypal/
│       ├── paypal-checkout.php     # PayPal Subscribe button generation
│       ├── paypal-webhooks.php     # PayPal webhook handler
│       ├── paypal-api.php          # PayPal API calls (status checks)
│       └── paypal-portal.php       # PayPal billing portal redirect
│
├── admin/
│   ├── admin-menu.php              # Admin menu registration
│   ├── settings-page.php           # Plugin settings page
│   ├── subscribers-page.php        # Subscriber management page
│   ├── membership-levels.php       # Membership level configuration
│   └── admin-ajax.php              # Admin AJAX handlers
│
├── assets/
│   ├── css/
│   │   ├── admin.css               # Admin styles
│   │   └── frontend.css            # Frontend styles
│   └── js/
│       ├── admin.js                # Admin scripts (vanilla JS)
│       └── frontend.js             # Frontend scripts (vanilla JS)
│
└── vendor/
    └── stripe-php/                 # Stripe PHP library (manual include)

Database Schema

User Meta Keys

Meta Key Description Example Value
_fxm_subscription_id Stripe/PayPal subscription ID sub_1234567890
_fxm_subscription_status Current status active, canceled, past_due, paused
_fxm_subscription_provider Payment provider stripe or paypal
_fxm_customer_id Provider customer ID cus_1234567890
_fxm_membership_level Current membership level slug premium, basic
_fxm_subscription_start Subscription start timestamp 1702147200
_fxm_subscription_end Current period end timestamp 1704825600
_fxm_subscription_updated Last update timestamp 1702147200

Options (wp_options)

Option Key Description
fxm_settings Main plugin settings (serialized array)
fxm_membership_levels Defined membership levels (serialized array)
fxm_version Installed plugin version

Settings Structure

$fxm_settings = [
    // General.
    'default_restricted_message' => 'This content is for members only.',
    'login_redirect_url'         => '',
    'after_login_redirect'       => 'previous_page',

    // Stripe.
    'stripe_enabled'         => false,
    'stripe_mode'            => 'test',
    'stripe_test_secret_key' => '',
    'stripe_live_secret_key' => '',
    'stripe_webhook_secret'  => '',

    // PayPal.
    'paypal_enabled'           => false,
    'paypal_mode'              => 'sandbox',
    'paypal_sandbox_client_id' => '',
    'paypal_sandbox_secret'    => '',
    'paypal_live_client_id'    => '',
    'paypal_live_secret'       => '',
    'paypal_webhook_id'        => '',
];

Membership Levels Structure

$membership_levels = [
    'basic'   => [
        'name'            => 'Basic',
        'description'     => 'Access to basic content',
        'stripe_price_id' => 'price_xxxxx',
        'paypal_plan_id'  => 'P-xxxxx',
        'capabilities'    => [ 'basic_content' ],
        'order'           => 1,
    ],
    'premium' => [
        'name'            => 'Premium',
        'description'     => 'Access to all content',
        'stripe_price_id' => 'price_yyyyy',
        'paypal_plan_id'  => 'P-yyyyy',
        'capabilities'    => [ 'basic_content', 'premium_content', 'downloads' ],
        'order'           => 2,
    ],
];

Core Functions API

User Subscription Functions (includes/user-meta.php)

function fxm_user_has_active_subscription( $user_id = null );
function fxm_get_subscription_status( $user_id = null );
function fxm_get_user_membership_level( $user_id = null );
function fxm_user_has_capability( $capability, $user_id = null );
function fxm_update_user_subscription( $user_id, $data );
function fxm_get_user_by_subscription_id( $subscription_id );
function fxm_get_user_by_customer_id( $customer_id, $provider );

Content Restriction (includes/content-restriction.php)

function fxm_can_access_content( $required_level = null, $user_id = null );
function fxm_get_restricted_message( $level = null );

Stripe Functions (includes/stripe/)

function fxm_stripe_get_checkout_url( $price_id, $user_id = null, $args = [] );
function fxm_stripe_get_portal_url( $customer_id );
function fxm_stripe_verify_subscription( $subscription_id );
function fxm_stripe_handle_webhook();
function fxm_stripe_get_or_create_customer( $user_id );

PayPal Functions (includes/paypal/)

function fxm_paypal_get_subscribe_url( $plan_id, $user_id = null, $args = [] );
function fxm_paypal_get_portal_url( $subscription_id );
function fxm_paypal_verify_subscription( $subscription_id );
function fxm_paypal_handle_webhook();
function fxm_paypal_get_access_token();

Webhook Endpoints

URL Structure

/wp-json/fxm/v1/stripe/webhook
/wp-json/fxm/v1/paypal/webhook

Stripe Events to Handle

Event Action
checkout.session.completed Create subscription, link to user
customer.subscription.created Store subscription data
customer.subscription.updated Update status, dates
customer.subscription.deleted Mark as canceled
invoice.payment_succeeded Update period end date
invoice.payment_failed Update status to past_due

PayPal Events to Handle

Event Action
BILLING.SUBSCRIPTION.CREATED Store subscription data
BILLING.SUBSCRIPTION.ACTIVATED Update status to active
BILLING.SUBSCRIPTION.UPDATED Update subscription data
BILLING.SUBSCRIPTION.SUSPENDED Update status to paused
BILLING.SUBSCRIPTION.CANCELLED Mark as canceled
PAYMENT.SALE.COMPLETED Update period end date

Shortcodes

Shortcode Description Attributes
[fxm_restricted] Wrap restricted content level, message
[fxm_subscribe_button] Show subscribe buttons level, provider, text
[fxm_account] User account/subscription info -
[fxm_portal_link] Link to billing portal text, provider
[fxm_login_form] Login form for non-logged users redirect
[fxm_if_member] Conditional content for members level
[fxm_if_not_member] Conditional content for non-members level

Checkout Flow

Stripe Flow

  1. User clicks [fxm_subscribe_button level="premium" provider="stripe"]
  2. Generate Stripe Checkout URL with:
    • price_id from level config
    • client_reference_id = user_id (if logged in)
    • success_url = site/membership-success/
    • cancel_url = site/membership-cancel/
    • customer_email = user email (if logged in)
  3. Redirect to Stripe Checkout
  4. User completes payment
  5. Stripe sends checkout.session.completed webhook
  6. Plugin extracts client_reference_id, matches/creates user
  7. Stores subscription data in user meta
  8. User redirected to success_url with active subscription

PayPal Flow

  1. User clicks [fxm_subscribe_button level="premium" provider="paypal"]
  2. Generate PayPal subscription link with:
    • plan_id from level config
    • custom_id = user_id (if logged in)
    • return_url = site/membership-success/
    • cancel_url = site/membership-cancel/
  3. Redirect to PayPal
  4. User approves subscription
  5. PayPal sends BILLING.SUBSCRIPTION.ACTIVATED webhook
  6. Plugin extracts custom_id, matches/creates user
  7. Stores subscription data in user meta
  8. User redirected to return_url with active subscription

User Linking Strategy

For Logged-In Users

$checkout_args = [
    'client_reference_id' => $user_id,          // Stripe.
    'custom_id'           => $user_id,          // PayPal.
    'customer_email'      => $user->user_email,
];

For Guest Checkout

$user = get_user_by( 'email', $customer_email );

if ( ! $user ) {
    // Create new user from checkout data.
    $user_id = fxm_create_user_from_checkout( $customer_data );
}

Hooks & Filters

Actions

do_action( 'fxm_subscription_created', $user_id, $subscription_data );
do_action( 'fxm_subscription_activated', $user_id, $subscription_data );
do_action( 'fxm_subscription_updated', $user_id, $subscription_data );
do_action( 'fxm_subscription_canceled', $user_id, $subscription_data );
do_action( 'fxm_subscription_expired', $user_id, $subscription_data );
do_action( 'fxm_before_checkout_redirect', $user_id, $level, $provider );
do_action( 'fxm_after_checkout_success', $user_id, $subscription_data );

Filters

apply_filters( 'fxm_restricted_message', $message, $level );
apply_filters( 'fxm_can_access_content', $can_access, $user_id, $level );
apply_filters( 'fxm_checkout_success_url', $url, $level );
apply_filters( 'fxm_checkout_cancel_url', $url, $level );
apply_filters( 'fxm_membership_levels', $levels );
apply_filters( 'fxm_level_capabilities', $capabilities, $level );

Security

  1. Webhook Verification - Verify signatures for both Stripe and PayPal
  2. Nonce Verification - All admin AJAX and forms use nonces
  3. Capability Checks - Admin pages require manage_options
  4. Input Sanitization - All inputs sanitized before storage
  5. Output Escaping - All outputs escaped before display

Implementation Phases

Phase 1: Core Foundation

  • [x] Main plugin file with activation/deactivation hooks
  • [x] Settings infrastructure and admin menu
  • [x] User meta functions
  • [x] Basic membership level storage
  • [x] Helper functions

Phase 2: Stripe Integration

  • [x] Stripe API wrapper functions
  • [x] Checkout URL generation
  • [x] Webhook endpoint and handler
  • [x] Customer portal integration
  • [x] Webhook signature verification

Phase 3: PayPal Integration

  • [x] PayPal API authentication (OAuth)
  • [x] Subscription link generation
  • [x] Webhook endpoint and handler
  • [x] Webhook signature verification
  • [x] Billing portal redirect

Phase 4: Content Restriction

  • [x] Shortcode registration
  • [x] Content filtering
  • [x] Level-based access checks
  • [x] Restricted message display

Phase 5: Admin Interface

  • [x] Settings page with tabs
  • [x] Membership levels CRUD
  • [x] Subscribers list with filters
  • [x] Webhook status/logs
  • [x] Export functionality

Phase 6: Frontend & Polish

  • [x] Account shortcode/page
  • [x] Subscribe button styling
  • [x] Login/registration flow
  • [ ] Email notifications (not yet implemented)

Setup Instructions

1. Stripe PHP Library

Download the Stripe PHP library and place it in vendor/stripe-php/:

# Download from https://github.com/stripe/stripe-php/releases
# Extract so that init.php is at: vendor/stripe-php/init.php

Read the full README on GitHub →

Releases

TagPublished
1.0.1 May 27, 2026
1.0.0 Dec 10, 2025

These releases are tags only. The author does not attach a packaged zip, so there are no download counts to report.