For developers

Developer documentation

The REST API, hooks, template overrides and database schema, for anyone extending or integrating with the plugin.

Architecture

PHP lives under includes/, namespaced SLW_Pro\ and autoloaded by a small PSR-4-style autoloader (no Composer autoload needed at runtime). The admin screens are a React app under admin/src, built with Vite in WordPress’s own script format, so it loads WordPress’s bundled React rather than shipping its own.

Services are built once per request by SLW_Pro\Bootstrap\Services and handed to controllers and hooks through their constructors — there is no service locator to call from outside the plugin. Requires WordPress 6.6+, WooCommerce 8.3+, PHP 8.2+. Text domain: smart-loyalty-rewards-wallet-referral-engine-pro.

REST API

Every endpoint lives under /wp-json/slw/v1/. Authentication is WordPress’s standard REST auth (cookie + X-WP-Nonce for logged-in requests, or Application Passwords for external calls). Customer routes just need a logged-in user; admin routes need manage_woocommerce. Routes that change points are rate-limited per user (filterable with slw_rest_rate_limit).

RouteAccessWhat it does
GET /dashboardCustomerThe logged-in customer’s balance, level and referral summary.
GET /historyCustomerThe logged-in customer’s points ledger.
GET /couponsCustomerThe logged-in customer’s vouchers.
POST /redeemCustomerExchange points for a voucher.
POST /cart/redeem, POST /cart/applyCustomerThe “Pay with points” panel: redeem, then apply a voucher to the current cart.
GET /checkin/status, POST /checkin/claimCustomerThe daily check-in bonus.
GET/POST /settingsAdminEvery store setting (earning, redemption, bonuses, referrals, display).
GET/POST /admin/earning-rulesAdminEarning rules (order total, items in cart, product, category).
GET /admin/overviewAdminStore-wide loyalty metrics for the dashboard.
GET /admin/users, GET /admin/users/{id}AdminThe customer list and one customer’s full panel (balance, history, vouchers, referrals).
POST /admin/users/{id}/adjustmentsAdminManually add or remove a customer’s points, with a required reason.
GET/POST/DELETE /admin/licenseAdminActivate, check or release the site’s licence key.
GET/POST /admin/level-badgesAdminLevels and badges.

Example: read the logged-in customer’s balance.

curl "https://yourstore.com/wp-json/slw/v1/dashboard" \
  -H "X-WP-Nonce: <nonce>" \
  --cookie "wordpress_logged_in_...=..."

Actions and filters

Actions — fire after something happens; use them to sync loyalty events to another system.

ActionArgumentsFires when
slw_wallet_updated(int $user_id, array $result)Fires whenever a customer’s wallet changes: an order earns points, a voucher is redeemed, points expire, or the store adjusts a balance. $result holds the new totals.
slw_voucher_issued(int $user_id, array $voucher)Fires when a points voucher is created, whether redeemed from My Account, the cart panel, or the REST API.
slw_level_changed(int $user_id, ?int $previous_level_id, ?int $current_level_id, string $reason)Fires when a customer moves to a different level. $reason is points after an order, or recalculation after an admin edits the level ladder.

Filters — change a value before the plugin uses it.

FilterArgumentsChanges
slw_product_points(int $points, WC_Product $product, array $breakdown)The final points a product earns, after product, category and variation rules.
slw_order_points(int $points, WC_Order $order, array $breakdown)The final points an order earns, after every earning rule.
slw_cart_points(int $points, WC_Cart $cart, array $breakdown)The points the current cart would earn, used by the product and cart point messages.
slw_points_label(string $label, int $count)The word “point” or “points” wherever the plugin prints an amount.
slw_pending_points_statuses(string[] $statuses)Which order statuses keep points pending rather than available.
slw_leaderboard_public_name(string $name, int $user_id, string $format)How a customer’s name appears on the opt-in leaderboard (initials by default).
slw_referral_matches_referrer(bool $match, WP_User $referrer, WC_Order $order)Extra fraud checks before a referral reward is paid.
slw_is_self_registration(bool $is_self, int $new_user_id)Whether a new signup counts as self-referral and is blocked from a reward.
slw_privacy_erase_points(bool $erase, WP_User $user)Whether a privacy erase request deletes a customer’s ledger and wallet rows, or only anonymises them (the default).
slw_rest_rate_limit(array $limit, string $route, int $user_id)The requests-per-window allowed on a point-changing REST route. $limit is ['max' => int, 'window' => int].
slw_account_accent_color(string $hex)The accent colour used across the My Account loyalty pages (defaults to the plugin’s purple).
slw_client_ip(string $ip)The visitor IP address used for anti-abuse checks, for stores behind a proxy or load balancer.
slw_license_store_url / slw_license_item_id(string $url) / (int $id)Override the licence server address or product id, in place of the two constants in the plugin file.

Example: send a webhook whenever a customer’s wallet changes.

add_action('slw_wallet_updated', function (int $user_id, array $result) {
    wp_remote_post('https://example.com/webhook', [
        'body' => wp_json_encode([
            'user_id'      => $user_id,
            'total_points' => $result['total_points'],
        ]),
    ]);
}, 10, 2);

Template overrides

Customer-facing templates load through wc_get_template(), the same mechanism WooCommerce itself uses, so any of them can be overridden from a theme without editing plugin files. Copy the file from the plugin’s templates/ folder into your theme under smart-loyalty/, keeping the same path.

yourtheme/
  smart-loyalty/
    myaccount/
      points.php
      history.php
      vouchers.php
      referral.php
    cart/
      vouchers.php
    emails/
      slw-points-earned.php
      slw-voucher-ready.php
      slw-level-reached.php
      slw-points-expiring.php
      slw-points-expired.php

Each email template also has a plain/ counterpart for plain-text email clients.

Shortcodes and widgets

ShortcodeShows
[slw_points]The current user’s balance and level.
[slw_history]The current user’s points history.
[slw_referral]The current user’s referral link.

Two widgets are also available from Appearance › Widgets: Points balance and Leaderboard (opt-in; customers choose to appear from My Account › Account details, and names show as initials by default — see the slw_leaderboard_public_name filter).

Database schema

Three custom tables, created and upgraded by SLW_Pro\Database\Migrations. Earning rules and levels are stored as their own post types (slw_rule, slw_level) with post meta, not custom tables.

TableColumns
wp_slw_walletuser_id, total_points, earned_points, spent_points, pending_points, updated_at — one row per customer.
wp_slw_points_logid, user_id, action, points, remaining, balance_after, status, reference_id, reference_type, idempotency_key, note, actor_id, expires_at, expired_at, created_at — the full ledger.
wp_slw_couponsid, user_id, coupon_code, points_cost, created_at, expires_at, usage_limit, usage_count, source_type, reference_id — vouchers issued for points, each backed by a real WooCommerce coupon.

Reading these tables directly is fine for reporting; changing them directly is not — go through Services::wallet() and Services::ledger() (via the actions and filters above) so balances, idempotency and expiry stay consistent.

Local development

Clone the repository, then:

# PHP: coding standards, static analysis, the integration suites
composer install
composer check   # PHPCS + PHPStan
composer test    # 27 suites against a real WordPress site (tests/config.php)

# Admin app
cd admin
npm ci
npm run dev      # Vite dev server
npm test         # Vitest + axe-core accessibility checks
npm run build    # production build into admin/dist

# Release zip
bin/build-release.sh

The integration suites run against a real WordPress install rather than mocks, because many checks cross HTTP requests and WooCommerce order state. See tests/README.md in the repository for how to point them at a local site.