Loyalty apps charge monthly for two things: a points ledger and a redemption flow. Both run natively on Shopify primitives — metafields, webhooks, and the Discount API — with no per-order fees and full control of the rules. This is the four-piece build.
The balance is a number_integer metafield at loyalty.points_balance. Metafield values are strings in the API — "150", not 150 — and the write is one mutation:
mutation SetPointsBalance($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
metafields { id key value }
userErrors { field message }
}
}
A custom app subscribes to orders/paid. On each event it computes points from the order subtotal — 1 point per dollar is the common multiplier — reads the current balance, and writes the new one. Two rules keep the ledger honest: make the handler idempotent (Shopify redelivers webhooks; the same order must never accrue twice), and log every accrual with its order ID so any balance can be explained line by line.
A theme snippet or app-proxy page shows the balance from customer.metafields.loyalty.points_balance, earning history from the accrual log, and a redemption form. Gate redemption at a minimum (say 100 points) and cap it at the current balance — enforce both again server-side; the storefront check is a courtesy, the endpoint check is the rule.
mutation CreateRedemptionCode($basicCodeDiscount: DiscountCodeBasicInput!) {
discountCodeBasicCreate(basicCodeDiscount: $basicCodeDiscount) {
codeDiscountNode { id }
userErrors { field message }
}
}
The redemption endpoint validates the balance, creates the code — restricted to that customer, usage limit one, value set by your points-to-dollars rate — deducts the points via metafieldsSet, and returns the code. Order matters: create the code first, deduct second, and roll the deduction back if code creation fails. A customer should never lose points without holding a code.
A dashboard query aggregating balances outstanding, points issued versus redeemed, and codes generated tells the merchant what the program owes and whether it moves repeat purchases. Outstanding points are a real liability — the number belongs in front of whoever owns the P&L, not buried in metafields.
Referral tracking, VIP tier emails, and prebuilt integrations are genuine app value. The custom build wins when the rules are yours — odd multipliers, B2B exclusions, points tied to subscription renewals — or when the app bill outruns what the program uses. Migrating balances out of an existing app into metafields is the customer-import problem, and it's routine.
Email Tom the program rules — earn rate, redemption steps, tiers if any — and current monthly app cost. The build is quoted against what the program actually does.
Email Tom →