Pointz for Craft CMS

Free for Craft Commerce 5 · Pro $129

Loyalty points you can account for

WooCommerce shops have had Points and Rewards for a decade. Craft Commerce had nothing. Pointz adds earning rules a merchandiser can write, redemption at checkout that spends nothing until the order completes, and a ledger where every balance adds up to the lots behind it.

Pointz

Features

Built on one rule: balances are derived, never typed in. Anything that couldn't survive that rule didn't ship.

Balances are derived

Every movement is a row in an append-only ledger, and every credit is a lot with its own remaining figure and its own expiry date. A balance is the sum of those lots, and one console command rebuilds every balance in the store from them.

  • A correction is written as its opposite, never edited in place
  • The rebuild is safe to run at any time

Soonest expiry first

Spending takes the lots that expire soonest, and a lot that never expires is spent last. Spend in any other order and dated points lapse while the customer still had points to spare.

  • Every spend records which lots it drew on, and how much

Refunds that reverse exactly

Points an order spent go back into the lots they came from, with their original expiry. Points it earned are reversed pro rata and cumulatively, so two 20% refunds take 40%. This is in Lite: a free edition that over-awards is a bug, not an upsell.

  • Points already spent are reported as a shortfall, never a negative balance

Redemption that can't compound

The customer's request is stored as intent and re-clamped on every recalculation by an adjuster that runs last, after shipping, discounts and tax. Nothing is spent until the order completes, and then exactly what the adjustment says.

  • An abandoned cart costs the customer nothing
  • A rate change between cart and checkout can't alter the spend

Rules a merchandiser can write

A rate per unit spent or a fixed award, plus a signup bonus. Pro adds stacked and exclusive rules, per-line-item rules, product, order and customer conditions, campaign windows with multipliers, and per-rule and per-customer caps.

  • Installs with one rule per store, switched off
  • Awarding is idempotent: an order can't earn twice

Store credit, in Pro

A second balance in the store's own currency, moving through the same ledger, so it expires, reverses and audits the same way. Award it from a rule, grant it by hand, or issue it as a refund from the order panel.

  • Inactivity expiry, backfill with a dry run and an undo, CSV export
  • A dashboard widget for the unredeemed liability

A balance you can take apart

A customer's page from the control panel, running the ledger's own rules. Spend at checkout and see which lots it takes. Refund the order and the points go back into those same lots, expiry dates intact. Move the clock past an expiry first, and the refunded share comes back as a fresh lot instead.

Pointz → Balances → Dana Whitfield

Where the balance sits

Spending takes from whatever expires soonest, so this is the order it will go in.

LotFromRemainingOfEarnedExpires

History

WhenWhatAmountBalanceOrderNote

Complete the order, then refund #1042: the 800 points go back into the same three lots, with the same expiry dates. Spend again, sweep on 16 Oct 2026 and refund: the lot that expired can't be refilled, so its share comes back as a fresh lot. Refund 20% of #1019 twice and 360 points come back out in total, not 540.

Checkouts in this demo spend points but earn none, so every movement stays readable on one screen. Refunds follow the default settings: earned points are reversed in proportion to the refund, and points an order spent are handed back.

Nothing in Twig can move a balance

Every method on craft.pointz reads. A quote can't spend a point and a product-page preview can't award one, so the balance, the redemption form and the earn line can go anywhere. The preview runs the same rule engine as the real accrual, so the number on the product page is the number that lands in the ledger.

twig
{# Product page: the same rule engine the checkout runs #}
{% set award = craft.pointz.earnFor(product.defaultVariant) %}
{% if award.points > 0 %}
    <p>Earn {{ craft.pointz.formatted(award.points) }} with this order.</p>
{% endif %}

{# Cart: store the customer's intent. Nothing is spent until the order completes. #}
{% set quote = craft.pointz.quote() %}
{% if quote and quote.maxPoints > 0 %}
    <form method="post">
        {{ csrfInput() }}
        {{ actionInput('pointz/cart/redeem') }}
        <input type="number" name="points" max="{{ quote.maxPoints }}" value="{{ quote.points }}">
        <button>Apply</button>
    </form>

    <p>You have {{ craft.pointz.formatted(quote.pointsBalance) }}.</p>
    {% for notice in quote.notices %}
        <p>{{ notice }}</p>
    {% endfor %}
{% endif %}

Frequently Asked Questions

The questions worth answering before you install it.

Start free, upgrade when the scheme grows

Lite is free and runs a complete points scheme, refund reversal and expiry included. Pro is $129 with a $99/year renewal, and adds rule conditions, campaigns, caps, store credit, backfill and the liability widget.