skills/pricing-promotions/loyalty-points-system/SKILL.md
Reward repeat customers with points they earn on every purchase, redeem for discounts, and accumulate to unlock higher-tier benefits
npx skillsauth add finsilabs/awesome-ecommerce-skills loyalty-points-systemInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A loyalty points program rewards repeat customers with points earned on purchases and other actions (reviews, referrals, social shares), which they can redeem for discounts at checkout. Well-designed programs include tier progression (Bronze → Silver → Gold → Platinum) that unlocks perks like free shipping or higher point multipliers as customers spend more. Most merchants should start with a dedicated loyalty app — the major platforms have excellent options — rather than building from scratch.
| Platform | Recommended Tool | Why | |----------|-----------------|-----| | Shopify | Smile.io (formerly Sweet Tooth) or LoyaltyLion | Both integrate natively with Shopify, handle points earning/redemption at checkout, and include tier management and referral programs | | Shopify (budget) | Rivo Loyalty & Rewards or Bon Loyalty | Lower-cost alternatives with core points and tier features | | WooCommerce | WooCommerce Points and Rewards (official, ~$79/year) or YITH WooCommerce Points & Rewards | WooCommerce-native integration; the official plugin is well-maintained | | BigCommerce | Smile.io or LoyaltyLion | Both have BigCommerce integrations | | Multi-platform / Headless | Loyaltylion API, Smile.io API, or Yotpo Loyalty | All expose APIs for integration with custom storefronts | | Custom / Headless | Build with ledger-based points tracking | Only when app capabilities are insufficient for your specific requirements |
Using Smile.io (most popular Shopify loyalty app):
Testing the integration:
Using WooCommerce Points and Rewards:
For tiers with WooCommerce: The official plugin does not include tier management. Options:
Using Smile.io on BigCommerce:
Using LoyaltyLion:
For headless storefronts or when app capabilities are insufficient, build a ledger-based points system. The ledger pattern (append-only transactions, no balance column) ensures accuracy, enables full audit trails, and makes point reversals trivial.
CREATE TABLE loyalty_accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
customer_id UUID NOT NULL UNIQUE,
tier VARCHAR(16) NOT NULL DEFAULT 'bronze',
lifetime_spend_cents INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE loyalty_ledger (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
account_id UUID NOT NULL REFERENCES loyalty_accounts(id),
points INTEGER NOT NULL, -- positive = earned, negative = redeemed/expired
type VARCHAR(32) NOT NULL CHECK (type IN ('purchase', 'bonus', 'referral', 'redemption', 'expiration', 'adjustment')),
reference_id UUID, -- order_id, review_id, etc.
description TEXT NOT NULL,
expires_at TIMESTAMPTZ, -- NULL = never expires
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
Points balance (excluding expired transactions):
async function getPointsBalance(accountId: string): Promise<number> {
const result = await db.raw(`
SELECT COALESCE(SUM(points), 0) AS balance
FROM loyalty_ledger
WHERE account_id = ?
AND (expires_at IS NULL OR expires_at > NOW())
`, [accountId]);
return Math.max(0, parseInt(result.rows[0].balance, 10));
}
Award points after order fulfillment (not at purchase — prevents points fraud from returns):
const TIER_MULTIPLIERS = { bronze: 1, silver: 1.25, gold: 1.5, platinum: 2 };
const POINTS_PER_DOLLAR = 1;
const EXPIRY_MONTHS = 12;
async function awardPurchasePoints(customerId: string, orderId: string, subtotalCents: number) {
const account = await getOrCreateAccount(customerId);
const multiplier = TIER_MULTIPLIERS[account.tier];
const points = Math.round((subtotalCents / 100) * POINTS_PER_DOLLAR * multiplier);
const expiresAt = new Date();
expiresAt.setMonth(expiresAt.getMonth() + EXPIRY_MONTHS);
await db.loyaltyLedger.insert({
account_id: account.id, points, type: 'purchase',
reference_id: orderId, description: `Points for order ${orderId}`, expires_at: expiresAt,
});
// Update lifetime spend and recalculate tier
await db.loyaltyAccounts.update(account.id, {
lifetime_spend_cents: account.lifetime_spend_cents + subtotalCents,
});
await recalculateTier(account.id);
}
Redeem points at checkout:
const REDEMPTION_RATE = 0.01; // 100 points = $1
async function redeemPoints(customerId: string, orderId: string, pointsToRedeem: number): Promise<{ discountCents: number }> {
const account = await db.loyaltyAccounts.findByCustomerId(customerId);
const balance = await getPointsBalance(account.id);
if (pointsToRedeem > balance) throw new Error('Insufficient points');
const discountCents = Math.floor(pointsToRedeem * REDEMPTION_RATE * 100);
await db.loyaltyLedger.insert({
account_id: account.id, points: -pointsToRedeem, type: 'redemption',
reference_id: orderId, description: `Redeemed for order ${orderId}`,
});
return { discountCents };
}
| Problem | Solution | |---------|----------| | Customer earns points on a returned order | Award points only after the return window closes, or reverse points when a return is processed | | Tier downgrade confuses customers | Only evaluate tier downgrades at pre-defined calendar dates (e.g., annually), not after each order; communicate the policy clearly | | Referral fraud — customers referring themselves | Validate referrals by checking that referrer and referee have different email addresses and/or billing addresses | | App stops awarding points after a platform update | Set up monitoring alerts on your loyalty app; test point awards weekly with an automated test order | | Points balance shown as zero on checkout due to sync lag | For custom builds, calculate balance in real-time from the ledger; for app-based programs, ensure the app is fully integrated with your checkout |
tools
Let shoppers save products to a wishlist, share it with friends, and get notified when saved items come back in stock or drop in price
development
Build a themeable storefront with design tokens and CSS custom properties that supports white-labeling, multi-brand variants, and dark mode
development
Speed up product discovery with instant search suggestions, fuzzy typo matching, and category-aware results powered by Algolia or Elasticsearch
development
Build a mobile-first storefront with thumb-friendly navigation, sticky add-to-cart buttons, and touch-optimized components for high mobile conversion