Demo store: log in with demo / demo. No real orders are charged or shipped. Dismiss
Skip to contentPoints, vouchers and referrals for WooCommerce
For developers
The REST API, hooks, template overrides and database schema, for anyone extending or integrating with the plugin.
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.
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).
| Route | Access | What it does |
|---|---|---|
GET /dashboard | Customer | The logged-in customer’s balance, level and referral summary. |
GET /history | Customer | The logged-in customer’s points ledger. |
GET /coupons | Customer | The logged-in customer’s vouchers. |
POST /redeem | Customer | Exchange points for a voucher. |
POST /cart/redeem, POST /cart/apply | Customer | The “Pay with points” panel: redeem, then apply a voucher to the current cart. |
GET /checkin/status, POST /checkin/claim | Customer | The daily check-in bonus. |
GET/POST /settings | Admin | Every store setting (earning, redemption, bonuses, referrals, display). |
GET/POST /admin/earning-rules | Admin | Earning rules (order total, items in cart, product, category). |
GET /admin/overview | Admin | Store-wide loyalty metrics for the dashboard. |
GET /admin/users, GET /admin/users/{id} | Admin | The customer list and one customer’s full panel (balance, history, vouchers, referrals). |
POST /admin/users/{id}/adjustments | Admin | Manually add or remove a customer’s points, with a required reason. |
GET/POST/DELETE /admin/license | Admin | Activate, check or release the site’s licence key. |
GET/POST /admin/level-badges | Admin | Levels 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 — fire after something happens; use them to sync loyalty events to another system.
| Action | Arguments | Fires 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.
| Filter | Arguments | Changes |
|---|---|---|
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); 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.phpEach email template also has a plain/ counterpart for plain-text email clients.
| Shortcode | Shows |
|---|---|
[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).
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.
| Table | Columns |
|---|---|
wp_slw_wallet | user_id, total_points, earned_points, spent_points, pending_points, updated_at — one row per customer. |
wp_slw_points_log | id, 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_coupons | id, 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.
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.