AstroBaaS

Pricing & promotions

Loyalty Rewards Program

Paid pluginsize Lplanned, not built

Indicative price, not an offer: EUR 250-400/year or EUR 500 one-off

Generated from docs/plan/paid/loyalty-rewards-program/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.

One-time buyers don’t return; merchants need to gamify repeat purchases. This paid feature adds a loyalty account per customer, point-earning on purchases (with tier multipliers), point redemption at checkout, tier progression (bronze/silver/gold), referral rewards, point expiry, and admin analytics.

The problem

Merchants have no mechanism to reward repeat customers, encourage referrals, or build retention. Customers make one-time purchases and leave. Revenue is transactional, not recurring. Competitors offer loyalty programs; merchants lose market share.

What it does

  • Loyalty account per customer: points balance, current tier, tier_updated_at, created_at
  • Award points on purchase: default 1 point per €1, configurable per product (multiplier)
  • Tier system: bronze (0-€1k spend), silver (€1k-€5k), gold (€5k+) with point multipliers (1x, 1.5x, 2x)
  • Redeem points at checkout: 100 points = €1 (configurable rate)
  • Point expiry policy: points expire after 2 years of inactivity (no purchase, no redemption)
  • Referral rewards: customer shares code, referred friend signs up and makes purchase; both get points
  • Birthday bonus: customer receives bonus points on their birthday month (if DOB stored)
  • Point ledger: track all point transactions (purchase, redemption, expiry, referral, birthday, admin adjustment)
  • Admin dashboard: top customers by points, tier breakdown, point distribution, revenue by tier
  • Customer self-service: view points, redeem, view tier status, view referral link

What it deliberately does NOT do

Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.

  • Does not build mobile app—storefront is web-based only; loyalty accessed via web UI
  • Does not gamify via leaderboards or social features—that’s engagement mechanics, out of scope
  • Does not auto-email point offers (e.g., ‘Redeem €5 today’)—that’s email marketing, separate
  • Does not integrate external loyalty networks (e.g., Shopify Points, Mileage Plus)—merchant’s responsibility
  • Does not support international tiering (different tiers per country/currency)—each shop is single currency; merchants manage regions separately
  • Does not auto-refund gift card balance or points on order refund—merchant decides policy; system tracks refunded amount but policy is manual

Data model

New entity: LoyaltyAccount with customer_id (FK, unique), points_balance (integer), tier (enum: bronze/silver/gold), tier_updated_at (timestamp), created_at. New entity: PointsLedger with account_id (FK), points_delta (signed integer, positive=earn, negative=redeem/expiry), reason (enum: purchase, redemption, expiry, referral, birthday, admin_adjustment), order_id (nullable FK), created_at. New entity: PointsExpiry with account_id, points_amount (integer), expires_at (timestamp; e.g., 2 years from last activity). New entity: Referral with referrer_customer_id (FK), referred_customer_id (nullable FK), referral_code (unique), referred_purchase_order_id (nullable FK), status (enum: pending, completed, failed), created_at. New field on Order: points_earned (integer), points_redeemed_value (integer minor units, e.g., customer redeemed 100 points = €10). New field on Product: points_multiplier (float, default 1.0). New field on Customer: loyalty_tier (denormalized from LoyaltyAccount for query performance), date_of_birth (date, nullable). Migration: create loyalty accounts for existing customers with points_balance=0, tier=bronze.

API

  • GET /store/api/loyalty/account — get customer’s LoyaltyAccount (points, tier, tier benefits)
  • POST /store/api/loyalty/redeem — redeem N points at checkout (POST body { points_to_redeem }; returns discount amount in minor units)
  • GET /store/api/loyalty/referral-link — get customer’s unique referral code and shareable link
  • POST /store/api/loyalty/refer — refer a friend (POST body { referred_email_or_phone }; sends invite with referral code embedded)
  • GET /admin/api/loyalty/ledger/:customer_id — view customer’s points ledger (all transactions, paginated)
  • GET /admin/api/loyalty/analytics — dashboard stats: total active points, tier breakdown (bronze/silver/gold counts), trending customers, points distribution
  • PUT /admin/api/loyalty/tiers/:tier/multiplier — set tier point multiplier (e.g., gold=2.0)
  • PUT /admin/api/loyalty/tiers/:tier/threshold — set tier threshold (e.g., silver=€1000 lifetime spend)
  • POST /admin/api/loyalty/account/:customer_id/adjust — admin manual point adjustment (POST body { points_delta, reason }; creates PointsLedger entry)

Every route added here must also appear in src/pages/openapi.json.ts — a test fails the build if it does not.

Admin

Loyalty dashboard: total active points pool, pie chart of tier breakdown (bronze/silver/gold), trending customers (top earners), point distribution histogram. Customer detail view: loyalty account (points, tier, tier status—e.g., ‘Silver, €2k of €5k to Gold’), referral history (code, referred customers, completions). Tier settings: edit point multipliers per tier, edit tier thresholds (spend amounts), edit redemption rate (e.g., 100 points = €1). Point adjustment UI: search customer, grant/deduct points, select reason, confirm.

The seam — why this is paid

Core owns: customer entity, order entity. Paid pack owns: loyalty account entity, point ledger, tier system, referral tracking, point expiry policies, analytics. Why: loyalty program is a merchant business-model choice; it requires sophisticated state tracking (point expiry, tier transitions, referral attribution, concurrent point deductions); support commitment includes point reconciliation (e.g., customer claims they lost points) and tier-dispute resolution.

Paid business model choice. Merchants decide whether loyalty program ROI justifies the implementation and operational overhead.

Dependencies

  • Customer entity must exist and support date_of_birth (nullable) field
  • Order entity must support points_earned and points_redeemed_value fields
  • Email system must exist (to send referral invites, point expiry warnings)
  • Scheduler must exist (to run point expiry checks, tier recalculation, birthday bonus grants)

Acceptance checks

Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.

  • Customer makes €100 purchase with standard 1 point/euro; LoyaltyAccount.points_balance = 100
  • Product configured with points_multiplier=2.0; customer buys €50 of that product; points earned = 100 (2x), total balance = 200
  • Customer has 500 points; redemption rate is 100 points = €10; redeem 500 points for €50 discount; points_balance = 0, order.points_redeemed_value = 5000 (minor units €50)
  • Customer lifetime spend = €10,000; tier promoted from silver to gold; multiplier changes 1.5x → 2x; next purchase earns 2x points
  • Customer lifetime spend drops below gold threshold (€5k); tier remains gold (no auto-demotion; manual or policy-driven only)
  • Referral: customer A creates referral code, shares with customer B; customer B signs up and makes €100 purchase; both A and B receive 50 bonus points
  • Referral: code created but referred customer never makes purchase; status=‘pending’; referrer does not receive points yet
  • Customer DOB = today; scheduler runs daily; customer receives 25 birthday bonus points (once per year)
  • Customer inactive (no purchase, no activity) for 2 years; 200 points expire; PointsLedger records expiry event, balance reduced to (balance - 200)
  • Admin adjusts customer points +100 with reason ‘customer service credit’; PointsLedger records adjustment, balance updated
  • Attempt to redeem more points than balance: request fails with ‘Insufficient points (have 50, need 100)’
  • Referral link unique per customer: two different customers generate referral links; links are different; each link correctly attributes to referrer

Risks

Point expiry calculation complexity: if 2-year window is from last purchase (not account creation), expiry logic becomes complex—define explicitly: inactivity means no purchase AND no redemption for 2 years. Tier demotion: if customer’s spend drops below tier threshold, auto-demotion can upset customers; recommend no auto-demotion, or announce with advance warning. Referral attribution: if referred customer signs up but doesn’t complete purchase for months, when are referrer points awarded—on sign-up or on purchase? Decide explicitly; recommend on-purchase to avoid abuse. Point ledger explosion: millions of transactions per large merchant—add archival/summary strategy (e.g., daily snapshot of balance, archive old ledger entries monthly). Concurrent point deduction (expired + redeemed same moment): use transaction isolation or pessimistic lock to prevent double-deduction. Birthday bonus logic: where is DOB stored? Add date_of_birth to Customer entity or Loyalty Profile? Decide. Birthday month vs. exact day? Recommend exact day but grant bonus for entire month window. Admin point adjustment audit trail: ensure all adjustments are logged with admin user, timestamp, and reason for compliance.

Commercial context

Suggested priceEUR 250-400/year or EUR 500 one-off
Rival anchorMageplaza Reward Points, bLoyal

The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.


Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full catalogue and ../../AI-GUIDE.md for how to work on this repository.