Skip to content

API Reference

Complete reference for the ICoTA Members plugin classes and methods.

MembershipService

Location: src/services/MembershipService.php

The MembershipService handles all membership-related operations including creation, updates, queries, and lifecycle management.

createMembershipFromPurchase

Creates a new membership record from a successful Stripe purchase.

php
public function createMembershipFromPurchase(
    PurchaseRecord $purchase,
    ?int $durationDays = null,
    bool $strictMode = false,
    ?string $chapterId = null
): ?MembershipRecord

Parameters:

ParameterTypeRequiredDescription
$purchasePurchaseRecordYesThe purchase record from Stripe
$durationDaysint|nullNoCustom membership duration in days (null for standard year-based expiry)
$strictModeboolNoIf true, disables October 1st extension rule (default: false)
$chapterIdstring|nullNoSite UID for chapter assignment

Returns: MembershipRecord|null - The created membership or null on failure

Behavior:

  1. Checks if membership already exists for this purchase (prevents duplicates)
  2. Finds the Craft user associated with the purchase:
    • First tries by purchase.userId
    • Falls back to matching Stripe customer ID via email
  3. Calculates membership dates:
    • startDate: Set to now (when membership is created)
    • expiryDate: Calculated using calculateExpiryDate() method
  4. Creates MembershipRecord with:
    • Status: active
    • Source: purchase
    • Links to purchase via purchaseId

Example Usage:

php
$membershipService = Plugin::getInstance()->memberships;

// Create with default year-based expiry
$membership = $membershipService->createMembershipFromPurchase($purchase);

// Create with 365-day duration and October extension
$membership = $membershipService->createMembershipFromPurchase(
    $purchase,
    365,           // duration in days
    false,         // strict mode off
    $chapterUid    // chapter assignment
);

// Create with strict 180-day duration (no October extension)
$membership = $membershipService->createMembershipFromPurchase(
    $purchase,
    180,           // duration in days
    true           // strict mode on
);

Error Handling:

  • Returns existing membership if one already exists for this purchase
  • Returns null if user cannot be found
  • Logs warnings and errors to Craft logs

createManualMembership

Creates a manually-assigned membership without a purchase record.

php
public function createManualMembership(
    User $user,
    \DateTime $startDate,
    \DateTime $expiryDate,
    string $source = 'manual',
    ?string $chapterId = null,
    ?string $notes = null
): ?MembershipRecord

Parameters:

ParameterTypeRequiredDescription
$userUserYesThe Craft user to assign membership to
$startDate\DateTimeYesMembership start date
$expiryDate\DateTimeYesMembership expiry date
$sourcestringNoSource identifier (default: 'manual')
$chapterIdstring|nullNoSite UID for chapter assignment
$notesstring|nullNoOptional notes about this membership

Returns: MembershipRecord|null - The created membership or null on failure

Behavior:

  1. Creates membership record without Stripe data
  2. Sets customerId and purchaseId to null
  3. Uses startDate as datePurchased
  4. Status set to active
  5. Allows custom source identifier

Example Usage:

php
$membershipService = Plugin::getInstance()->memberships;

$user = User::find()->email('user@example.com')->one();
$startDate = new \DateTime('2025-01-01');
$expiryDate = new \DateTime('2025-12-31');

// Create basic manual membership
$membership = $membershipService->createManualMembership(
    $user,
    $startDate,
    $expiryDate
);

// Create with custom source and chapter
$membership = $membershipService->createManualMembership(
    $user,
    $startDate,
    $expiryDate,
    'admin-override',           // custom source
    'e3cae15b-...',            // chapter UID
    'Granted by administrator'  // notes
);

createLegacyMembership

Creates a membership from imported legacy data.

php
public function createLegacyMembership(
    User $user,
    \DateTime $expiryDate,
    string $status = 'active',
    string $source = 'legacy',
    ?string $chapterId = null
): ?MembershipRecord

Parameters:

ParameterTypeRequiredDescription
$userUserYesThe Craft user to assign membership to
$expiryDate\DateTimeYesMembership expiry date
$statusstringNoStatus: 'active', 'expired', or 'cancelled' (default: 'active')
$sourcestringNoSource identifier (default: 'legacy')
$chapterIdstring|nullNoSite UID for chapter assignment

Returns: MembershipRecord|null - The created membership or null on failure

Behavior:

  1. Creates membership without purchase data
  2. Sets customerId, purchaseId, datePurchased, and startDate to null
  3. Only requires expiry date and status
  4. Designed for importing from old systems

Example Usage:

php
$membershipService = Plugin::getInstance()->memberships;

$user = User::find()->email('user@example.com')->one();
$expiryDate = new \DateTime('2025-12-31');

// Create active legacy membership
$membership = $membershipService->createLegacyMembership(
    $user,
    $expiryDate
);

// Create expired legacy membership with chapter
$membership = $membershipService->createLegacyMembership(
    $user,
    new \DateTime('2024-01-01'),
    'expired',
    'migration-2024',
    'e3cae15b-...'
);

updateMembership

Updates an existing membership's dates and chapter.

php
public function updateMembership(
    MembershipRecord $membership,
    \DateTime $startDate,
    \DateTime $expiryDate,
    ?string $chapterId = null,
    ?string $notes = null
): bool

Parameters:

ParameterTypeRequiredDescription
$membershipMembershipRecordYesThe membership to update
$startDate\DateTimeYesNew start date
$expiryDate\DateTimeYesNew expiry date
$chapterIdstring|nullNoSite UID for chapter (null = don't change)
$notesstring|nullNoOptional notes (null = don't change)

Returns: bool - True on success, false on failure

Example Usage:

php
$membershipService = Plugin::getInstance()->memberships;

$membership = MembershipRecord::find()->where(['id' => 123])->one();

// Extend membership by one year
$newExpiry = (clone $membership->expiryDate)->add(new \DateInterval('P1Y'));
$success = $membershipService->updateMembership(
    $membership,
    $membership->startDate,
    $newExpiry
);

// Update with new chapter
$success = $membershipService->updateMembership(
    $membership,
    $membership->startDate,
    $membership->expiryDate,
    'new-chapter-uid',
    'Updated chapter assignment'
);

getMembershipsByUser

Gets all memberships for a specific user.

php
public function getMembershipsByUser(User $user): array

Returns: MembershipRecord[] - Array of membership records, ordered by most recent

Example:

php
$membershipService = Plugin::getInstance()->memberships;
$user = User::find()->email('user@example.com')->one();

$memberships = $membershipService->getMembershipsByUser($user);

foreach ($memberships as $membership) {
    echo "Status: {$membership->status}, Expires: {$membership->expiryDate->format('Y-m-d')}\n";
}

getActiveMembershipsByUser

Gets only active memberships for a user.

php
public function getActiveMembershipsByUser(User $user): array

Returns: MembershipRecord[] - Active memberships only, ordered by expiry date (latest first)

Filters by:

  • Status: active
  • Start date: null or in the past
  • Expiry date: in the future

Example:

php
$membershipService = Plugin::getInstance()->memberships;
$user = User::find()->email('user@example.com')->one();

$activeMemberships = $membershipService->getActiveMembershipsByUser($user);

if (!empty($activeMemberships)) {
    $latest = $activeMemberships[0];
    echo "Active until: {$latest->expiryDate->format('Y-m-d')}\n";
}

userHasActiveMembership

Checks if a user has any active membership.

php
public function userHasActiveMembership(User $user): bool

Returns: bool - True if user has at least one active membership

Example:

php
$membershipService = Plugin::getInstance()->memberships;
$user = User::find()->email('user@example.com')->one();

if ($membershipService->userHasActiveMembership($user)) {
    echo "User has active membership";
} else {
    echo "User membership expired or not found";
}

getMembershipsByCustomerId

Gets all memberships associated with a Stripe customer ID.

php
public function getMembershipsByCustomerId(string $customerId): array

Parameters:

ParameterTypeRequiredDescription
$customerIdstringYesStripe customer ID (e.g., "cus_abc123")

Returns: MembershipRecord[] - Array of membership records


cancelMembership

Cancels a membership by setting its status to 'cancelled'.

php
public function cancelMembership(MembershipRecord $membership): bool

Returns: bool - True on success, false on failure

Note: Does not modify dates, only changes status

Example:

php
$membershipService = Plugin::getInstance()->memberships;
$membership = MembershipRecord::find()->where(['id' => 123])->one();

if ($membershipService->cancelMembership($membership)) {
    echo "Membership cancelled successfully";
}

expireOldMemberships

Finds and expires all memberships past their expiry date.

php
public function expireOldMemberships(): int

Returns: int - Number of memberships expired

Behavior:

  1. Finds all memberships with:
    • Status: active
    • Expiry date: in the past
  2. Changes status to expired
  3. Saves each membership

Example:

php
$membershipService = Plugin::getInstance()->memberships;
$expiredCount = $membershipService->expireOldMemberships();

echo "Expired {$expiredCount} membership(s)";

Recommended Usage: Run daily via cron job

bash
craft _icota-members/memberships/expire

getMembershipsExpiringSoon

Gets memberships expiring within a specified number of days.

php
public function getMembershipsExpiringSoon(int $days = 7): array

Parameters:

ParameterTypeRequiredDescription
$daysintNoNumber of days to look ahead (default: 7)

Returns: MembershipRecord[] - Memberships expiring soon

Example:

php
$membershipService = Plugin::getInstance()->memberships;

// Get memberships expiring in next 30 days
$expiringSoon = $membershipService->getMembershipsExpiringSoon(30);

foreach ($expiringSoon as $membership) {
    $user = $membership->getUser()->one();
    $daysRemaining = ceil(($membership->expiryDate->getTimestamp() - time()) / (24 * 60 * 60));

    echo "{$user->email} expires in {$daysRemaining} days\n";
}

getAllMemberships

Gets all memberships in the system.

php
public function getAllMemberships(): array

Returns: MembershipRecord[] - All membership records, ordered by most recent


getMembershipsByStatus

Gets memberships filtered by status.

php
public function getMembershipsByStatus(string $status): array

Parameters:

ParameterTypeRequiredDescription
$statusstringYesStatus: 'active', 'expired', or 'cancelled'

Returns: MembershipRecord[] - Filtered memberships

Example:

php
$membershipService = Plugin::getInstance()->memberships;

$activeMemberships = $membershipService->getMembershipsByStatus('active');
$expiredMemberships = $membershipService->getMembershipsByStatus('expired');
$cancelledMemberships = $membershipService->getMembershipsByStatus('cancelled');

echo "Active: " . count($activeMemberships) . "\n";
echo "Expired: " . count($expiredMemberships) . "\n";
echo "Cancelled: " . count($cancelledMemberships) . "\n";

extendMembership

Extends a membership's expiry date by adding months.

php
public function extendMembership(
    MembershipRecord $membership,
    int $additionalMonths
): bool

Parameters:

ParameterTypeRequiredDescription
$membershipMembershipRecordYesThe membership to extend
$additionalMonthsintYesNumber of months to add

Returns: bool - True on success, false on failure

Example:

php
$membershipService = Plugin::getInstance()->memberships;
$membership = MembershipRecord::find()->where(['id' => 123])->one();

// Extend by 6 months
if ($membershipService->extendMembership($membership, 6)) {
    echo "Extended until: {$membership->expiryDate->format('Y-m-d')}";
}

// Extend by 1 year
$membershipService->extendMembership($membership, 12);

MembershipRecord

Location: src/records/MembershipRecord.php

Active Record class for querying and manipulating membership data.

Properties

PropertyTypeDescription
idintPrimary key
userIdintCraft user ID
chapterIdstringSite UID for chapter
customerIdstringStripe customer ID
purchaseIdstringStripe payment intent ID
datePurchased\DateTimePurchase date
startDate\DateTimeMembership start date
expiryDate\DateTimeMembership expiry date
statusstringStatus: 'active', 'expired', 'cancelled'
sourcestringSource: 'purchase', 'legacy', 'manual', etc.
notesstringOptional notes
dateCreated\DateTimeRecord creation date
dateUpdated\DateTimeLast update date
uidstringUnique identifier

Query Methods

findByUserId

php
public static function findByUserId(int $userId): ActiveQueryInterface

Returns query for all memberships for a user.

Example:

php
$memberships = MembershipRecord::findByUserId(123)
    ->orderBy(['dateCreated' => SORT_DESC])
    ->all();

findByCustomerId

php
public static function findByCustomerId(string $customerId): ActiveQueryInterface

Returns query for memberships by Stripe customer ID.


findByPurchaseId

php
public static function findByPurchaseId(string $purchaseId): ActiveQueryInterface

Returns query for membership by Stripe payment intent ID.


findByStatus

php
public static function findByStatus(string $status): ActiveQueryInterface

Returns query for memberships by status.

Example:

php
$activeCount = MembershipRecord::findByStatus('active')->count();

findActiveByUserId

php
public static function findActiveByUserId(int $userId): ActiveQueryInterface

Returns query for active memberships for a user.

Filters by:

  • Status: active
  • Start date: null or in the past
  • Expiry date: in the future

findExpired

php
public static function findExpired(): ActiveQueryInterface

Returns query for expired memberships (status='active' but expiry date passed).


findExpiringSoon

php
public static function findExpiringSoon(int $days = 7): ActiveQueryInterface

Returns query for memberships expiring within specified days.


findByChapterId

php
public static function findByChapterId(string $chapterId): ActiveQueryInterface

Returns query for memberships by chapter ID.

Example:

php
$usaMemberships = MembershipRecord::findByChapterId('e3cae15b-9623-4eb8-95db-8f96f1a691a8')
    ->andWhere(['status' => 'active'])
    ->all();

findActiveByUserIdAndChapterId

php
public static function findActiveByUserIdAndChapterId(
    int $userId,
    string $chapterId
): ActiveQueryInterface

Returns query for active memberships for a specific user and chapter.


PurchaseService

Location: src/services/PurchaseService.php

Handles Stripe purchase record operations.

Methods

Methods for managing purchase records (detailed documentation available in source code).


PurchaseRecord

Location: src/records/PurchaseRecord.php

Active Record class for Stripe purchase data.

Properties

PropertyTypeDescription
idintPrimary key
stripeCustomerIdstringStripe customer ID
stripePaymentIntentIdstringStripe payment intent ID
amountintAmount in cents
currencystringCurrency code (e.g., 'usd')
statusstringPayment status
purchaseDatastringJSON of Stripe payment intent data
userIdintLinked Craft user ID
dateCreated\DateTimeRecord creation date
dateUpdated\DateTimeLast update date
uidstringUnique identifier

Query Methods

  • findByCustomerId(string $customerId) - Find by Stripe customer ID
  • findByPaymentIntentId(string $paymentIntentId) - Find by payment intent ID
  • findByUserId(int $userId) - Find by Craft user ID
  • findByStatus(string $status) - Find by payment status

Usage Examples

Complete Purchase Flow

php
// 1. Stripe webhook creates purchase record
$purchase = new PurchaseRecord();
$purchase->stripeCustomerId = $paymentIntent->customer;
$purchase->stripePaymentIntentId = $paymentIntent->id;
$purchase->amount = $paymentIntent->amount;
$purchase->currency = $paymentIntent->currency;
$purchase->status = $paymentIntent->status;
$purchase->save();

// 2. Create membership from purchase
$membershipService = Plugin::getInstance()->memberships;
$membership = $membershipService->createMembershipFromPurchase(
    $purchase,
    365,        // 1 year
    false,      // allow October extension
    $chapterUid // chapter assignment
);

// 3. User now has active membership
if ($membership) {
    $user = $membership->getUser()->one();
    echo "{$user->email} membership active until {$membership->expiryDate->format('Y-m-d')}";
}

Membership Management

php
$membershipService = Plugin::getInstance()->memberships;
$user = User::find()->email('user@example.com')->one();

// Check if user has active membership
if ($membershipService->userHasActiveMembership($user)) {
    // Get their active memberships
    $memberships = $membershipService->getActiveMembershipsByUser($user);

    // Extend the first one by 6 months
    if (!empty($memberships)) {
        $membershipService->extendMembership($memberships[0], 6);
    }
}

// Get upcoming expirations
$expiringSoon = $membershipService->getMembershipsExpiringSoon(30);
foreach ($expiringSoon as $membership) {
    // Send reminder email...
}

// Expire old memberships (run daily)
$expiredCount = $membershipService->expireOldMemberships();

Advanced Queries

php
// Find all active USA Chapter memberships
$usaMemberships = MembershipRecord::find()
    ->where(['status' => 'active', 'chapterId' => 'e3cae15b-...'])
    ->orderBy(['expiryDate' => SORT_ASC])
    ->all();

// Find memberships purchased in 2025
$purchases2025 = MembershipRecord::find()
    ->where(['source' => 'purchase'])
    ->andWhere(['>=', 'datePurchased', '2025-01-01'])
    ->andWhere(['<', 'datePurchased', '2026-01-01'])
    ->count();

// Find legacy memberships that need chapter assignment
$needsChapter = MembershipRecord::find()
    ->where(['source' => 'legacy', 'chapterId' => null])
    ->all();

ICoTA Members Plugin Documentation