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.
public function createMembershipFromPurchase(
PurchaseRecord $purchase,
?int $durationDays = null,
bool $strictMode = false,
?string $chapterId = null
): ?MembershipRecordParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
$purchase | PurchaseRecord | Yes | The purchase record from Stripe |
$durationDays | int|null | No | Custom membership duration in days (null for standard year-based expiry) |
$strictMode | bool | No | If true, disables October 1st extension rule (default: false) |
$chapterId | string|null | No | Site UID for chapter assignment |
Returns: MembershipRecord|null - The created membership or null on failure
Behavior:
- Checks if membership already exists for this purchase (prevents duplicates)
- Finds the Craft user associated with the purchase:
- First tries by
purchase.userId - Falls back to matching Stripe customer ID via email
- First tries by
- Calculates membership dates:
startDate: Set to now (when membership is created)expiryDate: Calculated usingcalculateExpiryDate()method
- Creates
MembershipRecordwith:- Status:
active - Source:
purchase - Links to purchase via
purchaseId
- Status:
Example Usage:
$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
nullif user cannot be found - Logs warnings and errors to Craft logs
createManualMembership
Creates a manually-assigned membership without a purchase record.
public function createManualMembership(
User $user,
\DateTime $startDate,
\DateTime $expiryDate,
string $source = 'manual',
?string $chapterId = null,
?string $notes = null
): ?MembershipRecordParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
$user | User | Yes | The Craft user to assign membership to |
$startDate | \DateTime | Yes | Membership start date |
$expiryDate | \DateTime | Yes | Membership expiry date |
$source | string | No | Source identifier (default: 'manual') |
$chapterId | string|null | No | Site UID for chapter assignment |
$notes | string|null | No | Optional notes about this membership |
Returns: MembershipRecord|null - The created membership or null on failure
Behavior:
- Creates membership record without Stripe data
- Sets
customerIdandpurchaseIdto null - Uses
startDateasdatePurchased - Status set to
active - Allows custom
sourceidentifier
Example Usage:
$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.
public function createLegacyMembership(
User $user,
\DateTime $expiryDate,
string $status = 'active',
string $source = 'legacy',
?string $chapterId = null
): ?MembershipRecordParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
$user | User | Yes | The Craft user to assign membership to |
$expiryDate | \DateTime | Yes | Membership expiry date |
$status | string | No | Status: 'active', 'expired', or 'cancelled' (default: 'active') |
$source | string | No | Source identifier (default: 'legacy') |
$chapterId | string|null | No | Site UID for chapter assignment |
Returns: MembershipRecord|null - The created membership or null on failure
Behavior:
- Creates membership without purchase data
- Sets
customerId,purchaseId,datePurchased, andstartDateto null - Only requires expiry date and status
- Designed for importing from old systems
Example Usage:
$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.
public function updateMembership(
MembershipRecord $membership,
\DateTime $startDate,
\DateTime $expiryDate,
?string $chapterId = null,
?string $notes = null
): boolParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
$membership | MembershipRecord | Yes | The membership to update |
$startDate | \DateTime | Yes | New start date |
$expiryDate | \DateTime | Yes | New expiry date |
$chapterId | string|null | No | Site UID for chapter (null = don't change) |
$notes | string|null | No | Optional notes (null = don't change) |
Returns: bool - True on success, false on failure
Example Usage:
$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.
public function getMembershipsByUser(User $user): arrayReturns: MembershipRecord[] - Array of membership records, ordered by most recent
Example:
$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.
public function getActiveMembershipsByUser(User $user): arrayReturns: 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:
$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.
public function userHasActiveMembership(User $user): boolReturns: bool - True if user has at least one active membership
Example:
$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.
public function getMembershipsByCustomerId(string $customerId): arrayParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
$customerId | string | Yes | Stripe customer ID (e.g., "cus_abc123") |
Returns: MembershipRecord[] - Array of membership records
cancelMembership
Cancels a membership by setting its status to 'cancelled'.
public function cancelMembership(MembershipRecord $membership): boolReturns: bool - True on success, false on failure
Note: Does not modify dates, only changes status
Example:
$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.
public function expireOldMemberships(): intReturns: int - Number of memberships expired
Behavior:
- Finds all memberships with:
- Status:
active - Expiry date: in the past
- Status:
- Changes status to
expired - Saves each membership
Example:
$membershipService = Plugin::getInstance()->memberships;
$expiredCount = $membershipService->expireOldMemberships();
echo "Expired {$expiredCount} membership(s)";Recommended Usage: Run daily via cron job
craft _icota-members/memberships/expiregetMembershipsExpiringSoon
Gets memberships expiring within a specified number of days.
public function getMembershipsExpiringSoon(int $days = 7): arrayParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
$days | int | No | Number of days to look ahead (default: 7) |
Returns: MembershipRecord[] - Memberships expiring soon
Example:
$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.
public function getAllMemberships(): arrayReturns: MembershipRecord[] - All membership records, ordered by most recent
getMembershipsByStatus
Gets memberships filtered by status.
public function getMembershipsByStatus(string $status): arrayParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
$status | string | Yes | Status: 'active', 'expired', or 'cancelled' |
Returns: MembershipRecord[] - Filtered memberships
Example:
$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.
public function extendMembership(
MembershipRecord $membership,
int $additionalMonths
): boolParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
$membership | MembershipRecord | Yes | The membership to extend |
$additionalMonths | int | Yes | Number of months to add |
Returns: bool - True on success, false on failure
Example:
$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
| Property | Type | Description |
|---|---|---|
id | int | Primary key |
userId | int | Craft user ID |
chapterId | string | Site UID for chapter |
customerId | string | Stripe customer ID |
purchaseId | string | Stripe payment intent ID |
datePurchased | \DateTime | Purchase date |
startDate | \DateTime | Membership start date |
expiryDate | \DateTime | Membership expiry date |
status | string | Status: 'active', 'expired', 'cancelled' |
source | string | Source: 'purchase', 'legacy', 'manual', etc. |
notes | string | Optional notes |
dateCreated | \DateTime | Record creation date |
dateUpdated | \DateTime | Last update date |
uid | string | Unique identifier |
Query Methods
findByUserId
public static function findByUserId(int $userId): ActiveQueryInterfaceReturns query for all memberships for a user.
Example:
$memberships = MembershipRecord::findByUserId(123)
->orderBy(['dateCreated' => SORT_DESC])
->all();findByCustomerId
public static function findByCustomerId(string $customerId): ActiveQueryInterfaceReturns query for memberships by Stripe customer ID.
findByPurchaseId
public static function findByPurchaseId(string $purchaseId): ActiveQueryInterfaceReturns query for membership by Stripe payment intent ID.
findByStatus
public static function findByStatus(string $status): ActiveQueryInterfaceReturns query for memberships by status.
Example:
$activeCount = MembershipRecord::findByStatus('active')->count();findActiveByUserId
public static function findActiveByUserId(int $userId): ActiveQueryInterfaceReturns query for active memberships for a user.
Filters by:
- Status:
active - Start date: null or in the past
- Expiry date: in the future
findExpired
public static function findExpired(): ActiveQueryInterfaceReturns query for expired memberships (status='active' but expiry date passed).
findExpiringSoon
public static function findExpiringSoon(int $days = 7): ActiveQueryInterfaceReturns query for memberships expiring within specified days.
findByChapterId
public static function findByChapterId(string $chapterId): ActiveQueryInterfaceReturns query for memberships by chapter ID.
Example:
$usaMemberships = MembershipRecord::findByChapterId('e3cae15b-9623-4eb8-95db-8f96f1a691a8')
->andWhere(['status' => 'active'])
->all();findActiveByUserIdAndChapterId
public static function findActiveByUserIdAndChapterId(
int $userId,
string $chapterId
): ActiveQueryInterfaceReturns 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
| Property | Type | Description |
|---|---|---|
id | int | Primary key |
stripeCustomerId | string | Stripe customer ID |
stripePaymentIntentId | string | Stripe payment intent ID |
amount | int | Amount in cents |
currency | string | Currency code (e.g., 'usd') |
status | string | Payment status |
purchaseData | string | JSON of Stripe payment intent data |
userId | int | Linked Craft user ID |
dateCreated | \DateTime | Record creation date |
dateUpdated | \DateTime | Last update date |
uid | string | Unique identifier |
Query Methods
findByCustomerId(string $customerId)- Find by Stripe customer IDfindByPaymentIntentId(string $paymentIntentId)- Find by payment intent IDfindByUserId(int $userId)- Find by Craft user IDfindByStatus(string $status)- Find by payment status
Usage Examples
Complete Purchase Flow
// 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
$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
// 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();