Skip to content
  1. Extras
  2. MiniShop3

Customer API

Programmatic interface for working with MiniShop3 customers from PHP.

In MiniShop3, a customer is a separate entity from modUser:

  • msCustomer — customer profile (email, phone, password, order stats)
  • msCustomerToken — authentication and verification tokens
  • msCustomerAddress — saved delivery addresses

Link to modUser is optional (field user_id). A customer can exist without a MODX user.

Customer controller

High-level interface for working with customers from snippets and plugins.

php
$ms3 = $modx->services->get('ms3');

// Get current customer data (from session token)
$fields = $ms3->customer->getFields();
// ['id' => 5, 'email' => 'user@example.com', 'first_name' => 'John', ...]

// Get msCustomer object
$customer = $ms3->customer->getObject();

// Find customer by token
$customer = $ms3->customer->getByToken($token);

// Set multiple fields
$result = $ms3->customer->set([
    'first_name' => 'John',
    'last_name' => 'Doe',
    'phone' => '+79991234567',
]);

// Add/update one field (with validation and events)
$result = $ms3->customer->add('email', 'new@example.com');

Creating a customer

php
// Programmatic creation
$customer = $ms3->customer->create([
    'email' => 'user@example.com',
    'first_name' => 'John',
    'phone' => '+79991234567',
]);
// Fires msOnBeforeCreateCustomer / msOnCreateCustomer

// Find or create during checkout
$customerId = $ms3->customer->getOrCreate($orderData);
// Looks up by token → by email → auto-register or create record

Managing addresses

php
// Add address
$ms3->customer->addAddress([
    'customer_id' => $customerId,
    'city' => 'Moscow',
    'street' => 'Lenina',
    'building' => '10',
    'room' => '5',
]);

// Get customer addresses
$addresses = $ms3->customer->getAddresses($customerId);

Field validation

php
// Register validation rules
$ms3->customer->registerValidation(
    // Rules (Rakit Validator format)
    ['email' => 'required|email', 'phone' => 'required|min:10'],
    // Error messages
    ['email:required' => 'Enter email']
);

// Validate value
$result = $ms3->customer->validate('email', 'user@example.com');
// Value or array of errors

Session tokens

php
// Generate new token
$data = $ms3->customer->generateToken();
// ['token' => 'abc123...', 'lifetime' => 86400]

// Refresh existing token
$data = $ms3->customer->updateToken($currentToken);

Authentication (AuthManager)

AuthManager implements a strategy with pluggable authentication providers.

php
$authManager = $modx->services->get('ms3_auth_manager');

Authentication

php
// Standard auth (email + password)
$customer = $authManager->authenticate([
    'email' => 'user@example.com',
    'password' => 'secret123',
]);
// Returns msCustomer or null

if ($customer) {
    // Updates last_login_at, resets failed_login_attempts
}

AuthManager checks is_active and is_blocked before authenticating.

Pluggable providers

By default PasswordAuthProvider (email + password) is registered. You can add your own:

php
use MiniShop3\Controllers\Auth\AuthProviderInterface;
use MiniShop3\Model\msCustomer;

class TelegramAuthProvider implements AuthProviderInterface
{
    public function getName(): string
    {
        return 'telegram';
    }

    public function supports(array $credentials): bool
    {
        return isset($credentials['telegram_hash'], $credentials['telegram_id']);
    }

    public function authenticate(array $credentials): ?msCustomer
    {
        if (!$this->verifyTelegramHash($credentials)) {
            return null;
        }
        return $this->modx->getObject(msCustomer::class, [
            'phone' => $credentials['phone'],  // or other identifier
        ]);
    }
}

// Register provider
$authManager->registerProvider(new TelegramAuthProvider($modx));

When authenticate() is called, the manager iterates providers and uses the first whose supports() returns true.

Token management

php
// Create auth token
$token = $authManager->createToken($customer, 'api', 86400);
// msCustomerToken: token, type='api', expires_at = now + 86400

// Validate token
$customer = $authManager->validateToken($tokenString, 'api');
// Returns msCustomer or null (if expired/used)

// Revoke all customer tokens
$count = $authManager->revokeTokens($customer);

// Revoke only a specific type
$count = $authManager->revokeTokens($customer, 'api');

// Clean expired tokens (for cron)
$deleted = $authManager->cleanupExpiredTokens();

Token types

TypeConstantDescriptionOne-time
apimsCustomerToken::TYPE_APIAPI session tokenNo
refreshmsCustomerToken::TYPE_REFRESHSession refresh tokenNo
magic_linkmsCustomerToken::TYPE_MAGIC_LINKPasswordless login linkYes
email_verificationmsCustomerToken::TYPE_EMAIL_VERIFICATIONEmail verificationYes

One-time tokens are marked as used (used_at) after first use.

Blocking on failed attempts

php
// Handle failed login (called automatically)
$authManager->handleFailedLogin($customer);
// Increments failed_login_attempts
// When ms3_customer_max_login_attempts (default 5) is reached
// blocks for ms3_customer_block_duration seconds (default 3600)

Registration (RegisterService)

php
$registerService = $modx->services->get('ms3_register_service');

$result = $registerService->register([
    'email' => 'user@example.com',
    'password' => 'Secret123!',       // optional — auto-generated if omitted
    'first_name' => 'John',
    'last_name' => 'Doe',
    'phone' => '+79991234567',
    'privacy_accepted' => true,        // GDPR consent
    'token' => $sessionToken,          // bind to session (from checkout)
]);

if ($result['success']) {
    $customer = $result['customer'];        // msCustomer
    $autoPassword = $result['auto_password']; // null if password was provided
}

Registration flow

  1. Check email and phone uniqueness
  2. If no password — generate 16-char random password
  3. Hash password (bcrypt)
  4. Create msCustomer
  5. If ms3_customer_require_email_verification = true — send verification email
  6. If password was generated and ms3_customer_send_welcome_email = true — send welcome email with password

Password validation

php
$result = $registerService->validatePassword('weak');
// ['valid' => false, 'message' => 'Password must be at least 8 characters']
SettingDefaultDescription
ms3_password_min_length8Min length
ms3_password_require_uppercasefalseRequire uppercase
ms3_password_require_numberfalseRequire digit
ms3_password_require_specialfalseRequire special char

Email verification (EmailVerificationService)

php
$verification = $modx->services->get('ms3_email_verification_service');

// Send verification email
$sent = $verification->sendVerificationEmail($customer);

// Verify token (from email link)
$customer = $verification->verifyToken($tokenString);
// Sets email_verified_at, marks token as used

// Check if email is verified
if ($verification->isVerified($customer)) {
    // Email verified
}

// Resend (with spam protection — 5 min between sends)
$result = $verification->resendVerificationEmail($customer);

Setting ms3_email_verification_token_ttl (default 86400) — verification token lifetime in seconds.

Rate limiting (RateLimiter)

Service to protect against brute-force. Uses MODX cache.

php
$limiter = $modx->services->get('ms3_rate_limiter');

// Check and increment counter
$allowed = $limiter->check(
    'login',              // action
    $clientIp,            // identifier (IP, email, etc.)
    5,                    // max attempts
    300                   // window in seconds
);

if (!$allowed) {
    // Limit exceeded
}

// Check without increment
if ($limiter->isBlocked('login', $clientIp, 5)) {
    // Blocked
}

// Reset counter (after successful login)
$limiter->reset('login', $clientIp);

Customer addresses (CustomerAddressManager)

php
$addressManager = $modx->services->get('ms3_customer_address_manager');

// Add address
$success = $addressManager->add([
    'customer_id' => $customerId,
    'city' => 'Moscow',
    'street' => 'Lenina',
    'building' => '10',
    'room' => '5',
]);
// Auto: generates name, computes hash for deduplication
// Fires msOnBeforeAddCustomerAddress / msOnAddCustomerAddress

// Get all addresses
$addresses = $addressManager->getByCustomerId($customerId);

// Get one address
$address = $addressManager->getById($addressId);

// Update address
$addressManager->update($addressId, $customerId, [
    'city' => 'Saint Petersburg',
    'street' => 'Nevsky',
]);

// Delete address (checks customer ownership)
$addressManager->delete($addressId, $customerId);

Address deduplication

Address hash is MD5 of city|street|building|room (lowercased). Adding an address with an existing hash does not create a duplicate.

Duplicate check (CustomerDuplicateChecker)

Used when creating customers to prevent duplicates.

php
$checker = $modx->services->get('ms3_customer_duplicate_checker');

// Find existing customer by data
$existing = $checker->findDuplicate([
    'email' => 'user@example.com',
    'phone' => '+79991234567',
]);
// Searches by OR: email OR phone match

// Check if there is data to search by
if ($checker->hasCheckableData($data)) {
    // Has email or phone
}

// Change fields to check
$checker->setCheckFields(['email']);  // only by email

Setting ms3_customer_duplicate_fields (JSON) defines which fields to check. Default: ["email", "phone"].

Normalization when comparing

FieldNormalization
emailLowercase
phoneDigits only (min 7)
OthersTrim whitespace

Customer factory (CustomerFactory)

Creates a customer from order data. Used when finalizing an order from the manager.

php
$factory = $modx->services->get('ms3_customer_factory');

$customer = $factory->createFromOrderData([
    'first_name' => 'John',
    'last_name' => 'Doe',
    'email' => 'user@example.com',
    'phone' => '+79991234567',
]);

If ms3_customer_sync_create_moduser = true, also creates modUser + modUserProfile and adds to the group from ms3_customer_sync_user_group.

msCustomer fields

FieldTypeDefaultDescription
user_idinteger0MODX user ID (optional)
first_namestring''First name
last_namestring''Last name
emailstring''Email
phonestring''Phone
tokenstring''Current session token (unique)
passwordstringnullPassword hash (bcrypt)
email_verified_atdatetimenullEmail verification date
is_activebooleantrueActive
is_blockedbooleanfalseBlocked
failed_login_attemptsinteger0Failed login attempts
blocked_untildatetimenullBlocked until
created_atdatetimenowCreated at
updated_atdatetimenullUpdated at
last_login_atdatetimenullLast login
orders_countinteger0Order count
total_spentfloat0.00Total order amount
last_order_atdatetimenullLast order date
privacy_accepted_atdatetimenullPrivacy consent date
privacy_ipstringnullIP at consent

msCustomerAddress fields

FieldTypeDefaultDescription
customer_idinteger0Customer ID
hashstringnullMD5 address hash (deduplication)
namestringnullLabel (auto from city/street)
countrystringnullCountry
indexstringnullPostal code
regionstringnullRegion
citystringnullCity
metrostringnullMetro station
streetstringnullStreet
buildingstringnullBuilding
entrancestringnullEntrance
floorstringnullFloor
roomstringnullApartment/office
commenttextnullComment
is_defaultinteger0Default address

msCustomerToken fields

FieldTypeDefaultDescription
customer_idinteger0Customer ID
tokenstring''Token string (128 chars, unique)
typeenum'api'Type: api, refresh, magic_link, email_verification
expires_atdatetimeExpiry
created_atdatetimenowCreated at
used_atdatetimenullUsed at (for one-time tokens)

System settings

SettingDefaultDescription
ms3_customer_max_login_attempts5Login attempts before block
ms3_customer_block_duration3600Block duration (seconds)
ms3_customer_api_token_ttl86400API token TTL (seconds)
ms3_customer_require_email_verificationtrueRequire email verification
ms3_customer_send_welcome_emailtrueSend welcome email
ms3_customer_auto_login_after_registerfalseAuto-login after register
ms3_customer_auto_register_on_ordertrueAuto-register on checkout
ms3_customer_auto_login_on_orderfalseAuto-login on checkout
ms3_customer_require_privacy_consenttrueRequire privacy consent
ms3_customer_duplicate_fields["email","phone"]Fields for duplicate check
ms3_customer_sync_create_moduserfalseCreate modUser when creating customer
ms3_customer_sync_user_group''MODX group for new users
ms3_password_min_length8Min password length
ms3_password_reset_token_ttl3600Password reset token TTL
ms3_email_verification_token_ttl86400Verification token TTL

Events

EventWhen fired
msOnBeforeCreateCustomer / msOnCreateCustomerCustomer creation
msOnBeforeUpdateCustomer / msOnUpdateCustomerUpdate from manager
msOnBeforeAddToCustomer / msOnAddToCustomerField change via controller
msOnBeforeValidateCustomerValue / msOnValidateCustomerValueField value validation
msOnBeforeGetOrderCustomer / msOnGetOrderCustomerFind/create on checkout
msOnBeforeAddCustomerAddress / msOnAddCustomerAddressAdd address

Event parameter details: Events.